diff options
author | Thomas Huth <thuth@redhat.com> | 2016-10-11 08:56:52 +0200 |
---|---|---|
committer | Thomas Huth <thuth@redhat.com> | 2016-12-20 21:52:12 +0100 |
commit | fcf5ef2ab52c621a4617ebbef36bf43b4003f4c0 (patch) | |
tree | 2b450d96b01455df8ed908bf8f26ddc388a03380 /target/ppc | |
parent | 82ecffa8c050bf5bbc13329e9b65eac1caa5b55c (diff) |
Move target-* CPU file into a target/ folder
We've currently got 18 architectures in QEMU, and thus 18 target-xxx
folders in the root folder of the QEMU source tree. More architectures
(e.g. RISC-V, AVR) are likely to be included soon, too, so the main
folder of the QEMU sources slowly gets quite overcrowded with the
target-xxx folders.
To disburden the main folder a little bit, let's move the target-xxx
folders into a dedicated target/ folder, so that target-xxx/ simply
becomes target/xxx/ instead.
Acked-by: Laurent Vivier <laurent@vivier.eu> [m68k part]
Acked-by: Bastian Koppelmann <kbastian@mail.uni-paderborn.de> [tricore part]
Acked-by: Michael Walle <michael@walle.cc> [lm32 part]
Acked-by: Cornelia Huck <cornelia.huck@de.ibm.com> [s390x part]
Reviewed-by: Christian Borntraeger <borntraeger@de.ibm.com> [s390x part]
Acked-by: Eduardo Habkost <ehabkost@redhat.com> [i386 part]
Acked-by: Artyom Tarasenko <atar4qemu@gmail.com> [sparc part]
Acked-by: Richard Henderson <rth@twiddle.net> [alpha part]
Acked-by: Max Filippov <jcmvbkbc@gmail.com> [xtensa part]
Reviewed-by: David Gibson <david@gibson.dropbear.id.au> [ppc part]
Acked-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> [crisµblaze part]
Acked-by: Guan Xuetao <gxt@mprc.pku.edu.cn> [unicore32 part]
Signed-off-by: Thomas Huth <thuth@redhat.com>
Diffstat (limited to 'target/ppc')
44 files changed, 48252 insertions, 0 deletions
diff --git a/target/ppc/Makefile.objs b/target/ppc/Makefile.objs new file mode 100644 index 0000000000..e667e69701 --- /dev/null +++ b/target/ppc/Makefile.objs @@ -0,0 +1,17 @@ +obj-y += cpu-models.o +obj-y += translate.o +ifeq ($(CONFIG_SOFTMMU),y) +obj-y += machine.o mmu_helper.o mmu-hash32.o monitor.o +obj-$(TARGET_PPC64) += mmu-hash64.o arch_dump.o +endif +obj-$(CONFIG_KVM) += kvm.o +obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o +obj-y += dfp_helper.o +obj-y += excp_helper.o +obj-y += fpu_helper.o +obj-y += int_helper.o +obj-y += timebase_helper.o +obj-y += misc_helper.o +obj-y += mem_helper.o +obj-$(CONFIG_USER_ONLY) += user_only_helper.o +obj-y += gdbstub.o diff --git a/target/ppc/STATUS b/target/ppc/STATUS new file mode 100644 index 0000000000..a4d48a7ca2 --- /dev/null +++ b/target/ppc/STATUS @@ -0,0 +1,550 @@ +PowerPC emulation status. +The goal of this file is to provide a reference status to avoid regressions. + +=============================================================================== +PowerPC core emulation status + +INSN: instruction set. + OK => all instructions are emulated + KO => some insns are missing or some should be removed + ? => unchecked +SPR: special purpose registers set + OK => all SPR registered (but some may be fake) + KO => some SPR are missing or should be removed + ? => unchecked +MSR: MSR bits definitions + OK => all MSR bits properly defined + KO => MSR definition is incorrect + ? => unchecked +IRQ: input signals definitions (mostly interrupts) + OK => input signals are properly defined + KO => input signals are not implemented (system emulation does not work) + ? => input signals definitions may be incorrect +MMU: MMU model implementation + OK => MMU model is implemented and Linux is able to boot + KO => MMU model not implemented or bugged + ? => MMU model not tested +EXCP: exceptions model implementation + OK => exception model is implemented and Linux is able to boot + KO => exception model not implemented or known to be buggy + ? => exception model may be incorrect or is untested + +Embedded PowerPC cores +*** +PowerPC 401: +INSN OK +SPR OK 401A1 +MSR OK +IRQ KO partially implemented +MMU OK +EXCP ? + +PowerPC 401x2: +INSN OK +SPR OK 401B2 401C2 401D2 401E2 401F2 +MSR OK +IRQ KO partially implemented +MMU OK +EXCP ? + +PowerPC IOP480: +INSN OK +SPR OK IOP480 +MSR OK +IRQ KO partially implemented +MMU OK +EXCP ? + +To be checked: 401G2 401B3 Cobra + +*** +PowerPC 403: +INSN OK +SPR OK 403GA 403GB +MMU OK +MSR OK +IRQ KO not implemented +EXCP ? + +PowerPC 403GCX: +INSN OK +SPR OK 403GCX +MMU OK +MSR OK +IRQ KO not implemented +EXCP ? + +To be checked: 403GC + +*** +PowerPC 405: +Checked: 405CRa 405CRb 405CRc 405EP 405GPa 405GPb 405GPc 405GPd 405GPe 405GPR + Npe405H Npe405H2 Npe405L +INSN OK +SPR OK +MSR OK +IRQ OK +MMU OK +EXCP OK +Remarks: Linux 2.4 boots (at least 1 proprietary firmware). + uboot seems to freeze at boot time. +To be checked: 405D2 405D4 405EZ 405LP Npe4GS3 STB03 STB04 STB25 + x2vp4 x2vp7 x2vp20 x2vp50 + +XXX: find what is IBM e407b4 + +*** +PowerPC 440: +Checked: 440EPa 440EPb 440GXa 440GXb 440GXc 440GXf 440SP 440SP2 +INSN OK +SPR OK +MSR OK +IRQ KO not implemented +MMU ? +EXCP ? + +PowerPC 440GP: +Checked: 440GPb 440GPc +INSN OK +SPR OK +MSR OK +IRQ KO not implemented +MMU ? +EXCP ? + +PowerPC 440x4: +Checked: 440A4 440B4 440G4 440H4 +INSN OK +SPR OK +MSR OK +IRQ KO not implemented +MMU ? +EXCP ? + +PowerPC 440x5: +Checked: 440A5 440F5 440G5 440H6 440GRa +INSN OK +SPR OK +MSR OK +IRQ KO not implemented +MMU ? +EXCP ? + +To be checked: 440EPx 440GRx 440SPE + +*** +PowerPC 460: (disabled: lack of detailed specifications) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +PowerPC 460F: (disabled: lack of detailed specifications) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +*** +PowerPC e200: (not implemented) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +*** +PowerPC e300: (not implemented) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +*** +PowerPC e500: (not implemented) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +*** +PowerPC e600: (not implemented) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +*** +32 bits PowerPC +PowerPC 601: (601 601v2) +INSN OK +SPR OK is HID15 only on 601v2 ? +MSR OK +IRQ KO not implemented +MMU ? +EXCP ? +Remarks: some instructions should have a specific behavior (not implemented) + +PowerPC 602: 602 +INSN OK +SPR OK +MSR OK +IRQ OK +MMU ? +EXCP ? at least timer and external interrupt are OK +Remarks: Linux 2.4 crashes when entering user-mode. + Linux 2.6.22 boots on this CPU but does not recognize it. + +PowerPC 603: (603) +INSN OK +SPR OK +MSR OK +IRQ OK +MMU OK +EXCP OK +Remarks: Linux 2.4 boots and properly recognizes the CPU + Linux 2.6.22 idem. + +PowerPC 603e: (603e11) +INSN OK +SPR OK +MSR OK +IRQ OK +MMU OK +EXCP OK +Remarks: Linux 2.4 boots and properly recognizes the CPU + Linux 2.6.22 idem. + +PowerPC G2: +INSN OK +SPR OK +MSR OK +IRQ OK +MMU OK +EXCP OK +Remarks: Linux 2.4 boots, recognizes the CPU as a 82xx. + Linux 2.6.22 idem. + +PowerPC G2le: +INSN OK +SPR OK +MSR OK +IRQ OK +MMU OK +EXCP OK +Remarks: Linux 2.4 does not boots. Same symptoms as 602. + Linux 2.6.22 boots and properly recognizes the CPU. + +PowerPC 604: +INSN OK +SPR OK +MSR OK +IRQ OK +MMU OK +EXCP OK +Remarks: Linux 2.4 boots and properly recognizes the CPU. + Linux 2.6.22 idem. + +PowerPC 7x0: +INSN OK +SPR OK +MSR OK +IRQ OK +MMU OK +EXCP OK +Remarks: Linux 2.4 boots and properly recognizes the CPU. + Linux 2.6.22 idem. + +PowerPC 750fx: +INSN OK +SPR OK +MSR OK +IRQ OK +MMU OK +EXCP OK +Remarks: Linux 2.4 boots but does not properly recognizes the CPU. + Linux 2.6.22 boots and properly recognizes the CPU. + +PowerPC 7x5: +INSN ? +SPR ? +MSR ? +IRQ OK +MMU ? +EXCP OK +Remarks: Linux 2.4 does not boot. + Linux 2.6.22 idem. + +PowerPC 7400: +INSN KO Altivec missing +SPR OK +MSR OK +IRQ OK +MMU OK +EXCP ? Altivec, ... +Remarks: Linux 2.4 boots and properly recognize the CPU. + Linux 2.6.22 idem. + +PowerPC 7410: +INSN KO Altivec missing +SPR OK +MSR OK +IRQ OK +MMU OK +EXCP ? Altivec, ... +Remarks: Linux 2.4 boots and properly recognize the CPU. + Linux 2.6.22 idem. + Note that UM says tlbld & tlbli are implemented but this may be a mistake + as TLB loads are managed by the hardware and the CPU does not implement the + needed registers. + +PowerPC 7441: +INSN KO Altivec missing +SPR OK +MSR OK +IRQ OK +MMU OK +EXCP ? Altivec, ... +Remarks: Linux does not have the code to handle TLB miss on this CPU + Linux 2.6.22 idem. + +PowerPC 7450/7451: +INSN KO Altivec missing +SPR OK +MSR OK +IRQ OK +MMU OK +EXCP ? Altivec, ... +Remarks: Linux does not have the code to handle TLB miss on this CPU + Linux 2.6.22 idem. + +PowerPC 7445/7447: +INSN KO Altivec missing +SPR OK +MSR OK +IRQ OK +MMU OK +EXCP ? Altivec, ... +Remarks: Linux does not have the code to handle TLB miss on this CPU + Linux 2.6.22 idem. + +PowerPC 7455/7457: +INSN KO Altivec missing +SPR OK +MSR OK +IRQ OK +MMU OK +EXCP ? Altivec, ... +Remarks: Linux does not have the code to handle TLB miss on this CPU + Linux 2.6.22 idem. + +64 bits PowerPC +PowerPC 620: (disabled) +INSN KO +SPR KO +MSR ? +IRQ KO +MMU KO +EXCP KO +Remarks: not much documentation for this implementation... + +PowerPC 970: +INSN KO Altivec missing and more +SPR KO +MSR ? +IRQ OK +MMU OK +EXCP KO partially implemented +Remarks: Should be able to boot but there is no hw platform currently emulated. + +PowerPC 970FX: +INSN KO Altivec missing and more +SPR KO +MSR ? +IRQ OK +MMU OK +EXCP KO partially implemented +Remarks: Should be able to boot but there is no hw platform currently emulated. + +PowerPC Cell: +INSN KO Altivec missing and more +SPR KO +MSR ? +IRQ ? +MMU ? +EXCP ? partially implemented +Remarks: As the core is mostly a 970, should be able to boot. + SPE are not implemented. + +PowerPC 630: (disabled: lack of detailed specifications) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +PowerPC 631: (disabled: lack of detailed specifications) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +POWER4: (disabled: lack of detailed specifications) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +POWER4+: (disabled: lack of detailed specifications) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +POWER5: (disabled: lack of detailed specifications) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +POWER5+: (disabled: lack of detailed specifications) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +POWER6: (disabled: lack of detailed specifications) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +RS64: (disabled: lack of detailed specifications) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +RS64-II: (disabled: lack of detailed specifications) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +RS64-III: (disabled: lack of detailed specifications) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +RS64-IV: (disabled: lack of detailed specifications) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +Original POWER +POWER: (disabled: lack of detailed specifications) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +POWER2: (disabled: lack of detailed specifications) +INSN KO +SPR KO +MSR KO +IRQ KO +MMU KO +EXCP KO + +=============================================================================== +PowerPC microcontrollers emulation status + +Implemementation should be sufficient to boot Linux: +(there seem to be problems with uboot freezing at some point) +- PowerPC 405CR +- PowerPC 405EP + +TODO: +- PowerPC 401 microcontrollers emulation +- PowerPC 403 microcontrollers emulation +- more PowerPC 405 microcontrollers emulation +- Fixes / more features for implemented PowerPC 405 microcontrollers emulation +- PowerPC 440 microcontrollers emulation +- e200 microcontrollers emulation +- e300 microcontrollers emulation +- e500 microcontrollers emulation +- e600 microcontrollers emulation + +=============================================================================== +PowerPC based platforms emulation status + +* PREP platform (RS/6000 7043...) - TO BE CHECKED (broken) +- Gentoo Linux live CDROM 1.4 +- Debian Linux 3.0 +- Mandrake Linux 9 + +* heathrow PowerMac platform (beige PowerMac) - TO BE CHECKED (broken) +- Gentoo Linux live CDROM 1.4 +- Debian Linux 3.0 +- Mandrake Linux 9 + +* mac99 platform (white and blue PowerMac, ...) +- Gentoo Linux live CDROM 1.4 - boots, compiles linux kernel +- Debian Linux woody - boots from CDROM and HDD +- Mandrake Linux 9 - boots from CDROM, freezes during install +- Knoppix 2003-07-13_4 boots from CDROM, pb with X configuration + distribution bug: X runs with a properly hand-coded configuration. +- rock Linux 2.0 runs from CDROM + +* Linux 2.6 support seems deadly broken (used to boot...). + +* PowerPC 405EP reference boards: +- can boot Linux 2.4 & 2.6. + Need to provide a flash image ready to boot for reproductible tests. + +TODO: +- URGENT: fix PreP and heathrow platforms +- PowerPC 64 reference platform +- MCA based RS/6000 emulation +- CHRP emulation (not PowerMac) +- PPAR emulation +- ePPAR emulation +- misc PowerPC reference boards emulation + +=============================================================================== diff --git a/target/ppc/arch_dump.c b/target/ppc/arch_dump.c new file mode 100644 index 0000000000..40282a1f50 --- /dev/null +++ b/target/ppc/arch_dump.c @@ -0,0 +1,286 @@ +/* + * writing ELF notes for ppc64 arch + * + * + * Copyright IBM, Corp. 2013 + * + * Authors: + * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "elf.h" +#include "exec/cpu-all.h" +#include "sysemu/dump.h" +#include "sysemu/kvm.h" + +struct PPC64UserRegStruct { + uint64_t gpr[32]; + uint64_t nip; + uint64_t msr; + uint64_t orig_gpr3; + uint64_t ctr; + uint64_t link; + uint64_t xer; + uint64_t ccr; + uint64_t softe; + uint64_t trap; + uint64_t dar; + uint64_t dsisr; + uint64_t result; +} QEMU_PACKED; + +struct PPC64ElfPrstatus { + char pad1[112]; + struct PPC64UserRegStruct pr_reg; + uint64_t pad2[4]; +} QEMU_PACKED; + + +struct PPC64ElfFpregset { + uint64_t fpr[32]; + uint64_t fpscr; +} QEMU_PACKED; + + +struct PPC64ElfVmxregset { + ppc_avr_t avr[32]; + ppc_avr_t vscr; + union { + ppc_avr_t unused; + uint32_t value; + } vrsave; +} QEMU_PACKED; + +struct PPC64ElfVsxregset { + uint64_t vsr[32]; +} QEMU_PACKED; + +struct PPC64ElfSperegset { + uint32_t evr[32]; + uint64_t spe_acc; + uint32_t spe_fscr; +} QEMU_PACKED; + +typedef struct noteStruct { + Elf64_Nhdr hdr; + char name[5]; + char pad3[3]; + union { + struct PPC64ElfPrstatus prstatus; + struct PPC64ElfFpregset fpregset; + struct PPC64ElfVmxregset vmxregset; + struct PPC64ElfVsxregset vsxregset; + struct PPC64ElfSperegset speregset; + } contents; +} QEMU_PACKED Note; + +typedef struct NoteFuncArg { + Note note; + DumpState *state; +} NoteFuncArg; + +static void ppc64_write_elf64_prstatus(NoteFuncArg *arg, PowerPCCPU *cpu) +{ + int i; + uint64_t cr; + struct PPC64ElfPrstatus *prstatus; + struct PPC64UserRegStruct *reg; + Note *note = &arg->note; + DumpState *s = arg->state; + + note->hdr.n_type = cpu_to_dump32(s, NT_PRSTATUS); + + prstatus = ¬e->contents.prstatus; + memset(prstatus, 0, sizeof(*prstatus)); + reg = &prstatus->pr_reg; + + for (i = 0; i < 32; i++) { + reg->gpr[i] = cpu_to_dump64(s, cpu->env.gpr[i]); + } + reg->nip = cpu_to_dump64(s, cpu->env.nip); + reg->msr = cpu_to_dump64(s, cpu->env.msr); + reg->ctr = cpu_to_dump64(s, cpu->env.ctr); + reg->link = cpu_to_dump64(s, cpu->env.lr); + reg->xer = cpu_to_dump64(s, cpu_read_xer(&cpu->env)); + + cr = 0; + for (i = 0; i < 8; i++) { + cr |= (cpu->env.crf[i] & 15) << (4 * (7 - i)); + } + reg->ccr = cpu_to_dump64(s, cr); +} + +static void ppc64_write_elf64_fpregset(NoteFuncArg *arg, PowerPCCPU *cpu) +{ + int i; + struct PPC64ElfFpregset *fpregset; + Note *note = &arg->note; + DumpState *s = arg->state; + + note->hdr.n_type = cpu_to_dump32(s, NT_PRFPREG); + + fpregset = ¬e->contents.fpregset; + memset(fpregset, 0, sizeof(*fpregset)); + + for (i = 0; i < 32; i++) { + fpregset->fpr[i] = cpu_to_dump64(s, cpu->env.fpr[i]); + } + fpregset->fpscr = cpu_to_dump64(s, cpu->env.fpscr); +} + +static void ppc64_write_elf64_vmxregset(NoteFuncArg *arg, PowerPCCPU *cpu) +{ + int i; + struct PPC64ElfVmxregset *vmxregset; + Note *note = &arg->note; + DumpState *s = arg->state; + + note->hdr.n_type = cpu_to_dump32(s, NT_PPC_VMX); + vmxregset = ¬e->contents.vmxregset; + memset(vmxregset, 0, sizeof(*vmxregset)); + + for (i = 0; i < 32; i++) { + bool needs_byteswap; + +#ifdef HOST_WORDS_BIGENDIAN + needs_byteswap = s->dump_info.d_endian == ELFDATA2LSB; +#else + needs_byteswap = s->dump_info.d_endian == ELFDATA2MSB; +#endif + + if (needs_byteswap) { + vmxregset->avr[i].u64[0] = bswap64(cpu->env.avr[i].u64[1]); + vmxregset->avr[i].u64[1] = bswap64(cpu->env.avr[i].u64[0]); + } else { + vmxregset->avr[i].u64[0] = cpu->env.avr[i].u64[0]; + vmxregset->avr[i].u64[1] = cpu->env.avr[i].u64[1]; + } + } + vmxregset->vscr.u32[3] = cpu_to_dump32(s, cpu->env.vscr); +} +static void ppc64_write_elf64_vsxregset(NoteFuncArg *arg, PowerPCCPU *cpu) +{ + int i; + struct PPC64ElfVsxregset *vsxregset; + Note *note = &arg->note; + DumpState *s = arg->state; + + note->hdr.n_type = cpu_to_dump32(s, NT_PPC_VSX); + vsxregset = ¬e->contents.vsxregset; + memset(vsxregset, 0, sizeof(*vsxregset)); + + for (i = 0; i < 32; i++) { + vsxregset->vsr[i] = cpu_to_dump64(s, cpu->env.vsr[i]); + } +} +static void ppc64_write_elf64_speregset(NoteFuncArg *arg, PowerPCCPU *cpu) +{ + struct PPC64ElfSperegset *speregset; + Note *note = &arg->note; + DumpState *s = arg->state; + + note->hdr.n_type = cpu_to_dump32(s, NT_PPC_SPE); + speregset = ¬e->contents.speregset; + memset(speregset, 0, sizeof(*speregset)); + + speregset->spe_acc = cpu_to_dump64(s, cpu->env.spe_acc); + speregset->spe_fscr = cpu_to_dump32(s, cpu->env.spe_fscr); +} + +static const struct NoteFuncDescStruct { + int contents_size; + void (*note_contents_func)(NoteFuncArg *arg, PowerPCCPU *cpu); +} note_func[] = { + {sizeof(((Note *)0)->contents.prstatus), ppc64_write_elf64_prstatus}, + {sizeof(((Note *)0)->contents.fpregset), ppc64_write_elf64_fpregset}, + {sizeof(((Note *)0)->contents.vmxregset), ppc64_write_elf64_vmxregset}, + {sizeof(((Note *)0)->contents.vsxregset), ppc64_write_elf64_vsxregset}, + {sizeof(((Note *)0)->contents.speregset), ppc64_write_elf64_speregset}, + { 0, NULL} +}; + +typedef struct NoteFuncDescStruct NoteFuncDesc; + +int cpu_get_dump_info(ArchDumpInfo *info, + const struct GuestPhysBlockList *guest_phys_blocks) +{ + PowerPCCPU *cpu = POWERPC_CPU(first_cpu); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + + info->d_machine = EM_PPC64; + info->d_class = ELFCLASS64; + if ((*pcc->interrupts_big_endian)(cpu)) { + info->d_endian = ELFDATA2MSB; + } else { + info->d_endian = ELFDATA2LSB; + } + /* 64KB is the max page size for pseries kernel */ + if (strncmp(object_get_typename(qdev_get_machine()), + "pseries-", 8) == 0) { + info->page_size = (1U << 16); + } + + return 0; +} + +ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) +{ + int name_size = 8; /* "CORE" or "QEMU" rounded */ + size_t elf_note_size = 0; + int note_head_size; + const NoteFuncDesc *nf; + + if (class != ELFCLASS64) { + return -1; + } + assert(machine == EM_PPC64); + + note_head_size = sizeof(Elf64_Nhdr); + + for (nf = note_func; nf->note_contents_func; nf++) { + elf_note_size = elf_note_size + note_head_size + name_size + + nf->contents_size; + } + + return (elf_note_size) * nr_cpus; +} + +static int ppc64_write_all_elf64_notes(const char *note_name, + WriteCoreDumpFunction f, + PowerPCCPU *cpu, int id, + void *opaque) +{ + NoteFuncArg arg = { .state = opaque }; + int ret = -1; + int note_size; + const NoteFuncDesc *nf; + + for (nf = note_func; nf->note_contents_func; nf++) { + arg.note.hdr.n_namesz = cpu_to_dump32(opaque, sizeof(arg.note.name)); + arg.note.hdr.n_descsz = cpu_to_dump32(opaque, nf->contents_size); + strncpy(arg.note.name, note_name, sizeof(arg.note.name)); + + (*nf->note_contents_func)(&arg, cpu); + + note_size = + sizeof(arg.note) - sizeof(arg.note.contents) + nf->contents_size; + ret = f(&arg.note, note_size, opaque); + if (ret < 0) { + return -1; + } + } + return 0; +} + +int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, + int cpuid, void *opaque) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + return ppc64_write_all_elf64_notes("CORE", f, cpu, cpuid, opaque); +} diff --git a/target/ppc/cpu-models.c b/target/ppc/cpu-models.c new file mode 100644 index 0000000000..506dee1ee8 --- /dev/null +++ b/target/ppc/cpu-models.c @@ -0,0 +1,1426 @@ +/* + * PowerPC CPU initialization for qemu. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * Copyright 2011 Freescale Semiconductor, Inc. + * Copyright 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/>. + */ + +/* A lot of PowerPC definition have been included here. + * Most of them are not usable for now but have been kept + * inside "#if defined(TODO) ... #endif" statements to make tests easier. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "cpu-models.h" + +#if defined(CONFIG_USER_ONLY) +#define TODO_USER_ONLY 1 +#endif + +/***************************************************************************/ +/* PowerPC CPU definitions */ +#define POWERPC_DEF_PREFIX(pvr, svr, type) \ + glue(glue(glue(glue(pvr, _), svr), _), type) +#define POWERPC_DEF_SVR(_name, _desc, _pvr, _svr, _type) \ + static void \ + glue(POWERPC_DEF_PREFIX(_pvr, _svr, _type), _cpu_class_init) \ + (ObjectClass *oc, void *data) \ + { \ + DeviceClass *dc = DEVICE_CLASS(oc); \ + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); \ + \ + pcc->pvr = _pvr; \ + pcc->svr = _svr; \ + dc->desc = _desc; \ + } \ + \ + static const TypeInfo \ + glue(POWERPC_DEF_PREFIX(_pvr, _svr, _type), _cpu_type_info) = { \ + .name = _name "-" TYPE_POWERPC_CPU, \ + .parent = stringify(_type) "-family-" TYPE_POWERPC_CPU, \ + .class_init = \ + glue(POWERPC_DEF_PREFIX(_pvr, _svr, _type), _cpu_class_init), \ + }; \ + \ + static void \ + glue(POWERPC_DEF_PREFIX(_pvr, _svr, _type), _cpu_register_types)(void) \ + { \ + type_register_static( \ + &glue(POWERPC_DEF_PREFIX(_pvr, _svr, _type), _cpu_type_info)); \ + } \ + \ + type_init( \ + glue(POWERPC_DEF_PREFIX(_pvr, _svr, _type), _cpu_register_types)) + +#define POWERPC_DEF(_name, _pvr, _type, _desc) \ + POWERPC_DEF_SVR(_name, _desc, _pvr, POWERPC_SVR_NONE, _type) + + /* Embedded PowerPC */ + /* PowerPC 401 family */ + POWERPC_DEF("401", CPU_POWERPC_401, 401, + "Generic PowerPC 401") + /* PowerPC 401 cores */ + POWERPC_DEF("401A1", CPU_POWERPC_401A1, 401, + "PowerPC 401A1") + POWERPC_DEF("401B2", CPU_POWERPC_401B2, 401x2, + "PowerPC 401B2") +#if defined(TODO) + POWERPC_DEF("401B3", CPU_POWERPC_401B3, 401x3, + "PowerPC 401B3") +#endif + POWERPC_DEF("401C2", CPU_POWERPC_401C2, 401x2, + "PowerPC 401C2") + POWERPC_DEF("401D2", CPU_POWERPC_401D2, 401x2, + "PowerPC 401D2") + POWERPC_DEF("401E2", CPU_POWERPC_401E2, 401x2, + "PowerPC 401E2") + POWERPC_DEF("401F2", CPU_POWERPC_401F2, 401x2, + "PowerPC 401F2") + /* XXX: to be checked */ + POWERPC_DEF("401G2", CPU_POWERPC_401G2, 401x2, + "PowerPC 401G2") + /* PowerPC 401 microcontrollers */ +#if defined(TODO) + POWERPC_DEF("401GF", CPU_POWERPC_401GF, 401, + "PowerPC 401GF") +#endif + POWERPC_DEF("IOP480", CPU_POWERPC_IOP480, IOP480, + "IOP480 (401 microcontroller)") + POWERPC_DEF("Cobra", CPU_POWERPC_COBRA, 401, + "IBM Processor for Network Resources") +#if defined(TODO) + POWERPC_DEF("Xipchip", CPU_POWERPC_XIPCHIP, 401, + NULL) +#endif + /* PowerPC 403 family */ + /* PowerPC 403 microcontrollers */ + POWERPC_DEF("403GA", CPU_POWERPC_403GA, 403, + "PowerPC 403 GA") + POWERPC_DEF("403GB", CPU_POWERPC_403GB, 403, + "PowerPC 403 GB") + POWERPC_DEF("403GC", CPU_POWERPC_403GC, 403, + "PowerPC 403 GC") + POWERPC_DEF("403GCX", CPU_POWERPC_403GCX, 403GCX, + "PowerPC 403 GCX") +#if defined(TODO) + POWERPC_DEF("403GP", CPU_POWERPC_403GP, 403, + "PowerPC 403 GP") +#endif + /* PowerPC 405 family */ + /* PowerPC 405 cores */ +#if defined(TODO) + POWERPC_DEF("405A3", CPU_POWERPC_405A3, 405, + "PowerPC 405 A3") +#endif +#if defined(TODO) + POWERPC_DEF("405A4", CPU_POWERPC_405A4, 405, + "PowerPC 405 A4") +#endif +#if defined(TODO) + POWERPC_DEF("405B3", CPU_POWERPC_405B3, 405, + "PowerPC 405 B3") +#endif +#if defined(TODO) + POWERPC_DEF("405B4", CPU_POWERPC_405B4, 405, + "PowerPC 405 B4") +#endif +#if defined(TODO) + POWERPC_DEF("405C3", CPU_POWERPC_405C3, 405, + "PowerPC 405 C3") +#endif +#if defined(TODO) + POWERPC_DEF("405C4", CPU_POWERPC_405C4, 405, + "PowerPC 405 C4") +#endif + POWERPC_DEF("405D2", CPU_POWERPC_405D2, 405, + "PowerPC 405 D2") +#if defined(TODO) + POWERPC_DEF("405D3", CPU_POWERPC_405D3, 405, + "PowerPC 405 D3") +#endif + POWERPC_DEF("405D4", CPU_POWERPC_405D4, 405, + "PowerPC 405 D4") +#if defined(TODO) + POWERPC_DEF("405D5", CPU_POWERPC_405D5, 405, + "PowerPC 405 D5") +#endif +#if defined(TODO) + POWERPC_DEF("405E4", CPU_POWERPC_405E4, 405, + "PowerPC 405 E4") +#endif +#if defined(TODO) + POWERPC_DEF("405F4", CPU_POWERPC_405F4, 405, + "PowerPC 405 F4") +#endif +#if defined(TODO) + POWERPC_DEF("405F5", CPU_POWERPC_405F5, 405, + "PowerPC 405 F5") +#endif +#if defined(TODO) + POWERPC_DEF("405F6", CPU_POWERPC_405F6, 405, + "PowerPC 405 F6") +#endif + /* PowerPC 405 microcontrollers */ + POWERPC_DEF("405CRa", CPU_POWERPC_405CRa, 405, + "PowerPC 405 CRa") + POWERPC_DEF("405CRb", CPU_POWERPC_405CRb, 405, + "PowerPC 405 CRb") + POWERPC_DEF("405CRc", CPU_POWERPC_405CRc, 405, + "PowerPC 405 CRc") + POWERPC_DEF("405EP", CPU_POWERPC_405EP, 405, + "PowerPC 405 EP") +#if defined(TODO) + POWERPC_DEF("405EXr", CPU_POWERPC_405EXr, 405, + "PowerPC 405 EXr") +#endif + POWERPC_DEF("405EZ", CPU_POWERPC_405EZ, 405, + "PowerPC 405 EZ") +#if defined(TODO) + POWERPC_DEF("405FX", CPU_POWERPC_405FX, 405, + "PowerPC 405 FX") +#endif + POWERPC_DEF("405GPa", CPU_POWERPC_405GPa, 405, + "PowerPC 405 GPa") + POWERPC_DEF("405GPb", CPU_POWERPC_405GPb, 405, + "PowerPC 405 GPb") + POWERPC_DEF("405GPc", CPU_POWERPC_405GPc, 405, + "PowerPC 405 GPc") + POWERPC_DEF("405GPd", CPU_POWERPC_405GPd, 405, + "PowerPC 405 GPd") + POWERPC_DEF("405GPR", CPU_POWERPC_405GPR, 405, + "PowerPC 405 GPR") +#if defined(TODO) + POWERPC_DEF("405H", CPU_POWERPC_405H, 405, + "PowerPC 405 H") +#endif +#if defined(TODO) + POWERPC_DEF("405L", CPU_POWERPC_405L, 405, + "PowerPC 405 L") +#endif + POWERPC_DEF("405LP", CPU_POWERPC_405LP, 405, + "PowerPC 405 LP") +#if defined(TODO) + POWERPC_DEF("405PM", CPU_POWERPC_405PM, 405, + "PowerPC 405 PM") +#endif +#if defined(TODO) + POWERPC_DEF("405PS", CPU_POWERPC_405PS, 405, + "PowerPC 405 PS") +#endif +#if defined(TODO) + POWERPC_DEF("405S", CPU_POWERPC_405S, 405, + "PowerPC 405 S") +#endif + POWERPC_DEF("Npe405H", CPU_POWERPC_NPE405H, 405, + "Npe405 H") + POWERPC_DEF("Npe405H2", CPU_POWERPC_NPE405H2, 405, + "Npe405 H2") + POWERPC_DEF("Npe405L", CPU_POWERPC_NPE405L, 405, + "Npe405 L") + POWERPC_DEF("Npe4GS3", CPU_POWERPC_NPE4GS3, 405, + "Npe4GS3") +#if defined(TODO) + POWERPC_DEF("Npcxx1", CPU_POWERPC_NPCxx1, 405, + NULL) +#endif +#if defined(TODO) + POWERPC_DEF("Npr161", CPU_POWERPC_NPR161, 405, + NULL) +#endif +#if defined(TODO) + POWERPC_DEF("LC77700", CPU_POWERPC_LC77700, 405, + "PowerPC LC77700 (Sanyo)") +#endif + /* PowerPC 401/403/405 based set-top-box microcontrollers */ +#if defined(TODO) + POWERPC_DEF("STB01000", CPU_POWERPC_STB01000, 401x2, + "STB010000") +#endif +#if defined(TODO) + POWERPC_DEF("STB01010", CPU_POWERPC_STB01010, 401x2, + "STB01010") +#endif +#if defined(TODO) + POWERPC_DEF("STB0210", CPU_POWERPC_STB0210, 401x3, + "STB0210") +#endif + POWERPC_DEF("STB03", CPU_POWERPC_STB03, 405, + "STB03xx") +#if defined(TODO) + POWERPC_DEF("STB043", CPU_POWERPC_STB043, 405, + "STB043x") +#endif +#if defined(TODO) + POWERPC_DEF("STB045", CPU_POWERPC_STB045, 405, + "STB045x") +#endif + POWERPC_DEF("STB04", CPU_POWERPC_STB04, 405, + "STB04xx") + POWERPC_DEF("STB25", CPU_POWERPC_STB25, 405, + "STB25xx") +#if defined(TODO) + POWERPC_DEF("STB130", CPU_POWERPC_STB130, 405, + "STB130") +#endif + /* Xilinx PowerPC 405 cores */ + POWERPC_DEF("x2vp4", CPU_POWERPC_X2VP4, 405, + NULL) + POWERPC_DEF("x2vp20", CPU_POWERPC_X2VP20, 405, + NULL) +#if defined(TODO) + POWERPC_DEF("zl10310", CPU_POWERPC_ZL10310, 405, + "Zarlink ZL10310") +#endif +#if defined(TODO) + POWERPC_DEF("zl10311", CPU_POWERPC_ZL10311, 405, + "Zarlink ZL10311") +#endif +#if defined(TODO) + POWERPC_DEF("zl10320", CPU_POWERPC_ZL10320, 405, + "Zarlink ZL10320") +#endif +#if defined(TODO) + POWERPC_DEF("zl10321", CPU_POWERPC_ZL10321, 405, + "Zarlink ZL10321") +#endif + /* PowerPC 440 family */ +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440", CPU_POWERPC_440, 440GP, + "Generic PowerPC 440") +#endif + /* PowerPC 440 cores */ +#if defined(TODO) + POWERPC_DEF("440A4", CPU_POWERPC_440A4, 440x4, + "PowerPC 440 A4") +#endif + POWERPC_DEF("440-Xilinx", CPU_POWERPC_440_XILINX, 440x5, + "PowerPC 440 Xilinx 5") + + POWERPC_DEF("440-Xilinx-w-dfpu", CPU_POWERPC_440_XILINX, 440x5wDFPU, + "PowerPC 440 Xilinx 5 With a Double Prec. FPU") +#if defined(TODO) + POWERPC_DEF("440A5", CPU_POWERPC_440A5, 440x5, + "PowerPC 440 A5") +#endif +#if defined(TODO) + POWERPC_DEF("440B4", CPU_POWERPC_440B4, 440x4, + "PowerPC 440 B4") +#endif +#if defined(TODO) + POWERPC_DEF("440G4", CPU_POWERPC_440G4, 440x4, + "PowerPC 440 G4") +#endif +#if defined(TODO) + POWERPC_DEF("440F5", CPU_POWERPC_440F5, 440x5, + "PowerPC 440 F5") +#endif +#if defined(TODO) + POWERPC_DEF("440G5", CPU_POWERPC_440G5, 440x5, + "PowerPC 440 G5") +#endif +#if defined(TODO) + POWERPC_DEF("440H4", CPU_POWERPC_440H4, 440x4, + "PowerPC 440H4") +#endif +#if defined(TODO) + POWERPC_DEF("440H6", CPU_POWERPC_440H6, 440Gx5, + "PowerPC 440H6") +#endif + /* PowerPC 440 microcontrollers */ + POWERPC_DEF("440EPa", CPU_POWERPC_440EPa, 440EP, + "PowerPC 440 EPa") + POWERPC_DEF("440EPb", CPU_POWERPC_440EPb, 440EP, + "PowerPC 440 EPb") + POWERPC_DEF("440EPX", CPU_POWERPC_440EPX, 440EP, + "PowerPC 440 EPX") +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440GPb", CPU_POWERPC_440GPb, 440GP, + "PowerPC 440 GPb") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440GPc", CPU_POWERPC_440GPc, 440GP, + "PowerPC 440 GPc") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440GRa", CPU_POWERPC_440GRa, 440x5, + "PowerPC 440 GRa") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440GRX", CPU_POWERPC_440GRX, 440x5, + "PowerPC 440 GRX") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440GXa", CPU_POWERPC_440GXa, 440EP, + "PowerPC 440 GXa") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440GXb", CPU_POWERPC_440GXb, 440EP, + "PowerPC 440 GXb") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440GXc", CPU_POWERPC_440GXc, 440EP, + "PowerPC 440 GXc") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440GXf", CPU_POWERPC_440GXf, 440EP, + "PowerPC 440 GXf") +#endif +#if defined(TODO) + POWERPC_DEF("440S", CPU_POWERPC_440S, 440, + "PowerPC 440 S") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440SP", CPU_POWERPC_440SP, 440EP, + "PowerPC 440 SP") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440SP2", CPU_POWERPC_440SP2, 440EP, + "PowerPC 440 SP2") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440SPE", CPU_POWERPC_440SPE, 440EP, + "PowerPC 440 SPE") +#endif + /* PowerPC 460 family */ +#if defined(TODO) + POWERPC_DEF("464", CPU_POWERPC_464, 460, + "Generic PowerPC 464") +#endif + /* PowerPC 464 microcontrollers */ +#if defined(TODO) + POWERPC_DEF("464H90", CPU_POWERPC_464H90, 460, + "PowerPC 464H90") +#endif +#if defined(TODO) + POWERPC_DEF("464H90F", CPU_POWERPC_464H90F, 460F, + "PowerPC 464H90F") +#endif + /* Freescale embedded PowerPC cores */ + /* MPC5xx family (aka RCPU) */ +#if defined(TODO_USER_ONLY) + POWERPC_DEF("MPC5xx", CPU_POWERPC_MPC5xx, MPC5xx, + "Generic MPC5xx core") +#endif + /* MPC8xx family (aka PowerQUICC) */ +#if defined(TODO_USER_ONLY) + POWERPC_DEF("MPC8xx", CPU_POWERPC_MPC8xx, MPC8xx, + "Generic MPC8xx core") +#endif + /* MPC82xx family (aka PowerQUICC-II) */ + POWERPC_DEF("G2", CPU_POWERPC_G2, G2, + "PowerPC G2 core") + POWERPC_DEF("G2H4", CPU_POWERPC_G2H4, G2, + "PowerPC G2 H4 core") + POWERPC_DEF("G2GP", CPU_POWERPC_G2gp, G2, + "PowerPC G2 GP core") + POWERPC_DEF("G2LS", CPU_POWERPC_G2ls, G2, + "PowerPC G2 LS core") + POWERPC_DEF("G2HiP3", CPU_POWERPC_G2_HIP3, G2, + "PowerPC G2 HiP3 core") + POWERPC_DEF("G2HiP4", CPU_POWERPC_G2_HIP4, G2, + "PowerPC G2 HiP4 core") + POWERPC_DEF("MPC603", CPU_POWERPC_MPC603, 603E, + "PowerPC MPC603 core") + POWERPC_DEF("G2le", CPU_POWERPC_G2LE, G2LE, + "PowerPC G2le core (same as G2 plus little-endian mode support)") + POWERPC_DEF("G2leGP", CPU_POWERPC_G2LEgp, G2LE, + "PowerPC G2LE GP core") + POWERPC_DEF("G2leLS", CPU_POWERPC_G2LEls, G2LE, + "PowerPC G2LE LS core") + POWERPC_DEF("G2leGP1", CPU_POWERPC_G2LEgp1, G2LE, + "PowerPC G2LE GP1 core") + POWERPC_DEF("G2leGP3", CPU_POWERPC_G2LEgp3, G2LE, + "PowerPC G2LE GP3 core") + /* PowerPC G2 microcontrollers */ +#if defined(TODO) + POWERPC_DEF_SVR("MPC5121", "MPC5121", + CPU_POWERPC_MPC5121, POWERPC_SVR_5121, G2LE) +#endif + POWERPC_DEF_SVR("MPC5200_v10", "MPC5200 v1.0", + CPU_POWERPC_MPC5200_v10, POWERPC_SVR_5200_v10, G2LE) + POWERPC_DEF_SVR("MPC5200_v11", "MPC5200 v1.1", + CPU_POWERPC_MPC5200_v11, POWERPC_SVR_5200_v11, G2LE) + POWERPC_DEF_SVR("MPC5200_v12", "MPC5200 v1.2", + CPU_POWERPC_MPC5200_v12, POWERPC_SVR_5200_v12, G2LE) + POWERPC_DEF_SVR("MPC5200B_v20", "MPC5200B v2.0", + CPU_POWERPC_MPC5200B_v20, POWERPC_SVR_5200B_v20, G2LE) + POWERPC_DEF_SVR("MPC5200B_v21", "MPC5200B v2.1", + CPU_POWERPC_MPC5200B_v21, POWERPC_SVR_5200B_v21, G2LE) + /* e200 family */ +#if defined(TODO) + POWERPC_DEF_SVR("MPC55xx", "Generic MPC55xx core", + CPU_POWERPC_MPC55xx, POWERPC_SVR_55xx, e200) +#endif +#if defined(TODO) + POWERPC_DEF("e200z0", CPU_POWERPC_e200z0, e200, + "PowerPC e200z0 core") +#endif +#if defined(TODO) + POWERPC_DEF("e200z1", CPU_POWERPC_e200z1, e200, + "PowerPC e200z1 core") +#endif +#if defined(TODO) + POWERPC_DEF("e200z3", CPU_POWERPC_e200z3, e200, + "PowerPC e200z3 core") +#endif + POWERPC_DEF("e200z5", CPU_POWERPC_e200z5, e200, + "PowerPC e200z5 core") + POWERPC_DEF("e200z6", CPU_POWERPC_e200z6, e200, + "PowerPC e200z6 core") + /* PowerPC e200 microcontrollers */ +#if defined(TODO) + POWERPC_DEF_SVR("MPC5514E", "MPC5514E", + CPU_POWERPC_MPC5514E, POWERPC_SVR_5514E, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5514E_v0", "MPC5514E v0", + CPU_POWERPC_MPC5514E_v0, POWERPC_SVR_5514E_v0, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5514E_v1", "MPC5514E v1", + CPU_POWERPC_MPC5514E_v1, POWERPC_SVR_5514E_v1, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5514G", "MPC5514G", + CPU_POWERPC_MPC5514G, POWERPC_SVR_5514G, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5514G_v0", "MPC5514G v0", + CPU_POWERPC_MPC5514G_v0, POWERPC_SVR_5514G_v0, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5514G_v1", "MPC5514G v1", + CPU_POWERPC_MPC5514G_v1, POWERPC_SVR_5514G_v1, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5515S", "MPC5515S", + CPU_POWERPC_MPC5515S, POWERPC_SVR_5515S, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5516E", "MPC5516E", + CPU_POWERPC_MPC5516E, POWERPC_SVR_5516E, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5516E_v0", "MPC5516E v0", + CPU_POWERPC_MPC5516E_v0, POWERPC_SVR_5516E_v0, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5516E_v1", "MPC5516E v1", + CPU_POWERPC_MPC5516E_v1, POWERPC_SVR_5516E_v1, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5516G", "MPC5516G", + CPU_POWERPC_MPC5516G, POWERPC_SVR_5516G, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5516G_v0", "MPC5516G v0", + CPU_POWERPC_MPC5516G_v0, POWERPC_SVR_5516G_v0, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5516G_v1", "MPC5516G v1", + CPU_POWERPC_MPC5516G_v1, POWERPC_SVR_5516G_v1, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5516S", "MPC5516S", + CPU_POWERPC_MPC5516S, POWERPC_SVR_5516S, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5533", "MPC5533", + CPU_POWERPC_MPC5533, POWERPC_SVR_5533, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5534", "MPC5534", + CPU_POWERPC_MPC5534, POWERPC_SVR_5534, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5553", "MPC5553", + CPU_POWERPC_MPC5553, POWERPC_SVR_5553, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5554", "MPC5554", + CPU_POWERPC_MPC5554, POWERPC_SVR_5554, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5561", "MPC5561", + CPU_POWERPC_MPC5561, POWERPC_SVR_5561, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5565", "MPC5565", + CPU_POWERPC_MPC5565, POWERPC_SVR_5565, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5566", "MPC5566", + CPU_POWERPC_MPC5566, POWERPC_SVR_5566, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5567", "MPC5567", + CPU_POWERPC_MPC5567, POWERPC_SVR_5567, e200) +#endif + /* e300 family */ + POWERPC_DEF("e300c1", CPU_POWERPC_e300c1, e300, + "PowerPC e300c1 core") + POWERPC_DEF("e300c2", CPU_POWERPC_e300c2, e300, + "PowerPC e300c2 core") + POWERPC_DEF("e300c3", CPU_POWERPC_e300c3, e300, + "PowerPC e300c3 core") + POWERPC_DEF("e300c4", CPU_POWERPC_e300c4, e300, + "PowerPC e300c4 core") + /* PowerPC e300 microcontrollers */ +#if defined(TODO) + POWERPC_DEF_SVR("MPC8313", "MPC8313", + CPU_POWERPC_MPC831x, POWERPC_SVR_8313, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8313E", "MPC8313E", + CPU_POWERPC_MPC831x, POWERPC_SVR_8313E, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8314", "MPC8314", + CPU_POWERPC_MPC831x, POWERPC_SVR_8314, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8314E", "MPC8314E", + CPU_POWERPC_MPC831x, POWERPC_SVR_8314E, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8315", "MPC8315", + CPU_POWERPC_MPC831x, POWERPC_SVR_8315, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8315E", "MPC8315E", + CPU_POWERPC_MPC831x, POWERPC_SVR_8315E, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8321", "MPC8321", + CPU_POWERPC_MPC832x, POWERPC_SVR_8321, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8321E", "MPC8321E", + CPU_POWERPC_MPC832x, POWERPC_SVR_8321E, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8323", "MPC8323", + CPU_POWERPC_MPC832x, POWERPC_SVR_8323, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8323E", "MPC8323E", + CPU_POWERPC_MPC832x, POWERPC_SVR_8323E, e300) +#endif + POWERPC_DEF_SVR("MPC8343", "MPC8343", + CPU_POWERPC_MPC834x, POWERPC_SVR_8343, e300) + POWERPC_DEF_SVR("MPC8343A", "MPC8343A", + CPU_POWERPC_MPC834x, POWERPC_SVR_8343A, e300) + POWERPC_DEF_SVR("MPC8343E", "MPC8343E", + CPU_POWERPC_MPC834x, POWERPC_SVR_8343E, e300) + POWERPC_DEF_SVR("MPC8343EA", "MPC8343EA", + CPU_POWERPC_MPC834x, POWERPC_SVR_8343EA, e300) + POWERPC_DEF_SVR("MPC8347T", "MPC8347T", + CPU_POWERPC_MPC834x, POWERPC_SVR_8347T, e300) + POWERPC_DEF_SVR("MPC8347P", "MPC8347P", + CPU_POWERPC_MPC834x, POWERPC_SVR_8347P, e300) + POWERPC_DEF_SVR("MPC8347AT", "MPC8347AT", + CPU_POWERPC_MPC834x, POWERPC_SVR_8347AT, e300) + POWERPC_DEF_SVR("MPC8347AP", "MPC8347AP", + CPU_POWERPC_MPC834x, POWERPC_SVR_8347AP, e300) + POWERPC_DEF_SVR("MPC8347ET", "MPC8347ET", + CPU_POWERPC_MPC834x, POWERPC_SVR_8347ET, e300) + POWERPC_DEF_SVR("MPC8347EP", "MPC8343EP", + CPU_POWERPC_MPC834x, POWERPC_SVR_8347EP, e300) + POWERPC_DEF_SVR("MPC8347EAT", "MPC8347EAT", + CPU_POWERPC_MPC834x, POWERPC_SVR_8347EAT, e300) + POWERPC_DEF_SVR("MPC8347EAP", "MPC8343EAP", + CPU_POWERPC_MPC834x, POWERPC_SVR_8347EAP, e300) + POWERPC_DEF_SVR("MPC8349", "MPC8349", + CPU_POWERPC_MPC834x, POWERPC_SVR_8349, e300) + POWERPC_DEF_SVR("MPC8349A", "MPC8349A", + CPU_POWERPC_MPC834x, POWERPC_SVR_8349A, e300) + POWERPC_DEF_SVR("MPC8349E", "MPC8349E", + CPU_POWERPC_MPC834x, POWERPC_SVR_8349E, e300) + POWERPC_DEF_SVR("MPC8349EA", "MPC8349EA", + CPU_POWERPC_MPC834x, POWERPC_SVR_8349EA, e300) +#if defined(TODO) + POWERPC_DEF_SVR("MPC8358E", "MPC8358E", + CPU_POWERPC_MPC835x, POWERPC_SVR_8358E, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8360E", "MPC8360E", + CPU_POWERPC_MPC836x, POWERPC_SVR_8360E, e300) +#endif + POWERPC_DEF_SVR("MPC8377", "MPC8377", + CPU_POWERPC_MPC837x, POWERPC_SVR_8377, e300) + POWERPC_DEF_SVR("MPC8377E", "MPC8377E", + CPU_POWERPC_MPC837x, POWERPC_SVR_8377E, e300) + POWERPC_DEF_SVR("MPC8378", "MPC8378", + CPU_POWERPC_MPC837x, POWERPC_SVR_8378, e300) + POWERPC_DEF_SVR("MPC8378E", "MPC8378E", + CPU_POWERPC_MPC837x, POWERPC_SVR_8378E, e300) + POWERPC_DEF_SVR("MPC8379", "MPC8379", + CPU_POWERPC_MPC837x, POWERPC_SVR_8379, e300) + POWERPC_DEF_SVR("MPC8379E", "MPC8379E", + CPU_POWERPC_MPC837x, POWERPC_SVR_8379E, e300) + /* e500 family */ + POWERPC_DEF_SVR("e500_v10", "PowerPC e500 v1.0 core", + CPU_POWERPC_e500v1_v10, POWERPC_SVR_E500, e500v1); + POWERPC_DEF_SVR("e500_v20", "PowerPC e500 v2.0 core", + CPU_POWERPC_e500v1_v20, POWERPC_SVR_E500, e500v1); + POWERPC_DEF_SVR("e500v2_v10", "PowerPC e500v2 v1.0 core", + CPU_POWERPC_e500v2_v10, POWERPC_SVR_E500, e500v2); + POWERPC_DEF_SVR("e500v2_v20", "PowerPC e500v2 v2.0 core", + CPU_POWERPC_e500v2_v20, POWERPC_SVR_E500, e500v2); + POWERPC_DEF_SVR("e500v2_v21", "PowerPC e500v2 v2.1 core", + CPU_POWERPC_e500v2_v21, POWERPC_SVR_E500, e500v2); + POWERPC_DEF_SVR("e500v2_v22", "PowerPC e500v2 v2.2 core", + CPU_POWERPC_e500v2_v22, POWERPC_SVR_E500, e500v2); + POWERPC_DEF_SVR("e500v2_v30", "PowerPC e500v2 v3.0 core", + CPU_POWERPC_e500v2_v30, POWERPC_SVR_E500, e500v2); + POWERPC_DEF_SVR("e500mc", "e500mc", + CPU_POWERPC_e500mc, POWERPC_SVR_E500, e500mc) +#ifdef TARGET_PPC64 + POWERPC_DEF_SVR("e5500", "e5500", + CPU_POWERPC_e5500, POWERPC_SVR_E500, e5500) +#endif + /* PowerPC e500 microcontrollers */ + POWERPC_DEF_SVR("MPC8533_v10", "MPC8533 v1.0", + CPU_POWERPC_MPC8533_v10, POWERPC_SVR_8533_v10, e500v2) + POWERPC_DEF_SVR("MPC8533_v11", "MPC8533 v1.1", + CPU_POWERPC_MPC8533_v11, POWERPC_SVR_8533_v11, e500v2) + POWERPC_DEF_SVR("MPC8533E_v10", "MPC8533E v1.0", + CPU_POWERPC_MPC8533E_v10, POWERPC_SVR_8533E_v10, e500v2) + POWERPC_DEF_SVR("MPC8533E_v11", "MPC8533E v1.1", + CPU_POWERPC_MPC8533E_v11, POWERPC_SVR_8533E_v11, e500v2) + POWERPC_DEF_SVR("MPC8540_v10", "MPC8540 v1.0", + CPU_POWERPC_MPC8540_v10, POWERPC_SVR_8540_v10, e500v1) + POWERPC_DEF_SVR("MPC8540_v20", "MPC8540 v2.0", + CPU_POWERPC_MPC8540_v20, POWERPC_SVR_8540_v20, e500v1) + POWERPC_DEF_SVR("MPC8540_v21", "MPC8540 v2.1", + CPU_POWERPC_MPC8540_v21, POWERPC_SVR_8540_v21, e500v1) + POWERPC_DEF_SVR("MPC8541_v10", "MPC8541 v1.0", + CPU_POWERPC_MPC8541_v10, POWERPC_SVR_8541_v10, e500v1) + POWERPC_DEF_SVR("MPC8541_v11", "MPC8541 v1.1", + CPU_POWERPC_MPC8541_v11, POWERPC_SVR_8541_v11, e500v1) + POWERPC_DEF_SVR("MPC8541E_v10", "MPC8541E v1.0", + CPU_POWERPC_MPC8541E_v10, POWERPC_SVR_8541E_v10, e500v1) + POWERPC_DEF_SVR("MPC8541E_v11", "MPC8541E v1.1", + CPU_POWERPC_MPC8541E_v11, POWERPC_SVR_8541E_v11, e500v1) + POWERPC_DEF_SVR("MPC8543_v10", "MPC8543 v1.0", + CPU_POWERPC_MPC8543_v10, POWERPC_SVR_8543_v10, e500v2) + POWERPC_DEF_SVR("MPC8543_v11", "MPC8543 v1.1", + CPU_POWERPC_MPC8543_v11, POWERPC_SVR_8543_v11, e500v2) + POWERPC_DEF_SVR("MPC8543_v20", "MPC8543 v2.0", + CPU_POWERPC_MPC8543_v20, POWERPC_SVR_8543_v20, e500v2) + POWERPC_DEF_SVR("MPC8543_v21", "MPC8543 v2.1", + CPU_POWERPC_MPC8543_v21, POWERPC_SVR_8543_v21, e500v2) + POWERPC_DEF_SVR("MPC8543E_v10", "MPC8543E v1.0", + CPU_POWERPC_MPC8543E_v10, POWERPC_SVR_8543E_v10, e500v2) + POWERPC_DEF_SVR("MPC8543E_v11", "MPC8543E v1.1", + CPU_POWERPC_MPC8543E_v11, POWERPC_SVR_8543E_v11, e500v2) + POWERPC_DEF_SVR("MPC8543E_v20", "MPC8543E v2.0", + CPU_POWERPC_MPC8543E_v20, POWERPC_SVR_8543E_v20, e500v2) + POWERPC_DEF_SVR("MPC8543E_v21", "MPC8543E v2.1", + CPU_POWERPC_MPC8543E_v21, POWERPC_SVR_8543E_v21, e500v2) + POWERPC_DEF_SVR("MPC8544_v10", "MPC8544 v1.0", + CPU_POWERPC_MPC8544_v10, POWERPC_SVR_8544_v10, e500v2) + POWERPC_DEF_SVR("MPC8544_v11", "MPC8544 v1.1", + CPU_POWERPC_MPC8544_v11, POWERPC_SVR_8544_v11, e500v2) + POWERPC_DEF_SVR("MPC8544E_v10", "MPC8544E v1.0", + CPU_POWERPC_MPC8544E_v10, POWERPC_SVR_8544E_v10, e500v2) + POWERPC_DEF_SVR("MPC8544E_v11", "MPC8544E v1.1", + CPU_POWERPC_MPC8544E_v11, POWERPC_SVR_8544E_v11, e500v2) + POWERPC_DEF_SVR("MPC8545_v20", "MPC8545 v2.0", + CPU_POWERPC_MPC8545_v20, POWERPC_SVR_8545_v20, e500v2) + POWERPC_DEF_SVR("MPC8545_v21", "MPC8545 v2.1", + CPU_POWERPC_MPC8545_v21, POWERPC_SVR_8545_v21, e500v2) + POWERPC_DEF_SVR("MPC8545E_v20", "MPC8545E v2.0", + CPU_POWERPC_MPC8545E_v20, POWERPC_SVR_8545E_v20, e500v2) + POWERPC_DEF_SVR("MPC8545E_v21", "MPC8545E v2.1", + CPU_POWERPC_MPC8545E_v21, POWERPC_SVR_8545E_v21, e500v2) + POWERPC_DEF_SVR("MPC8547E_v20", "MPC8547E v2.0", + CPU_POWERPC_MPC8547E_v20, POWERPC_SVR_8547E_v20, e500v2) + POWERPC_DEF_SVR("MPC8547E_v21", "MPC8547E v2.1", + CPU_POWERPC_MPC8547E_v21, POWERPC_SVR_8547E_v21, e500v2) + POWERPC_DEF_SVR("MPC8548_v10", "MPC8548 v1.0", + CPU_POWERPC_MPC8548_v10, POWERPC_SVR_8548_v10, e500v2) + POWERPC_DEF_SVR("MPC8548_v11", "MPC8548 v1.1", + CPU_POWERPC_MPC8548_v11, POWERPC_SVR_8548_v11, e500v2) + POWERPC_DEF_SVR("MPC8548_v20", "MPC8548 v2.0", + CPU_POWERPC_MPC8548_v20, POWERPC_SVR_8548_v20, e500v2) + POWERPC_DEF_SVR("MPC8548_v21", "MPC8548 v2.1", + CPU_POWERPC_MPC8548_v21, POWERPC_SVR_8548_v21, e500v2) + POWERPC_DEF_SVR("MPC8548E_v10", "MPC8548E v1.0", + CPU_POWERPC_MPC8548E_v10, POWERPC_SVR_8548E_v10, e500v2) + POWERPC_DEF_SVR("MPC8548E_v11", "MPC8548E v1.1", + CPU_POWERPC_MPC8548E_v11, POWERPC_SVR_8548E_v11, e500v2) + POWERPC_DEF_SVR("MPC8548E_v20", "MPC8548E v2.0", + CPU_POWERPC_MPC8548E_v20, POWERPC_SVR_8548E_v20, e500v2) + POWERPC_DEF_SVR("MPC8548E_v21", "MPC8548E v2.1", + CPU_POWERPC_MPC8548E_v21, POWERPC_SVR_8548E_v21, e500v2) + POWERPC_DEF_SVR("MPC8555_v10", "MPC8555 v1.0", + CPU_POWERPC_MPC8555_v10, POWERPC_SVR_8555_v10, e500v2) + POWERPC_DEF_SVR("MPC8555_v11", "MPC8555 v1.1", + CPU_POWERPC_MPC8555_v11, POWERPC_SVR_8555_v11, e500v2) + POWERPC_DEF_SVR("MPC8555E_v10", "MPC8555E v1.0", + CPU_POWERPC_MPC8555E_v10, POWERPC_SVR_8555E_v10, e500v2) + POWERPC_DEF_SVR("MPC8555E_v11", "MPC8555E v1.1", + CPU_POWERPC_MPC8555E_v11, POWERPC_SVR_8555E_v11, e500v2) + POWERPC_DEF_SVR("MPC8560_v10", "MPC8560 v1.0", + CPU_POWERPC_MPC8560_v10, POWERPC_SVR_8560_v10, e500v2) + POWERPC_DEF_SVR("MPC8560_v20", "MPC8560 v2.0", + CPU_POWERPC_MPC8560_v20, POWERPC_SVR_8560_v20, e500v2) + POWERPC_DEF_SVR("MPC8560_v21", "MPC8560 v2.1", + CPU_POWERPC_MPC8560_v21, POWERPC_SVR_8560_v21, e500v2) + POWERPC_DEF_SVR("MPC8567", "MPC8567", + CPU_POWERPC_MPC8567, POWERPC_SVR_8567, e500v2) + POWERPC_DEF_SVR("MPC8567E", "MPC8567E", + CPU_POWERPC_MPC8567E, POWERPC_SVR_8567E, e500v2) + POWERPC_DEF_SVR("MPC8568", "MPC8568", + CPU_POWERPC_MPC8568, POWERPC_SVR_8568, e500v2) + POWERPC_DEF_SVR("MPC8568E", "MPC8568E", + CPU_POWERPC_MPC8568E, POWERPC_SVR_8568E, e500v2) + POWERPC_DEF_SVR("MPC8572", "MPC8572", + CPU_POWERPC_MPC8572, POWERPC_SVR_8572, e500v2) + POWERPC_DEF_SVR("MPC8572E", "MPC8572E", + CPU_POWERPC_MPC8572E, POWERPC_SVR_8572E, e500v2) + /* e600 family */ + POWERPC_DEF("e600", CPU_POWERPC_e600, e600, + "PowerPC e600 core") + /* PowerPC e600 microcontrollers */ + POWERPC_DEF_SVR("MPC8610", "MPC8610", + CPU_POWERPC_MPC8610, POWERPC_SVR_8610, e600) + POWERPC_DEF_SVR("MPC8641", "MPC8641", + CPU_POWERPC_MPC8641, POWERPC_SVR_8641, e600) + POWERPC_DEF_SVR("MPC8641D", "MPC8641D", + CPU_POWERPC_MPC8641D, POWERPC_SVR_8641D, e600) + /* 32 bits "classic" PowerPC */ + /* PowerPC 6xx family */ + POWERPC_DEF("601_v0", CPU_POWERPC_601_v0, 601, + "PowerPC 601v0") + POWERPC_DEF("601_v1", CPU_POWERPC_601_v1, 601, + "PowerPC 601v1") + POWERPC_DEF("601_v2", CPU_POWERPC_601_v2, 601v, + "PowerPC 601v2") + POWERPC_DEF("602", CPU_POWERPC_602, 602, + "PowerPC 602") + POWERPC_DEF("603", CPU_POWERPC_603, 603, + "PowerPC 603") + POWERPC_DEF("603e_v1.1", CPU_POWERPC_603E_v11, 603E, + "PowerPC 603e v1.1") + POWERPC_DEF("603e_v1.2", CPU_POWERPC_603E_v12, 603E, + "PowerPC 603e v1.2") + POWERPC_DEF("603e_v1.3", CPU_POWERPC_603E_v13, 603E, + "PowerPC 603e v1.3") + POWERPC_DEF("603e_v1.4", CPU_POWERPC_603E_v14, 603E, + "PowerPC 603e v1.4") + POWERPC_DEF("603e_v2.2", CPU_POWERPC_603E_v22, 603E, + "PowerPC 603e v2.2") + POWERPC_DEF("603e_v3", CPU_POWERPC_603E_v3, 603E, + "PowerPC 603e v3") + POWERPC_DEF("603e_v4", CPU_POWERPC_603E_v4, 603E, + "PowerPC 603e v4") + POWERPC_DEF("603e_v4.1", CPU_POWERPC_603E_v41, 603E, + "PowerPC 603e v4.1") + POWERPC_DEF("603e7", CPU_POWERPC_603E7, 603E, + "PowerPC 603e (aka PID7)") + POWERPC_DEF("603e7t", CPU_POWERPC_603E7t, 603E, + "PowerPC 603e7t") + POWERPC_DEF("603e7v", CPU_POWERPC_603E7v, 603E, + "PowerPC 603e7v") + POWERPC_DEF("603e7v1", CPU_POWERPC_603E7v1, 603E, + "PowerPC 603e7v1") + POWERPC_DEF("603e7v2", CPU_POWERPC_603E7v2, 603E, + "PowerPC 603e7v2") + POWERPC_DEF("603p", CPU_POWERPC_603P, 603E, + "PowerPC 603p (aka PID7v)") + POWERPC_DEF("604", CPU_POWERPC_604, 604, + "PowerPC 604") + POWERPC_DEF("604e_v1.0", CPU_POWERPC_604E_v10, 604E, + "PowerPC 604e v1.0") + POWERPC_DEF("604e_v2.2", CPU_POWERPC_604E_v22, 604E, + "PowerPC 604e v2.2") + POWERPC_DEF("604e_v2.4", CPU_POWERPC_604E_v24, 604E, + "PowerPC 604e v2.4") + POWERPC_DEF("604r", CPU_POWERPC_604R, 604E, + "PowerPC 604r (aka PIDA)") +#if defined(TODO) + POWERPC_DEF("604ev", CPU_POWERPC_604EV, 604E, + "PowerPC 604ev") +#endif + /* PowerPC 7xx family */ + POWERPC_DEF("740_v1.0", CPU_POWERPC_7x0_v10, 740, + "PowerPC 740 v1.0 (G3)") + POWERPC_DEF("750_v1.0", CPU_POWERPC_7x0_v10, 750, + "PowerPC 750 v1.0 (G3)") + POWERPC_DEF("740_v2.0", CPU_POWERPC_7x0_v20, 740, + "PowerPC 740 v2.0 (G3)") + POWERPC_DEF("750_v2.0", CPU_POWERPC_7x0_v20, 750, + "PowerPC 750 v2.0 (G3)") + POWERPC_DEF("740_v2.1", CPU_POWERPC_7x0_v21, 740, + "PowerPC 740 v2.1 (G3)") + POWERPC_DEF("750_v2.1", CPU_POWERPC_7x0_v21, 750, + "PowerPC 750 v2.1 (G3)") + POWERPC_DEF("740_v2.2", CPU_POWERPC_7x0_v22, 740, + "PowerPC 740 v2.2 (G3)") + POWERPC_DEF("750_v2.2", CPU_POWERPC_7x0_v22, 750, + "PowerPC 750 v2.2 (G3)") + POWERPC_DEF("740_v3.0", CPU_POWERPC_7x0_v30, 740, + "PowerPC 740 v3.0 (G3)") + POWERPC_DEF("750_v3.0", CPU_POWERPC_7x0_v30, 750, + "PowerPC 750 v3.0 (G3)") + POWERPC_DEF("740_v3.1", CPU_POWERPC_7x0_v31, 740, + "PowerPC 740 v3.1 (G3)") + POWERPC_DEF("750_v3.1", CPU_POWERPC_7x0_v31, 750, + "PowerPC 750 v3.1 (G3)") + POWERPC_DEF("740e", CPU_POWERPC_740E, 740, + "PowerPC 740E (G3)") + POWERPC_DEF("750e", CPU_POWERPC_750E, 750, + "PowerPC 750E (G3)") + POWERPC_DEF("740p", CPU_POWERPC_7x0P, 740, + "PowerPC 740P (G3)") + POWERPC_DEF("750p", CPU_POWERPC_7x0P, 750, + "PowerPC 750P (G3)") + POWERPC_DEF("750cl_v1.0", CPU_POWERPC_750CL_v10, 750cl, + "PowerPC 750CL v1.0") + POWERPC_DEF("750cl_v2.0", CPU_POWERPC_750CL_v20, 750cl, + "PowerPC 750CL v2.0") + POWERPC_DEF("750cx_v1.0", CPU_POWERPC_750CX_v10, 750cx, + "PowerPC 750CX v1.0 (G3 embedded)") + POWERPC_DEF("750cx_v2.0", CPU_POWERPC_750CX_v20, 750cx, + "PowerPC 750CX v2.1 (G3 embedded)") + POWERPC_DEF("750cx_v2.1", CPU_POWERPC_750CX_v21, 750cx, + "PowerPC 750CX v2.1 (G3 embedded)") + POWERPC_DEF("750cx_v2.2", CPU_POWERPC_750CX_v22, 750cx, + "PowerPC 750CX v2.2 (G3 embedded)") + POWERPC_DEF("750cxe_v2.1", CPU_POWERPC_750CXE_v21, 750cx, + "PowerPC 750CXe v2.1 (G3 embedded)") + POWERPC_DEF("750cxe_v2.2", CPU_POWERPC_750CXE_v22, 750cx, + "PowerPC 750CXe v2.2 (G3 embedded)") + POWERPC_DEF("750cxe_v2.3", CPU_POWERPC_750CXE_v23, 750cx, + "PowerPC 750CXe v2.3 (G3 embedded)") + POWERPC_DEF("750cxe_v2.4", CPU_POWERPC_750CXE_v24, 750cx, + "PowerPC 750CXe v2.4 (G3 embedded)") + POWERPC_DEF("750cxe_v2.4b", CPU_POWERPC_750CXE_v24b, 750cx, + "PowerPC 750CXe v2.4b (G3 embedded)") + POWERPC_DEF("750cxe_v3.0", CPU_POWERPC_750CXE_v30, 750cx, + "PowerPC 750CXe v3.0 (G3 embedded)") + POWERPC_DEF("750cxe_v3.1", CPU_POWERPC_750CXE_v31, 750cx, + "PowerPC 750CXe v3.1 (G3 embedded)") + POWERPC_DEF("750cxe_v3.1b", CPU_POWERPC_750CXE_v31b, 750cx, + "PowerPC 750CXe v3.1b (G3 embedded)") + POWERPC_DEF("750cxr", CPU_POWERPC_750CXR, 750cx, + "PowerPC 750CXr (G3 embedded)") + POWERPC_DEF("750fl", CPU_POWERPC_750FL, 750fx, + "PowerPC 750FL (G3 embedded)") + POWERPC_DEF("750fx_v1.0", CPU_POWERPC_750FX_v10, 750fx, + "PowerPC 750FX v1.0 (G3 embedded)") + POWERPC_DEF("750fx_v2.0", CPU_POWERPC_750FX_v20, 750fx, + "PowerPC 750FX v2.0 (G3 embedded)") + POWERPC_DEF("750fx_v2.1", CPU_POWERPC_750FX_v21, 750fx, + "PowerPC 750FX v2.1 (G3 embedded)") + POWERPC_DEF("750fx_v2.2", CPU_POWERPC_750FX_v22, 750fx, + "PowerPC 750FX v2.2 (G3 embedded)") + POWERPC_DEF("750fx_v2.3", CPU_POWERPC_750FX_v23, 750fx, + "PowerPC 750FX v2.3 (G3 embedded)") + POWERPC_DEF("750gl", CPU_POWERPC_750GL, 750gx, + "PowerPC 750GL (G3 embedded)") + POWERPC_DEF("750gx_v1.0", CPU_POWERPC_750GX_v10, 750gx, + "PowerPC 750GX v1.0 (G3 embedded)") + POWERPC_DEF("750gx_v1.1", CPU_POWERPC_750GX_v11, 750gx, + "PowerPC 750GX v1.1 (G3 embedded)") + POWERPC_DEF("750gx_v1.2", CPU_POWERPC_750GX_v12, 750gx, + "PowerPC 750GX v1.2 (G3 embedded)") + POWERPC_DEF("750l_v2.0", CPU_POWERPC_750L_v20, 750, + "PowerPC 750L v2.0 (G3 embedded)") + POWERPC_DEF("750l_v2.1", CPU_POWERPC_750L_v21, 750, + "PowerPC 750L v2.1 (G3 embedded)") + POWERPC_DEF("750l_v2.2", CPU_POWERPC_750L_v22, 750, + "PowerPC 750L v2.2 (G3 embedded)") + POWERPC_DEF("750l_v3.0", CPU_POWERPC_750L_v30, 750, + "PowerPC 750L v3.0 (G3 embedded)") + POWERPC_DEF("750l_v3.2", CPU_POWERPC_750L_v32, 750, + "PowerPC 750L v3.2 (G3 embedded)") + POWERPC_DEF("745_v1.0", CPU_POWERPC_7x5_v10, 745, + "PowerPC 745 v1.0") + POWERPC_DEF("755_v1.0", CPU_POWERPC_7x5_v10, 755, + "PowerPC 755 v1.0") + POWERPC_DEF("745_v1.1", CPU_POWERPC_7x5_v11, 745, + "PowerPC 745 v1.1") + POWERPC_DEF("755_v1.1", CPU_POWERPC_7x5_v11, 755, + "PowerPC 755 v1.1") + POWERPC_DEF("745_v2.0", CPU_POWERPC_7x5_v20, 745, + "PowerPC 745 v2.0") + POWERPC_DEF("755_v2.0", CPU_POWERPC_7x5_v20, 755, + "PowerPC 755 v2.0") + POWERPC_DEF("745_v2.1", CPU_POWERPC_7x5_v21, 745, + "PowerPC 745 v2.1") + POWERPC_DEF("755_v2.1", CPU_POWERPC_7x5_v21, 755, + "PowerPC 755 v2.1") + POWERPC_DEF("745_v2.2", CPU_POWERPC_7x5_v22, 745, + "PowerPC 745 v2.2") + POWERPC_DEF("755_v2.2", CPU_POWERPC_7x5_v22, 755, + "PowerPC 755 v2.2") + POWERPC_DEF("745_v2.3", CPU_POWERPC_7x5_v23, 745, + "PowerPC 745 v2.3") + POWERPC_DEF("755_v2.3", CPU_POWERPC_7x5_v23, 755, + "PowerPC 755 v2.3") + POWERPC_DEF("745_v2.4", CPU_POWERPC_7x5_v24, 745, + "PowerPC 745 v2.4") + POWERPC_DEF("755_v2.4", CPU_POWERPC_7x5_v24, 755, + "PowerPC 755 v2.4") + POWERPC_DEF("745_v2.5", CPU_POWERPC_7x5_v25, 745, + "PowerPC 745 v2.5") + POWERPC_DEF("755_v2.5", CPU_POWERPC_7x5_v25, 755, + "PowerPC 755 v2.5") + POWERPC_DEF("745_v2.6", CPU_POWERPC_7x5_v26, 745, + "PowerPC 745 v2.6") + POWERPC_DEF("755_v2.6", CPU_POWERPC_7x5_v26, 755, + "PowerPC 755 v2.6") + POWERPC_DEF("745_v2.7", CPU_POWERPC_7x5_v27, 745, + "PowerPC 745 v2.7") + POWERPC_DEF("755_v2.7", CPU_POWERPC_7x5_v27, 755, + "PowerPC 755 v2.7") + POWERPC_DEF("745_v2.8", CPU_POWERPC_7x5_v28, 745, + "PowerPC 745 v2.8") + POWERPC_DEF("755_v2.8", CPU_POWERPC_7x5_v28, 755, + "PowerPC 755 v2.8") +#if defined(TODO) + POWERPC_DEF("745p", CPU_POWERPC_7x5P, 745, + "PowerPC 745P (G3)") + POWERPC_DEF("755p", CPU_POWERPC_7x5P, 755, + "PowerPC 755P (G3)") +#endif + /* PowerPC 74xx family */ + POWERPC_DEF("7400_v1.0", CPU_POWERPC_7400_v10, 7400, + "PowerPC 7400 v1.0 (G4)") + POWERPC_DEF("7400_v1.1", CPU_POWERPC_7400_v11, 7400, + "PowerPC 7400 v1.1 (G4)") + POWERPC_DEF("7400_v2.0", CPU_POWERPC_7400_v20, 7400, + "PowerPC 7400 v2.0 (G4)") + POWERPC_DEF("7400_v2.1", CPU_POWERPC_7400_v21, 7400, + "PowerPC 7400 v2.1 (G4)") + POWERPC_DEF("7400_v2.2", CPU_POWERPC_7400_v22, 7400, + "PowerPC 7400 v2.2 (G4)") + POWERPC_DEF("7400_v2.6", CPU_POWERPC_7400_v26, 7400, + "PowerPC 7400 v2.6 (G4)") + POWERPC_DEF("7400_v2.7", CPU_POWERPC_7400_v27, 7400, + "PowerPC 7400 v2.7 (G4)") + POWERPC_DEF("7400_v2.8", CPU_POWERPC_7400_v28, 7400, + "PowerPC 7400 v2.8 (G4)") + POWERPC_DEF("7400_v2.9", CPU_POWERPC_7400_v29, 7400, + "PowerPC 7400 v2.9 (G4)") + POWERPC_DEF("7410_v1.0", CPU_POWERPC_7410_v10, 7410, + "PowerPC 7410 v1.0 (G4)") + POWERPC_DEF("7410_v1.1", CPU_POWERPC_7410_v11, 7410, + "PowerPC 7410 v1.1 (G4)") + POWERPC_DEF("7410_v1.2", CPU_POWERPC_7410_v12, 7410, + "PowerPC 7410 v1.2 (G4)") + POWERPC_DEF("7410_v1.3", CPU_POWERPC_7410_v13, 7410, + "PowerPC 7410 v1.3 (G4)") + POWERPC_DEF("7410_v1.4", CPU_POWERPC_7410_v14, 7410, + "PowerPC 7410 v1.4 (G4)") + POWERPC_DEF("7448_v1.0", CPU_POWERPC_7448_v10, 7400, + "PowerPC 7448 v1.0 (G4)") + POWERPC_DEF("7448_v1.1", CPU_POWERPC_7448_v11, 7400, + "PowerPC 7448 v1.1 (G4)") + POWERPC_DEF("7448_v2.0", CPU_POWERPC_7448_v20, 7400, + "PowerPC 7448 v2.0 (G4)") + POWERPC_DEF("7448_v2.1", CPU_POWERPC_7448_v21, 7400, + "PowerPC 7448 v2.1 (G4)") + POWERPC_DEF("7450_v1.0", CPU_POWERPC_7450_v10, 7450, + "PowerPC 7450 v1.0 (G4)") + POWERPC_DEF("7450_v1.1", CPU_POWERPC_7450_v11, 7450, + "PowerPC 7450 v1.1 (G4)") + POWERPC_DEF("7450_v1.2", CPU_POWERPC_7450_v12, 7450, + "PowerPC 7450 v1.2 (G4)") + POWERPC_DEF("7450_v2.0", CPU_POWERPC_7450_v20, 7450, + "PowerPC 7450 v2.0 (G4)") + POWERPC_DEF("7450_v2.1", CPU_POWERPC_7450_v21, 7450, + "PowerPC 7450 v2.1 (G4)") + POWERPC_DEF("7441_v2.1", CPU_POWERPC_7450_v21, 7440, + "PowerPC 7441 v2.1 (G4)") + POWERPC_DEF("7441_v2.3", CPU_POWERPC_74x1_v23, 7440, + "PowerPC 7441 v2.3 (G4)") + POWERPC_DEF("7451_v2.3", CPU_POWERPC_74x1_v23, 7450, + "PowerPC 7451 v2.3 (G4)") + POWERPC_DEF("7441_v2.10", CPU_POWERPC_74x1_v210, 7440, + "PowerPC 7441 v2.10 (G4)") + POWERPC_DEF("7451_v2.10", CPU_POWERPC_74x1_v210, 7450, + "PowerPC 7451 v2.10 (G4)") + POWERPC_DEF("7445_v1.0", CPU_POWERPC_74x5_v10, 7445, + "PowerPC 7445 v1.0 (G4)") + POWERPC_DEF("7455_v1.0", CPU_POWERPC_74x5_v10, 7455, + "PowerPC 7455 v1.0 (G4)") + POWERPC_DEF("7445_v2.1", CPU_POWERPC_74x5_v21, 7445, + "PowerPC 7445 v2.1 (G4)") + POWERPC_DEF("7455_v2.1", CPU_POWERPC_74x5_v21, 7455, + "PowerPC 7455 v2.1 (G4)") + POWERPC_DEF("7445_v3.2", CPU_POWERPC_74x5_v32, 7445, + "PowerPC 7445 v3.2 (G4)") + POWERPC_DEF("7455_v3.2", CPU_POWERPC_74x5_v32, 7455, + "PowerPC 7455 v3.2 (G4)") + POWERPC_DEF("7445_v3.3", CPU_POWERPC_74x5_v33, 7445, + "PowerPC 7445 v3.3 (G4)") + POWERPC_DEF("7455_v3.3", CPU_POWERPC_74x5_v33, 7455, + "PowerPC 7455 v3.3 (G4)") + POWERPC_DEF("7445_v3.4", CPU_POWERPC_74x5_v34, 7445, + "PowerPC 7445 v3.4 (G4)") + POWERPC_DEF("7455_v3.4", CPU_POWERPC_74x5_v34, 7455, + "PowerPC 7455 v3.4 (G4)") + POWERPC_DEF("7447_v1.0", CPU_POWERPC_74x7_v10, 7445, + "PowerPC 7447 v1.0 (G4)") + POWERPC_DEF("7457_v1.0", CPU_POWERPC_74x7_v10, 7455, + "PowerPC 7457 v1.0 (G4)") + POWERPC_DEF("7447_v1.1", CPU_POWERPC_74x7_v11, 7445, + "PowerPC 7447 v1.1 (G4)") + POWERPC_DEF("7457_v1.1", CPU_POWERPC_74x7_v11, 7455, + "PowerPC 7457 v1.1 (G4)") + POWERPC_DEF("7457_v1.2", CPU_POWERPC_74x7_v12, 7455, + "PowerPC 7457 v1.2 (G4)") + POWERPC_DEF("7447A_v1.0", CPU_POWERPC_74x7A_v10, 7445, + "PowerPC 7447A v1.0 (G4)") + POWERPC_DEF("7457A_v1.0", CPU_POWERPC_74x7A_v10, 7455, + "PowerPC 7457A v1.0 (G4)") + POWERPC_DEF("7447A_v1.1", CPU_POWERPC_74x7A_v11, 7445, + "PowerPC 7447A v1.1 (G4)") + POWERPC_DEF("7457A_v1.1", CPU_POWERPC_74x7A_v11, 7455, + "PowerPC 7457A v1.1 (G4)") + POWERPC_DEF("7447A_v1.2", CPU_POWERPC_74x7A_v12, 7445, + "PowerPC 7447A v1.2 (G4)") + POWERPC_DEF("7457A_v1.2", CPU_POWERPC_74x7A_v12, 7455, + "PowerPC 7457A v1.2 (G4)") + /* 64 bits PowerPC */ +#if defined (TARGET_PPC64) +#if defined(TODO) + POWERPC_DEF("620", CPU_POWERPC_620, 620, + "PowerPC 620") + POWERPC_DEF("630", CPU_POWERPC_630, 630, + "PowerPC 630 (POWER3)") +#endif +#if defined(TODO) + POWERPC_DEF("631", CPU_POWERPC_631, 631, + "PowerPC 631 (Power 3+)") +#endif +#if defined(TODO) + POWERPC_DEF("POWER4", CPU_POWERPC_POWER4, POWER4, + "POWER4") +#endif +#if defined(TODO) + POWERPC_DEF("POWER4+", CPU_POWERPC_POWER4P, POWER4P, + "POWER4p") +#endif +#if defined(TODO) + POWERPC_DEF("POWER5", CPU_POWERPC_POWER5, POWER5, + "POWER5") +#endif + POWERPC_DEF("POWER5+_v2.1", CPU_POWERPC_POWER5P_v21, POWER5P, + "POWER5+ v2.1") +#if defined(TODO) + POWERPC_DEF("POWER6", CPU_POWERPC_POWER6, POWER6, + "POWER6") +#endif + POWERPC_DEF("POWER7_v2.3", CPU_POWERPC_POWER7_v23, POWER7, + "POWER7 v2.3") + POWERPC_DEF("POWER7+_v2.1", CPU_POWERPC_POWER7P_v21, POWER7, + "POWER7+ v2.1") + POWERPC_DEF("POWER8E_v2.1", CPU_POWERPC_POWER8E_v21, POWER8, + "POWER8E v2.1") + POWERPC_DEF("POWER8_v2.0", CPU_POWERPC_POWER8_v20, POWER8, + "POWER8 v2.0") + POWERPC_DEF("POWER8NVL_v1.0",CPU_POWERPC_POWER8NVL_v10, POWER8, + "POWER8NVL v1.0") + POWERPC_DEF("970_v2.2", CPU_POWERPC_970_v22, 970, + "PowerPC 970 v2.2") + + POWERPC_DEF("POWER9_v1.0", CPU_POWERPC_POWER9_BASE, POWER9, + "POWER9 v1.0") + + POWERPC_DEF("970fx_v1.0", CPU_POWERPC_970FX_v10, 970, + "PowerPC 970FX v1.0 (G5)") + POWERPC_DEF("970fx_v2.0", CPU_POWERPC_970FX_v20, 970, + "PowerPC 970FX v2.0 (G5)") + POWERPC_DEF("970fx_v2.1", CPU_POWERPC_970FX_v21, 970, + "PowerPC 970FX v2.1 (G5)") + POWERPC_DEF("970fx_v3.0", CPU_POWERPC_970FX_v30, 970, + "PowerPC 970FX v3.0 (G5)") + POWERPC_DEF("970fx_v3.1", CPU_POWERPC_970FX_v31, 970, + "PowerPC 970FX v3.1 (G5)") + POWERPC_DEF("970mp_v1.0", CPU_POWERPC_970MP_v10, 970, + "PowerPC 970MP v1.0") + POWERPC_DEF("970mp_v1.1", CPU_POWERPC_970MP_v11, 970, + "PowerPC 970MP v1.1") +#if defined(TODO) + POWERPC_DEF("Cell", CPU_POWERPC_CELL, 970, + "PowerPC Cell") +#endif +#if defined(TODO) + POWERPC_DEF("Cell_v1.0", CPU_POWERPC_CELL_v10, 970, + "PowerPC Cell v1.0") +#endif +#if defined(TODO) + POWERPC_DEF("Cell_v2.0", CPU_POWERPC_CELL_v20, 970, + "PowerPC Cell v2.0") +#endif +#if defined(TODO) + POWERPC_DEF("Cell_v3.0", CPU_POWERPC_CELL_v30, 970, + "PowerPC Cell v3.0") +#endif +#if defined(TODO) + POWERPC_DEF("Cell_v3.1", CPU_POWERPC_CELL_v31, 970, + "PowerPC Cell v3.1") +#endif +#if defined(TODO) + POWERPC_DEF("Cell_v3.2", CPU_POWERPC_CELL_v32, 970, + "PowerPC Cell v3.2") +#endif +#if defined(TODO) + /* This one seems to support the whole POWER2 instruction set + * and the PowerPC 64 one. + */ + /* What about A10 & A30 ? */ + POWERPC_DEF("RS64", CPU_POWERPC_RS64, RS64, + "RS64 (Apache/A35)") +#endif +#if defined(TODO) + POWERPC_DEF("RS64-II", CPU_POWERPC_RS64II, RS64, + "RS64-II (NorthStar/A50)") +#endif +#if defined(TODO) + POWERPC_DEF("RS64-III", CPU_POWERPC_RS64III, RS64, + "RS64-III (Pulsar)") +#endif +#if defined(TODO) + POWERPC_DEF("RS64-IV", CPU_POWERPC_RS64IV, RS64, + "RS64-IV (IceStar/IStar/SStar)") +#endif +#endif /* defined (TARGET_PPC64) */ + /* POWER */ +#if defined(TODO) + POWERPC_DEF("POWER", CPU_POWERPC_POWER, POWER, + "Original POWER") +#endif +#if defined(TODO) + POWERPC_DEF("POWER2", CPU_POWERPC_POWER2, POWER, + "POWER2") +#endif + /* PA semi cores */ +#if defined(TODO) + POWERPC_DEF("PA6T", CPU_POWERPC_PA6T, PA6T, + "PA PA6T") +#endif + + +/***************************************************************************/ +/* PowerPC CPU aliases */ + +PowerPCCPUAlias ppc_cpu_aliases[] = { + { "403", "403GC" }, + { "405", "405D4" }, + { "405CR", "405CRc" }, + { "405GP", "405GPd" }, + { "405GPe", "405CRc" }, + { "x2vp7", "x2vp4" }, + { "x2vp50", "x2vp20" }, + + { "440EP", "440EPb" }, + { "440GP", "440GPc" }, + { "440GR", "440GRa" }, + { "440GX", "440GXf" }, + + { "RCPU", "MPC5xx" }, + /* MPC5xx microcontrollers */ + { "MGT560", "MPC5xx" }, + { "MPC509", "MPC5xx" }, + { "MPC533", "MPC5xx" }, + { "MPC534", "MPC5xx" }, + { "MPC555", "MPC5xx" }, + { "MPC556", "MPC5xx" }, + { "MPC560", "MPC5xx" }, + { "MPC561", "MPC5xx" }, + { "MPC562", "MPC5xx" }, + { "MPC563", "MPC5xx" }, + { "MPC564", "MPC5xx" }, + { "MPC565", "MPC5xx" }, + { "MPC566", "MPC5xx" }, + + { "PowerQUICC", "MPC8xx" }, + /* MPC8xx microcontrollers */ + { "MGT823", "MPC8xx" }, + { "MPC821", "MPC8xx" }, + { "MPC823", "MPC8xx" }, + { "MPC850", "MPC8xx" }, + { "MPC852T", "MPC8xx" }, + { "MPC855T", "MPC8xx" }, + { "MPC857", "MPC8xx" }, + { "MPC859", "MPC8xx" }, + { "MPC860", "MPC8xx" }, + { "MPC862", "MPC8xx" }, + { "MPC866", "MPC8xx" }, + { "MPC870", "MPC8xx" }, + { "MPC875", "MPC8xx" }, + { "MPC880", "MPC8xx" }, + { "MPC885", "MPC8xx" }, + + /* PowerPC MPC603 microcontrollers */ + { "MPC8240", "603" }, + + { "MPC52xx", "MPC5200" }, + { "MPC5200", "MPC5200_v12" }, + { "MPC5200B", "MPC5200B_v21" }, + + { "MPC82xx", "MPC8280" }, + { "PowerQUICC-II", "MPC82xx" }, + { "MPC8241", "G2HiP4" }, + { "MPC8245", "G2HiP4" }, + { "MPC8247", "G2leGP3" }, + { "MPC8248", "G2leGP3" }, + { "MPC8250", "MPC8250_HiP4" }, + { "MPC8250_HiP3", "G2HiP3" }, + { "MPC8250_HiP4", "G2HiP4" }, + { "MPC8255", "MPC8255_HiP4" }, + { "MPC8255_HiP3", "G2HiP3" }, + { "MPC8255_HiP4", "G2HiP4" }, + { "MPC8260", "MPC8260_HiP4" }, + { "MPC8260_HiP3", "G2HiP3" }, + { "MPC8260_HiP4", "G2HiP4" }, + { "MPC8264", "MPC8264_HiP4" }, + { "MPC8264_HiP3", "G2HiP3" }, + { "MPC8264_HiP4", "G2HiP4" }, + { "MPC8265", "MPC8265_HiP4" }, + { "MPC8265_HiP3", "G2HiP3" }, + { "MPC8265_HiP4", "G2HiP4" }, + { "MPC8266", "MPC8266_HiP4" }, + { "MPC8266_HiP3", "G2HiP3" }, + { "MPC8266_HiP4", "G2HiP4" }, + { "MPC8270", "G2leGP3" }, + { "MPC8271", "G2leGP3" }, + { "MPC8272", "G2leGP3" }, + { "MPC8275", "G2leGP3" }, + { "MPC8280", "G2leGP3" }, + { "e200", "e200z6" }, + { "e300", "e300c3" }, + { "MPC8347", "MPC8347T" }, + { "MPC8347A", "MPC8347AT" }, + { "MPC8347E", "MPC8347ET" }, + { "MPC8347EA", "MPC8347EAT" }, + { "e500", "e500v2_v22" }, + { "e500v1", "e500_v20" }, + { "e500v2", "e500v2_v22" }, + { "MPC8533", "MPC8533_v11" }, + { "MPC8533E", "MPC8533E_v11" }, + { "MPC8540", "MPC8540_v21" }, + { "MPC8541", "MPC8541_v11" }, + { "MPC8541E", "MPC8541E_v11" }, + { "MPC8543", "MPC8543_v21" }, + { "MPC8543E", "MPC8543E_v21" }, + { "MPC8544", "MPC8544_v11" }, + { "MPC8544E", "MPC8544E_v11" }, + { "MPC8545", "MPC8545_v21" }, + { "MPC8545E", "MPC8545E_v21" }, + { "MPC8547E", "MPC8547E_v21" }, + { "MPC8548", "MPC8548_v21" }, + { "MPC8548E", "MPC8548E_v21" }, + { "MPC8555", "MPC8555_v11" }, + { "MPC8555E", "MPC8555E_v11" }, + { "MPC8560", "MPC8560_v21" }, + { "601", "601_v2" }, + { "601v", "601_v2" }, + { "Vanilla", "603" }, + { "603e", "603e_v4.1" }, + { "Stretch", "603e" }, + { "Vaillant", "603e7v" }, + { "603r", "603e7t" }, + { "Goldeneye", "603r" }, + { "604e", "604e_v2.4" }, + { "Sirocco", "604e" }, + { "Mach5", "604r" }, + { "740", "740_v3.1" }, + { "Arthur", "740" }, + { "750", "750_v3.1" }, + { "Typhoon", "750" }, + { "G3", "750" }, + { "Conan/Doyle", "750p" }, + { "750cl", "750cl_v2.0" }, + { "750cx", "750cx_v2.2" }, + { "750cxe", "750cxe_v3.1b" }, + { "750fx", "750fx_v2.3" }, + { "750gx", "750gx_v1.2" }, + { "750l", "750l_v3.2" }, + { "LoneStar", "750l" }, + { "745", "745_v2.8" }, + { "755", "755_v2.8" }, + { "Goldfinger", "755" }, + { "7400", "7400_v2.9" }, + { "Max", "7400" }, + { "G4", "7400" }, + { "7410", "7410_v1.4" }, + { "Nitro", "7410" }, + { "7448", "7448_v2.1" }, + { "7450", "7450_v2.1" }, + { "Vger", "7450" }, + { "7441", "7441_v2.3" }, + { "7451", "7451_v2.3" }, + { "7445", "7445_v3.2" }, + { "7455", "7455_v3.2" }, + { "Apollo6", "7455" }, + { "7447", "7447_v1.2" }, + { "7457", "7457_v1.2" }, + { "Apollo7", "7457" }, + { "7447A", "7447A_v1.2" }, + { "7457A", "7457A_v1.2" }, + { "Apollo7PM", "7457A_v1.0" }, +#if defined(TARGET_PPC64) + { "Trident", "620" }, + { "POWER3", "630" }, + { "Boxer", "POWER3" }, + { "Dino", "POWER3" }, + { "POWER3+", "631" }, + { "POWER5gr", "POWER5" }, + { "POWER5+", "POWER5+_v2.1" }, + { "POWER5gs", "POWER5+_v2.1" }, + { "POWER7", "POWER7_v2.3" }, + { "POWER7+", "POWER7+_v2.1" }, + { "POWER8E", "POWER8E_v2.1" }, + { "POWER8", "POWER8_v2.0" }, + { "POWER8NVL", "POWER8NVL_v1.0" }, + { "POWER9", "POWER9_v1.0" }, + { "970", "970_v2.2" }, + { "970fx", "970fx_v3.1" }, + { "970mp", "970mp_v1.1" }, + { "Apache", "RS64" }, + { "A35", "RS64" }, + { "NorthStar", "RS64-II" }, + { "A50", "RS64-II" }, + { "Pulsar", "RS64-III" }, + { "IceStar", "RS64-IV" }, + { "IStar", "RS64-IV" }, + { "SStar", "RS64-IV" }, +#endif + { "RIOS", "POWER" }, + { "RSC", "POWER" }, + { "RSC3308", "POWER" }, + { "RSC4608", "POWER" }, + { "RSC2", "POWER2" }, + { "P2SC", "POWER2" }, + + /* Generic PowerPCs */ +#if defined(TARGET_PPC64) + { "ppc64", "970fx" }, +#endif + { "ppc32", "604" }, + { "ppc", "ppc32" }, + { "default", "ppc" }, + { NULL, NULL } +}; diff --git a/target/ppc/cpu-models.h b/target/ppc/cpu-models.h new file mode 100644 index 0000000000..aafbbd7d5d --- /dev/null +++ b/target/ppc/cpu-models.h @@ -0,0 +1,754 @@ +/* + * PowerPC CPU initialization for qemu. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * Copyright 2011 Freescale Semiconductor, Inc. + * Copyright 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/>. + */ +#ifndef TARGET_PPC_CPU_MODELS_H +#define TARGET_PPC_CPU_MODELS_H + +/** + * PowerPCCPUAlias: + * @alias: The alias name. + * @model: The CPU model @alias refers to. + * + * A mapping entry from CPU @alias to CPU @model. + */ +typedef struct PowerPCCPUAlias { + const char *alias; + const char *model; + ObjectClass *oc; +} PowerPCCPUAlias; + +extern PowerPCCPUAlias ppc_cpu_aliases[]; + +/*****************************************************************************/ +/* PVR definitions for most known PowerPC */ +enum { + /* PowerPC 401 family */ + /* Generic PowerPC 401 */ +#define CPU_POWERPC_401 CPU_POWERPC_401G2 + /* PowerPC 401 cores */ + CPU_POWERPC_401A1 = 0x00210000, + CPU_POWERPC_401B2 = 0x00220000, +#if 0 + CPU_POWERPC_401B3 = xxx, +#endif + CPU_POWERPC_401C2 = 0x00230000, + CPU_POWERPC_401D2 = 0x00240000, + CPU_POWERPC_401E2 = 0x00250000, + CPU_POWERPC_401F2 = 0x00260000, + CPU_POWERPC_401G2 = 0x00270000, + /* PowerPC 401 microcontrolers */ +#if 0 + CPU_POWERPC_401GF = xxx, +#endif +#define CPU_POWERPC_IOP480 CPU_POWERPC_401B2 + /* IBM Processor for Network Resources */ + CPU_POWERPC_COBRA = 0x10100000, /* XXX: 405 ? */ +#if 0 + CPU_POWERPC_XIPCHIP = xxx, +#endif + /* PowerPC 403 family */ + /* PowerPC 403 microcontrollers */ + CPU_POWERPC_403GA = 0x00200011, + CPU_POWERPC_403GB = 0x00200100, + CPU_POWERPC_403GC = 0x00200200, + CPU_POWERPC_403GCX = 0x00201400, +#if 0 + CPU_POWERPC_403GP = xxx, +#endif + /* PowerPC 405 family */ + /* PowerPC 405 cores */ +#if 0 + CPU_POWERPC_405A3 = xxx, +#endif +#if 0 + CPU_POWERPC_405A4 = xxx, +#endif +#if 0 + CPU_POWERPC_405B3 = xxx, +#endif +#if 0 + CPU_POWERPC_405B4 = xxx, +#endif +#if 0 + CPU_POWERPC_405C3 = xxx, +#endif +#if 0 + CPU_POWERPC_405C4 = xxx, +#endif + CPU_POWERPC_405D2 = 0x20010000, +#if 0 + CPU_POWERPC_405D3 = xxx, +#endif + CPU_POWERPC_405D4 = 0x41810000, +#if 0 + CPU_POWERPC_405D5 = xxx, +#endif +#if 0 + CPU_POWERPC_405E4 = xxx, +#endif +#if 0 + CPU_POWERPC_405F4 = xxx, +#endif +#if 0 + CPU_POWERPC_405F5 = xxx, +#endif +#if 0 + CPU_POWERPC_405F6 = xxx, +#endif + /* PowerPC 405 microcontrolers */ + /* XXX: missing 0x200108a0 */ + CPU_POWERPC_405CRa = 0x40110041, + CPU_POWERPC_405CRb = 0x401100C5, + CPU_POWERPC_405CRc = 0x40110145, + CPU_POWERPC_405EP = 0x51210950, +#if 0 + CPU_POWERPC_405EXr = xxx, +#endif + CPU_POWERPC_405EZ = 0x41511460, /* 0x51210950 ? */ +#if 0 + CPU_POWERPC_405FX = xxx, +#endif + CPU_POWERPC_405GPa = 0x40110000, + CPU_POWERPC_405GPb = 0x40110040, + CPU_POWERPC_405GPc = 0x40110082, + CPU_POWERPC_405GPd = 0x401100C4, + CPU_POWERPC_405GPR = 0x50910951, +#if 0 + CPU_POWERPC_405H = xxx, +#endif +#if 0 + CPU_POWERPC_405L = xxx, +#endif + CPU_POWERPC_405LP = 0x41F10000, +#if 0 + CPU_POWERPC_405PM = xxx, +#endif +#if 0 + CPU_POWERPC_405PS = xxx, +#endif +#if 0 + CPU_POWERPC_405S = xxx, +#endif + /* IBM network processors */ + CPU_POWERPC_NPE405H = 0x414100C0, + CPU_POWERPC_NPE405H2 = 0x41410140, + CPU_POWERPC_NPE405L = 0x416100C0, + CPU_POWERPC_NPE4GS3 = 0x40B10000, +#if 0 + CPU_POWERPC_NPCxx1 = xxx, +#endif +#if 0 + CPU_POWERPC_NPR161 = xxx, +#endif +#if 0 + CPU_POWERPC_LC77700 = xxx, +#endif + /* IBM STBxxx (PowerPC 401/403/405 core based microcontrollers) */ +#if 0 + CPU_POWERPC_STB01000 = xxx, +#endif +#if 0 + CPU_POWERPC_STB01010 = xxx, +#endif +#if 0 + CPU_POWERPC_STB0210 = xxx, /* 401B3 */ +#endif + CPU_POWERPC_STB03 = 0x40310000, /* 0x40130000 ? */ +#if 0 + CPU_POWERPC_STB043 = xxx, +#endif +#if 0 + CPU_POWERPC_STB045 = xxx, +#endif + CPU_POWERPC_STB04 = 0x41810000, + CPU_POWERPC_STB25 = 0x51510950, +#if 0 + CPU_POWERPC_STB130 = xxx, +#endif + /* Xilinx cores */ + CPU_POWERPC_X2VP4 = 0x20010820, + CPU_POWERPC_X2VP20 = 0x20010860, +#if 0 + CPU_POWERPC_ZL10310 = xxx, +#endif +#if 0 + CPU_POWERPC_ZL10311 = xxx, +#endif +#if 0 + CPU_POWERPC_ZL10320 = xxx, +#endif +#if 0 + CPU_POWERPC_ZL10321 = xxx, +#endif + /* PowerPC 440 family */ + /* Generic PowerPC 440 */ +#define CPU_POWERPC_440 CPU_POWERPC_440GXf + /* PowerPC 440 cores */ +#if 0 + CPU_POWERPC_440A4 = xxx, +#endif + CPU_POWERPC_440_XILINX = 0x7ff21910, +#if 0 + CPU_POWERPC_440A5 = xxx, +#endif +#if 0 + CPU_POWERPC_440B4 = xxx, +#endif +#if 0 + CPU_POWERPC_440F5 = xxx, +#endif +#if 0 + CPU_POWERPC_440G5 = xxx, +#endif +#if 0 + CPU_POWERPC_440H4 = xxx, +#endif +#if 0 + CPU_POWERPC_440H6 = xxx, +#endif + /* PowerPC 440 microcontrolers */ + CPU_POWERPC_440EPa = 0x42221850, + CPU_POWERPC_440EPb = 0x422218D3, + CPU_POWERPC_440GPb = 0x40120440, + CPU_POWERPC_440GPc = 0x40120481, +#define CPU_POWERPC_440GRa CPU_POWERPC_440EPb + CPU_POWERPC_440GRX = 0x200008D0, +#define CPU_POWERPC_440EPX CPU_POWERPC_440GRX + CPU_POWERPC_440GXa = 0x51B21850, + CPU_POWERPC_440GXb = 0x51B21851, + CPU_POWERPC_440GXc = 0x51B21892, + CPU_POWERPC_440GXf = 0x51B21894, +#if 0 + CPU_POWERPC_440S = xxx, +#endif + CPU_POWERPC_440SP = 0x53221850, + CPU_POWERPC_440SP2 = 0x53221891, + CPU_POWERPC_440SPE = 0x53421890, + /* PowerPC 460 family */ +#if 0 + /* Generic PowerPC 464 */ +#define CPU_POWERPC_464 CPU_POWERPC_464H90 +#endif + /* PowerPC 464 microcontrolers */ +#if 0 + CPU_POWERPC_464H90 = xxx, +#endif +#if 0 + CPU_POWERPC_464H90FP = xxx, +#endif + /* Freescale embedded PowerPC cores */ + /* PowerPC MPC 5xx cores (aka RCPU) */ + CPU_POWERPC_MPC5xx = 0x00020020, + /* PowerPC MPC 8xx cores (aka PowerQUICC) */ + CPU_POWERPC_MPC8xx = 0x00500000, + /* G2 cores (aka PowerQUICC-II) */ + CPU_POWERPC_G2 = 0x00810011, + CPU_POWERPC_G2H4 = 0x80811010, + CPU_POWERPC_G2gp = 0x80821010, + CPU_POWERPC_G2ls = 0x90810010, + CPU_POWERPC_MPC603 = 0x00810100, + CPU_POWERPC_G2_HIP3 = 0x00810101, + CPU_POWERPC_G2_HIP4 = 0x80811014, + /* G2_LE core (aka PowerQUICC-II) */ + CPU_POWERPC_G2LE = 0x80820010, + CPU_POWERPC_G2LEgp = 0x80822010, + CPU_POWERPC_G2LEls = 0xA0822010, + CPU_POWERPC_G2LEgp1 = 0x80822011, + CPU_POWERPC_G2LEgp3 = 0x80822013, + /* MPC52xx microcontrollers */ + /* XXX: MPC 5121 ? */ +#define CPU_POWERPC_MPC5200_v10 CPU_POWERPC_G2LEgp1 +#define CPU_POWERPC_MPC5200_v11 CPU_POWERPC_G2LEgp1 +#define CPU_POWERPC_MPC5200_v12 CPU_POWERPC_G2LEgp1 +#define CPU_POWERPC_MPC5200B_v20 CPU_POWERPC_G2LEgp1 +#define CPU_POWERPC_MPC5200B_v21 CPU_POWERPC_G2LEgp1 + /* e200 family */ + /* e200 cores */ +#if 0 + CPU_POWERPC_e200z0 = xxx, +#endif +#if 0 + CPU_POWERPC_e200z1 = xxx, +#endif +#if 0 /* ? */ + CPU_POWERPC_e200z3 = 0x81120000, +#endif + CPU_POWERPC_e200z5 = 0x81000000, + CPU_POWERPC_e200z6 = 0x81120000, + /* MPC55xx microcontrollers */ +#define CPU_POWERPC_MPC55xx CPU_POWERPC_MPC5567 +#if 0 +#define CPU_POWERPC_MPC5514E CPU_POWERPC_MPC5514E_v1 +#define CPU_POWERPC_MPC5514E_v0 CPU_POWERPC_e200z0 +#define CPU_POWERPC_MPC5514E_v1 CPU_POWERPC_e200z1 +#define CPU_POWERPC_MPC5514G CPU_POWERPC_MPC5514G_v1 +#define CPU_POWERPC_MPC5514G_v0 CPU_POWERPC_e200z0 +#define CPU_POWERPC_MPC5514G_v1 CPU_POWERPC_e200z1 +#define CPU_POWERPC_MPC5515S CPU_POWERPC_e200z1 +#define CPU_POWERPC_MPC5516E CPU_POWERPC_MPC5516E_v1 +#define CPU_POWERPC_MPC5516E_v0 CPU_POWERPC_e200z0 +#define CPU_POWERPC_MPC5516E_v1 CPU_POWERPC_e200z1 +#define CPU_POWERPC_MPC5516G CPU_POWERPC_MPC5516G_v1 +#define CPU_POWERPC_MPC5516G_v0 CPU_POWERPC_e200z0 +#define CPU_POWERPC_MPC5516G_v1 CPU_POWERPC_e200z1 +#define CPU_POWERPC_MPC5516S CPU_POWERPC_e200z1 +#endif +#if 0 +#define CPU_POWERPC_MPC5533 CPU_POWERPC_e200z3 +#define CPU_POWERPC_MPC5534 CPU_POWERPC_e200z3 +#endif +#define CPU_POWERPC_MPC5553 CPU_POWERPC_e200z6 +#define CPU_POWERPC_MPC5554 CPU_POWERPC_e200z6 +#define CPU_POWERPC_MPC5561 CPU_POWERPC_e200z6 +#define CPU_POWERPC_MPC5565 CPU_POWERPC_e200z6 +#define CPU_POWERPC_MPC5566 CPU_POWERPC_e200z6 +#define CPU_POWERPC_MPC5567 CPU_POWERPC_e200z6 + /* e300 family */ + /* e300 cores */ + CPU_POWERPC_e300c1 = 0x00830010, + CPU_POWERPC_e300c2 = 0x00840010, + CPU_POWERPC_e300c3 = 0x00850010, + CPU_POWERPC_e300c4 = 0x00860010, + /* MPC83xx microcontrollers */ +#define CPU_POWERPC_MPC831x CPU_POWERPC_e300c3 +#define CPU_POWERPC_MPC832x CPU_POWERPC_e300c2 +#define CPU_POWERPC_MPC834x CPU_POWERPC_e300c1 +#define CPU_POWERPC_MPC835x CPU_POWERPC_e300c1 +#define CPU_POWERPC_MPC836x CPU_POWERPC_e300c1 +#define CPU_POWERPC_MPC837x CPU_POWERPC_e300c4 + /* e500 family */ + /* e500 cores */ +#define CPU_POWERPC_e500 CPU_POWERPC_e500v2_v22 + CPU_POWERPC_e500v1_v10 = 0x80200010, + CPU_POWERPC_e500v1_v20 = 0x80200020, + CPU_POWERPC_e500v2_v10 = 0x80210010, + CPU_POWERPC_e500v2_v11 = 0x80210011, + CPU_POWERPC_e500v2_v20 = 0x80210020, + CPU_POWERPC_e500v2_v21 = 0x80210021, + CPU_POWERPC_e500v2_v22 = 0x80210022, + CPU_POWERPC_e500v2_v30 = 0x80210030, + CPU_POWERPC_e500mc = 0x80230020, + CPU_POWERPC_e5500 = 0x80240020, + /* MPC85xx microcontrollers */ +#define CPU_POWERPC_MPC8533_v10 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8533_v11 CPU_POWERPC_e500v2_v22 +#define CPU_POWERPC_MPC8533E_v10 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8533E_v11 CPU_POWERPC_e500v2_v22 +#define CPU_POWERPC_MPC8540_v10 CPU_POWERPC_e500v1_v10 +#define CPU_POWERPC_MPC8540_v20 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8540_v21 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8541_v10 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8541_v11 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8541E_v10 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8541E_v11 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8543_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8543_v11 CPU_POWERPC_e500v2_v11 +#define CPU_POWERPC_MPC8543_v20 CPU_POWERPC_e500v2_v20 +#define CPU_POWERPC_MPC8543_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8543E_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8543E_v11 CPU_POWERPC_e500v2_v11 +#define CPU_POWERPC_MPC8543E_v20 CPU_POWERPC_e500v2_v20 +#define CPU_POWERPC_MPC8543E_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8544_v10 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8544_v11 CPU_POWERPC_e500v2_v22 +#define CPU_POWERPC_MPC8544E_v11 CPU_POWERPC_e500v2_v22 +#define CPU_POWERPC_MPC8544E_v10 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8545_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8545_v20 CPU_POWERPC_e500v2_v20 +#define CPU_POWERPC_MPC8545_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8545E_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8545E_v20 CPU_POWERPC_e500v2_v20 +#define CPU_POWERPC_MPC8545E_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8547E_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8547E_v20 CPU_POWERPC_e500v2_v20 +#define CPU_POWERPC_MPC8547E_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8548_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8548_v11 CPU_POWERPC_e500v2_v11 +#define CPU_POWERPC_MPC8548_v20 CPU_POWERPC_e500v2_v20 +#define CPU_POWERPC_MPC8548_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8548E_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8548E_v11 CPU_POWERPC_e500v2_v11 +#define CPU_POWERPC_MPC8548E_v20 CPU_POWERPC_e500v2_v20 +#define CPU_POWERPC_MPC8548E_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8555_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8555_v11 CPU_POWERPC_e500v2_v11 +#define CPU_POWERPC_MPC8555E_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8555E_v11 CPU_POWERPC_e500v2_v11 +#define CPU_POWERPC_MPC8560_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8560_v20 CPU_POWERPC_e500v2_v20 +#define CPU_POWERPC_MPC8560_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8567 CPU_POWERPC_e500v2_v22 +#define CPU_POWERPC_MPC8567E CPU_POWERPC_e500v2_v22 +#define CPU_POWERPC_MPC8568 CPU_POWERPC_e500v2_v22 +#define CPU_POWERPC_MPC8568E CPU_POWERPC_e500v2_v22 +#define CPU_POWERPC_MPC8572 CPU_POWERPC_e500v2_v30 +#define CPU_POWERPC_MPC8572E CPU_POWERPC_e500v2_v30 + /* e600 family */ + /* e600 cores */ + CPU_POWERPC_e600 = 0x80040010, + /* MPC86xx microcontrollers */ +#define CPU_POWERPC_MPC8610 CPU_POWERPC_e600 +#define CPU_POWERPC_MPC8641 CPU_POWERPC_e600 +#define CPU_POWERPC_MPC8641D CPU_POWERPC_e600 + /* PowerPC 6xx cores */ + CPU_POWERPC_601_v0 = 0x00010001, + CPU_POWERPC_601_v1 = 0x00010001, + CPU_POWERPC_601_v2 = 0x00010002, + CPU_POWERPC_602 = 0x00050100, + CPU_POWERPC_603 = 0x00030100, + CPU_POWERPC_603E_v11 = 0x00060101, + CPU_POWERPC_603E_v12 = 0x00060102, + CPU_POWERPC_603E_v13 = 0x00060103, + CPU_POWERPC_603E_v14 = 0x00060104, + CPU_POWERPC_603E_v22 = 0x00060202, + CPU_POWERPC_603E_v3 = 0x00060300, + CPU_POWERPC_603E_v4 = 0x00060400, + CPU_POWERPC_603E_v41 = 0x00060401, + CPU_POWERPC_603E7t = 0x00071201, + CPU_POWERPC_603E7v = 0x00070100, + CPU_POWERPC_603E7v1 = 0x00070101, + CPU_POWERPC_603E7v2 = 0x00070201, + CPU_POWERPC_603E7 = 0x00070200, + CPU_POWERPC_603P = 0x00070000, + /* XXX: missing 0x00040303 (604) */ + CPU_POWERPC_604 = 0x00040103, + /* XXX: missing 0x00091203 */ + /* XXX: missing 0x00092110 */ + /* XXX: missing 0x00092120 */ + CPU_POWERPC_604E_v10 = 0x00090100, + CPU_POWERPC_604E_v22 = 0x00090202, + CPU_POWERPC_604E_v24 = 0x00090204, + /* XXX: missing 0x000a0100 */ + /* XXX: missing 0x00093102 */ + CPU_POWERPC_604R = 0x000a0101, +#if 0 + CPU_POWERPC_604EV = xxx, /* XXX: same as 604R ? */ +#endif + /* PowerPC 740/750 cores (aka G3) */ + /* XXX: missing 0x00084202 */ + CPU_POWERPC_7x0_v10 = 0x00080100, + CPU_POWERPC_7x0_v20 = 0x00080200, + CPU_POWERPC_7x0_v21 = 0x00080201, + CPU_POWERPC_7x0_v22 = 0x00080202, + CPU_POWERPC_7x0_v30 = 0x00080300, + CPU_POWERPC_7x0_v31 = 0x00080301, + CPU_POWERPC_740E = 0x00080100, + CPU_POWERPC_750E = 0x00080200, + CPU_POWERPC_7x0P = 0x10080000, + /* XXX: missing 0x00087010 (CL ?) */ + CPU_POWERPC_750CL_v10 = 0x00087200, + CPU_POWERPC_750CL_v20 = 0x00087210, /* aka rev E */ + CPU_POWERPC_750CX_v10 = 0x00082100, + CPU_POWERPC_750CX_v20 = 0x00082200, + CPU_POWERPC_750CX_v21 = 0x00082201, + CPU_POWERPC_750CX_v22 = 0x00082202, + CPU_POWERPC_750CXE_v21 = 0x00082211, + CPU_POWERPC_750CXE_v22 = 0x00082212, + CPU_POWERPC_750CXE_v23 = 0x00082213, + CPU_POWERPC_750CXE_v24 = 0x00082214, + CPU_POWERPC_750CXE_v24b = 0x00083214, + CPU_POWERPC_750CXE_v30 = 0x00082310, + CPU_POWERPC_750CXE_v31 = 0x00082311, + CPU_POWERPC_750CXE_v31b = 0x00083311, + CPU_POWERPC_750CXR = 0x00083410, + CPU_POWERPC_750FL = 0x70000203, + CPU_POWERPC_750FX_v10 = 0x70000100, + CPU_POWERPC_750FX_v20 = 0x70000200, + CPU_POWERPC_750FX_v21 = 0x70000201, + CPU_POWERPC_750FX_v22 = 0x70000202, + CPU_POWERPC_750FX_v23 = 0x70000203, + CPU_POWERPC_750GL = 0x70020102, + CPU_POWERPC_750GX_v10 = 0x70020100, + CPU_POWERPC_750GX_v11 = 0x70020101, + CPU_POWERPC_750GX_v12 = 0x70020102, + CPU_POWERPC_750L_v20 = 0x00088200, + CPU_POWERPC_750L_v21 = 0x00088201, + CPU_POWERPC_750L_v22 = 0x00088202, + CPU_POWERPC_750L_v30 = 0x00088300, + CPU_POWERPC_750L_v32 = 0x00088302, + /* PowerPC 745/755 cores */ + CPU_POWERPC_7x5_v10 = 0x00083100, + CPU_POWERPC_7x5_v11 = 0x00083101, + CPU_POWERPC_7x5_v20 = 0x00083200, + CPU_POWERPC_7x5_v21 = 0x00083201, + CPU_POWERPC_7x5_v22 = 0x00083202, /* aka D */ + CPU_POWERPC_7x5_v23 = 0x00083203, /* aka E */ + CPU_POWERPC_7x5_v24 = 0x00083204, + CPU_POWERPC_7x5_v25 = 0x00083205, + CPU_POWERPC_7x5_v26 = 0x00083206, + CPU_POWERPC_7x5_v27 = 0x00083207, + CPU_POWERPC_7x5_v28 = 0x00083208, +#if 0 + CPU_POWERPC_7x5P = xxx, +#endif + /* PowerPC 74xx cores (aka G4) */ + /* XXX: missing 0x000C1101 */ + CPU_POWERPC_7400_v10 = 0x000C0100, + CPU_POWERPC_7400_v11 = 0x000C0101, + CPU_POWERPC_7400_v20 = 0x000C0200, + CPU_POWERPC_7400_v21 = 0x000C0201, + CPU_POWERPC_7400_v22 = 0x000C0202, + CPU_POWERPC_7400_v26 = 0x000C0206, + CPU_POWERPC_7400_v27 = 0x000C0207, + CPU_POWERPC_7400_v28 = 0x000C0208, + CPU_POWERPC_7400_v29 = 0x000C0209, + CPU_POWERPC_7410_v10 = 0x800C1100, + CPU_POWERPC_7410_v11 = 0x800C1101, + CPU_POWERPC_7410_v12 = 0x800C1102, /* aka C */ + CPU_POWERPC_7410_v13 = 0x800C1103, /* aka D */ + CPU_POWERPC_7410_v14 = 0x800C1104, /* aka E */ + CPU_POWERPC_7448_v10 = 0x80040100, + CPU_POWERPC_7448_v11 = 0x80040101, + CPU_POWERPC_7448_v20 = 0x80040200, + CPU_POWERPC_7448_v21 = 0x80040201, + CPU_POWERPC_7450_v10 = 0x80000100, + CPU_POWERPC_7450_v11 = 0x80000101, + CPU_POWERPC_7450_v12 = 0x80000102, + CPU_POWERPC_7450_v20 = 0x80000200, /* aka A, B, C, D: 2.04 */ + CPU_POWERPC_7450_v21 = 0x80000201, /* aka E */ + CPU_POWERPC_74x1_v23 = 0x80000203, /* aka G: 2.3 */ + /* XXX: this entry might be a bug in some documentation */ + CPU_POWERPC_74x1_v210 = 0x80000210, /* aka G: 2.3 ? */ + CPU_POWERPC_74x5_v10 = 0x80010100, + /* XXX: missing 0x80010200 */ + CPU_POWERPC_74x5_v21 = 0x80010201, /* aka C: 2.1 */ + CPU_POWERPC_74x5_v32 = 0x80010302, + CPU_POWERPC_74x5_v33 = 0x80010303, /* aka F: 3.3 */ + CPU_POWERPC_74x5_v34 = 0x80010304, /* aka G: 3.4 */ + CPU_POWERPC_74x7_v10 = 0x80020100, /* aka A: 1.0 */ + CPU_POWERPC_74x7_v11 = 0x80020101, /* aka B: 1.1 */ + CPU_POWERPC_74x7_v12 = 0x80020102, /* aka C: 1.2 */ + CPU_POWERPC_74x7A_v10 = 0x80030100, /* aka A: 1.0 */ + CPU_POWERPC_74x7A_v11 = 0x80030101, /* aka B: 1.1 */ + CPU_POWERPC_74x7A_v12 = 0x80030102, /* aka C: 1.2 */ + /* 64 bits PowerPC */ +#if defined(TARGET_PPC64) + CPU_POWERPC_620 = 0x00140000, + CPU_POWERPC_630 = 0x00400000, + CPU_POWERPC_631 = 0x00410104, + CPU_POWERPC_POWER4 = 0x00350000, + CPU_POWERPC_POWER4P = 0x00380000, + /* XXX: missing 0x003A0201 */ + CPU_POWERPC_POWER5 = 0x003A0203, + CPU_POWERPC_POWER5P_v21 = 0x003B0201, + CPU_POWERPC_POWER6 = 0x003E0000, + CPU_POWERPC_POWER_SERVER_MASK = 0xFFFF0000, + CPU_POWERPC_POWER7_BASE = 0x003F0000, + CPU_POWERPC_POWER7_v23 = 0x003F0203, + CPU_POWERPC_POWER7P_BASE = 0x004A0000, + CPU_POWERPC_POWER7P_v21 = 0x004A0201, + CPU_POWERPC_POWER8E_BASE = 0x004B0000, + CPU_POWERPC_POWER8E_v21 = 0x004B0201, + CPU_POWERPC_POWER8_BASE = 0x004D0000, + CPU_POWERPC_POWER8_v20 = 0x004D0200, + CPU_POWERPC_POWER8NVL_BASE = 0x004C0000, + CPU_POWERPC_POWER8NVL_v10 = 0x004C0100, + CPU_POWERPC_POWER9_BASE = 0x004E0000, + CPU_POWERPC_970_v22 = 0x00390202, + CPU_POWERPC_970FX_v10 = 0x00391100, + CPU_POWERPC_970FX_v20 = 0x003C0200, + CPU_POWERPC_970FX_v21 = 0x003C0201, + CPU_POWERPC_970FX_v30 = 0x003C0300, + CPU_POWERPC_970FX_v31 = 0x003C0301, + CPU_POWERPC_970MP_v10 = 0x00440100, + CPU_POWERPC_970MP_v11 = 0x00440101, +#define CPU_POWERPC_CELL CPU_POWERPC_CELL_v32 + CPU_POWERPC_CELL_v10 = 0x00700100, + CPU_POWERPC_CELL_v20 = 0x00700400, + CPU_POWERPC_CELL_v30 = 0x00700500, + CPU_POWERPC_CELL_v31 = 0x00700501, +#define CPU_POWERPC_CELL_v32 CPU_POWERPC_CELL_v31 + CPU_POWERPC_RS64 = 0x00330000, + CPU_POWERPC_RS64II = 0x00340000, + CPU_POWERPC_RS64III = 0x00360000, + CPU_POWERPC_RS64IV = 0x00370000, +#endif /* defined(TARGET_PPC64) */ + /* Original POWER */ + /* XXX: should be POWER (RIOS), RSC3308, RSC4608, + * POWER2 (RIOS2) & RSC2 (P2SC) here + */ +#if 0 + CPU_POWER = xxx, /* 0x20000 ? 0x30000 for RSC ? */ +#endif +#if 0 + CPU_POWER2 = xxx, /* 0x40000 ? */ +#endif + /* PA Semi core */ + CPU_POWERPC_PA6T = 0x00900000, +}; + +/* Logical PVR definitions for sPAPR */ +enum { + CPU_POWERPC_LOGICAL_2_04 = 0x0F000001, + CPU_POWERPC_LOGICAL_2_05 = 0x0F000002, + CPU_POWERPC_LOGICAL_2_06 = 0x0F000003, + CPU_POWERPC_LOGICAL_2_06_PLUS = 0x0F100003, + CPU_POWERPC_LOGICAL_2_07 = 0x0F000004, + CPU_POWERPC_LOGICAL_2_08 = 0x0F000005, +}; + +/* System version register (used on MPC 8xxx) */ +enum { + POWERPC_SVR_NONE = 0x00000000, + POWERPC_SVR_5200_v10 = 0x80110010, + POWERPC_SVR_5200_v11 = 0x80110011, + POWERPC_SVR_5200_v12 = 0x80110012, + POWERPC_SVR_5200B_v20 = 0x80110020, + POWERPC_SVR_5200B_v21 = 0x80110021, +#define POWERPC_SVR_55xx POWERPC_SVR_5567 +#if 0 + POWERPC_SVR_5533 = xxx, +#endif +#if 0 + POWERPC_SVR_5534 = xxx, +#endif +#if 0 + POWERPC_SVR_5553 = xxx, +#endif +#if 0 + POWERPC_SVR_5554 = xxx, +#endif +#if 0 + POWERPC_SVR_5561 = xxx, +#endif +#if 0 + POWERPC_SVR_5565 = xxx, +#endif +#if 0 + POWERPC_SVR_5566 = xxx, +#endif +#if 0 + POWERPC_SVR_5567 = xxx, +#endif +#if 0 + POWERPC_SVR_8313 = xxx, +#endif +#if 0 + POWERPC_SVR_8313E = xxx, +#endif +#if 0 + POWERPC_SVR_8314 = xxx, +#endif +#if 0 + POWERPC_SVR_8314E = xxx, +#endif +#if 0 + POWERPC_SVR_8315 = xxx, +#endif +#if 0 + POWERPC_SVR_8315E = xxx, +#endif +#if 0 + POWERPC_SVR_8321 = xxx, +#endif +#if 0 + POWERPC_SVR_8321E = xxx, +#endif +#if 0 + POWERPC_SVR_8323 = xxx, +#endif +#if 0 + POWERPC_SVR_8323E = xxx, +#endif + POWERPC_SVR_8343 = 0x80570010, + POWERPC_SVR_8343A = 0x80570030, + POWERPC_SVR_8343E = 0x80560010, + POWERPC_SVR_8343EA = 0x80560030, + POWERPC_SVR_8347P = 0x80550010, /* PBGA package */ + POWERPC_SVR_8347T = 0x80530010, /* TBGA package */ + POWERPC_SVR_8347AP = 0x80550030, /* PBGA package */ + POWERPC_SVR_8347AT = 0x80530030, /* TBGA package */ + POWERPC_SVR_8347EP = 0x80540010, /* PBGA package */ + POWERPC_SVR_8347ET = 0x80520010, /* TBGA package */ + POWERPC_SVR_8347EAP = 0x80540030, /* PBGA package */ + POWERPC_SVR_8347EAT = 0x80520030, /* TBGA package */ + POWERPC_SVR_8349 = 0x80510010, + POWERPC_SVR_8349A = 0x80510030, + POWERPC_SVR_8349E = 0x80500010, + POWERPC_SVR_8349EA = 0x80500030, +#if 0 + POWERPC_SVR_8358E = xxx, +#endif +#if 0 + POWERPC_SVR_8360E = xxx, +#endif +#define POWERPC_SVR_E500 0x40000000 + POWERPC_SVR_8377 = 0x80C70010 | POWERPC_SVR_E500, + POWERPC_SVR_8377E = 0x80C60010 | POWERPC_SVR_E500, + POWERPC_SVR_8378 = 0x80C50010 | POWERPC_SVR_E500, + POWERPC_SVR_8378E = 0x80C40010 | POWERPC_SVR_E500, + POWERPC_SVR_8379 = 0x80C30010 | POWERPC_SVR_E500, + POWERPC_SVR_8379E = 0x80C00010 | POWERPC_SVR_E500, + POWERPC_SVR_8533_v10 = 0x80340010 | POWERPC_SVR_E500, + POWERPC_SVR_8533_v11 = 0x80340011 | POWERPC_SVR_E500, + POWERPC_SVR_8533E_v10 = 0x803C0010 | POWERPC_SVR_E500, + POWERPC_SVR_8533E_v11 = 0x803C0011 | POWERPC_SVR_E500, + POWERPC_SVR_8540_v10 = 0x80300010 | POWERPC_SVR_E500, + POWERPC_SVR_8540_v20 = 0x80300020 | POWERPC_SVR_E500, + POWERPC_SVR_8540_v21 = 0x80300021 | POWERPC_SVR_E500, + POWERPC_SVR_8541_v10 = 0x80720010 | POWERPC_SVR_E500, + POWERPC_SVR_8541_v11 = 0x80720011 | POWERPC_SVR_E500, + POWERPC_SVR_8541E_v10 = 0x807A0010 | POWERPC_SVR_E500, + POWERPC_SVR_8541E_v11 = 0x807A0011 | POWERPC_SVR_E500, + POWERPC_SVR_8543_v10 = 0x80320010 | POWERPC_SVR_E500, + POWERPC_SVR_8543_v11 = 0x80320011 | POWERPC_SVR_E500, + POWERPC_SVR_8543_v20 = 0x80320020 | POWERPC_SVR_E500, + POWERPC_SVR_8543_v21 = 0x80320021 | POWERPC_SVR_E500, + POWERPC_SVR_8543E_v10 = 0x803A0010 | POWERPC_SVR_E500, + POWERPC_SVR_8543E_v11 = 0x803A0011 | POWERPC_SVR_E500, + POWERPC_SVR_8543E_v20 = 0x803A0020 | POWERPC_SVR_E500, + POWERPC_SVR_8543E_v21 = 0x803A0021 | POWERPC_SVR_E500, + POWERPC_SVR_8544_v10 = 0x80340110 | POWERPC_SVR_E500, + POWERPC_SVR_8544_v11 = 0x80340111 | POWERPC_SVR_E500, + POWERPC_SVR_8544E_v10 = 0x803C0110 | POWERPC_SVR_E500, + POWERPC_SVR_8544E_v11 = 0x803C0111 | POWERPC_SVR_E500, + POWERPC_SVR_8545_v20 = 0x80310220 | POWERPC_SVR_E500, + POWERPC_SVR_8545_v21 = 0x80310221 | POWERPC_SVR_E500, + POWERPC_SVR_8545E_v20 = 0x80390220 | POWERPC_SVR_E500, + POWERPC_SVR_8545E_v21 = 0x80390221 | POWERPC_SVR_E500, + POWERPC_SVR_8547E_v20 = 0x80390120 | POWERPC_SVR_E500, + POWERPC_SVR_8547E_v21 = 0x80390121 | POWERPC_SVR_E500, + POWERPC_SVR_8548_v10 = 0x80310010 | POWERPC_SVR_E500, + POWERPC_SVR_8548_v11 = 0x80310011 | POWERPC_SVR_E500, + POWERPC_SVR_8548_v20 = 0x80310020 | POWERPC_SVR_E500, + POWERPC_SVR_8548_v21 = 0x80310021 | POWERPC_SVR_E500, + POWERPC_SVR_8548E_v10 = 0x80390010 | POWERPC_SVR_E500, + POWERPC_SVR_8548E_v11 = 0x80390011 | POWERPC_SVR_E500, + POWERPC_SVR_8548E_v20 = 0x80390020 | POWERPC_SVR_E500, + POWERPC_SVR_8548E_v21 = 0x80390021 | POWERPC_SVR_E500, + POWERPC_SVR_8555_v10 = 0x80710010 | POWERPC_SVR_E500, + POWERPC_SVR_8555_v11 = 0x80710011 | POWERPC_SVR_E500, + POWERPC_SVR_8555E_v10 = 0x80790010 | POWERPC_SVR_E500, + POWERPC_SVR_8555E_v11 = 0x80790011 | POWERPC_SVR_E500, + POWERPC_SVR_8560_v10 = 0x80700010 | POWERPC_SVR_E500, + POWERPC_SVR_8560_v20 = 0x80700020 | POWERPC_SVR_E500, + POWERPC_SVR_8560_v21 = 0x80700021 | POWERPC_SVR_E500, + POWERPC_SVR_8567 = 0x80750111 | POWERPC_SVR_E500, + POWERPC_SVR_8567E = 0x807D0111 | POWERPC_SVR_E500, + POWERPC_SVR_8568 = 0x80750011 | POWERPC_SVR_E500, + POWERPC_SVR_8568E = 0x807D0011 | POWERPC_SVR_E500, + POWERPC_SVR_8572 = 0x80E00010 | POWERPC_SVR_E500, + POWERPC_SVR_8572E = 0x80E80010 | POWERPC_SVR_E500, + POWERPC_SVR_8610 = 0x80A00011, + POWERPC_SVR_8641 = 0x80900021, + POWERPC_SVR_8641D = 0x80900121, +}; + +#endif diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h new file mode 100644 index 0000000000..d46c31a15d --- /dev/null +++ b/target/ppc/cpu-qom.h @@ -0,0 +1,219 @@ +/* + * QEMU PowerPC 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_PPC_CPU_QOM_H +#define QEMU_PPC_CPU_QOM_H + +#include "qom/cpu.h" + +#ifdef TARGET_PPC64 +#define TYPE_POWERPC_CPU "powerpc64-cpu" +#elif defined(TARGET_PPCEMB) +#define TYPE_POWERPC_CPU "embedded-powerpc-cpu" +#else +#define TYPE_POWERPC_CPU "powerpc-cpu" +#endif + +#define POWERPC_CPU_CLASS(klass) \ + OBJECT_CLASS_CHECK(PowerPCCPUClass, (klass), TYPE_POWERPC_CPU) +#define POWERPC_CPU(obj) \ + OBJECT_CHECK(PowerPCCPU, (obj), TYPE_POWERPC_CPU) +#define POWERPC_CPU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PowerPCCPUClass, (obj), TYPE_POWERPC_CPU) + +typedef struct PowerPCCPU PowerPCCPU; +typedef struct CPUPPCState CPUPPCState; +typedef struct ppc_tb_t ppc_tb_t; +typedef struct ppc_dcr_t ppc_dcr_t; + +/*****************************************************************************/ +/* MMU model */ +typedef enum powerpc_mmu_t powerpc_mmu_t; +enum powerpc_mmu_t { + POWERPC_MMU_UNKNOWN = 0x00000000, + /* Standard 32 bits PowerPC MMU */ + POWERPC_MMU_32B = 0x00000001, + /* PowerPC 6xx MMU with software TLB */ + POWERPC_MMU_SOFT_6xx = 0x00000002, + /* PowerPC 74xx MMU with software TLB */ + POWERPC_MMU_SOFT_74xx = 0x00000003, + /* PowerPC 4xx MMU with software TLB */ + POWERPC_MMU_SOFT_4xx = 0x00000004, + /* PowerPC 4xx MMU with software TLB and zones protections */ + POWERPC_MMU_SOFT_4xx_Z = 0x00000005, + /* PowerPC MMU in real mode only */ + POWERPC_MMU_REAL = 0x00000006, + /* Freescale MPC8xx MMU model */ + POWERPC_MMU_MPC8xx = 0x00000007, + /* BookE MMU model */ + POWERPC_MMU_BOOKE = 0x00000008, + /* BookE 2.06 MMU model */ + POWERPC_MMU_BOOKE206 = 0x00000009, + /* PowerPC 601 MMU model (specific BATs format) */ + POWERPC_MMU_601 = 0x0000000A, +#define POWERPC_MMU_64 0x00010000 +#define POWERPC_MMU_1TSEG 0x00020000 +#define POWERPC_MMU_AMR 0x00040000 +#define POWERPC_MMU_64K 0x00080000 + /* 64 bits PowerPC MMU */ + POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001, + /* Architecture 2.03 and later (has LPCR) */ + POWERPC_MMU_2_03 = POWERPC_MMU_64 | 0x00000002, + /* Architecture 2.06 variant */ + POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG + | POWERPC_MMU_64K + | POWERPC_MMU_AMR | 0x00000003, + /* Architecture 2.06 "degraded" (no 1T segments) */ + POWERPC_MMU_2_06a = POWERPC_MMU_64 | POWERPC_MMU_AMR + | 0x00000003, + /* Architecture 2.07 variant */ + POWERPC_MMU_2_07 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG + | POWERPC_MMU_64K + | POWERPC_MMU_AMR | 0x00000004, + /* FIXME Add POWERPC_MMU_3_OO defines */ + /* Architecture 2.07 "degraded" (no 1T segments) */ + POWERPC_MMU_2_07a = POWERPC_MMU_64 | POWERPC_MMU_AMR + | 0x00000004, +}; + +/*****************************************************************************/ +/* Exception model */ +typedef enum powerpc_excp_t powerpc_excp_t; +enum powerpc_excp_t { + POWERPC_EXCP_UNKNOWN = 0, + /* Standard PowerPC exception model */ + POWERPC_EXCP_STD, + /* PowerPC 40x exception model */ + POWERPC_EXCP_40x, + /* PowerPC 601 exception model */ + POWERPC_EXCP_601, + /* PowerPC 602 exception model */ + POWERPC_EXCP_602, + /* PowerPC 603 exception model */ + POWERPC_EXCP_603, + /* PowerPC 603e exception model */ + POWERPC_EXCP_603E, + /* PowerPC G2 exception model */ + POWERPC_EXCP_G2, + /* PowerPC 604 exception model */ + POWERPC_EXCP_604, + /* PowerPC 7x0 exception model */ + POWERPC_EXCP_7x0, + /* PowerPC 7x5 exception model */ + POWERPC_EXCP_7x5, + /* PowerPC 74xx exception model */ + POWERPC_EXCP_74xx, + /* BookE exception model */ + POWERPC_EXCP_BOOKE, + /* PowerPC 970 exception model */ + POWERPC_EXCP_970, + /* POWER7 exception model */ + POWERPC_EXCP_POWER7, + /* POWER8 exception model */ + POWERPC_EXCP_POWER8, +}; + +/*****************************************************************************/ +/* PM instructions */ +typedef enum { + PPC_PM_DOZE, + PPC_PM_NAP, + PPC_PM_SLEEP, + PPC_PM_RVWINKLE, +} powerpc_pm_insn_t; + +/*****************************************************************************/ +/* Input pins model */ +typedef enum powerpc_input_t powerpc_input_t; +enum powerpc_input_t { + PPC_FLAGS_INPUT_UNKNOWN = 0, + /* PowerPC 6xx bus */ + PPC_FLAGS_INPUT_6xx, + /* BookE bus */ + PPC_FLAGS_INPUT_BookE, + /* PowerPC 405 bus */ + PPC_FLAGS_INPUT_405, + /* PowerPC 970 bus */ + PPC_FLAGS_INPUT_970, + /* PowerPC POWER7 bus */ + PPC_FLAGS_INPUT_POWER7, + /* PowerPC 401 bus */ + PPC_FLAGS_INPUT_401, + /* Freescale RCPU bus */ + PPC_FLAGS_INPUT_RCPU, +}; + +struct ppc_segment_page_sizes; + +/** + * PowerPCCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * + * A PowerPC CPU model. + */ +typedef struct PowerPCCPUClass { + /*< private >*/ + CPUClass parent_class; + /*< public >*/ + + DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; + void (*parent_reset)(CPUState *cpu); + + uint32_t pvr; + bool (*pvr_match)(struct PowerPCCPUClass *pcc, uint32_t pvr); + uint64_t pcr_mask; /* Available bits in PCR register */ + uint64_t pcr_supported; /* Bits for supported PowerISA versions */ + uint32_t svr; + uint64_t insns_flags; + uint64_t insns_flags2; + uint64_t msr_mask; + powerpc_mmu_t mmu_model; + powerpc_excp_t excp_model; + powerpc_input_t bus_model; + uint32_t flags; + int bfd_mach; + uint32_t l1_dcache_size, l1_icache_size; + const struct ppc_segment_page_sizes *sps; + void (*init_proc)(CPUPPCState *env); + int (*check_pow)(CPUPPCState *env); + int (*handle_mmu_fault)(PowerPCCPU *cpu, vaddr eaddr, int rwx, int mmu_idx); + bool (*interrupts_big_endian)(PowerPCCPU *cpu); +} PowerPCCPUClass; + +#ifndef CONFIG_USER_ONLY +typedef struct PPCTimebase { + uint64_t guest_timebase; + int64_t time_of_the_day_ns; +} PPCTimebase; + +extern const struct VMStateDescription vmstate_ppc_timebase; + +#define VMSTATE_PPC_TIMEBASE_V(_field, _state, _version) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .size = sizeof(PPCTimebase), \ + .vmsd = &vmstate_ppc_timebase, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, PPCTimebase), \ +} +#endif + +#endif diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h new file mode 100644 index 0000000000..2a50c43689 --- /dev/null +++ b/target/ppc/cpu.h @@ -0,0 +1,2454 @@ +/* + * PowerPC emulation cpu definitions for qemu. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * + * 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 PPC_CPU_H +#define PPC_CPU_H + +#include "qemu-common.h" + +//#define PPC_EMULATE_32BITS_HYPV + +#if defined (TARGET_PPC64) +/* PowerPC 64 definitions */ +#define TARGET_LONG_BITS 64 +#define TARGET_PAGE_BITS 12 + +/* Note that the official physical address space bits is 62-M where M + is implementation dependent. I've not looked up M for the set of + cpus we emulate at the system level. */ +#define TARGET_PHYS_ADDR_SPACE_BITS 62 + +/* Note that the PPC environment architecture talks about 80 bit virtual + addresses, with segmentation. Obviously that's not all visible to a + single process, which is all we're concerned with here. */ +#ifdef TARGET_ABI32 +# define TARGET_VIRT_ADDR_SPACE_BITS 32 +#else +# define TARGET_VIRT_ADDR_SPACE_BITS 64 +#endif + +#define TARGET_PAGE_BITS_64K 16 +#define TARGET_PAGE_BITS_16M 24 + +#else /* defined (TARGET_PPC64) */ +/* PowerPC 32 definitions */ +#define TARGET_LONG_BITS 32 + +#if defined(TARGET_PPCEMB) +/* Specific definitions for PowerPC embedded */ +/* BookE have 36 bits physical address space */ +#if defined(CONFIG_USER_ONLY) +/* It looks like a lot of Linux programs assume page size + * is 4kB long. This is evil, but we have to deal with it... + */ +#define TARGET_PAGE_BITS 12 +#else /* defined(CONFIG_USER_ONLY) */ +/* Pages can be 1 kB small */ +#define TARGET_PAGE_BITS 10 +#endif /* defined(CONFIG_USER_ONLY) */ +#else /* defined(TARGET_PPCEMB) */ +/* "standard" PowerPC 32 definitions */ +#define TARGET_PAGE_BITS 12 +#endif /* defined(TARGET_PPCEMB) */ + +#define TARGET_PHYS_ADDR_SPACE_BITS 36 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + +#endif /* defined (TARGET_PPC64) */ + +#define CPUArchState struct CPUPPCState + +#include "exec/cpu-defs.h" +#include "cpu-qom.h" +#include "fpu/softfloat.h" + +#if defined (TARGET_PPC64) +#define PPC_ELF_MACHINE EM_PPC64 +#else +#define PPC_ELF_MACHINE EM_PPC +#endif + +/*****************************************************************************/ +/* Exception vectors definitions */ +enum { + POWERPC_EXCP_NONE = -1, + /* The 64 first entries are used by the PowerPC embedded specification */ + POWERPC_EXCP_CRITICAL = 0, /* Critical input */ + POWERPC_EXCP_MCHECK = 1, /* Machine check exception */ + POWERPC_EXCP_DSI = 2, /* Data storage exception */ + POWERPC_EXCP_ISI = 3, /* Instruction storage exception */ + POWERPC_EXCP_EXTERNAL = 4, /* External input */ + POWERPC_EXCP_ALIGN = 5, /* Alignment exception */ + POWERPC_EXCP_PROGRAM = 6, /* Program exception */ + POWERPC_EXCP_FPU = 7, /* Floating-point unavailable exception */ + POWERPC_EXCP_SYSCALL = 8, /* System call exception */ + POWERPC_EXCP_APU = 9, /* Auxiliary processor unavailable */ + POWERPC_EXCP_DECR = 10, /* Decrementer exception */ + POWERPC_EXCP_FIT = 11, /* Fixed-interval timer interrupt */ + POWERPC_EXCP_WDT = 12, /* Watchdog timer interrupt */ + POWERPC_EXCP_DTLB = 13, /* Data TLB miss */ + POWERPC_EXCP_ITLB = 14, /* Instruction TLB miss */ + POWERPC_EXCP_DEBUG = 15, /* Debug interrupt */ + /* Vectors 16 to 31 are reserved */ + POWERPC_EXCP_SPEU = 32, /* SPE/embedded floating-point unavailable */ + POWERPC_EXCP_EFPDI = 33, /* Embedded floating-point data interrupt */ + POWERPC_EXCP_EFPRI = 34, /* Embedded floating-point round interrupt */ + POWERPC_EXCP_EPERFM = 35, /* Embedded performance monitor interrupt */ + POWERPC_EXCP_DOORI = 36, /* Embedded doorbell interrupt */ + POWERPC_EXCP_DOORCI = 37, /* Embedded doorbell critical interrupt */ + POWERPC_EXCP_GDOORI = 38, /* Embedded guest doorbell interrupt */ + POWERPC_EXCP_GDOORCI = 39, /* Embedded guest doorbell critical interrupt*/ + POWERPC_EXCP_HYPPRIV = 41, /* Embedded hypervisor priv instruction */ + /* Vectors 42 to 63 are reserved */ + /* Exceptions defined in the PowerPC server specification */ + /* Server doorbell variants */ +#define POWERPC_EXCP_SDOOR POWERPC_EXCP_GDOORI +#define POWERPC_EXCP_SDOOR_HV POWERPC_EXCP_DOORI + POWERPC_EXCP_RESET = 64, /* System reset exception */ + POWERPC_EXCP_DSEG = 65, /* Data segment exception */ + POWERPC_EXCP_ISEG = 66, /* Instruction segment exception */ + POWERPC_EXCP_HDECR = 67, /* Hypervisor decrementer exception */ + POWERPC_EXCP_TRACE = 68, /* Trace exception */ + POWERPC_EXCP_HDSI = 69, /* Hypervisor data storage exception */ + POWERPC_EXCP_HISI = 70, /* Hypervisor instruction storage exception */ + POWERPC_EXCP_HDSEG = 71, /* Hypervisor data segment exception */ + POWERPC_EXCP_HISEG = 72, /* Hypervisor instruction segment exception */ + POWERPC_EXCP_VPU = 73, /* Vector unavailable exception */ + /* 40x specific exceptions */ + POWERPC_EXCP_PIT = 74, /* Programmable interval timer interrupt */ + /* 601 specific exceptions */ + POWERPC_EXCP_IO = 75, /* IO error exception */ + POWERPC_EXCP_RUNM = 76, /* Run mode exception */ + /* 602 specific exceptions */ + POWERPC_EXCP_EMUL = 77, /* Emulation trap exception */ + /* 602/603 specific exceptions */ + POWERPC_EXCP_IFTLB = 78, /* Instruction fetch TLB miss */ + POWERPC_EXCP_DLTLB = 79, /* Data load TLB miss */ + POWERPC_EXCP_DSTLB = 80, /* Data store TLB miss */ + /* Exceptions available on most PowerPC */ + POWERPC_EXCP_FPA = 81, /* Floating-point assist exception */ + POWERPC_EXCP_DABR = 82, /* Data address breakpoint */ + POWERPC_EXCP_IABR = 83, /* Instruction address breakpoint */ + POWERPC_EXCP_SMI = 84, /* System management interrupt */ + POWERPC_EXCP_PERFM = 85, /* Embedded performance monitor interrupt */ + /* 7xx/74xx specific exceptions */ + POWERPC_EXCP_THERM = 86, /* Thermal interrupt */ + /* 74xx specific exceptions */ + POWERPC_EXCP_VPUA = 87, /* Vector assist exception */ + /* 970FX specific exceptions */ + POWERPC_EXCP_SOFTP = 88, /* Soft patch exception */ + POWERPC_EXCP_MAINT = 89, /* Maintenance exception */ + /* Freescale embedded cores specific exceptions */ + POWERPC_EXCP_MEXTBR = 90, /* Maskable external breakpoint */ + POWERPC_EXCP_NMEXTBR = 91, /* Non maskable external breakpoint */ + POWERPC_EXCP_ITLBE = 92, /* Instruction TLB error */ + POWERPC_EXCP_DTLBE = 93, /* Data TLB error */ + /* VSX Unavailable (Power ISA 2.06 and later) */ + POWERPC_EXCP_VSXU = 94, /* VSX Unavailable */ + POWERPC_EXCP_FU = 95, /* Facility Unavailable */ + /* Additional ISA 2.06 and later server exceptions */ + POWERPC_EXCP_HV_EMU = 96, /* HV emulation assistance */ + POWERPC_EXCP_HV_MAINT = 97, /* HMI */ + POWERPC_EXCP_HV_FU = 98, /* Hypervisor Facility unavailable */ + /* EOL */ + POWERPC_EXCP_NB = 99, + /* QEMU exceptions: used internally during code translation */ + POWERPC_EXCP_STOP = 0x200, /* stop translation */ + POWERPC_EXCP_BRANCH = 0x201, /* branch instruction */ + /* QEMU exceptions: special cases we want to stop translation */ + POWERPC_EXCP_SYNC = 0x202, /* context synchronizing instruction */ + POWERPC_EXCP_SYSCALL_USER = 0x203, /* System call in user mode only */ + POWERPC_EXCP_STCX = 0x204 /* Conditional stores in user mode */ +}; + +/* Exceptions error codes */ +enum { + /* Exception subtypes for POWERPC_EXCP_ALIGN */ + POWERPC_EXCP_ALIGN_FP = 0x01, /* FP alignment exception */ + POWERPC_EXCP_ALIGN_LST = 0x02, /* Unaligned mult/extern load/store */ + POWERPC_EXCP_ALIGN_LE = 0x03, /* Multiple little-endian access */ + POWERPC_EXCP_ALIGN_PROT = 0x04, /* Access cross protection boundary */ + POWERPC_EXCP_ALIGN_BAT = 0x05, /* Access cross a BAT/seg boundary */ + POWERPC_EXCP_ALIGN_CACHE = 0x06, /* Impossible dcbz access */ + /* Exception subtypes for POWERPC_EXCP_PROGRAM */ + /* FP exceptions */ + POWERPC_EXCP_FP = 0x10, + POWERPC_EXCP_FP_OX = 0x01, /* FP overflow */ + POWERPC_EXCP_FP_UX = 0x02, /* FP underflow */ + POWERPC_EXCP_FP_ZX = 0x03, /* FP divide by zero */ + POWERPC_EXCP_FP_XX = 0x04, /* FP inexact */ + POWERPC_EXCP_FP_VXSNAN = 0x05, /* FP invalid SNaN op */ + POWERPC_EXCP_FP_VXISI = 0x06, /* FP invalid infinite subtraction */ + POWERPC_EXCP_FP_VXIDI = 0x07, /* FP invalid infinite divide */ + POWERPC_EXCP_FP_VXZDZ = 0x08, /* FP invalid zero divide */ + POWERPC_EXCP_FP_VXIMZ = 0x09, /* FP invalid infinite * zero */ + POWERPC_EXCP_FP_VXVC = 0x0A, /* FP invalid compare */ + POWERPC_EXCP_FP_VXSOFT = 0x0B, /* FP invalid operation */ + POWERPC_EXCP_FP_VXSQRT = 0x0C, /* FP invalid square root */ + POWERPC_EXCP_FP_VXCVI = 0x0D, /* FP invalid integer conversion */ + /* Invalid instruction */ + POWERPC_EXCP_INVAL = 0x20, + POWERPC_EXCP_INVAL_INVAL = 0x01, /* Invalid instruction */ + POWERPC_EXCP_INVAL_LSWX = 0x02, /* Invalid lswx instruction */ + POWERPC_EXCP_INVAL_SPR = 0x03, /* Invalid SPR access */ + POWERPC_EXCP_INVAL_FP = 0x04, /* Unimplemented mandatory fp instr */ + /* Privileged instruction */ + POWERPC_EXCP_PRIV = 0x30, + POWERPC_EXCP_PRIV_OPC = 0x01, /* Privileged operation exception */ + POWERPC_EXCP_PRIV_REG = 0x02, /* Privileged register exception */ + /* Trap */ + POWERPC_EXCP_TRAP = 0x40, +}; + +#define PPC_INPUT(env) (env->bus_model) + +/*****************************************************************************/ +typedef struct opc_handler_t opc_handler_t; + +/*****************************************************************************/ +/* Types used to describe some PowerPC registers */ +typedef struct DisasContext DisasContext; +typedef struct ppc_spr_t ppc_spr_t; +typedef union ppc_avr_t ppc_avr_t; +typedef union ppc_tlb_t ppc_tlb_t; + +/* SPR access micro-ops generations callbacks */ +struct ppc_spr_t { + void (*uea_read)(DisasContext *ctx, int gpr_num, int spr_num); + void (*uea_write)(DisasContext *ctx, int spr_num, int gpr_num); +#if !defined(CONFIG_USER_ONLY) + void (*oea_read)(DisasContext *ctx, int gpr_num, int spr_num); + void (*oea_write)(DisasContext *ctx, int spr_num, int gpr_num); + void (*hea_read)(DisasContext *ctx, int gpr_num, int spr_num); + void (*hea_write)(DisasContext *ctx, int spr_num, int gpr_num); +#endif + const char *name; + target_ulong default_value; +#ifdef CONFIG_KVM + /* We (ab)use the fact that all the SPRs will have ids for the + * ONE_REG interface will have KVM_REG_PPC to use 0 as meaning, + * don't sync this */ + uint64_t one_reg_id; +#endif +}; + +/* Altivec registers (128 bits) */ +union ppc_avr_t { + float32 f[4]; + uint8_t u8[16]; + uint16_t u16[8]; + uint32_t u32[4]; + int8_t s8[16]; + int16_t s16[8]; + int32_t s32[4]; + uint64_t u64[2]; + int64_t s64[2]; +#ifdef CONFIG_INT128 + __uint128_t u128; +#endif +}; + +#if !defined(CONFIG_USER_ONLY) +/* Software TLB cache */ +typedef struct ppc6xx_tlb_t ppc6xx_tlb_t; +struct ppc6xx_tlb_t { + target_ulong pte0; + target_ulong pte1; + target_ulong EPN; +}; + +typedef struct ppcemb_tlb_t ppcemb_tlb_t; +struct ppcemb_tlb_t { + uint64_t RPN; + target_ulong EPN; + target_ulong PID; + target_ulong size; + uint32_t prot; + uint32_t attr; /* Storage attributes */ +}; + +typedef struct ppcmas_tlb_t { + uint32_t mas8; + uint32_t mas1; + uint64_t mas2; + uint64_t mas7_3; +} ppcmas_tlb_t; + +union ppc_tlb_t { + ppc6xx_tlb_t *tlb6; + ppcemb_tlb_t *tlbe; + ppcmas_tlb_t *tlbm; +}; + +/* possible TLB variants */ +#define TLB_NONE 0 +#define TLB_6XX 1 +#define TLB_EMB 2 +#define TLB_MAS 3 +#endif + +#define SDR_32_HTABORG 0xFFFF0000UL +#define SDR_32_HTABMASK 0x000001FFUL + +#if defined(TARGET_PPC64) +#define SDR_64_HTABORG 0xFFFFFFFFFFFC0000ULL +#define SDR_64_HTABSIZE 0x000000000000001FULL +#endif /* defined(TARGET_PPC64 */ + +typedef struct ppc_slb_t ppc_slb_t; +struct ppc_slb_t { + uint64_t esid; + uint64_t vsid; + const struct ppc_one_seg_page_size *sps; +}; + +#define MAX_SLB_ENTRIES 64 +#define SEGMENT_SHIFT_256M 28 +#define SEGMENT_MASK_256M (~((1ULL << SEGMENT_SHIFT_256M) - 1)) + +#define SEGMENT_SHIFT_1T 40 +#define SEGMENT_MASK_1T (~((1ULL << SEGMENT_SHIFT_1T) - 1)) + + +/*****************************************************************************/ +/* Machine state register bits definition */ +#define MSR_SF 63 /* Sixty-four-bit mode hflags */ +#define MSR_TAG 62 /* Tag-active mode (POWERx ?) */ +#define MSR_ISF 61 /* Sixty-four-bit interrupt mode on 630 */ +#define MSR_SHV 60 /* hypervisor state hflags */ +#define MSR_TS0 34 /* Transactional state, 2 bits (Book3s) */ +#define MSR_TS1 33 +#define MSR_TM 32 /* Transactional Memory Available (Book3s) */ +#define MSR_CM 31 /* Computation mode for BookE hflags */ +#define MSR_ICM 30 /* Interrupt computation mode for BookE */ +#define MSR_THV 29 /* hypervisor state for 32 bits PowerPC hflags */ +#define MSR_GS 28 /* guest state for BookE */ +#define MSR_UCLE 26 /* User-mode cache lock enable for BookE */ +#define MSR_VR 25 /* altivec available x hflags */ +#define MSR_SPE 25 /* SPE enable for BookE x hflags */ +#define MSR_AP 23 /* Access privilege state on 602 hflags */ +#define MSR_VSX 23 /* Vector Scalar Extension (ISA 2.06 and later) x hflags */ +#define MSR_SA 22 /* Supervisor access mode on 602 hflags */ +#define MSR_KEY 19 /* key bit on 603e */ +#define MSR_POW 18 /* Power management */ +#define MSR_TGPR 17 /* TGPR usage on 602/603 x */ +#define MSR_CE 17 /* Critical interrupt enable on embedded PowerPC x */ +#define MSR_ILE 16 /* Interrupt little-endian mode */ +#define MSR_EE 15 /* External interrupt enable */ +#define MSR_PR 14 /* Problem state hflags */ +#define MSR_FP 13 /* Floating point available hflags */ +#define MSR_ME 12 /* Machine check interrupt enable */ +#define MSR_FE0 11 /* Floating point exception mode 0 hflags */ +#define MSR_SE 10 /* Single-step trace enable x hflags */ +#define MSR_DWE 10 /* Debug wait enable on 405 x */ +#define MSR_UBLE 10 /* User BTB lock enable on e500 x */ +#define MSR_BE 9 /* Branch trace enable x hflags */ +#define MSR_DE 9 /* Debug interrupts enable on embedded PowerPC x */ +#define MSR_FE1 8 /* Floating point exception mode 1 hflags */ +#define MSR_AL 7 /* AL bit on POWER */ +#define MSR_EP 6 /* Exception prefix on 601 */ +#define MSR_IR 5 /* Instruction relocate */ +#define MSR_DR 4 /* Data relocate */ +#define MSR_IS 5 /* Instruction address space (BookE) */ +#define MSR_DS 4 /* Data address space (BookE) */ +#define MSR_PE 3 /* Protection enable on 403 */ +#define MSR_PX 2 /* Protection exclusive on 403 x */ +#define MSR_PMM 2 /* Performance monitor mark on POWER x */ +#define MSR_RI 1 /* Recoverable interrupt 1 */ +#define MSR_LE 0 /* Little-endian mode 1 hflags */ + +/* LPCR bits */ +#define LPCR_VPM0 (1ull << (63 - 0)) +#define LPCR_VPM1 (1ull << (63 - 1)) +#define LPCR_ISL (1ull << (63 - 2)) +#define LPCR_KBV (1ull << (63 - 3)) +#define LPCR_DPFD_SHIFT (63 - 11) +#define LPCR_DPFD (0x3ull << LPCR_DPFD_SHIFT) +#define LPCR_VRMASD_SHIFT (63 - 16) +#define LPCR_VRMASD (0x1full << LPCR_VRMASD_SHIFT) +#define LPCR_RMLS_SHIFT (63 - 37) +#define LPCR_RMLS (0xfull << LPCR_RMLS_SHIFT) +#define LPCR_ILE (1ull << (63 - 38)) +#define LPCR_AIL_SHIFT (63 - 40) /* Alternate interrupt location */ +#define LPCR_AIL (3ull << LPCR_AIL_SHIFT) +#define LPCR_ONL (1ull << (63 - 45)) +#define LPCR_P7_PECE0 (1ull << (63 - 49)) +#define LPCR_P7_PECE1 (1ull << (63 - 50)) +#define LPCR_P7_PECE2 (1ull << (63 - 51)) +#define LPCR_P8_PECE0 (1ull << (63 - 47)) +#define LPCR_P8_PECE1 (1ull << (63 - 48)) +#define LPCR_P8_PECE2 (1ull << (63 - 49)) +#define LPCR_P8_PECE3 (1ull << (63 - 50)) +#define LPCR_P8_PECE4 (1ull << (63 - 51)) +#define LPCR_MER (1ull << (63 - 52)) +#define LPCR_TC (1ull << (63 - 54)) +#define LPCR_LPES0 (1ull << (63 - 60)) +#define LPCR_LPES1 (1ull << (63 - 61)) +#define LPCR_RMI (1ull << (63 - 62)) +#define LPCR_HDICE (1ull << (63 - 63)) + +#define msr_sf ((env->msr >> MSR_SF) & 1) +#define msr_isf ((env->msr >> MSR_ISF) & 1) +#define msr_shv ((env->msr >> MSR_SHV) & 1) +#define msr_cm ((env->msr >> MSR_CM) & 1) +#define msr_icm ((env->msr >> MSR_ICM) & 1) +#define msr_thv ((env->msr >> MSR_THV) & 1) +#define msr_gs ((env->msr >> MSR_GS) & 1) +#define msr_ucle ((env->msr >> MSR_UCLE) & 1) +#define msr_vr ((env->msr >> MSR_VR) & 1) +#define msr_spe ((env->msr >> MSR_SPE) & 1) +#define msr_ap ((env->msr >> MSR_AP) & 1) +#define msr_vsx ((env->msr >> MSR_VSX) & 1) +#define msr_sa ((env->msr >> MSR_SA) & 1) +#define msr_key ((env->msr >> MSR_KEY) & 1) +#define msr_pow ((env->msr >> MSR_POW) & 1) +#define msr_tgpr ((env->msr >> MSR_TGPR) & 1) +#define msr_ce ((env->msr >> MSR_CE) & 1) +#define msr_ile ((env->msr >> MSR_ILE) & 1) +#define msr_ee ((env->msr >> MSR_EE) & 1) +#define msr_pr ((env->msr >> MSR_PR) & 1) +#define msr_fp ((env->msr >> MSR_FP) & 1) +#define msr_me ((env->msr >> MSR_ME) & 1) +#define msr_fe0 ((env->msr >> MSR_FE0) & 1) +#define msr_se ((env->msr >> MSR_SE) & 1) +#define msr_dwe ((env->msr >> MSR_DWE) & 1) +#define msr_uble ((env->msr >> MSR_UBLE) & 1) +#define msr_be ((env->msr >> MSR_BE) & 1) +#define msr_de ((env->msr >> MSR_DE) & 1) +#define msr_fe1 ((env->msr >> MSR_FE1) & 1) +#define msr_al ((env->msr >> MSR_AL) & 1) +#define msr_ep ((env->msr >> MSR_EP) & 1) +#define msr_ir ((env->msr >> MSR_IR) & 1) +#define msr_dr ((env->msr >> MSR_DR) & 1) +#define msr_is ((env->msr >> MSR_IS) & 1) +#define msr_ds ((env->msr >> MSR_DS) & 1) +#define msr_pe ((env->msr >> MSR_PE) & 1) +#define msr_px ((env->msr >> MSR_PX) & 1) +#define msr_pmm ((env->msr >> MSR_PMM) & 1) +#define msr_ri ((env->msr >> MSR_RI) & 1) +#define msr_le ((env->msr >> MSR_LE) & 1) +#define msr_ts ((env->msr >> MSR_TS1) & 3) +#define msr_tm ((env->msr >> MSR_TM) & 1) + +/* Hypervisor bit is more specific */ +#if defined(TARGET_PPC64) +#define MSR_HVB (1ULL << MSR_SHV) +#define msr_hv msr_shv +#else +#if defined(PPC_EMULATE_32BITS_HYPV) +#define MSR_HVB (1ULL << MSR_THV) +#define msr_hv msr_thv +#else +#define MSR_HVB (0ULL) +#define msr_hv (0) +#endif +#endif + +/* Facility Status and Control (FSCR) bits */ +#define FSCR_EBB (63 - 56) /* Event-Based Branch Facility */ +#define FSCR_TAR (63 - 55) /* Target Address Register */ +/* Interrupt cause mask and position in FSCR. HFSCR has the same format */ +#define FSCR_IC_MASK (0xFFULL) +#define FSCR_IC_POS (63 - 7) +#define FSCR_IC_DSCR_SPR3 2 +#define FSCR_IC_PMU 3 +#define FSCR_IC_BHRB 4 +#define FSCR_IC_TM 5 +#define FSCR_IC_EBB 7 +#define FSCR_IC_TAR 8 + +/* Exception state register bits definition */ +#define ESR_PIL (1 << (63 - 36)) /* Illegal Instruction */ +#define ESR_PPR (1 << (63 - 37)) /* Privileged Instruction */ +#define ESR_PTR (1 << (63 - 38)) /* Trap */ +#define ESR_FP (1 << (63 - 39)) /* Floating-Point Operation */ +#define ESR_ST (1 << (63 - 40)) /* Store Operation */ +#define ESR_AP (1 << (63 - 44)) /* Auxiliary Processor Operation */ +#define ESR_PUO (1 << (63 - 45)) /* Unimplemented Operation */ +#define ESR_BO (1 << (63 - 46)) /* Byte Ordering */ +#define ESR_PIE (1 << (63 - 47)) /* Imprecise exception */ +#define ESR_DATA (1 << (63 - 53)) /* Data Access (Embedded page table) */ +#define ESR_TLBI (1 << (63 - 54)) /* TLB Ineligible (Embedded page table) */ +#define ESR_PT (1 << (63 - 55)) /* Page Table (Embedded page table) */ +#define ESR_SPV (1 << (63 - 56)) /* SPE/VMX operation */ +#define ESR_EPID (1 << (63 - 57)) /* External Process ID operation */ +#define ESR_VLEMI (1 << (63 - 58)) /* VLE operation */ +#define ESR_MIF (1 << (63 - 62)) /* Misaligned instruction (VLE) */ + +/* Transaction EXception And Summary Register bits */ +#define TEXASR_FAILURE_PERSISTENT (63 - 7) +#define TEXASR_DISALLOWED (63 - 8) +#define TEXASR_NESTING_OVERFLOW (63 - 9) +#define TEXASR_FOOTPRINT_OVERFLOW (63 - 10) +#define TEXASR_SELF_INDUCED_CONFLICT (63 - 11) +#define TEXASR_NON_TRANSACTIONAL_CONFLICT (63 - 12) +#define TEXASR_TRANSACTION_CONFLICT (63 - 13) +#define TEXASR_TRANSLATION_INVALIDATION_CONFLICT (63 - 14) +#define TEXASR_IMPLEMENTATION_SPECIFIC (63 - 15) +#define TEXASR_INSTRUCTION_FETCH_CONFLICT (63 - 16) +#define TEXASR_ABORT (63 - 31) +#define TEXASR_SUSPENDED (63 - 32) +#define TEXASR_PRIVILEGE_HV (63 - 34) +#define TEXASR_PRIVILEGE_PR (63 - 35) +#define TEXASR_FAILURE_SUMMARY (63 - 36) +#define TEXASR_TFIAR_EXACT (63 - 37) +#define TEXASR_ROT (63 - 38) +#define TEXASR_TRANSACTION_LEVEL (63 - 52) /* 12 bits */ + +enum { + POWERPC_FLAG_NONE = 0x00000000, + /* Flag for MSR bit 25 signification (VRE/SPE) */ + POWERPC_FLAG_SPE = 0x00000001, + POWERPC_FLAG_VRE = 0x00000002, + /* Flag for MSR bit 17 signification (TGPR/CE) */ + POWERPC_FLAG_TGPR = 0x00000004, + POWERPC_FLAG_CE = 0x00000008, + /* Flag for MSR bit 10 signification (SE/DWE/UBLE) */ + POWERPC_FLAG_SE = 0x00000010, + POWERPC_FLAG_DWE = 0x00000020, + POWERPC_FLAG_UBLE = 0x00000040, + /* Flag for MSR bit 9 signification (BE/DE) */ + POWERPC_FLAG_BE = 0x00000080, + POWERPC_FLAG_DE = 0x00000100, + /* Flag for MSR bit 2 signification (PX/PMM) */ + POWERPC_FLAG_PX = 0x00000200, + POWERPC_FLAG_PMM = 0x00000400, + /* Flag for special features */ + /* Decrementer clock: RTC clock (POWER, 601) or bus clock */ + POWERPC_FLAG_RTC_CLK = 0x00010000, + POWERPC_FLAG_BUS_CLK = 0x00020000, + /* Has CFAR */ + POWERPC_FLAG_CFAR = 0x00040000, + /* Has VSX */ + POWERPC_FLAG_VSX = 0x00080000, + /* Has Transaction Memory (ISA 2.07) */ + POWERPC_FLAG_TM = 0x00100000, +}; + +/*****************************************************************************/ +/* Floating point status and control register */ +#define FPSCR_FX 31 /* Floating-point exception summary */ +#define FPSCR_FEX 30 /* Floating-point enabled exception summary */ +#define FPSCR_VX 29 /* Floating-point invalid operation exception summ. */ +#define FPSCR_OX 28 /* Floating-point overflow exception */ +#define FPSCR_UX 27 /* Floating-point underflow exception */ +#define FPSCR_ZX 26 /* Floating-point zero divide exception */ +#define FPSCR_XX 25 /* Floating-point inexact exception */ +#define FPSCR_VXSNAN 24 /* Floating-point invalid operation exception (sNan) */ +#define FPSCR_VXISI 23 /* Floating-point invalid operation exception (inf) */ +#define FPSCR_VXIDI 22 /* Floating-point invalid operation exception (inf) */ +#define FPSCR_VXZDZ 21 /* Floating-point invalid operation exception (zero) */ +#define FPSCR_VXIMZ 20 /* Floating-point invalid operation exception (inf) */ +#define FPSCR_VXVC 19 /* Floating-point invalid operation exception (comp) */ +#define FPSCR_FR 18 /* Floating-point fraction rounded */ +#define FPSCR_FI 17 /* Floating-point fraction inexact */ +#define FPSCR_C 16 /* Floating-point result class descriptor */ +#define FPSCR_FL 15 /* Floating-point less than or negative */ +#define FPSCR_FG 14 /* Floating-point greater than or negative */ +#define FPSCR_FE 13 /* Floating-point equal or zero */ +#define FPSCR_FU 12 /* Floating-point unordered or NaN */ +#define FPSCR_FPCC 12 /* Floating-point condition code */ +#define FPSCR_FPRF 12 /* Floating-point result flags */ +#define FPSCR_VXSOFT 10 /* Floating-point invalid operation exception (soft) */ +#define FPSCR_VXSQRT 9 /* Floating-point invalid operation exception (sqrt) */ +#define FPSCR_VXCVI 8 /* Floating-point invalid operation exception (int) */ +#define FPSCR_VE 7 /* Floating-point invalid operation exception enable */ +#define FPSCR_OE 6 /* Floating-point overflow exception enable */ +#define FPSCR_UE 5 /* Floating-point undeflow exception enable */ +#define FPSCR_ZE 4 /* Floating-point zero divide exception enable */ +#define FPSCR_XE 3 /* Floating-point inexact exception enable */ +#define FPSCR_NI 2 /* Floating-point non-IEEE mode */ +#define FPSCR_RN1 1 +#define FPSCR_RN 0 /* Floating-point rounding control */ +#define fpscr_fex (((env->fpscr) >> FPSCR_FEX) & 0x1) +#define fpscr_vx (((env->fpscr) >> FPSCR_VX) & 0x1) +#define fpscr_ox (((env->fpscr) >> FPSCR_OX) & 0x1) +#define fpscr_ux (((env->fpscr) >> FPSCR_UX) & 0x1) +#define fpscr_zx (((env->fpscr) >> FPSCR_ZX) & 0x1) +#define fpscr_xx (((env->fpscr) >> FPSCR_XX) & 0x1) +#define fpscr_vxsnan (((env->fpscr) >> FPSCR_VXSNAN) & 0x1) +#define fpscr_vxisi (((env->fpscr) >> FPSCR_VXISI) & 0x1) +#define fpscr_vxidi (((env->fpscr) >> FPSCR_VXIDI) & 0x1) +#define fpscr_vxzdz (((env->fpscr) >> FPSCR_VXZDZ) & 0x1) +#define fpscr_vximz (((env->fpscr) >> FPSCR_VXIMZ) & 0x1) +#define fpscr_vxvc (((env->fpscr) >> FPSCR_VXVC) & 0x1) +#define fpscr_fpcc (((env->fpscr) >> FPSCR_FPCC) & 0xF) +#define fpscr_vxsoft (((env->fpscr) >> FPSCR_VXSOFT) & 0x1) +#define fpscr_vxsqrt (((env->fpscr) >> FPSCR_VXSQRT) & 0x1) +#define fpscr_vxcvi (((env->fpscr) >> FPSCR_VXCVI) & 0x1) +#define fpscr_ve (((env->fpscr) >> FPSCR_VE) & 0x1) +#define fpscr_oe (((env->fpscr) >> FPSCR_OE) & 0x1) +#define fpscr_ue (((env->fpscr) >> FPSCR_UE) & 0x1) +#define fpscr_ze (((env->fpscr) >> FPSCR_ZE) & 0x1) +#define fpscr_xe (((env->fpscr) >> FPSCR_XE) & 0x1) +#define fpscr_ni (((env->fpscr) >> FPSCR_NI) & 0x1) +#define fpscr_rn (((env->fpscr) >> FPSCR_RN) & 0x3) +/* Invalid operation exception summary */ +#define fpscr_ix ((env->fpscr) & ((1 << FPSCR_VXSNAN) | (1 << FPSCR_VXISI) | \ + (1 << FPSCR_VXIDI) | (1 << FPSCR_VXZDZ) | \ + (1 << FPSCR_VXIMZ) | (1 << FPSCR_VXVC) | \ + (1 << FPSCR_VXSOFT) | (1 << FPSCR_VXSQRT) | \ + (1 << FPSCR_VXCVI))) +/* exception summary */ +#define fpscr_ex (((env->fpscr) >> FPSCR_XX) & 0x1F) +/* enabled exception summary */ +#define fpscr_eex (((env->fpscr) >> FPSCR_XX) & ((env->fpscr) >> FPSCR_XE) & \ + 0x1F) + +#define FP_FX (1ull << FPSCR_FX) +#define FP_FEX (1ull << FPSCR_FEX) +#define FP_VX (1ull << FPSCR_VX) +#define FP_OX (1ull << FPSCR_OX) +#define FP_UX (1ull << FPSCR_UX) +#define FP_ZX (1ull << FPSCR_ZX) +#define FP_XX (1ull << FPSCR_XX) +#define FP_VXSNAN (1ull << FPSCR_VXSNAN) +#define FP_VXISI (1ull << FPSCR_VXISI) +#define FP_VXIDI (1ull << FPSCR_VXIDI) +#define FP_VXZDZ (1ull << FPSCR_VXZDZ) +#define FP_VXIMZ (1ull << FPSCR_VXIMZ) +#define FP_VXVC (1ull << FPSCR_VXVC) +#define FP_FR (1ull << FSPCR_FR) +#define FP_FI (1ull << FPSCR_FI) +#define FP_C (1ull << FPSCR_C) +#define FP_FL (1ull << FPSCR_FL) +#define FP_FG (1ull << FPSCR_FG) +#define FP_FE (1ull << FPSCR_FE) +#define FP_FU (1ull << FPSCR_FU) +#define FP_FPCC (FP_FL | FP_FG | FP_FE | FP_FU) +#define FP_FPRF (FP_C | FP_FL | FP_FG | FP_FE | FP_FU) +#define FP_VXSOFT (1ull << FPSCR_VXSOFT) +#define FP_VXSQRT (1ull << FPSCR_VXSQRT) +#define FP_VXCVI (1ull << FPSCR_VXCVI) +#define FP_VE (1ull << FPSCR_VE) +#define FP_OE (1ull << FPSCR_OE) +#define FP_UE (1ull << FPSCR_UE) +#define FP_ZE (1ull << FPSCR_ZE) +#define FP_XE (1ull << FPSCR_XE) +#define FP_NI (1ull << FPSCR_NI) +#define FP_RN1 (1ull << FPSCR_RN1) +#define FP_RN (1ull << FPSCR_RN) + +/* the exception bits which can be cleared by mcrfs - includes FX */ +#define FP_EX_CLEAR_BITS (FP_FX | FP_OX | FP_UX | FP_ZX | \ + FP_XX | FP_VXSNAN | FP_VXISI | FP_VXIDI | \ + FP_VXZDZ | FP_VXIMZ | FP_VXVC | FP_VXSOFT | \ + FP_VXSQRT | FP_VXCVI) + +/*****************************************************************************/ +/* Vector status and control register */ +#define VSCR_NJ 16 /* Vector non-java */ +#define VSCR_SAT 0 /* Vector saturation */ +#define vscr_nj (((env->vscr) >> VSCR_NJ) & 0x1) +#define vscr_sat (((env->vscr) >> VSCR_SAT) & 0x1) + +/*****************************************************************************/ +/* BookE e500 MMU registers */ + +#define MAS0_NV_SHIFT 0 +#define MAS0_NV_MASK (0xfff << MAS0_NV_SHIFT) + +#define MAS0_WQ_SHIFT 12 +#define MAS0_WQ_MASK (3 << MAS0_WQ_SHIFT) +/* Write TLB entry regardless of reservation */ +#define MAS0_WQ_ALWAYS (0 << MAS0_WQ_SHIFT) +/* Write TLB entry only already in use */ +#define MAS0_WQ_COND (1 << MAS0_WQ_SHIFT) +/* Clear TLB entry */ +#define MAS0_WQ_CLR_RSRV (2 << MAS0_WQ_SHIFT) + +#define MAS0_HES_SHIFT 14 +#define MAS0_HES (1 << MAS0_HES_SHIFT) + +#define MAS0_ESEL_SHIFT 16 +#define MAS0_ESEL_MASK (0xfff << MAS0_ESEL_SHIFT) + +#define MAS0_TLBSEL_SHIFT 28 +#define MAS0_TLBSEL_MASK (3 << MAS0_TLBSEL_SHIFT) +#define MAS0_TLBSEL_TLB0 (0 << MAS0_TLBSEL_SHIFT) +#define MAS0_TLBSEL_TLB1 (1 << MAS0_TLBSEL_SHIFT) +#define MAS0_TLBSEL_TLB2 (2 << MAS0_TLBSEL_SHIFT) +#define MAS0_TLBSEL_TLB3 (3 << MAS0_TLBSEL_SHIFT) + +#define MAS0_ATSEL_SHIFT 31 +#define MAS0_ATSEL (1 << MAS0_ATSEL_SHIFT) +#define MAS0_ATSEL_TLB 0 +#define MAS0_ATSEL_LRAT MAS0_ATSEL + +#define MAS1_TSIZE_SHIFT 7 +#define MAS1_TSIZE_MASK (0x1f << MAS1_TSIZE_SHIFT) + +#define MAS1_TS_SHIFT 12 +#define MAS1_TS (1 << MAS1_TS_SHIFT) + +#define MAS1_IND_SHIFT 13 +#define MAS1_IND (1 << MAS1_IND_SHIFT) + +#define MAS1_TID_SHIFT 16 +#define MAS1_TID_MASK (0x3fff << MAS1_TID_SHIFT) + +#define MAS1_IPROT_SHIFT 30 +#define MAS1_IPROT (1 << MAS1_IPROT_SHIFT) + +#define MAS1_VALID_SHIFT 31 +#define MAS1_VALID 0x80000000 + +#define MAS2_EPN_SHIFT 12 +#define MAS2_EPN_MASK (~0ULL << MAS2_EPN_SHIFT) + +#define MAS2_ACM_SHIFT 6 +#define MAS2_ACM (1 << MAS2_ACM_SHIFT) + +#define MAS2_VLE_SHIFT 5 +#define MAS2_VLE (1 << MAS2_VLE_SHIFT) + +#define MAS2_W_SHIFT 4 +#define MAS2_W (1 << MAS2_W_SHIFT) + +#define MAS2_I_SHIFT 3 +#define MAS2_I (1 << MAS2_I_SHIFT) + +#define MAS2_M_SHIFT 2 +#define MAS2_M (1 << MAS2_M_SHIFT) + +#define MAS2_G_SHIFT 1 +#define MAS2_G (1 << MAS2_G_SHIFT) + +#define MAS2_E_SHIFT 0 +#define MAS2_E (1 << MAS2_E_SHIFT) + +#define MAS3_RPN_SHIFT 12 +#define MAS3_RPN_MASK (0xfffff << MAS3_RPN_SHIFT) + +#define MAS3_U0 0x00000200 +#define MAS3_U1 0x00000100 +#define MAS3_U2 0x00000080 +#define MAS3_U3 0x00000040 +#define MAS3_UX 0x00000020 +#define MAS3_SX 0x00000010 +#define MAS3_UW 0x00000008 +#define MAS3_SW 0x00000004 +#define MAS3_UR 0x00000002 +#define MAS3_SR 0x00000001 +#define MAS3_SPSIZE_SHIFT 1 +#define MAS3_SPSIZE_MASK (0x3e << MAS3_SPSIZE_SHIFT) + +#define MAS4_TLBSELD_SHIFT MAS0_TLBSEL_SHIFT +#define MAS4_TLBSELD_MASK MAS0_TLBSEL_MASK +#define MAS4_TIDSELD_MASK 0x00030000 +#define MAS4_TIDSELD_PID0 0x00000000 +#define MAS4_TIDSELD_PID1 0x00010000 +#define MAS4_TIDSELD_PID2 0x00020000 +#define MAS4_TIDSELD_PIDZ 0x00030000 +#define MAS4_INDD 0x00008000 /* Default IND */ +#define MAS4_TSIZED_SHIFT MAS1_TSIZE_SHIFT +#define MAS4_TSIZED_MASK MAS1_TSIZE_MASK +#define MAS4_ACMD 0x00000040 +#define MAS4_VLED 0x00000020 +#define MAS4_WD 0x00000010 +#define MAS4_ID 0x00000008 +#define MAS4_MD 0x00000004 +#define MAS4_GD 0x00000002 +#define MAS4_ED 0x00000001 +#define MAS4_WIMGED_MASK 0x0000001f /* Default WIMGE */ +#define MAS4_WIMGED_SHIFT 0 + +#define MAS5_SGS 0x80000000 +#define MAS5_SLPID_MASK 0x00000fff + +#define MAS6_SPID0 0x3fff0000 +#define MAS6_SPID1 0x00007ffe +#define MAS6_ISIZE(x) MAS1_TSIZE(x) +#define MAS6_SAS 0x00000001 +#define MAS6_SPID MAS6_SPID0 +#define MAS6_SIND 0x00000002 /* Indirect page */ +#define MAS6_SIND_SHIFT 1 +#define MAS6_SPID_MASK 0x3fff0000 +#define MAS6_SPID_SHIFT 16 +#define MAS6_ISIZE_MASK 0x00000f80 +#define MAS6_ISIZE_SHIFT 7 + +#define MAS7_RPN 0xffffffff + +#define MAS8_TGS 0x80000000 +#define MAS8_VF 0x40000000 +#define MAS8_TLBPID 0x00000fff + +/* Bit definitions for MMUCFG */ +#define MMUCFG_MAVN 0x00000003 /* MMU Architecture Version Number */ +#define MMUCFG_MAVN_V1 0x00000000 /* v1.0 */ +#define MMUCFG_MAVN_V2 0x00000001 /* v2.0 */ +#define MMUCFG_NTLBS 0x0000000c /* Number of TLBs */ +#define MMUCFG_PIDSIZE 0x000007c0 /* PID Reg Size */ +#define MMUCFG_TWC 0x00008000 /* TLB Write Conditional (v2.0) */ +#define MMUCFG_LRAT 0x00010000 /* LRAT Supported (v2.0) */ +#define MMUCFG_RASIZE 0x00fe0000 /* Real Addr Size */ +#define MMUCFG_LPIDSIZE 0x0f000000 /* LPID Reg Size */ + +/* Bit definitions for MMUCSR0 */ +#define MMUCSR0_TLB1FI 0x00000002 /* TLB1 Flash invalidate */ +#define MMUCSR0_TLB0FI 0x00000004 /* TLB0 Flash invalidate */ +#define MMUCSR0_TLB2FI 0x00000040 /* TLB2 Flash invalidate */ +#define MMUCSR0_TLB3FI 0x00000020 /* TLB3 Flash invalidate */ +#define MMUCSR0_TLBFI (MMUCSR0_TLB0FI | MMUCSR0_TLB1FI | \ + MMUCSR0_TLB2FI | MMUCSR0_TLB3FI) +#define MMUCSR0_TLB0PS 0x00000780 /* TLB0 Page Size */ +#define MMUCSR0_TLB1PS 0x00007800 /* TLB1 Page Size */ +#define MMUCSR0_TLB2PS 0x00078000 /* TLB2 Page Size */ +#define MMUCSR0_TLB3PS 0x00780000 /* TLB3 Page Size */ + +/* TLBnCFG encoding */ +#define TLBnCFG_N_ENTRY 0x00000fff /* number of entries */ +#define TLBnCFG_HES 0x00002000 /* HW select supported */ +#define TLBnCFG_AVAIL 0x00004000 /* variable page size */ +#define TLBnCFG_IPROT 0x00008000 /* IPROT supported */ +#define TLBnCFG_GTWE 0x00010000 /* Guest can write */ +#define TLBnCFG_IND 0x00020000 /* IND entries supported */ +#define TLBnCFG_PT 0x00040000 /* Can load from page table */ +#define TLBnCFG_MINSIZE 0x00f00000 /* Minimum Page Size (v1.0) */ +#define TLBnCFG_MINSIZE_SHIFT 20 +#define TLBnCFG_MAXSIZE 0x000f0000 /* Maximum Page Size (v1.0) */ +#define TLBnCFG_MAXSIZE_SHIFT 16 +#define TLBnCFG_ASSOC 0xff000000 /* Associativity */ +#define TLBnCFG_ASSOC_SHIFT 24 + +/* TLBnPS encoding */ +#define TLBnPS_4K 0x00000004 +#define TLBnPS_8K 0x00000008 +#define TLBnPS_16K 0x00000010 +#define TLBnPS_32K 0x00000020 +#define TLBnPS_64K 0x00000040 +#define TLBnPS_128K 0x00000080 +#define TLBnPS_256K 0x00000100 +#define TLBnPS_512K 0x00000200 +#define TLBnPS_1M 0x00000400 +#define TLBnPS_2M 0x00000800 +#define TLBnPS_4M 0x00001000 +#define TLBnPS_8M 0x00002000 +#define TLBnPS_16M 0x00004000 +#define TLBnPS_32M 0x00008000 +#define TLBnPS_64M 0x00010000 +#define TLBnPS_128M 0x00020000 +#define TLBnPS_256M 0x00040000 +#define TLBnPS_512M 0x00080000 +#define TLBnPS_1G 0x00100000 +#define TLBnPS_2G 0x00200000 +#define TLBnPS_4G 0x00400000 +#define TLBnPS_8G 0x00800000 +#define TLBnPS_16G 0x01000000 +#define TLBnPS_32G 0x02000000 +#define TLBnPS_64G 0x04000000 +#define TLBnPS_128G 0x08000000 +#define TLBnPS_256G 0x10000000 + +/* tlbilx action encoding */ +#define TLBILX_T_ALL 0 +#define TLBILX_T_TID 1 +#define TLBILX_T_FULLMATCH 3 +#define TLBILX_T_CLASS0 4 +#define TLBILX_T_CLASS1 5 +#define TLBILX_T_CLASS2 6 +#define TLBILX_T_CLASS3 7 + +/* BookE 2.06 helper defines */ + +#define BOOKE206_FLUSH_TLB0 (1 << 0) +#define BOOKE206_FLUSH_TLB1 (1 << 1) +#define BOOKE206_FLUSH_TLB2 (1 << 2) +#define BOOKE206_FLUSH_TLB3 (1 << 3) + +/* number of possible TLBs */ +#define BOOKE206_MAX_TLBN 4 + +/*****************************************************************************/ +/* Embedded.Processor Control */ + +#define DBELL_TYPE_SHIFT 27 +#define DBELL_TYPE_MASK (0x1f << DBELL_TYPE_SHIFT) +#define DBELL_TYPE_DBELL (0x00 << DBELL_TYPE_SHIFT) +#define DBELL_TYPE_DBELL_CRIT (0x01 << DBELL_TYPE_SHIFT) +#define DBELL_TYPE_G_DBELL (0x02 << DBELL_TYPE_SHIFT) +#define DBELL_TYPE_G_DBELL_CRIT (0x03 << DBELL_TYPE_SHIFT) +#define DBELL_TYPE_G_DBELL_MC (0x04 << DBELL_TYPE_SHIFT) + +#define DBELL_BRDCAST (1 << 26) +#define DBELL_LPIDTAG_SHIFT 14 +#define DBELL_LPIDTAG_MASK (0xfff << DBELL_LPIDTAG_SHIFT) +#define DBELL_PIRTAG_MASK 0x3fff + +/*****************************************************************************/ +/* Segment page size information, used by recent hash MMUs + * The format of this structure mirrors kvm_ppc_smmu_info + */ + +#define PPC_PAGE_SIZES_MAX_SZ 8 + +struct ppc_one_page_size { + uint32_t page_shift; /* Page shift (or 0) */ + uint32_t pte_enc; /* Encoding in the HPTE (>>12) */ +}; + +struct ppc_one_seg_page_size { + uint32_t page_shift; /* Base page shift of segment (or 0) */ + uint32_t slb_enc; /* SLB encoding for BookS */ + struct ppc_one_page_size enc[PPC_PAGE_SIZES_MAX_SZ]; +}; + +struct ppc_segment_page_sizes { + struct ppc_one_seg_page_size sps[PPC_PAGE_SIZES_MAX_SZ]; +}; + + +/*****************************************************************************/ +/* The whole PowerPC CPU context */ +#define NB_MMU_MODES 8 + +#define PPC_CPU_OPCODES_LEN 0x40 +#define PPC_CPU_INDIRECT_OPCODES_LEN 0x20 + +struct CPUPPCState { + /* First are the most commonly used resources + * during translated code execution + */ + /* general purpose registers */ + target_ulong gpr[32]; + /* Storage for GPR MSB, used by the SPE extension */ + target_ulong gprh[32]; + /* LR */ + target_ulong lr; + /* CTR */ + target_ulong ctr; + /* condition register */ + uint32_t crf[8]; +#if defined(TARGET_PPC64) + /* CFAR */ + target_ulong cfar; +#endif + /* XER (with SO, OV, CA split out) */ + target_ulong xer; + target_ulong so; + target_ulong ov; + target_ulong ca; + /* Reservation address */ + target_ulong reserve_addr; + /* Reservation value */ + target_ulong reserve_val; + target_ulong reserve_val2; + /* Reservation store address */ + target_ulong reserve_ea; + /* Reserved store source register and size */ + target_ulong reserve_info; + + /* Those ones are used in supervisor mode only */ + /* machine state register */ + target_ulong msr; + /* temporary general purpose registers */ + target_ulong tgpr[4]; /* Used to speed-up TLB assist handlers */ + + /* Floating point execution context */ + float_status fp_status; + /* floating point registers */ + float64 fpr[32]; + /* floating point status and control register */ + target_ulong fpscr; + + /* Next instruction pointer */ + target_ulong nip; + + int access_type; /* when a memory exception occurs, the access + type is stored here */ + + CPU_COMMON + + /* MMU context - only relevant for full system emulation */ +#if !defined(CONFIG_USER_ONLY) +#if defined(TARGET_PPC64) + /* PowerPC 64 SLB area */ + ppc_slb_t slb[MAX_SLB_ENTRIES]; + int32_t slb_nr; + /* tcg TLB needs flush (deferred slb inval instruction typically) */ +#endif + /* segment registers */ + hwaddr htab_base; + /* mask used to normalize hash value to PTEG index */ + hwaddr htab_mask; + target_ulong sr[32]; + /* externally stored hash table */ + uint8_t *external_htab; + /* BATs */ + uint32_t nb_BATs; + target_ulong DBAT[2][8]; + target_ulong IBAT[2][8]; + /* PowerPC TLB registers (for 4xx, e500 and 60x software driven TLBs) */ + int32_t nb_tlb; /* Total number of TLB */ + int tlb_per_way; /* Speed-up helper: used to avoid divisions at run time */ + int nb_ways; /* Number of ways in the TLB set */ + int last_way; /* Last used way used to allocate TLB in a LRU way */ + int id_tlbs; /* If 1, MMU has separated TLBs for instructions & data */ + int nb_pids; /* Number of available PID registers */ + int tlb_type; /* Type of TLB we're dealing with */ + ppc_tlb_t tlb; /* TLB is optional. Allocate them only if needed */ + /* 403 dedicated access protection registers */ + target_ulong pb[4]; + bool tlb_dirty; /* Set to non-zero when modifying TLB */ + bool kvm_sw_tlb; /* non-zero if KVM SW TLB API is active */ + uint32_t tlb_need_flush; /* Delayed flush needed */ +#define TLB_NEED_LOCAL_FLUSH 0x1 +#define TLB_NEED_GLOBAL_FLUSH 0x2 +#endif + + /* Other registers */ + /* Special purpose registers */ + target_ulong spr[1024]; + ppc_spr_t spr_cb[1024]; + /* Altivec registers */ + ppc_avr_t avr[32]; + uint32_t vscr; + /* VSX registers */ + uint64_t vsr[32]; + /* SPE registers */ + uint64_t spe_acc; + uint32_t spe_fscr; + /* SPE and Altivec can share a status since they will never be used + * simultaneously */ + float_status vec_status; + + /* Internal devices resources */ + /* Time base and decrementer */ + ppc_tb_t *tb_env; + /* Device control registers */ + ppc_dcr_t *dcr_env; + + int dcache_line_size; + int icache_line_size; + + /* Those resources are used during exception processing */ + /* CPU model definition */ + target_ulong msr_mask; + powerpc_mmu_t mmu_model; + powerpc_excp_t excp_model; + powerpc_input_t bus_model; + int bfd_mach; + uint32_t flags; + uint64_t insns_flags; + uint64_t insns_flags2; +#if defined(TARGET_PPC64) + struct ppc_segment_page_sizes sps; + ppc_slb_t vrma_slb; + target_ulong rmls; + bool ci_large_pages; +#endif + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) + uint64_t vpa_addr; + uint64_t slb_shadow_addr, slb_shadow_size; + uint64_t dtl_addr, dtl_size; +#endif /* TARGET_PPC64 */ + + int error_code; + uint32_t pending_interrupts; +#if !defined(CONFIG_USER_ONLY) + /* This is the IRQ controller, which is implementation dependent + * and only relevant when emulating a complete machine. + */ + uint32_t irq_input_state; + void **irq_inputs; + /* Exception vectors */ + target_ulong excp_vectors[POWERPC_EXCP_NB]; + target_ulong excp_prefix; + target_ulong ivor_mask; + target_ulong ivpr_mask; + target_ulong hreset_vector; + hwaddr mpic_iack; + /* true when the external proxy facility mode is enabled */ + bool mpic_proxy; + /* set when the processor has an HV mode, thus HV priv + * instructions and SPRs are diallowed if MSR:HV is 0 + */ + bool has_hv_mode; + /* On P7/P8, set when in PM state, we need to handle resume + * in a special way (such as routing some resume causes to + * 0x100), so flag this here. + */ + bool in_pm_state; +#endif + + /* Those resources are used only during code translation */ + /* opcode handlers */ + opc_handler_t *opcodes[PPC_CPU_OPCODES_LEN]; + + /* Those resources are used only in QEMU core */ + target_ulong hflags; /* hflags is a MSR & HFLAGS_MASK */ + target_ulong hflags_nmsr; /* specific hflags, not coming from MSR */ + int immu_idx; /* precomputed MMU index to speed up insn access */ + int dmmu_idx; /* precomputed MMU index to speed up data accesses */ + + /* Power management */ + int (*check_pow)(CPUPPCState *env); + +#if !defined(CONFIG_USER_ONLY) + void *load_info; /* Holds boot loading state. */ +#endif + + /* booke timers */ + + /* Specifies bit locations of the Time Base used to signal a fixed timer + * exception on a transition from 0 to 1. (watchdog or fixed-interval timer) + * + * 0 selects the least significant bit. + * 63 selects the most significant bit. + */ + uint8_t fit_period[4]; + uint8_t wdt_period[4]; + + /* Transactional memory state */ + target_ulong tm_gpr[32]; + ppc_avr_t tm_vsr[64]; + uint64_t tm_cr; + uint64_t tm_lr; + uint64_t tm_ctr; + uint64_t tm_fpscr; + uint64_t tm_amr; + uint64_t tm_ppr; + uint64_t tm_vrsave; + uint32_t tm_vscr; + uint64_t tm_dscr; + uint64_t tm_tar; +}; + +#define SET_FIT_PERIOD(a_, b_, c_, d_) \ +do { \ + env->fit_period[0] = (a_); \ + env->fit_period[1] = (b_); \ + env->fit_period[2] = (c_); \ + env->fit_period[3] = (d_); \ + } while (0) + +#define SET_WDT_PERIOD(a_, b_, c_, d_) \ +do { \ + env->wdt_period[0] = (a_); \ + env->wdt_period[1] = (b_); \ + env->wdt_period[2] = (c_); \ + env->wdt_period[3] = (d_); \ + } while (0) + +/** + * PowerPCCPU: + * @env: #CPUPPCState + * @cpu_dt_id: CPU index used in the device tree. KVM uses this index too + * @max_compat: Maximal supported logical PVR from the command line + * @cpu_version: Current logical PVR, zero if in "raw" mode + * + * A PowerPC CPU. + */ +struct PowerPCCPU { + /*< private >*/ + CPUState parent_obj; + /*< public >*/ + + CPUPPCState env; + int cpu_dt_id; + uint32_t max_compat; + uint32_t cpu_version; + + /* Fields related to migration compatibility hacks */ + bool pre_2_8_migration; + target_ulong mig_msr_mask; + uint64_t mig_insns_flags; + uint64_t mig_insns_flags2; + uint32_t mig_nb_BATs; +}; + +static inline PowerPCCPU *ppc_env_get_cpu(CPUPPCState *env) +{ + return container_of(env, PowerPCCPU, env); +} + +#define ENV_GET_CPU(e) CPU(ppc_env_get_cpu(e)) + +#define ENV_OFFSET offsetof(PowerPCCPU, env) + +PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr); +PowerPCCPUClass *ppc_cpu_class_by_pvr_mask(uint32_t pvr); + +void ppc_cpu_do_interrupt(CPUState *cpu); +bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req); +void ppc_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, + int flags); +void ppc_cpu_dump_statistics(CPUState *cpu, FILE *f, + fprintf_function cpu_fprintf, int flags); +hwaddr ppc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int ppc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int ppc_cpu_gdb_read_register_apple(CPUState *cpu, uint8_t *buf, int reg); +int ppc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +int ppc_cpu_gdb_write_register_apple(CPUState *cpu, uint8_t *buf, int reg); +int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, + int cpuid, void *opaque); +#ifndef CONFIG_USER_ONLY +void ppc_cpu_do_system_reset(CPUState *cs); +extern const struct VMStateDescription vmstate_ppc_cpu; +#endif + +/*****************************************************************************/ +PowerPCCPU *cpu_ppc_init(const char *cpu_model); +void ppc_translate_init(void); +const char *ppc_cpu_lookup_alias(const char *alias); +/* 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_ppc_signal_handler (int host_signum, void *pinfo, + void *puc); +#if defined(CONFIG_USER_ONLY) +int ppc_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw, + int mmu_idx); +#endif + +#if !defined(CONFIG_USER_ONLY) +void ppc_store_sdr1 (CPUPPCState *env, target_ulong value); +#endif /* !defined(CONFIG_USER_ONLY) */ +void ppc_store_msr (CPUPPCState *env, target_ulong value); + +void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf); +int ppc_get_compat_smt_threads(PowerPCCPU *cpu); +#if defined(TARGET_PPC64) +void ppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version, Error **errp); +#endif + +/* Time-base and decrementer management */ +#ifndef NO_CPU_IO_DEFS +uint64_t cpu_ppc_load_tbl (CPUPPCState *env); +uint32_t cpu_ppc_load_tbu (CPUPPCState *env); +void cpu_ppc_store_tbu (CPUPPCState *env, uint32_t value); +void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value); +uint64_t cpu_ppc_load_atbl (CPUPPCState *env); +uint32_t cpu_ppc_load_atbu (CPUPPCState *env); +void cpu_ppc_store_atbl (CPUPPCState *env, uint32_t value); +void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value); +bool ppc_decr_clear_on_delivery(CPUPPCState *env); +uint32_t cpu_ppc_load_decr (CPUPPCState *env); +void cpu_ppc_store_decr (CPUPPCState *env, uint32_t value); +uint32_t cpu_ppc_load_hdecr (CPUPPCState *env); +void cpu_ppc_store_hdecr (CPUPPCState *env, uint32_t value); +uint64_t cpu_ppc_load_purr (CPUPPCState *env); +uint32_t cpu_ppc601_load_rtcl (CPUPPCState *env); +uint32_t cpu_ppc601_load_rtcu (CPUPPCState *env); +#if !defined(CONFIG_USER_ONLY) +void cpu_ppc601_store_rtcl (CPUPPCState *env, uint32_t value); +void cpu_ppc601_store_rtcu (CPUPPCState *env, uint32_t value); +target_ulong load_40x_pit (CPUPPCState *env); +void store_40x_pit (CPUPPCState *env, target_ulong val); +void store_40x_dbcr0 (CPUPPCState *env, uint32_t val); +void store_40x_sler (CPUPPCState *env, uint32_t val); +void store_booke_tcr (CPUPPCState *env, target_ulong val); +void store_booke_tsr (CPUPPCState *env, target_ulong val); +void ppc_tlb_invalidate_all (CPUPPCState *env); +void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr); +void cpu_ppc_set_papr(PowerPCCPU *cpu); +#endif +#endif + +void store_fpscr(CPUPPCState *env, uint64_t arg, uint32_t mask); + +static inline uint64_t ppc_dump_gpr(CPUPPCState *env, int gprn) +{ + uint64_t gprv; + + gprv = env->gpr[gprn]; + if (env->flags & POWERPC_FLAG_SPE) { + /* If the CPU implements the SPE extension, we have to get the + * high bits of the GPR from the gprh storage area + */ + gprv &= 0xFFFFFFFFULL; + gprv |= (uint64_t)env->gprh[gprn] << 32; + } + + return gprv; +} + +/* Device control registers */ +int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp); +int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val); + +#define cpu_init(cpu_model) CPU(cpu_ppc_init(cpu_model)) + +#define cpu_signal_handler cpu_ppc_signal_handler +#define cpu_list ppc_cpu_list + +/* MMU modes definitions */ +#define MMU_USER_IDX 0 +static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) +{ + return ifetch ? env->immu_idx : env->dmmu_idx; +} + +#include "exec/cpu-all.h" + +/*****************************************************************************/ +/* CRF definitions */ +#define CRF_LT 3 +#define CRF_GT 2 +#define CRF_EQ 1 +#define CRF_SO 0 +#define CRF_CH (1 << CRF_LT) +#define CRF_CL (1 << CRF_GT) +#define CRF_CH_OR_CL (1 << CRF_EQ) +#define CRF_CH_AND_CL (1 << CRF_SO) + +/* XER definitions */ +#define XER_SO 31 +#define XER_OV 30 +#define XER_CA 29 +#define XER_CMP 8 +#define XER_BC 0 +#define xer_so (env->so) +#define xer_ov (env->ov) +#define xer_ca (env->ca) +#define xer_cmp ((env->xer >> XER_CMP) & 0xFF) +#define xer_bc ((env->xer >> XER_BC) & 0x7F) + +/* SPR definitions */ +#define SPR_MQ (0x000) +#define SPR_XER (0x001) +#define SPR_601_VRTCU (0x004) +#define SPR_601_VRTCL (0x005) +#define SPR_601_UDECR (0x006) +#define SPR_LR (0x008) +#define SPR_CTR (0x009) +#define SPR_UAMR (0x00C) +#define SPR_DSCR (0x011) +#define SPR_DSISR (0x012) +#define SPR_DAR (0x013) /* DAE for PowerPC 601 */ +#define SPR_601_RTCU (0x014) +#define SPR_601_RTCL (0x015) +#define SPR_DECR (0x016) +#define SPR_SDR1 (0x019) +#define SPR_SRR0 (0x01A) +#define SPR_SRR1 (0x01B) +#define SPR_CFAR (0x01C) +#define SPR_AMR (0x01D) +#define SPR_ACOP (0x01F) +#define SPR_BOOKE_PID (0x030) +#define SPR_BOOKS_PID (0x030) +#define SPR_BOOKE_DECAR (0x036) +#define SPR_BOOKE_CSRR0 (0x03A) +#define SPR_BOOKE_CSRR1 (0x03B) +#define SPR_BOOKE_DEAR (0x03D) +#define SPR_IAMR (0x03D) +#define SPR_BOOKE_ESR (0x03E) +#define SPR_BOOKE_IVPR (0x03F) +#define SPR_MPC_EIE (0x050) +#define SPR_MPC_EID (0x051) +#define SPR_MPC_NRI (0x052) +#define SPR_TFHAR (0x080) +#define SPR_TFIAR (0x081) +#define SPR_TEXASR (0x082) +#define SPR_TEXASRU (0x083) +#define SPR_UCTRL (0x088) +#define SPR_MPC_CMPA (0x090) +#define SPR_MPC_CMPB (0x091) +#define SPR_MPC_CMPC (0x092) +#define SPR_MPC_CMPD (0x093) +#define SPR_MPC_ECR (0x094) +#define SPR_MPC_DER (0x095) +#define SPR_MPC_COUNTA (0x096) +#define SPR_MPC_COUNTB (0x097) +#define SPR_CTRL (0x098) +#define SPR_MPC_CMPE (0x098) +#define SPR_MPC_CMPF (0x099) +#define SPR_FSCR (0x099) +#define SPR_MPC_CMPG (0x09A) +#define SPR_MPC_CMPH (0x09B) +#define SPR_MPC_LCTRL1 (0x09C) +#define SPR_MPC_LCTRL2 (0x09D) +#define SPR_UAMOR (0x09D) +#define SPR_MPC_ICTRL (0x09E) +#define SPR_MPC_BAR (0x09F) +#define SPR_PSPB (0x09F) +#define SPR_DAWR (0x0B4) +#define SPR_RPR (0x0BA) +#define SPR_CIABR (0x0BB) +#define SPR_DAWRX (0x0BC) +#define SPR_HFSCR (0x0BE) +#define SPR_VRSAVE (0x100) +#define SPR_USPRG0 (0x100) +#define SPR_USPRG1 (0x101) +#define SPR_USPRG2 (0x102) +#define SPR_USPRG3 (0x103) +#define SPR_USPRG4 (0x104) +#define SPR_USPRG5 (0x105) +#define SPR_USPRG6 (0x106) +#define SPR_USPRG7 (0x107) +#define SPR_VTBL (0x10C) +#define SPR_VTBU (0x10D) +#define SPR_SPRG0 (0x110) +#define SPR_SPRG1 (0x111) +#define SPR_SPRG2 (0x112) +#define SPR_SPRG3 (0x113) +#define SPR_SPRG4 (0x114) +#define SPR_SCOMC (0x114) +#define SPR_SPRG5 (0x115) +#define SPR_SCOMD (0x115) +#define SPR_SPRG6 (0x116) +#define SPR_SPRG7 (0x117) +#define SPR_ASR (0x118) +#define SPR_EAR (0x11A) +#define SPR_TBL (0x11C) +#define SPR_TBU (0x11D) +#define SPR_TBU40 (0x11E) +#define SPR_SVR (0x11E) +#define SPR_BOOKE_PIR (0x11E) +#define SPR_PVR (0x11F) +#define SPR_HSPRG0 (0x130) +#define SPR_BOOKE_DBSR (0x130) +#define SPR_HSPRG1 (0x131) +#define SPR_HDSISR (0x132) +#define SPR_HDAR (0x133) +#define SPR_BOOKE_EPCR (0x133) +#define SPR_SPURR (0x134) +#define SPR_BOOKE_DBCR0 (0x134) +#define SPR_IBCR (0x135) +#define SPR_PURR (0x135) +#define SPR_BOOKE_DBCR1 (0x135) +#define SPR_DBCR (0x136) +#define SPR_HDEC (0x136) +#define SPR_BOOKE_DBCR2 (0x136) +#define SPR_HIOR (0x137) +#define SPR_MBAR (0x137) +#define SPR_RMOR (0x138) +#define SPR_BOOKE_IAC1 (0x138) +#define SPR_HRMOR (0x139) +#define SPR_BOOKE_IAC2 (0x139) +#define SPR_HSRR0 (0x13A) +#define SPR_BOOKE_IAC3 (0x13A) +#define SPR_HSRR1 (0x13B) +#define SPR_BOOKE_IAC4 (0x13B) +#define SPR_BOOKE_DAC1 (0x13C) +#define SPR_MMCRH (0x13C) +#define SPR_DABR2 (0x13D) +#define SPR_BOOKE_DAC2 (0x13D) +#define SPR_TFMR (0x13D) +#define SPR_BOOKE_DVC1 (0x13E) +#define SPR_LPCR (0x13E) +#define SPR_BOOKE_DVC2 (0x13F) +#define SPR_LPIDR (0x13F) +#define SPR_BOOKE_TSR (0x150) +#define SPR_HMER (0x150) +#define SPR_HMEER (0x151) +#define SPR_PCR (0x152) +#define SPR_BOOKE_LPIDR (0x152) +#define SPR_BOOKE_TCR (0x154) +#define SPR_BOOKE_TLB0PS (0x158) +#define SPR_BOOKE_TLB1PS (0x159) +#define SPR_BOOKE_TLB2PS (0x15A) +#define SPR_BOOKE_TLB3PS (0x15B) +#define SPR_AMOR (0x15D) +#define SPR_BOOKE_MAS7_MAS3 (0x174) +#define SPR_BOOKE_IVOR0 (0x190) +#define SPR_BOOKE_IVOR1 (0x191) +#define SPR_BOOKE_IVOR2 (0x192) +#define SPR_BOOKE_IVOR3 (0x193) +#define SPR_BOOKE_IVOR4 (0x194) +#define SPR_BOOKE_IVOR5 (0x195) +#define SPR_BOOKE_IVOR6 (0x196) +#define SPR_BOOKE_IVOR7 (0x197) +#define SPR_BOOKE_IVOR8 (0x198) +#define SPR_BOOKE_IVOR9 (0x199) +#define SPR_BOOKE_IVOR10 (0x19A) +#define SPR_BOOKE_IVOR11 (0x19B) +#define SPR_BOOKE_IVOR12 (0x19C) +#define SPR_BOOKE_IVOR13 (0x19D) +#define SPR_BOOKE_IVOR14 (0x19E) +#define SPR_BOOKE_IVOR15 (0x19F) +#define SPR_BOOKE_IVOR38 (0x1B0) +#define SPR_BOOKE_IVOR39 (0x1B1) +#define SPR_BOOKE_IVOR40 (0x1B2) +#define SPR_BOOKE_IVOR41 (0x1B3) +#define SPR_BOOKE_IVOR42 (0x1B4) +#define SPR_BOOKE_GIVOR2 (0x1B8) +#define SPR_BOOKE_GIVOR3 (0x1B9) +#define SPR_BOOKE_GIVOR4 (0x1BA) +#define SPR_BOOKE_GIVOR8 (0x1BB) +#define SPR_BOOKE_GIVOR13 (0x1BC) +#define SPR_BOOKE_GIVOR14 (0x1BD) +#define SPR_TIR (0x1BE) +#define SPR_BOOKE_SPEFSCR (0x200) +#define SPR_Exxx_BBEAR (0x201) +#define SPR_Exxx_BBTAR (0x202) +#define SPR_Exxx_L1CFG0 (0x203) +#define SPR_Exxx_L1CFG1 (0x204) +#define SPR_Exxx_NPIDR (0x205) +#define SPR_ATBL (0x20E) +#define SPR_ATBU (0x20F) +#define SPR_IBAT0U (0x210) +#define SPR_BOOKE_IVOR32 (0x210) +#define SPR_RCPU_MI_GRA (0x210) +#define SPR_IBAT0L (0x211) +#define SPR_BOOKE_IVOR33 (0x211) +#define SPR_IBAT1U (0x212) +#define SPR_BOOKE_IVOR34 (0x212) +#define SPR_IBAT1L (0x213) +#define SPR_BOOKE_IVOR35 (0x213) +#define SPR_IBAT2U (0x214) +#define SPR_BOOKE_IVOR36 (0x214) +#define SPR_IBAT2L (0x215) +#define SPR_BOOKE_IVOR37 (0x215) +#define SPR_IBAT3U (0x216) +#define SPR_IBAT3L (0x217) +#define SPR_DBAT0U (0x218) +#define SPR_RCPU_L2U_GRA (0x218) +#define SPR_DBAT0L (0x219) +#define SPR_DBAT1U (0x21A) +#define SPR_DBAT1L (0x21B) +#define SPR_DBAT2U (0x21C) +#define SPR_DBAT2L (0x21D) +#define SPR_DBAT3U (0x21E) +#define SPR_DBAT3L (0x21F) +#define SPR_IBAT4U (0x230) +#define SPR_RPCU_BBCMCR (0x230) +#define SPR_MPC_IC_CST (0x230) +#define SPR_Exxx_CTXCR (0x230) +#define SPR_IBAT4L (0x231) +#define SPR_MPC_IC_ADR (0x231) +#define SPR_Exxx_DBCR3 (0x231) +#define SPR_IBAT5U (0x232) +#define SPR_MPC_IC_DAT (0x232) +#define SPR_Exxx_DBCNT (0x232) +#define SPR_IBAT5L (0x233) +#define SPR_IBAT6U (0x234) +#define SPR_IBAT6L (0x235) +#define SPR_IBAT7U (0x236) +#define SPR_IBAT7L (0x237) +#define SPR_DBAT4U (0x238) +#define SPR_RCPU_L2U_MCR (0x238) +#define SPR_MPC_DC_CST (0x238) +#define SPR_Exxx_ALTCTXCR (0x238) +#define SPR_DBAT4L (0x239) +#define SPR_MPC_DC_ADR (0x239) +#define SPR_DBAT5U (0x23A) +#define SPR_BOOKE_MCSRR0 (0x23A) +#define SPR_MPC_DC_DAT (0x23A) +#define SPR_DBAT5L (0x23B) +#define SPR_BOOKE_MCSRR1 (0x23B) +#define SPR_DBAT6U (0x23C) +#define SPR_BOOKE_MCSR (0x23C) +#define SPR_DBAT6L (0x23D) +#define SPR_Exxx_MCAR (0x23D) +#define SPR_DBAT7U (0x23E) +#define SPR_BOOKE_DSRR0 (0x23E) +#define SPR_DBAT7L (0x23F) +#define SPR_BOOKE_DSRR1 (0x23F) +#define SPR_BOOKE_SPRG8 (0x25C) +#define SPR_BOOKE_SPRG9 (0x25D) +#define SPR_BOOKE_MAS0 (0x270) +#define SPR_BOOKE_MAS1 (0x271) +#define SPR_BOOKE_MAS2 (0x272) +#define SPR_BOOKE_MAS3 (0x273) +#define SPR_BOOKE_MAS4 (0x274) +#define SPR_BOOKE_MAS5 (0x275) +#define SPR_BOOKE_MAS6 (0x276) +#define SPR_BOOKE_PID1 (0x279) +#define SPR_BOOKE_PID2 (0x27A) +#define SPR_MPC_DPDR (0x280) +#define SPR_MPC_IMMR (0x288) +#define SPR_BOOKE_TLB0CFG (0x2B0) +#define SPR_BOOKE_TLB1CFG (0x2B1) +#define SPR_BOOKE_TLB2CFG (0x2B2) +#define SPR_BOOKE_TLB3CFG (0x2B3) +#define SPR_BOOKE_EPR (0x2BE) +#define SPR_PERF0 (0x300) +#define SPR_RCPU_MI_RBA0 (0x300) +#define SPR_MPC_MI_CTR (0x300) +#define SPR_POWER_USIER (0x300) +#define SPR_PERF1 (0x301) +#define SPR_RCPU_MI_RBA1 (0x301) +#define SPR_POWER_UMMCR2 (0x301) +#define SPR_PERF2 (0x302) +#define SPR_RCPU_MI_RBA2 (0x302) +#define SPR_MPC_MI_AP (0x302) +#define SPR_POWER_UMMCRA (0x302) +#define SPR_PERF3 (0x303) +#define SPR_RCPU_MI_RBA3 (0x303) +#define SPR_MPC_MI_EPN (0x303) +#define SPR_POWER_UPMC1 (0x303) +#define SPR_PERF4 (0x304) +#define SPR_POWER_UPMC2 (0x304) +#define SPR_PERF5 (0x305) +#define SPR_MPC_MI_TWC (0x305) +#define SPR_POWER_UPMC3 (0x305) +#define SPR_PERF6 (0x306) +#define SPR_MPC_MI_RPN (0x306) +#define SPR_POWER_UPMC4 (0x306) +#define SPR_PERF7 (0x307) +#define SPR_POWER_UPMC5 (0x307) +#define SPR_PERF8 (0x308) +#define SPR_RCPU_L2U_RBA0 (0x308) +#define SPR_MPC_MD_CTR (0x308) +#define SPR_POWER_UPMC6 (0x308) +#define SPR_PERF9 (0x309) +#define SPR_RCPU_L2U_RBA1 (0x309) +#define SPR_MPC_MD_CASID (0x309) +#define SPR_970_UPMC7 (0X309) +#define SPR_PERFA (0x30A) +#define SPR_RCPU_L2U_RBA2 (0x30A) +#define SPR_MPC_MD_AP (0x30A) +#define SPR_970_UPMC8 (0X30A) +#define SPR_PERFB (0x30B) +#define SPR_RCPU_L2U_RBA3 (0x30B) +#define SPR_MPC_MD_EPN (0x30B) +#define SPR_POWER_UMMCR0 (0X30B) +#define SPR_PERFC (0x30C) +#define SPR_MPC_MD_TWB (0x30C) +#define SPR_POWER_USIAR (0X30C) +#define SPR_PERFD (0x30D) +#define SPR_MPC_MD_TWC (0x30D) +#define SPR_POWER_USDAR (0X30D) +#define SPR_PERFE (0x30E) +#define SPR_MPC_MD_RPN (0x30E) +#define SPR_POWER_UMMCR1 (0X30E) +#define SPR_PERFF (0x30F) +#define SPR_MPC_MD_TW (0x30F) +#define SPR_UPERF0 (0x310) +#define SPR_POWER_SIER (0x310) +#define SPR_UPERF1 (0x311) +#define SPR_POWER_MMCR2 (0x311) +#define SPR_UPERF2 (0x312) +#define SPR_POWER_MMCRA (0X312) +#define SPR_UPERF3 (0x313) +#define SPR_POWER_PMC1 (0X313) +#define SPR_UPERF4 (0x314) +#define SPR_POWER_PMC2 (0X314) +#define SPR_UPERF5 (0x315) +#define SPR_POWER_PMC3 (0X315) +#define SPR_UPERF6 (0x316) +#define SPR_POWER_PMC4 (0X316) +#define SPR_UPERF7 (0x317) +#define SPR_POWER_PMC5 (0X317) +#define SPR_UPERF8 (0x318) +#define SPR_POWER_PMC6 (0X318) +#define SPR_UPERF9 (0x319) +#define SPR_970_PMC7 (0X319) +#define SPR_UPERFA (0x31A) +#define SPR_970_PMC8 (0X31A) +#define SPR_UPERFB (0x31B) +#define SPR_POWER_MMCR0 (0X31B) +#define SPR_UPERFC (0x31C) +#define SPR_POWER_SIAR (0X31C) +#define SPR_UPERFD (0x31D) +#define SPR_POWER_SDAR (0X31D) +#define SPR_UPERFE (0x31E) +#define SPR_POWER_MMCR1 (0X31E) +#define SPR_UPERFF (0x31F) +#define SPR_RCPU_MI_RA0 (0x320) +#define SPR_MPC_MI_DBCAM (0x320) +#define SPR_BESCRS (0x320) +#define SPR_RCPU_MI_RA1 (0x321) +#define SPR_MPC_MI_DBRAM0 (0x321) +#define SPR_BESCRSU (0x321) +#define SPR_RCPU_MI_RA2 (0x322) +#define SPR_MPC_MI_DBRAM1 (0x322) +#define SPR_BESCRR (0x322) +#define SPR_RCPU_MI_RA3 (0x323) +#define SPR_BESCRRU (0x323) +#define SPR_EBBHR (0x324) +#define SPR_EBBRR (0x325) +#define SPR_BESCR (0x326) +#define SPR_RCPU_L2U_RA0 (0x328) +#define SPR_MPC_MD_DBCAM (0x328) +#define SPR_RCPU_L2U_RA1 (0x329) +#define SPR_MPC_MD_DBRAM0 (0x329) +#define SPR_RCPU_L2U_RA2 (0x32A) +#define SPR_MPC_MD_DBRAM1 (0x32A) +#define SPR_RCPU_L2U_RA3 (0x32B) +#define SPR_TAR (0x32F) +#define SPR_IC (0x350) +#define SPR_VTB (0x351) +#define SPR_MMCRC (0x353) +#define SPR_440_INV0 (0x370) +#define SPR_440_INV1 (0x371) +#define SPR_440_INV2 (0x372) +#define SPR_440_INV3 (0x373) +#define SPR_440_ITV0 (0x374) +#define SPR_440_ITV1 (0x375) +#define SPR_440_ITV2 (0x376) +#define SPR_440_ITV3 (0x377) +#define SPR_440_CCR1 (0x378) +#define SPR_TACR (0x378) +#define SPR_TCSCR (0x379) +#define SPR_CSIGR (0x37a) +#define SPR_DCRIPR (0x37B) +#define SPR_POWER_SPMC1 (0x37C) +#define SPR_POWER_SPMC2 (0x37D) +#define SPR_POWER_MMCRS (0x37E) +#define SPR_WORT (0x37F) +#define SPR_PPR (0x380) +#define SPR_750_GQR0 (0x390) +#define SPR_440_DNV0 (0x390) +#define SPR_750_GQR1 (0x391) +#define SPR_440_DNV1 (0x391) +#define SPR_750_GQR2 (0x392) +#define SPR_440_DNV2 (0x392) +#define SPR_750_GQR3 (0x393) +#define SPR_440_DNV3 (0x393) +#define SPR_750_GQR4 (0x394) +#define SPR_440_DTV0 (0x394) +#define SPR_750_GQR5 (0x395) +#define SPR_440_DTV1 (0x395) +#define SPR_750_GQR6 (0x396) +#define SPR_440_DTV2 (0x396) +#define SPR_750_GQR7 (0x397) +#define SPR_440_DTV3 (0x397) +#define SPR_750_THRM4 (0x398) +#define SPR_750CL_HID2 (0x398) +#define SPR_440_DVLIM (0x398) +#define SPR_750_WPAR (0x399) +#define SPR_440_IVLIM (0x399) +#define SPR_TSCR (0x399) +#define SPR_750_DMAU (0x39A) +#define SPR_750_DMAL (0x39B) +#define SPR_440_RSTCFG (0x39B) +#define SPR_BOOKE_DCDBTRL (0x39C) +#define SPR_BOOKE_DCDBTRH (0x39D) +#define SPR_BOOKE_ICDBTRL (0x39E) +#define SPR_BOOKE_ICDBTRH (0x39F) +#define SPR_74XX_UMMCR2 (0x3A0) +#define SPR_7XX_UPMC5 (0x3A1) +#define SPR_7XX_UPMC6 (0x3A2) +#define SPR_UBAMR (0x3A7) +#define SPR_7XX_UMMCR0 (0x3A8) +#define SPR_7XX_UPMC1 (0x3A9) +#define SPR_7XX_UPMC2 (0x3AA) +#define SPR_7XX_USIAR (0x3AB) +#define SPR_7XX_UMMCR1 (0x3AC) +#define SPR_7XX_UPMC3 (0x3AD) +#define SPR_7XX_UPMC4 (0x3AE) +#define SPR_USDA (0x3AF) +#define SPR_40x_ZPR (0x3B0) +#define SPR_BOOKE_MAS7 (0x3B0) +#define SPR_74XX_MMCR2 (0x3B0) +#define SPR_7XX_PMC5 (0x3B1) +#define SPR_40x_PID (0x3B1) +#define SPR_7XX_PMC6 (0x3B2) +#define SPR_440_MMUCR (0x3B2) +#define SPR_4xx_CCR0 (0x3B3) +#define SPR_BOOKE_EPLC (0x3B3) +#define SPR_405_IAC3 (0x3B4) +#define SPR_BOOKE_EPSC (0x3B4) +#define SPR_405_IAC4 (0x3B5) +#define SPR_405_DVC1 (0x3B6) +#define SPR_405_DVC2 (0x3B7) +#define SPR_BAMR (0x3B7) +#define SPR_7XX_MMCR0 (0x3B8) +#define SPR_7XX_PMC1 (0x3B9) +#define SPR_40x_SGR (0x3B9) +#define SPR_7XX_PMC2 (0x3BA) +#define SPR_40x_DCWR (0x3BA) +#define SPR_7XX_SIAR (0x3BB) +#define SPR_405_SLER (0x3BB) +#define SPR_7XX_MMCR1 (0x3BC) +#define SPR_405_SU0R (0x3BC) +#define SPR_401_SKR (0x3BC) +#define SPR_7XX_PMC3 (0x3BD) +#define SPR_405_DBCR1 (0x3BD) +#define SPR_7XX_PMC4 (0x3BE) +#define SPR_SDA (0x3BF) +#define SPR_403_VTBL (0x3CC) +#define SPR_403_VTBU (0x3CD) +#define SPR_DMISS (0x3D0) +#define SPR_DCMP (0x3D1) +#define SPR_HASH1 (0x3D2) +#define SPR_HASH2 (0x3D3) +#define SPR_BOOKE_ICDBDR (0x3D3) +#define SPR_TLBMISS (0x3D4) +#define SPR_IMISS (0x3D4) +#define SPR_40x_ESR (0x3D4) +#define SPR_PTEHI (0x3D5) +#define SPR_ICMP (0x3D5) +#define SPR_40x_DEAR (0x3D5) +#define SPR_PTELO (0x3D6) +#define SPR_RPA (0x3D6) +#define SPR_40x_EVPR (0x3D6) +#define SPR_L3PM (0x3D7) +#define SPR_403_CDBCR (0x3D7) +#define SPR_L3ITCR0 (0x3D8) +#define SPR_TCR (0x3D8) +#define SPR_40x_TSR (0x3D8) +#define SPR_IBR (0x3DA) +#define SPR_40x_TCR (0x3DA) +#define SPR_ESASRR (0x3DB) +#define SPR_40x_PIT (0x3DB) +#define SPR_403_TBL (0x3DC) +#define SPR_403_TBU (0x3DD) +#define SPR_SEBR (0x3DE) +#define SPR_40x_SRR2 (0x3DE) +#define SPR_SER (0x3DF) +#define SPR_40x_SRR3 (0x3DF) +#define SPR_L3OHCR (0x3E8) +#define SPR_L3ITCR1 (0x3E9) +#define SPR_L3ITCR2 (0x3EA) +#define SPR_L3ITCR3 (0x3EB) +#define SPR_HID0 (0x3F0) +#define SPR_40x_DBSR (0x3F0) +#define SPR_HID1 (0x3F1) +#define SPR_IABR (0x3F2) +#define SPR_40x_DBCR0 (0x3F2) +#define SPR_601_HID2 (0x3F2) +#define SPR_Exxx_L1CSR0 (0x3F2) +#define SPR_ICTRL (0x3F3) +#define SPR_HID2 (0x3F3) +#define SPR_750CL_HID4 (0x3F3) +#define SPR_Exxx_L1CSR1 (0x3F3) +#define SPR_440_DBDR (0x3F3) +#define SPR_LDSTDB (0x3F4) +#define SPR_750_TDCL (0x3F4) +#define SPR_40x_IAC1 (0x3F4) +#define SPR_MMUCSR0 (0x3F4) +#define SPR_970_HID4 (0x3F4) +#define SPR_DABR (0x3F5) +#define DABR_MASK (~(target_ulong)0x7) +#define SPR_Exxx_BUCSR (0x3F5) +#define SPR_40x_IAC2 (0x3F5) +#define SPR_601_HID5 (0x3F5) +#define SPR_40x_DAC1 (0x3F6) +#define SPR_MSSCR0 (0x3F6) +#define SPR_970_HID5 (0x3F6) +#define SPR_MSSSR0 (0x3F7) +#define SPR_MSSCR1 (0x3F7) +#define SPR_DABRX (0x3F7) +#define SPR_40x_DAC2 (0x3F7) +#define SPR_MMUCFG (0x3F7) +#define SPR_LDSTCR (0x3F8) +#define SPR_L2PMCR (0x3F8) +#define SPR_750FX_HID2 (0x3F8) +#define SPR_Exxx_L1FINV0 (0x3F8) +#define SPR_L2CR (0x3F9) +#define SPR_L3CR (0x3FA) +#define SPR_750_TDCH (0x3FA) +#define SPR_IABR2 (0x3FA) +#define SPR_40x_DCCR (0x3FA) +#define SPR_ICTC (0x3FB) +#define SPR_40x_ICCR (0x3FB) +#define SPR_THRM1 (0x3FC) +#define SPR_403_PBL1 (0x3FC) +#define SPR_SP (0x3FD) +#define SPR_THRM2 (0x3FD) +#define SPR_403_PBU1 (0x3FD) +#define SPR_604_HID13 (0x3FD) +#define SPR_LT (0x3FE) +#define SPR_THRM3 (0x3FE) +#define SPR_RCPU_FPECR (0x3FE) +#define SPR_403_PBL2 (0x3FE) +#define SPR_PIR (0x3FF) +#define SPR_403_PBU2 (0x3FF) +#define SPR_601_HID15 (0x3FF) +#define SPR_604_HID15 (0x3FF) +#define SPR_E500_SVR (0x3FF) + +/* Disable MAS Interrupt Updates for Hypervisor */ +#define EPCR_DMIUH (1 << 22) +/* Disable Guest TLB Management Instructions */ +#define EPCR_DGTMI (1 << 23) +/* Guest Interrupt Computation Mode */ +#define EPCR_GICM (1 << 24) +/* Interrupt Computation Mode */ +#define EPCR_ICM (1 << 25) +/* Disable Embedded Hypervisor Debug */ +#define EPCR_DUVD (1 << 26) +/* Instruction Storage Interrupt Directed to Guest State */ +#define EPCR_ISIGS (1 << 27) +/* Data Storage Interrupt Directed to Guest State */ +#define EPCR_DSIGS (1 << 28) +/* Instruction TLB Error Interrupt Directed to Guest State */ +#define EPCR_ITLBGS (1 << 29) +/* Data TLB Error Interrupt Directed to Guest State */ +#define EPCR_DTLBGS (1 << 30) +/* External Input Interrupt Directed to Guest State */ +#define EPCR_EXTGS (1 << 31) + +#define L1CSR0_CPE 0x00010000 /* Data Cache Parity Enable */ +#define L1CSR0_CUL 0x00000400 /* (D-)Cache Unable to Lock */ +#define L1CSR0_DCLFR 0x00000100 /* D-Cache Lock Flash Reset */ +#define L1CSR0_DCFI 0x00000002 /* Data Cache Flash Invalidate */ +#define L1CSR0_DCE 0x00000001 /* Data Cache Enable */ + +#define L1CSR1_CPE 0x00010000 /* Instruction Cache Parity Enable */ +#define L1CSR1_ICUL 0x00000400 /* I-Cache Unable to Lock */ +#define L1CSR1_ICLFR 0x00000100 /* I-Cache Lock Flash Reset */ +#define L1CSR1_ICFI 0x00000002 /* Instruction Cache Flash Invalidate */ +#define L1CSR1_ICE 0x00000001 /* Instruction Cache Enable */ + +/* HID0 bits */ +#define HID0_DEEPNAP (1 << 24) /* pre-2.06 */ +#define HID0_DOZE (1 << 23) /* pre-2.06 */ +#define HID0_NAP (1 << 22) /* pre-2.06 */ +#define HID0_HILE (1ull << (63 - 19)) /* POWER8 */ + +/*****************************************************************************/ +/* PowerPC Instructions types definitions */ +enum { + PPC_NONE = 0x0000000000000000ULL, + /* PowerPC base instructions set */ + PPC_INSNS_BASE = 0x0000000000000001ULL, + /* integer operations instructions */ +#define PPC_INTEGER PPC_INSNS_BASE + /* flow control instructions */ +#define PPC_FLOW PPC_INSNS_BASE + /* virtual memory instructions */ +#define PPC_MEM PPC_INSNS_BASE + /* ld/st with reservation instructions */ +#define PPC_RES PPC_INSNS_BASE + /* spr/msr access instructions */ +#define PPC_MISC PPC_INSNS_BASE + /* Deprecated instruction sets */ + /* Original POWER instruction set */ + PPC_POWER = 0x0000000000000002ULL, + /* POWER2 instruction set extension */ + PPC_POWER2 = 0x0000000000000004ULL, + /* Power RTC support */ + PPC_POWER_RTC = 0x0000000000000008ULL, + /* Power-to-PowerPC bridge (601) */ + PPC_POWER_BR = 0x0000000000000010ULL, + /* 64 bits PowerPC instruction set */ + PPC_64B = 0x0000000000000020ULL, + /* New 64 bits extensions (PowerPC 2.0x) */ + PPC_64BX = 0x0000000000000040ULL, + /* 64 bits hypervisor extensions */ + PPC_64H = 0x0000000000000080ULL, + /* New wait instruction (PowerPC 2.0x) */ + PPC_WAIT = 0x0000000000000100ULL, + /* Time base mftb instruction */ + PPC_MFTB = 0x0000000000000200ULL, + + /* Fixed-point unit extensions */ + /* PowerPC 602 specific */ + PPC_602_SPEC = 0x0000000000000400ULL, + /* isel instruction */ + PPC_ISEL = 0x0000000000000800ULL, + /* popcntb instruction */ + PPC_POPCNTB = 0x0000000000001000ULL, + /* string load / store */ + PPC_STRING = 0x0000000000002000ULL, + /* real mode cache inhibited load / store */ + PPC_CILDST = 0x0000000000004000ULL, + + /* Floating-point unit extensions */ + /* Optional floating point instructions */ + PPC_FLOAT = 0x0000000000010000ULL, + /* New floating-point extensions (PowerPC 2.0x) */ + PPC_FLOAT_EXT = 0x0000000000020000ULL, + PPC_FLOAT_FSQRT = 0x0000000000040000ULL, + PPC_FLOAT_FRES = 0x0000000000080000ULL, + PPC_FLOAT_FRSQRTE = 0x0000000000100000ULL, + PPC_FLOAT_FRSQRTES = 0x0000000000200000ULL, + PPC_FLOAT_FSEL = 0x0000000000400000ULL, + PPC_FLOAT_STFIWX = 0x0000000000800000ULL, + + /* Vector/SIMD extensions */ + /* Altivec support */ + PPC_ALTIVEC = 0x0000000001000000ULL, + /* PowerPC 2.03 SPE extension */ + PPC_SPE = 0x0000000002000000ULL, + /* PowerPC 2.03 SPE single-precision floating-point extension */ + PPC_SPE_SINGLE = 0x0000000004000000ULL, + /* PowerPC 2.03 SPE double-precision floating-point extension */ + PPC_SPE_DOUBLE = 0x0000000008000000ULL, + + /* Optional memory control instructions */ + PPC_MEM_TLBIA = 0x0000000010000000ULL, + PPC_MEM_TLBIE = 0x0000000020000000ULL, + PPC_MEM_TLBSYNC = 0x0000000040000000ULL, + /* sync instruction */ + PPC_MEM_SYNC = 0x0000000080000000ULL, + /* eieio instruction */ + PPC_MEM_EIEIO = 0x0000000100000000ULL, + + /* Cache control instructions */ + PPC_CACHE = 0x0000000200000000ULL, + /* icbi instruction */ + PPC_CACHE_ICBI = 0x0000000400000000ULL, + /* dcbz instruction */ + PPC_CACHE_DCBZ = 0x0000000800000000ULL, + /* dcba instruction */ + PPC_CACHE_DCBA = 0x0000002000000000ULL, + /* Freescale cache locking instructions */ + PPC_CACHE_LOCK = 0x0000004000000000ULL, + + /* MMU related extensions */ + /* external control instructions */ + PPC_EXTERN = 0x0000010000000000ULL, + /* segment register access instructions */ + PPC_SEGMENT = 0x0000020000000000ULL, + /* PowerPC 6xx TLB management instructions */ + PPC_6xx_TLB = 0x0000040000000000ULL, + /* PowerPC 74xx TLB management instructions */ + PPC_74xx_TLB = 0x0000080000000000ULL, + /* PowerPC 40x TLB management instructions */ + PPC_40x_TLB = 0x0000100000000000ULL, + /* segment register access instructions for PowerPC 64 "bridge" */ + PPC_SEGMENT_64B = 0x0000200000000000ULL, + /* SLB management */ + PPC_SLBI = 0x0000400000000000ULL, + + /* Embedded PowerPC dedicated instructions */ + PPC_WRTEE = 0x0001000000000000ULL, + /* PowerPC 40x exception model */ + PPC_40x_EXCP = 0x0002000000000000ULL, + /* PowerPC 405 Mac instructions */ + PPC_405_MAC = 0x0004000000000000ULL, + /* PowerPC 440 specific instructions */ + PPC_440_SPEC = 0x0008000000000000ULL, + /* BookE (embedded) PowerPC specification */ + PPC_BOOKE = 0x0010000000000000ULL, + /* mfapidi instruction */ + PPC_MFAPIDI = 0x0020000000000000ULL, + /* tlbiva instruction */ + PPC_TLBIVA = 0x0040000000000000ULL, + /* tlbivax instruction */ + PPC_TLBIVAX = 0x0080000000000000ULL, + /* PowerPC 4xx dedicated instructions */ + PPC_4xx_COMMON = 0x0100000000000000ULL, + /* PowerPC 40x ibct instructions */ + PPC_40x_ICBT = 0x0200000000000000ULL, + /* rfmci is not implemented in all BookE PowerPC */ + PPC_RFMCI = 0x0400000000000000ULL, + /* rfdi instruction */ + PPC_RFDI = 0x0800000000000000ULL, + /* DCR accesses */ + PPC_DCR = 0x1000000000000000ULL, + /* DCR extended accesse */ + PPC_DCRX = 0x2000000000000000ULL, + /* user-mode DCR access, implemented in PowerPC 460 */ + PPC_DCRUX = 0x4000000000000000ULL, + /* popcntw and popcntd instructions */ + PPC_POPCNTWD = 0x8000000000000000ULL, + +#define PPC_TCG_INSNS (PPC_INSNS_BASE | PPC_POWER | PPC_POWER2 \ + | PPC_POWER_RTC | PPC_POWER_BR | PPC_64B \ + | PPC_64BX | PPC_64H | PPC_WAIT | PPC_MFTB \ + | PPC_602_SPEC | PPC_ISEL | PPC_POPCNTB \ + | PPC_STRING | PPC_FLOAT | PPC_FLOAT_EXT \ + | PPC_FLOAT_FSQRT | PPC_FLOAT_FRES \ + | PPC_FLOAT_FRSQRTE | PPC_FLOAT_FRSQRTES \ + | PPC_FLOAT_FSEL | PPC_FLOAT_STFIWX \ + | PPC_ALTIVEC | PPC_SPE | PPC_SPE_SINGLE \ + | PPC_SPE_DOUBLE | PPC_MEM_TLBIA \ + | PPC_MEM_TLBIE | PPC_MEM_TLBSYNC \ + | PPC_MEM_SYNC | PPC_MEM_EIEIO \ + | PPC_CACHE | PPC_CACHE_ICBI \ + | PPC_CACHE_DCBZ \ + | PPC_CACHE_DCBA | PPC_CACHE_LOCK \ + | PPC_EXTERN | PPC_SEGMENT | PPC_6xx_TLB \ + | PPC_74xx_TLB | PPC_40x_TLB | PPC_SEGMENT_64B \ + | PPC_SLBI | PPC_WRTEE | PPC_40x_EXCP \ + | PPC_405_MAC | PPC_440_SPEC | PPC_BOOKE \ + | PPC_MFAPIDI | PPC_TLBIVA | PPC_TLBIVAX \ + | PPC_4xx_COMMON | PPC_40x_ICBT | PPC_RFMCI \ + | PPC_RFDI | PPC_DCR | PPC_DCRX | PPC_DCRUX \ + | PPC_POPCNTWD | PPC_CILDST) + + /* extended type values */ + + /* BookE 2.06 PowerPC specification */ + PPC2_BOOKE206 = 0x0000000000000001ULL, + /* VSX (extensions to Altivec / VMX) */ + PPC2_VSX = 0x0000000000000002ULL, + /* Decimal Floating Point (DFP) */ + PPC2_DFP = 0x0000000000000004ULL, + /* Embedded.Processor Control */ + PPC2_PRCNTL = 0x0000000000000008ULL, + /* Byte-reversed, indexed, double-word load and store */ + PPC2_DBRX = 0x0000000000000010ULL, + /* Book I 2.05 PowerPC specification */ + PPC2_ISA205 = 0x0000000000000020ULL, + /* VSX additions in ISA 2.07 */ + PPC2_VSX207 = 0x0000000000000040ULL, + /* ISA 2.06B bpermd */ + PPC2_PERM_ISA206 = 0x0000000000000080ULL, + /* ISA 2.06B divide extended variants */ + PPC2_DIVE_ISA206 = 0x0000000000000100ULL, + /* ISA 2.06B larx/stcx. instructions */ + PPC2_ATOMIC_ISA206 = 0x0000000000000200ULL, + /* ISA 2.06B floating point integer conversion */ + PPC2_FP_CVT_ISA206 = 0x0000000000000400ULL, + /* ISA 2.06B floating point test instructions */ + PPC2_FP_TST_ISA206 = 0x0000000000000800ULL, + /* ISA 2.07 bctar instruction */ + PPC2_BCTAR_ISA207 = 0x0000000000001000ULL, + /* ISA 2.07 load/store quadword */ + PPC2_LSQ_ISA207 = 0x0000000000002000ULL, + /* ISA 2.07 Altivec */ + PPC2_ALTIVEC_207 = 0x0000000000004000ULL, + /* PowerISA 2.07 Book3s specification */ + PPC2_ISA207S = 0x0000000000008000ULL, + /* Double precision floating point conversion for signed integer 64 */ + PPC2_FP_CVT_S64 = 0x0000000000010000ULL, + /* Transactional Memory (ISA 2.07, Book II) */ + PPC2_TM = 0x0000000000020000ULL, + /* Server PM instructgions (ISA 2.06, Book III) */ + PPC2_PM_ISA206 = 0x0000000000040000ULL, + /* POWER ISA 3.0 */ + PPC2_ISA300 = 0x0000000000080000ULL, + +#define PPC_TCG_INSNS2 (PPC2_BOOKE206 | PPC2_VSX | PPC2_PRCNTL | PPC2_DBRX | \ + PPC2_ISA205 | PPC2_VSX207 | PPC2_PERM_ISA206 | \ + PPC2_DIVE_ISA206 | PPC2_ATOMIC_ISA206 | \ + PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206 | \ + PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | \ + PPC2_ALTIVEC_207 | PPC2_ISA207S | PPC2_DFP | \ + PPC2_FP_CVT_S64 | PPC2_TM | PPC2_PM_ISA206 | \ + PPC2_ISA300) +}; + +/*****************************************************************************/ +/* Memory access type : + * may be needed for precise access rights control and precise exceptions. + */ +enum { + /* 1 bit to define user level / supervisor access */ + ACCESS_USER = 0x00, + ACCESS_SUPER = 0x01, + /* Type of instruction that generated the access */ + ACCESS_CODE = 0x10, /* Code fetch access */ + ACCESS_INT = 0x20, /* Integer load/store access */ + ACCESS_FLOAT = 0x30, /* floating point load/store access */ + ACCESS_RES = 0x40, /* load/store with reservation */ + ACCESS_EXT = 0x50, /* external access */ + ACCESS_CACHE = 0x60, /* Cache manipulation */ +}; + +/* Hardware interruption sources: + * all those exception can be raised simulteaneously + */ +/* Input pins definitions */ +enum { + /* 6xx bus input pins */ + PPC6xx_INPUT_HRESET = 0, + PPC6xx_INPUT_SRESET = 1, + PPC6xx_INPUT_CKSTP_IN = 2, + PPC6xx_INPUT_MCP = 3, + PPC6xx_INPUT_SMI = 4, + PPC6xx_INPUT_INT = 5, + PPC6xx_INPUT_TBEN = 6, + PPC6xx_INPUT_WAKEUP = 7, + PPC6xx_INPUT_NB, +}; + +enum { + /* Embedded PowerPC input pins */ + PPCBookE_INPUT_HRESET = 0, + PPCBookE_INPUT_SRESET = 1, + PPCBookE_INPUT_CKSTP_IN = 2, + PPCBookE_INPUT_MCP = 3, + PPCBookE_INPUT_SMI = 4, + PPCBookE_INPUT_INT = 5, + PPCBookE_INPUT_CINT = 6, + PPCBookE_INPUT_NB, +}; + +enum { + /* PowerPC E500 input pins */ + PPCE500_INPUT_RESET_CORE = 0, + PPCE500_INPUT_MCK = 1, + PPCE500_INPUT_CINT = 3, + PPCE500_INPUT_INT = 4, + PPCE500_INPUT_DEBUG = 6, + PPCE500_INPUT_NB, +}; + +enum { + /* PowerPC 40x input pins */ + PPC40x_INPUT_RESET_CORE = 0, + PPC40x_INPUT_RESET_CHIP = 1, + PPC40x_INPUT_RESET_SYS = 2, + PPC40x_INPUT_CINT = 3, + PPC40x_INPUT_INT = 4, + PPC40x_INPUT_HALT = 5, + PPC40x_INPUT_DEBUG = 6, + PPC40x_INPUT_NB, +}; + +enum { + /* RCPU input pins */ + PPCRCPU_INPUT_PORESET = 0, + PPCRCPU_INPUT_HRESET = 1, + PPCRCPU_INPUT_SRESET = 2, + PPCRCPU_INPUT_IRQ0 = 3, + PPCRCPU_INPUT_IRQ1 = 4, + PPCRCPU_INPUT_IRQ2 = 5, + PPCRCPU_INPUT_IRQ3 = 6, + PPCRCPU_INPUT_IRQ4 = 7, + PPCRCPU_INPUT_IRQ5 = 8, + PPCRCPU_INPUT_IRQ6 = 9, + PPCRCPU_INPUT_IRQ7 = 10, + PPCRCPU_INPUT_NB, +}; + +#if defined(TARGET_PPC64) +enum { + /* PowerPC 970 input pins */ + PPC970_INPUT_HRESET = 0, + PPC970_INPUT_SRESET = 1, + PPC970_INPUT_CKSTP = 2, + PPC970_INPUT_TBEN = 3, + PPC970_INPUT_MCP = 4, + PPC970_INPUT_INT = 5, + PPC970_INPUT_THINT = 6, + PPC970_INPUT_NB, +}; + +enum { + /* POWER7 input pins */ + POWER7_INPUT_INT = 0, + /* POWER7 probably has other inputs, but we don't care about them + * for any existing machine. We can wire these up when we need + * them */ + POWER7_INPUT_NB, +}; +#endif + +/* Hardware exceptions definitions */ +enum { + /* External hardware exception sources */ + PPC_INTERRUPT_RESET = 0, /* Reset exception */ + PPC_INTERRUPT_WAKEUP, /* Wakeup exception */ + PPC_INTERRUPT_MCK, /* Machine check exception */ + PPC_INTERRUPT_EXT, /* External interrupt */ + PPC_INTERRUPT_SMI, /* System management interrupt */ + PPC_INTERRUPT_CEXT, /* Critical external interrupt */ + PPC_INTERRUPT_DEBUG, /* External debug exception */ + PPC_INTERRUPT_THERM, /* Thermal exception */ + /* Internal hardware exception sources */ + PPC_INTERRUPT_DECR, /* Decrementer exception */ + PPC_INTERRUPT_HDECR, /* Hypervisor decrementer exception */ + PPC_INTERRUPT_PIT, /* Programmable inteval timer interrupt */ + PPC_INTERRUPT_FIT, /* Fixed interval timer interrupt */ + PPC_INTERRUPT_WDT, /* Watchdog timer interrupt */ + PPC_INTERRUPT_CDOORBELL, /* Critical doorbell interrupt */ + PPC_INTERRUPT_DOORBELL, /* Doorbell interrupt */ + PPC_INTERRUPT_PERFM, /* Performance monitor interrupt */ + PPC_INTERRUPT_HMI, /* Hypervisor Maintainance interrupt */ + PPC_INTERRUPT_HDOORBELL, /* Hypervisor Doorbell interrupt */ +}; + +/* Processor Compatibility mask (PCR) */ +enum { + PCR_COMPAT_2_05 = 1ull << (63-62), + PCR_COMPAT_2_06 = 1ull << (63-61), + PCR_COMPAT_2_07 = 1ull << (63-60), + PCR_VEC_DIS = 1ull << (63-0), /* Vec. disable (bit NA since POWER8) */ + PCR_VSX_DIS = 1ull << (63-1), /* VSX disable (bit NA since POWER8) */ + PCR_TM_DIS = 1ull << (63-2), /* Trans. memory disable (POWER8) */ +}; + +/* HMER/HMEER */ +enum { + HMER_MALFUNCTION_ALERT = 1ull << (63 - 0), + HMER_PROC_RECV_DONE = 1ull << (63 - 2), + HMER_PROC_RECV_ERROR_MASKED = 1ull << (63 - 3), + HMER_TFAC_ERROR = 1ull << (63 - 4), + HMER_TFMR_PARITY_ERROR = 1ull << (63 - 5), + HMER_XSCOM_FAIL = 1ull << (63 - 8), + HMER_XSCOM_DONE = 1ull << (63 - 9), + HMER_PROC_RECV_AGAIN = 1ull << (63 - 11), + HMER_WARN_RISE = 1ull << (63 - 14), + HMER_WARN_FALL = 1ull << (63 - 15), + HMER_SCOM_FIR_HMI = 1ull << (63 - 16), + HMER_TRIG_FIR_HMI = 1ull << (63 - 17), + HMER_HYP_RESOURCE_ERR = 1ull << (63 - 20), + HMER_XSCOM_STATUS_MASK = 7ull << (63 - 23), + HMER_XSCOM_STATUS_LSH = (63 - 23), +}; + +/* Alternate Interrupt Location (AIL) */ +enum { + AIL_NONE = 0, + AIL_RESERVED = 1, + AIL_0001_8000 = 2, + AIL_C000_0000_0000_4000 = 3, +}; + +/*****************************************************************************/ + +static inline target_ulong cpu_read_xer(CPUPPCState *env) +{ + return env->xer | (env->so << XER_SO) | (env->ov << XER_OV) | (env->ca << XER_CA); +} + +static inline void cpu_write_xer(CPUPPCState *env, target_ulong xer) +{ + env->so = (xer >> XER_SO) & 1; + env->ov = (xer >> XER_OV) & 1; + env->ca = (xer >> XER_CA) & 1; + env->xer = xer & ~((1u << XER_SO) | (1u << XER_OV) | (1u << XER_CA)); +} + +static inline void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc, + target_ulong *cs_base, uint32_t *flags) +{ + *pc = env->nip; + *cs_base = 0; + *flags = env->hflags; +} + +void QEMU_NORETURN raise_exception(CPUPPCState *env, uint32_t exception); +void QEMU_NORETURN raise_exception_ra(CPUPPCState *env, uint32_t exception, + uintptr_t raddr); +void QEMU_NORETURN raise_exception_err(CPUPPCState *env, uint32_t exception, + uint32_t error_code); +void QEMU_NORETURN raise_exception_err_ra(CPUPPCState *env, uint32_t exception, + uint32_t error_code, uintptr_t raddr); + +#if !defined(CONFIG_USER_ONLY) +static inline int booke206_tlbm_id(CPUPPCState *env, ppcmas_tlb_t *tlbm) +{ + uintptr_t tlbml = (uintptr_t)tlbm; + uintptr_t tlbl = (uintptr_t)env->tlb.tlbm; + + return (tlbml - tlbl) / sizeof(env->tlb.tlbm[0]); +} + +static inline int booke206_tlb_size(CPUPPCState *env, int tlbn) +{ + uint32_t tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlbn]; + int r = tlbncfg & TLBnCFG_N_ENTRY; + return r; +} + +static inline int booke206_tlb_ways(CPUPPCState *env, int tlbn) +{ + uint32_t tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlbn]; + int r = tlbncfg >> TLBnCFG_ASSOC_SHIFT; + return r; +} + +static inline int booke206_tlbm_to_tlbn(CPUPPCState *env, ppcmas_tlb_t *tlbm) +{ + int id = booke206_tlbm_id(env, tlbm); + int end = 0; + int i; + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + end += booke206_tlb_size(env, i); + if (id < end) { + return i; + } + } + + cpu_abort(CPU(ppc_env_get_cpu(env)), "Unknown TLBe: %d\n", id); + return 0; +} + +static inline int booke206_tlbm_to_way(CPUPPCState *env, ppcmas_tlb_t *tlb) +{ + int tlbn = booke206_tlbm_to_tlbn(env, tlb); + int tlbid = booke206_tlbm_id(env, tlb); + return tlbid & (booke206_tlb_ways(env, tlbn) - 1); +} + +static inline ppcmas_tlb_t *booke206_get_tlbm(CPUPPCState *env, const int tlbn, + target_ulong ea, int way) +{ + int r; + uint32_t ways = booke206_tlb_ways(env, tlbn); + int ways_bits = ctz32(ways); + int tlb_bits = ctz32(booke206_tlb_size(env, tlbn)); + int i; + + way &= ways - 1; + ea >>= MAS2_EPN_SHIFT; + ea &= (1 << (tlb_bits - ways_bits)) - 1; + r = (ea << ways_bits) | way; + + if (r >= booke206_tlb_size(env, tlbn)) { + return NULL; + } + + /* bump up to tlbn index */ + for (i = 0; i < tlbn; i++) { + r += booke206_tlb_size(env, i); + } + + return &env->tlb.tlbm[r]; +} + +/* returns bitmap of supported page sizes for a given TLB */ +static inline uint32_t booke206_tlbnps(CPUPPCState *env, const int tlbn) +{ + bool mav2 = false; + uint32_t ret = 0; + + if (mav2) { + ret = env->spr[SPR_BOOKE_TLB0PS + tlbn]; + } else { + uint32_t tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlbn]; + uint32_t min = (tlbncfg & TLBnCFG_MINSIZE) >> TLBnCFG_MINSIZE_SHIFT; + uint32_t max = (tlbncfg & TLBnCFG_MAXSIZE) >> TLBnCFG_MAXSIZE_SHIFT; + int i; + for (i = min; i <= max; i++) { + ret |= (1 << (i << 1)); + } + } + + return ret; +} + +#endif + +static inline bool msr_is_64bit(CPUPPCState *env, target_ulong msr) +{ + if (env->mmu_model == POWERPC_MMU_BOOKE206) { + return msr & (1ULL << MSR_CM); + } + + return msr & (1ULL << MSR_SF); +} + +/** + * Check whether register rx is in the range between start and + * start + nregs (as needed by the LSWX and LSWI instructions) + */ +static inline bool lsw_reg_in_range(int start, int nregs, int rx) +{ + return (start + nregs <= 32 && rx >= start && rx < start + nregs) || + (start + nregs > 32 && (rx >= start || rx < start + nregs - 32)); +} + +extern void (*cpu_ppc_hypercall)(PowerPCCPU *); + +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env); + +/** + * ppc_get_vcpu_dt_id: + * @cs: a PowerPCCPU struct. + * + * Returns a device-tree ID for a CPU. + */ +int ppc_get_vcpu_dt_id(PowerPCCPU *cpu); + +/** + * ppc_get_vcpu_by_dt_id: + * @cpu_dt_id: a device tree id + * + * Searches for a CPU by @cpu_dt_id. + * + * Returns: a PowerPCCPU struct + */ +PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id); + +void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len); +#endif /* PPC_CPU_H */ diff --git a/target/ppc/dfp_helper.c b/target/ppc/dfp_helper.c new file mode 100644 index 0000000000..9164fe701b --- /dev/null +++ b/target/ppc/dfp_helper.c @@ -0,0 +1,1331 @@ +/* + * PowerPC Decimal Floating Point (DPF) emulation helpers for QEMU. + * + * Copyright (c) 2014 IBM Corporation. + * + * 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 "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" + +#define DECNUMDIGITS 34 +#include "libdecnumber/decContext.h" +#include "libdecnumber/decNumber.h" +#include "libdecnumber/dpd/decimal32.h" +#include "libdecnumber/dpd/decimal64.h" +#include "libdecnumber/dpd/decimal128.h" + +#if defined(HOST_WORDS_BIGENDIAN) +#define HI_IDX 0 +#define LO_IDX 1 +#else +#define HI_IDX 1 +#define LO_IDX 0 +#endif + +struct PPC_DFP { + CPUPPCState *env; + uint64_t t64[2], a64[2], b64[2]; + decNumber t, a, b; + decContext context; + uint8_t crbf; +}; + +static void dfp_prepare_rounding_mode(decContext *context, uint64_t fpscr) +{ + enum rounding rnd; + + switch ((fpscr >> 32) & 0x7) { + case 0: + rnd = DEC_ROUND_HALF_EVEN; + break; + case 1: + rnd = DEC_ROUND_DOWN; + break; + case 2: + rnd = DEC_ROUND_CEILING; + break; + case 3: + rnd = DEC_ROUND_FLOOR; + break; + case 4: + rnd = DEC_ROUND_HALF_UP; + break; + case 5: + rnd = DEC_ROUND_HALF_DOWN; + break; + case 6: + rnd = DEC_ROUND_UP; + break; + case 7: + rnd = DEC_ROUND_05UP; + break; + default: + g_assert_not_reached(); + } + + decContextSetRounding(context, rnd); +} + +static void dfp_set_round_mode_from_immediate(uint8_t r, uint8_t rmc, + struct PPC_DFP *dfp) +{ + enum rounding rnd; + if (r == 0) { + switch (rmc & 3) { + case 0: + rnd = DEC_ROUND_HALF_EVEN; + break; + case 1: + rnd = DEC_ROUND_DOWN; + break; + case 2: + rnd = DEC_ROUND_HALF_UP; + break; + case 3: /* use FPSCR rounding mode */ + return; + default: + assert(0); /* cannot get here */ + } + } else { /* r == 1 */ + switch (rmc & 3) { + case 0: + rnd = DEC_ROUND_CEILING; + break; + case 1: + rnd = DEC_ROUND_FLOOR; + break; + case 2: + rnd = DEC_ROUND_UP; + break; + case 3: + rnd = DEC_ROUND_HALF_DOWN; + break; + default: + assert(0); /* cannot get here */ + } + } + decContextSetRounding(&dfp->context, rnd); +} + +static void dfp_prepare_decimal64(struct PPC_DFP *dfp, uint64_t *a, + uint64_t *b, CPUPPCState *env) +{ + decContextDefault(&dfp->context, DEC_INIT_DECIMAL64); + dfp_prepare_rounding_mode(&dfp->context, env->fpscr); + dfp->env = env; + + if (a) { + dfp->a64[0] = *a; + decimal64ToNumber((decimal64 *)dfp->a64, &dfp->a); + } else { + dfp->a64[0] = 0; + decNumberZero(&dfp->a); + } + + if (b) { + dfp->b64[0] = *b; + decimal64ToNumber((decimal64 *)dfp->b64, &dfp->b); + } else { + dfp->b64[0] = 0; + decNumberZero(&dfp->b); + } +} + +static void dfp_prepare_decimal128(struct PPC_DFP *dfp, uint64_t *a, + uint64_t *b, CPUPPCState *env) +{ + decContextDefault(&dfp->context, DEC_INIT_DECIMAL128); + dfp_prepare_rounding_mode(&dfp->context, env->fpscr); + dfp->env = env; + + if (a) { + dfp->a64[0] = a[HI_IDX]; + dfp->a64[1] = a[LO_IDX]; + decimal128ToNumber((decimal128 *)dfp->a64, &dfp->a); + } else { + dfp->a64[0] = dfp->a64[1] = 0; + decNumberZero(&dfp->a); + } + + if (b) { + dfp->b64[0] = b[HI_IDX]; + dfp->b64[1] = b[LO_IDX]; + decimal128ToNumber((decimal128 *)dfp->b64, &dfp->b); + } else { + dfp->b64[0] = dfp->b64[1] = 0; + decNumberZero(&dfp->b); + } +} + +static void dfp_set_FPSCR_flag(struct PPC_DFP *dfp, uint64_t flag, + uint64_t enabled) +{ + dfp->env->fpscr |= (flag | FP_FX); + if (dfp->env->fpscr & enabled) { + dfp->env->fpscr |= FP_FEX; + } +} + +static void dfp_set_FPRF_from_FRT_with_context(struct PPC_DFP *dfp, + decContext *context) +{ + uint64_t fprf = 0; + + /* construct FPRF */ + switch (decNumberClass(&dfp->t, context)) { + case DEC_CLASS_SNAN: + fprf = 0x01; + break; + case DEC_CLASS_QNAN: + fprf = 0x11; + break; + case DEC_CLASS_NEG_INF: + fprf = 0x09; + break; + case DEC_CLASS_NEG_NORMAL: + fprf = 0x08; + break; + case DEC_CLASS_NEG_SUBNORMAL: + fprf = 0x18; + break; + case DEC_CLASS_NEG_ZERO: + fprf = 0x12; + break; + case DEC_CLASS_POS_ZERO: + fprf = 0x02; + break; + case DEC_CLASS_POS_SUBNORMAL: + fprf = 0x14; + break; + case DEC_CLASS_POS_NORMAL: + fprf = 0x04; + break; + case DEC_CLASS_POS_INF: + fprf = 0x05; + break; + default: + assert(0); /* should never get here */ + } + dfp->env->fpscr &= ~(0x1F << 12); + dfp->env->fpscr |= (fprf << 12); +} + +static void dfp_set_FPRF_from_FRT(struct PPC_DFP *dfp) +{ + dfp_set_FPRF_from_FRT_with_context(dfp, &dfp->context); +} + +static void dfp_set_FPRF_from_FRT_short(struct PPC_DFP *dfp) +{ + decContext shortContext; + decContextDefault(&shortContext, DEC_INIT_DECIMAL32); + dfp_set_FPRF_from_FRT_with_context(dfp, &shortContext); +} + +static void dfp_set_FPRF_from_FRT_long(struct PPC_DFP *dfp) +{ + decContext longContext; + decContextDefault(&longContext, DEC_INIT_DECIMAL64); + dfp_set_FPRF_from_FRT_with_context(dfp, &longContext); +} + +static void dfp_check_for_OX(struct PPC_DFP *dfp) +{ + if (dfp->context.status & DEC_Overflow) { + dfp_set_FPSCR_flag(dfp, FP_OX, FP_OE); + } +} + +static void dfp_check_for_UX(struct PPC_DFP *dfp) +{ + if (dfp->context.status & DEC_Underflow) { + dfp_set_FPSCR_flag(dfp, FP_UX, FP_UE); + } +} + +static void dfp_check_for_XX(struct PPC_DFP *dfp) +{ + if (dfp->context.status & DEC_Inexact) { + dfp_set_FPSCR_flag(dfp, FP_XX | FP_FI, FP_XE); + } +} + +static void dfp_check_for_ZX(struct PPC_DFP *dfp) +{ + if (dfp->context.status & DEC_Division_by_zero) { + dfp_set_FPSCR_flag(dfp, FP_ZX, FP_ZE); + } +} + +static void dfp_check_for_VXSNAN(struct PPC_DFP *dfp) +{ + if (dfp->context.status & DEC_Invalid_operation) { + if (decNumberIsSNaN(&dfp->a) || decNumberIsSNaN(&dfp->b)) { + dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXSNAN, FP_VE); + } + } +} + +static void dfp_check_for_VXSNAN_and_convert_to_QNaN(struct PPC_DFP *dfp) +{ + if (decNumberIsSNaN(&dfp->t)) { + dfp->t.bits &= ~DECSNAN; + dfp->t.bits |= DECNAN; + dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXSNAN, FP_VE); + } +} + +static void dfp_check_for_VXISI(struct PPC_DFP *dfp, int testForSameSign) +{ + if (dfp->context.status & DEC_Invalid_operation) { + if (decNumberIsInfinite(&dfp->a) && decNumberIsInfinite(&dfp->b)) { + int same = decNumberClass(&dfp->a, &dfp->context) == + decNumberClass(&dfp->b, &dfp->context); + if ((same && testForSameSign) || (!same && !testForSameSign)) { + dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXISI, FP_VE); + } + } + } +} + +static void dfp_check_for_VXISI_add(struct PPC_DFP *dfp) +{ + dfp_check_for_VXISI(dfp, 0); +} + +static void dfp_check_for_VXISI_subtract(struct PPC_DFP *dfp) +{ + dfp_check_for_VXISI(dfp, 1); +} + +static void dfp_check_for_VXIMZ(struct PPC_DFP *dfp) +{ + if (dfp->context.status & DEC_Invalid_operation) { + if ((decNumberIsInfinite(&dfp->a) && decNumberIsZero(&dfp->b)) || + (decNumberIsInfinite(&dfp->b) && decNumberIsZero(&dfp->a))) { + dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXIMZ, FP_VE); + } + } +} + +static void dfp_check_for_VXZDZ(struct PPC_DFP *dfp) +{ + if (dfp->context.status & DEC_Division_undefined) { + dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXZDZ, FP_VE); + } +} + +static void dfp_check_for_VXIDI(struct PPC_DFP *dfp) +{ + if (dfp->context.status & DEC_Invalid_operation) { + if (decNumberIsInfinite(&dfp->a) && decNumberIsInfinite(&dfp->b)) { + dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXIDI, FP_VE); + } + } +} + +static void dfp_check_for_VXVC(struct PPC_DFP *dfp) +{ + if (decNumberIsNaN(&dfp->a) || decNumberIsNaN(&dfp->b)) { + dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXVC, FP_VE); + } +} + +static void dfp_check_for_VXCVI(struct PPC_DFP *dfp) +{ + if ((dfp->context.status & DEC_Invalid_operation) && + (!decNumberIsSNaN(&dfp->a)) && + (!decNumberIsSNaN(&dfp->b))) { + dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXCVI, FP_VE); + } +} + +static void dfp_set_CRBF_from_T(struct PPC_DFP *dfp) +{ + if (decNumberIsNaN(&dfp->t)) { + dfp->crbf = 1; + } else if (decNumberIsZero(&dfp->t)) { + dfp->crbf = 2; + } else if (decNumberIsNegative(&dfp->t)) { + dfp->crbf = 8; + } else { + dfp->crbf = 4; + } +} + +static void dfp_set_FPCC_from_CRBF(struct PPC_DFP *dfp) +{ + dfp->env->fpscr &= ~(0xF << 12); + dfp->env->fpscr |= (dfp->crbf << 12); +} + +static inline void dfp_makeQNaN(decNumber *dn) +{ + dn->bits &= ~DECSPECIAL; + dn->bits |= DECNAN; +} + +static inline int dfp_get_digit(decNumber *dn, int n) +{ + assert(DECDPUN == 3); + int unit = n / DECDPUN; + int dig = n % DECDPUN; + switch (dig) { + case 0: + return dn->lsu[unit] % 10; + case 1: + return (dn->lsu[unit] / 10) % 10; + case 2: + return dn->lsu[unit] / 100; + } + g_assert_not_reached(); +} + +#define DFP_HELPER_TAB(op, dnop, postprocs, size) \ +void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *a, uint64_t *b) \ +{ \ + struct PPC_DFP dfp; \ + dfp_prepare_decimal##size(&dfp, a, b, env); \ + dnop(&dfp.t, &dfp.a, &dfp.b, &dfp.context); \ + decimal##size##FromNumber((decimal##size *)dfp.t64, &dfp.t, &dfp.context); \ + postprocs(&dfp); \ + if (size == 64) { \ + t[0] = dfp.t64[0]; \ + } else if (size == 128) { \ + t[0] = dfp.t64[HI_IDX]; \ + t[1] = dfp.t64[LO_IDX]; \ + } \ +} + +static void ADD_PPs(struct PPC_DFP *dfp) +{ + dfp_set_FPRF_from_FRT(dfp); + dfp_check_for_OX(dfp); + dfp_check_for_UX(dfp); + dfp_check_for_XX(dfp); + dfp_check_for_VXSNAN(dfp); + dfp_check_for_VXISI_add(dfp); +} + +DFP_HELPER_TAB(dadd, decNumberAdd, ADD_PPs, 64) +DFP_HELPER_TAB(daddq, decNumberAdd, ADD_PPs, 128) + +static void SUB_PPs(struct PPC_DFP *dfp) +{ + dfp_set_FPRF_from_FRT(dfp); + dfp_check_for_OX(dfp); + dfp_check_for_UX(dfp); + dfp_check_for_XX(dfp); + dfp_check_for_VXSNAN(dfp); + dfp_check_for_VXISI_subtract(dfp); +} + +DFP_HELPER_TAB(dsub, decNumberSubtract, SUB_PPs, 64) +DFP_HELPER_TAB(dsubq, decNumberSubtract, SUB_PPs, 128) + +static void MUL_PPs(struct PPC_DFP *dfp) +{ + dfp_set_FPRF_from_FRT(dfp); + dfp_check_for_OX(dfp); + dfp_check_for_UX(dfp); + dfp_check_for_XX(dfp); + dfp_check_for_VXSNAN(dfp); + dfp_check_for_VXIMZ(dfp); +} + +DFP_HELPER_TAB(dmul, decNumberMultiply, MUL_PPs, 64) +DFP_HELPER_TAB(dmulq, decNumberMultiply, MUL_PPs, 128) + +static void DIV_PPs(struct PPC_DFP *dfp) +{ + dfp_set_FPRF_from_FRT(dfp); + dfp_check_for_OX(dfp); + dfp_check_for_UX(dfp); + dfp_check_for_ZX(dfp); + dfp_check_for_XX(dfp); + dfp_check_for_VXSNAN(dfp); + dfp_check_for_VXZDZ(dfp); + dfp_check_for_VXIDI(dfp); +} + +DFP_HELPER_TAB(ddiv, decNumberDivide, DIV_PPs, 64) +DFP_HELPER_TAB(ddivq, decNumberDivide, DIV_PPs, 128) + +#define DFP_HELPER_BF_AB(op, dnop, postprocs, size) \ +uint32_t helper_##op(CPUPPCState *env, uint64_t *a, uint64_t *b) \ +{ \ + struct PPC_DFP dfp; \ + dfp_prepare_decimal##size(&dfp, a, b, env); \ + dnop(&dfp.t, &dfp.a, &dfp.b, &dfp.context); \ + decimal##size##FromNumber((decimal##size *)dfp.t64, &dfp.t, &dfp.context); \ + postprocs(&dfp); \ + return dfp.crbf; \ +} + +static void CMPU_PPs(struct PPC_DFP *dfp) +{ + dfp_set_CRBF_from_T(dfp); + dfp_set_FPCC_from_CRBF(dfp); + dfp_check_for_VXSNAN(dfp); +} + +DFP_HELPER_BF_AB(dcmpu, decNumberCompare, CMPU_PPs, 64) +DFP_HELPER_BF_AB(dcmpuq, decNumberCompare, CMPU_PPs, 128) + +static void CMPO_PPs(struct PPC_DFP *dfp) +{ + dfp_set_CRBF_from_T(dfp); + dfp_set_FPCC_from_CRBF(dfp); + dfp_check_for_VXSNAN(dfp); + dfp_check_for_VXVC(dfp); +} + +DFP_HELPER_BF_AB(dcmpo, decNumberCompare, CMPO_PPs, 64) +DFP_HELPER_BF_AB(dcmpoq, decNumberCompare, CMPO_PPs, 128) + +#define DFP_HELPER_TSTDC(op, size) \ +uint32_t helper_##op(CPUPPCState *env, uint64_t *a, uint32_t dcm) \ +{ \ + struct PPC_DFP dfp; \ + int match = 0; \ + \ + dfp_prepare_decimal##size(&dfp, a, 0, env); \ + \ + match |= (dcm & 0x20) && decNumberIsZero(&dfp.a); \ + match |= (dcm & 0x10) && decNumberIsSubnormal(&dfp.a, &dfp.context); \ + match |= (dcm & 0x08) && decNumberIsNormal(&dfp.a, &dfp.context); \ + match |= (dcm & 0x04) && decNumberIsInfinite(&dfp.a); \ + match |= (dcm & 0x02) && decNumberIsQNaN(&dfp.a); \ + match |= (dcm & 0x01) && decNumberIsSNaN(&dfp.a); \ + \ + if (decNumberIsNegative(&dfp.a)) { \ + dfp.crbf = match ? 0xA : 0x8; \ + } else { \ + dfp.crbf = match ? 0x2 : 0x0; \ + } \ + \ + dfp_set_FPCC_from_CRBF(&dfp); \ + return dfp.crbf; \ +} + +DFP_HELPER_TSTDC(dtstdc, 64) +DFP_HELPER_TSTDC(dtstdcq, 128) + +#define DFP_HELPER_TSTDG(op, size) \ +uint32_t helper_##op(CPUPPCState *env, uint64_t *a, uint32_t dcm) \ +{ \ + struct PPC_DFP dfp; \ + int minexp, maxexp, nzero_digits, nzero_idx, is_negative, is_zero, \ + is_extreme_exp, is_subnormal, is_normal, leftmost_is_nonzero, \ + match; \ + \ + dfp_prepare_decimal##size(&dfp, a, 0, env); \ + \ + if ((size) == 64) { \ + minexp = -398; \ + maxexp = 369; \ + nzero_digits = 16; \ + nzero_idx = 5; \ + } else if ((size) == 128) { \ + minexp = -6176; \ + maxexp = 6111; \ + nzero_digits = 34; \ + nzero_idx = 11; \ + } \ + \ + is_negative = decNumberIsNegative(&dfp.a); \ + is_zero = decNumberIsZero(&dfp.a); \ + is_extreme_exp = (dfp.a.exponent == maxexp) || \ + (dfp.a.exponent == minexp); \ + is_subnormal = decNumberIsSubnormal(&dfp.a, &dfp.context); \ + is_normal = decNumberIsNormal(&dfp.a, &dfp.context); \ + leftmost_is_nonzero = (dfp.a.digits == nzero_digits) && \ + (dfp.a.lsu[nzero_idx] != 0); \ + match = 0; \ + \ + match |= (dcm & 0x20) && is_zero && !is_extreme_exp; \ + match |= (dcm & 0x10) && is_zero && is_extreme_exp; \ + match |= (dcm & 0x08) && \ + (is_subnormal || (is_normal && is_extreme_exp)); \ + match |= (dcm & 0x04) && is_normal && !is_extreme_exp && \ + !leftmost_is_nonzero; \ + match |= (dcm & 0x02) && is_normal && !is_extreme_exp && \ + leftmost_is_nonzero; \ + match |= (dcm & 0x01) && decNumberIsSpecial(&dfp.a); \ + \ + if (is_negative) { \ + dfp.crbf = match ? 0xA : 0x8; \ + } else { \ + dfp.crbf = match ? 0x2 : 0x0; \ + } \ + \ + dfp_set_FPCC_from_CRBF(&dfp); \ + return dfp.crbf; \ +} + +DFP_HELPER_TSTDG(dtstdg, 64) +DFP_HELPER_TSTDG(dtstdgq, 128) + +#define DFP_HELPER_TSTEX(op, size) \ +uint32_t helper_##op(CPUPPCState *env, uint64_t *a, uint64_t *b) \ +{ \ + struct PPC_DFP dfp; \ + int expa, expb, a_is_special, b_is_special; \ + \ + dfp_prepare_decimal##size(&dfp, a, b, env); \ + \ + expa = dfp.a.exponent; \ + expb = dfp.b.exponent; \ + a_is_special = decNumberIsSpecial(&dfp.a); \ + b_is_special = decNumberIsSpecial(&dfp.b); \ + \ + if (a_is_special || b_is_special) { \ + int atype = a_is_special ? (decNumberIsNaN(&dfp.a) ? 4 : 2) : 1; \ + int btype = b_is_special ? (decNumberIsNaN(&dfp.b) ? 4 : 2) : 1; \ + dfp.crbf = (atype ^ btype) ? 0x1 : 0x2; \ + } else if (expa < expb) { \ + dfp.crbf = 0x8; \ + } else if (expa > expb) { \ + dfp.crbf = 0x4; \ + } else { \ + dfp.crbf = 0x2; \ + } \ + \ + dfp_set_FPCC_from_CRBF(&dfp); \ + return dfp.crbf; \ +} + +DFP_HELPER_TSTEX(dtstex, 64) +DFP_HELPER_TSTEX(dtstexq, 128) + +#define DFP_HELPER_TSTSF(op, size) \ +uint32_t helper_##op(CPUPPCState *env, uint64_t *a, uint64_t *b) \ +{ \ + struct PPC_DFP dfp; \ + unsigned k; \ + \ + dfp_prepare_decimal##size(&dfp, 0, b, env); \ + \ + k = *a & 0x3F; \ + \ + if (unlikely(decNumberIsSpecial(&dfp.b))) { \ + dfp.crbf = 1; \ + } else if (k == 0) { \ + dfp.crbf = 4; \ + } else if (unlikely(decNumberIsZero(&dfp.b))) { \ + /* Zero has no sig digits */ \ + dfp.crbf = 4; \ + } else { \ + unsigned nsd = dfp.b.digits; \ + if (k < nsd) { \ + dfp.crbf = 8; \ + } else if (k > nsd) { \ + dfp.crbf = 4; \ + } else { \ + dfp.crbf = 2; \ + } \ + } \ + \ + dfp_set_FPCC_from_CRBF(&dfp); \ + return dfp.crbf; \ +} + +DFP_HELPER_TSTSF(dtstsf, 64) +DFP_HELPER_TSTSF(dtstsfq, 128) + +#define DFP_HELPER_TSTSFI(op, size) \ +uint32_t helper_##op(CPUPPCState *env, uint32_t a, uint64_t *b) \ +{ \ + struct PPC_DFP dfp; \ + unsigned uim; \ + \ + dfp_prepare_decimal##size(&dfp, 0, b, env); \ + \ + uim = a & 0x3F; \ + \ + if (unlikely(decNumberIsSpecial(&dfp.b))) { \ + dfp.crbf = 1; \ + } else if (uim == 0) { \ + dfp.crbf = 4; \ + } else if (unlikely(decNumberIsZero(&dfp.b))) { \ + /* Zero has no sig digits */ \ + dfp.crbf = 4; \ + } else { \ + unsigned nsd = dfp.b.digits; \ + if (uim < nsd) { \ + dfp.crbf = 8; \ + } else if (uim > nsd) { \ + dfp.crbf = 4; \ + } else { \ + dfp.crbf = 2; \ + } \ + } \ + \ + dfp_set_FPCC_from_CRBF(&dfp); \ + return dfp.crbf; \ +} + +DFP_HELPER_TSTSFI(dtstsfi, 64) +DFP_HELPER_TSTSFI(dtstsfiq, 128) + +static void QUA_PPs(struct PPC_DFP *dfp) +{ + dfp_set_FPRF_from_FRT(dfp); + dfp_check_for_XX(dfp); + dfp_check_for_VXSNAN(dfp); + dfp_check_for_VXCVI(dfp); +} + +static void dfp_quantize(uint8_t rmc, struct PPC_DFP *dfp) +{ + dfp_set_round_mode_from_immediate(0, rmc, dfp); + decNumberQuantize(&dfp->t, &dfp->b, &dfp->a, &dfp->context); + if (decNumberIsSNaN(&dfp->a)) { + dfp->t = dfp->a; + dfp_makeQNaN(&dfp->t); + } else if (decNumberIsSNaN(&dfp->b)) { + dfp->t = dfp->b; + dfp_makeQNaN(&dfp->t); + } else if (decNumberIsQNaN(&dfp->a)) { + dfp->t = dfp->a; + } else if (decNumberIsQNaN(&dfp->b)) { + dfp->t = dfp->b; + } +} + +#define DFP_HELPER_QUAI(op, size) \ +void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *b, \ + uint32_t te, uint32_t rmc) \ +{ \ + struct PPC_DFP dfp; \ + \ + dfp_prepare_decimal##size(&dfp, 0, b, env); \ + \ + decNumberFromUInt32(&dfp.a, 1); \ + dfp.a.exponent = (int32_t)((int8_t)(te << 3) >> 3); \ + \ + dfp_quantize(rmc, &dfp); \ + decimal##size##FromNumber((decimal##size *)dfp.t64, &dfp.t, \ + &dfp.context); \ + QUA_PPs(&dfp); \ + \ + if (size == 64) { \ + t[0] = dfp.t64[0]; \ + } else if (size == 128) { \ + t[0] = dfp.t64[HI_IDX]; \ + t[1] = dfp.t64[LO_IDX]; \ + } \ +} + +DFP_HELPER_QUAI(dquai, 64) +DFP_HELPER_QUAI(dquaiq, 128) + +#define DFP_HELPER_QUA(op, size) \ +void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *a, \ + uint64_t *b, uint32_t rmc) \ +{ \ + struct PPC_DFP dfp; \ + \ + dfp_prepare_decimal##size(&dfp, a, b, env); \ + \ + dfp_quantize(rmc, &dfp); \ + decimal##size##FromNumber((decimal##size *)dfp.t64, &dfp.t, \ + &dfp.context); \ + QUA_PPs(&dfp); \ + \ + if (size == 64) { \ + t[0] = dfp.t64[0]; \ + } else if (size == 128) { \ + t[0] = dfp.t64[HI_IDX]; \ + t[1] = dfp.t64[LO_IDX]; \ + } \ +} + +DFP_HELPER_QUA(dqua, 64) +DFP_HELPER_QUA(dquaq, 128) + +static void _dfp_reround(uint8_t rmc, int32_t ref_sig, int32_t xmax, + struct PPC_DFP *dfp) +{ + int msd_orig, msd_rslt; + + if (unlikely((ref_sig == 0) || (dfp->b.digits <= ref_sig))) { + dfp->t = dfp->b; + if (decNumberIsSNaN(&dfp->b)) { + dfp_makeQNaN(&dfp->t); + dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXSNAN, FPSCR_VE); + } + return; + } + + /* Reround is equivalent to quantizing b with 1**E(n) where */ + /* n = exp(b) + numDigits(b) - reference_significance. */ + + decNumberFromUInt32(&dfp->a, 1); + dfp->a.exponent = dfp->b.exponent + dfp->b.digits - ref_sig; + + if (unlikely(dfp->a.exponent > xmax)) { + dfp->t.digits = 0; + dfp->t.bits &= ~DECNEG; + dfp_makeQNaN(&dfp->t); + dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXCVI, FPSCR_VE); + return; + } + + dfp_quantize(rmc, dfp); + + msd_orig = dfp_get_digit(&dfp->b, dfp->b.digits-1); + msd_rslt = dfp_get_digit(&dfp->t, dfp->t.digits-1); + + /* If the quantization resulted in rounding up to the next magnitude, */ + /* then we need to shift the significand and adjust the exponent. */ + + if (unlikely((msd_orig == 9) && (msd_rslt == 1))) { + + decNumber negone; + + decNumberFromInt32(&negone, -1); + decNumberShift(&dfp->t, &dfp->t, &negone, &dfp->context); + dfp->t.exponent++; + + if (unlikely(dfp->t.exponent > xmax)) { + dfp_makeQNaN(&dfp->t); + dfp->t.digits = 0; + dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXCVI, FP_VE); + /* Inhibit XX in this case */ + decContextClearStatus(&dfp->context, DEC_Inexact); + } + } +} + +#define DFP_HELPER_RRND(op, size) \ +void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *a, \ + uint64_t *b, uint32_t rmc) \ +{ \ + struct PPC_DFP dfp; \ + int32_t ref_sig = *a & 0x3F; \ + int32_t xmax = ((size) == 64) ? 369 : 6111; \ + \ + dfp_prepare_decimal##size(&dfp, 0, b, env); \ + \ + _dfp_reround(rmc, ref_sig, xmax, &dfp); \ + decimal##size##FromNumber((decimal##size *)dfp.t64, &dfp.t, \ + &dfp.context); \ + QUA_PPs(&dfp); \ + \ + if (size == 64) { \ + t[0] = dfp.t64[0]; \ + } else if (size == 128) { \ + t[0] = dfp.t64[HI_IDX]; \ + t[1] = dfp.t64[LO_IDX]; \ + } \ +} + +DFP_HELPER_RRND(drrnd, 64) +DFP_HELPER_RRND(drrndq, 128) + +#define DFP_HELPER_RINT(op, postprocs, size) \ +void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *b, \ + uint32_t r, uint32_t rmc) \ +{ \ + struct PPC_DFP dfp; \ + \ + dfp_prepare_decimal##size(&dfp, 0, b, env); \ + \ + dfp_set_round_mode_from_immediate(r, rmc, &dfp); \ + decNumberToIntegralExact(&dfp.t, &dfp.b, &dfp.context); \ + decimal##size##FromNumber((decimal##size *)dfp.t64, &dfp.t, &dfp.context); \ + postprocs(&dfp); \ + \ + if (size == 64) { \ + t[0] = dfp.t64[0]; \ + } else if (size == 128) { \ + t[0] = dfp.t64[HI_IDX]; \ + t[1] = dfp.t64[LO_IDX]; \ + } \ +} + +static void RINTX_PPs(struct PPC_DFP *dfp) +{ + dfp_set_FPRF_from_FRT(dfp); + dfp_check_for_XX(dfp); + dfp_check_for_VXSNAN(dfp); +} + +DFP_HELPER_RINT(drintx, RINTX_PPs, 64) +DFP_HELPER_RINT(drintxq, RINTX_PPs, 128) + +static void RINTN_PPs(struct PPC_DFP *dfp) +{ + dfp_set_FPRF_from_FRT(dfp); + dfp_check_for_VXSNAN(dfp); +} + +DFP_HELPER_RINT(drintn, RINTN_PPs, 64) +DFP_HELPER_RINT(drintnq, RINTN_PPs, 128) + +void helper_dctdp(CPUPPCState *env, uint64_t *t, uint64_t *b) +{ + struct PPC_DFP dfp; + uint32_t b_short = *b; + dfp_prepare_decimal64(&dfp, 0, 0, env); + decimal32ToNumber((decimal32 *)&b_short, &dfp.t); + decimal64FromNumber((decimal64 *)t, &dfp.t, &dfp.context); + dfp_set_FPRF_from_FRT(&dfp); +} + +void helper_dctqpq(CPUPPCState *env, uint64_t *t, uint64_t *b) +{ + struct PPC_DFP dfp; + dfp_prepare_decimal128(&dfp, 0, 0, env); + decimal64ToNumber((decimal64 *)b, &dfp.t); + + dfp_check_for_VXSNAN_and_convert_to_QNaN(&dfp); + dfp_set_FPRF_from_FRT(&dfp); + + decimal128FromNumber((decimal128 *)&dfp.t64, &dfp.t, &dfp.context); + t[0] = dfp.t64[HI_IDX]; + t[1] = dfp.t64[LO_IDX]; +} + +void helper_drsp(CPUPPCState *env, uint64_t *t, uint64_t *b) +{ + struct PPC_DFP dfp; + uint32_t t_short = 0; + dfp_prepare_decimal64(&dfp, 0, b, env); + decimal32FromNumber((decimal32 *)&t_short, &dfp.b, &dfp.context); + decimal32ToNumber((decimal32 *)&t_short, &dfp.t); + + dfp_set_FPRF_from_FRT_short(&dfp); + dfp_check_for_OX(&dfp); + dfp_check_for_UX(&dfp); + dfp_check_for_XX(&dfp); + + *t = t_short; +} + +void helper_drdpq(CPUPPCState *env, uint64_t *t, uint64_t *b) +{ + struct PPC_DFP dfp; + dfp_prepare_decimal128(&dfp, 0, b, env); + decimal64FromNumber((decimal64 *)&dfp.t64, &dfp.b, &dfp.context); + decimal64ToNumber((decimal64 *)&dfp.t64, &dfp.t); + + dfp_check_for_VXSNAN_and_convert_to_QNaN(&dfp); + dfp_set_FPRF_from_FRT_long(&dfp); + dfp_check_for_OX(&dfp); + dfp_check_for_UX(&dfp); + dfp_check_for_XX(&dfp); + + decimal64FromNumber((decimal64 *)dfp.t64, &dfp.t, &dfp.context); + t[0] = dfp.t64[0]; + t[1] = 0; +} + +#define DFP_HELPER_CFFIX(op, size) \ +void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *b) \ +{ \ + struct PPC_DFP dfp; \ + dfp_prepare_decimal##size(&dfp, 0, b, env); \ + decNumberFromInt64(&dfp.t, (int64_t)(*b)); \ + decimal##size##FromNumber((decimal##size *)dfp.t64, &dfp.t, &dfp.context); \ + CFFIX_PPs(&dfp); \ + \ + if (size == 64) { \ + t[0] = dfp.t64[0]; \ + } else if (size == 128) { \ + t[0] = dfp.t64[HI_IDX]; \ + t[1] = dfp.t64[LO_IDX]; \ + } \ +} + +static void CFFIX_PPs(struct PPC_DFP *dfp) +{ + dfp_set_FPRF_from_FRT(dfp); + dfp_check_for_XX(dfp); +} + +DFP_HELPER_CFFIX(dcffix, 64) +DFP_HELPER_CFFIX(dcffixq, 128) + +#define DFP_HELPER_CTFIX(op, size) \ +void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *b) \ +{ \ + struct PPC_DFP dfp; \ + dfp_prepare_decimal##size(&dfp, 0, b, env); \ + \ + if (unlikely(decNumberIsSpecial(&dfp.b))) { \ + uint64_t invalid_flags = FP_VX | FP_VXCVI; \ + if (decNumberIsInfinite(&dfp.b)) { \ + dfp.t64[0] = decNumberIsNegative(&dfp.b) ? INT64_MIN : INT64_MAX; \ + } else { /* NaN */ \ + dfp.t64[0] = INT64_MIN; \ + if (decNumberIsSNaN(&dfp.b)) { \ + invalid_flags |= FP_VXSNAN; \ + } \ + } \ + dfp_set_FPSCR_flag(&dfp, invalid_flags, FP_VE); \ + } else if (unlikely(decNumberIsZero(&dfp.b))) { \ + dfp.t64[0] = 0; \ + } else { \ + decNumberToIntegralExact(&dfp.b, &dfp.b, &dfp.context); \ + dfp.t64[0] = decNumberIntegralToInt64(&dfp.b, &dfp.context); \ + if (decContextTestStatus(&dfp.context, DEC_Invalid_operation)) { \ + dfp.t64[0] = decNumberIsNegative(&dfp.b) ? INT64_MIN : INT64_MAX; \ + dfp_set_FPSCR_flag(&dfp, FP_VX | FP_VXCVI, FP_VE); \ + } else { \ + dfp_check_for_XX(&dfp); \ + } \ + } \ + \ + *t = dfp.t64[0]; \ +} + +DFP_HELPER_CTFIX(dctfix, 64) +DFP_HELPER_CTFIX(dctfixq, 128) + +static inline void dfp_set_bcd_digit_64(uint64_t *t, uint8_t digit, + unsigned n) +{ + *t |= ((uint64_t)(digit & 0xF) << (n << 2)); +} + +static inline void dfp_set_bcd_digit_128(uint64_t *t, uint8_t digit, + unsigned n) +{ + t[(n & 0x10) ? HI_IDX : LO_IDX] |= + ((uint64_t)(digit & 0xF) << ((n & 15) << 2)); +} + +static inline void dfp_set_sign_64(uint64_t *t, uint8_t sgn) +{ + *t <<= 4; + *t |= (sgn & 0xF); +} + +static inline void dfp_set_sign_128(uint64_t *t, uint8_t sgn) +{ + t[HI_IDX] <<= 4; + t[HI_IDX] |= (t[LO_IDX] >> 60); + t[LO_IDX] <<= 4; + t[LO_IDX] |= (sgn & 0xF); +} + +#define DFP_HELPER_DEDPD(op, size) \ +void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *b, uint32_t sp) \ +{ \ + struct PPC_DFP dfp; \ + uint8_t digits[34]; \ + int i, N; \ + \ + dfp_prepare_decimal##size(&dfp, 0, b, env); \ + \ + decNumberGetBCD(&dfp.b, digits); \ + dfp.t64[0] = dfp.t64[1] = 0; \ + N = dfp.b.digits; \ + \ + for (i = 0; (i < N) && (i < (size)/4); i++) { \ + dfp_set_bcd_digit_##size(dfp.t64, digits[N-i-1], i); \ + } \ + \ + if (sp & 2) { \ + uint8_t sgn; \ + \ + if (decNumberIsNegative(&dfp.b)) { \ + sgn = 0xD; \ + } else { \ + sgn = ((sp & 1) ? 0xF : 0xC); \ + } \ + dfp_set_sign_##size(dfp.t64, sgn); \ + } \ + \ + if (size == 64) { \ + t[0] = dfp.t64[0]; \ + } else if (size == 128) { \ + t[0] = dfp.t64[HI_IDX]; \ + t[1] = dfp.t64[LO_IDX]; \ + } \ +} + +DFP_HELPER_DEDPD(ddedpd, 64) +DFP_HELPER_DEDPD(ddedpdq, 128) + +static inline uint8_t dfp_get_bcd_digit_64(uint64_t *t, unsigned n) +{ + return *t >> ((n << 2) & 63) & 15; +} + +static inline uint8_t dfp_get_bcd_digit_128(uint64_t *t, unsigned n) +{ + return t[(n & 0x10) ? HI_IDX : LO_IDX] >> ((n << 2) & 63) & 15; +} + +#define DFP_HELPER_ENBCD(op, size) \ +void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *b, uint32_t s) \ +{ \ + struct PPC_DFP dfp; \ + uint8_t digits[32]; \ + int n = 0, offset = 0, sgn = 0, nonzero = 0; \ + \ + dfp_prepare_decimal##size(&dfp, 0, b, env); \ + \ + decNumberZero(&dfp.t); \ + \ + if (s) { \ + uint8_t sgnNibble = dfp_get_bcd_digit_##size(dfp.b64, offset++); \ + switch (sgnNibble) { \ + case 0xD: \ + case 0xB: \ + sgn = 1; \ + break; \ + case 0xC: \ + case 0xF: \ + case 0xA: \ + case 0xE: \ + sgn = 0; \ + break; \ + default: \ + dfp_set_FPSCR_flag(&dfp, FP_VX | FP_VXCVI, FPSCR_VE); \ + return; \ + } \ + } \ + \ + while (offset < (size)/4) { \ + n++; \ + digits[(size)/4-n] = dfp_get_bcd_digit_##size(dfp.b64, offset++); \ + if (digits[(size)/4-n] > 10) { \ + dfp_set_FPSCR_flag(&dfp, FP_VX | FP_VXCVI, FPSCR_VE); \ + return; \ + } else { \ + nonzero |= (digits[(size)/4-n] > 0); \ + } \ + } \ + \ + if (nonzero) { \ + decNumberSetBCD(&dfp.t, digits+((size)/4)-n, n); \ + } \ + \ + if (s && sgn) { \ + dfp.t.bits |= DECNEG; \ + } \ + decimal##size##FromNumber((decimal##size *)dfp.t64, &dfp.t, \ + &dfp.context); \ + dfp_set_FPRF_from_FRT(&dfp); \ + if ((size) == 64) { \ + t[0] = dfp.t64[0]; \ + } else if ((size) == 128) { \ + t[0] = dfp.t64[HI_IDX]; \ + t[1] = dfp.t64[LO_IDX]; \ + } \ +} + +DFP_HELPER_ENBCD(denbcd, 64) +DFP_HELPER_ENBCD(denbcdq, 128) + +#define DFP_HELPER_XEX(op, size) \ +void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *b) \ +{ \ + struct PPC_DFP dfp; \ + \ + dfp_prepare_decimal##size(&dfp, 0, b, env); \ + \ + if (unlikely(decNumberIsSpecial(&dfp.b))) { \ + if (decNumberIsInfinite(&dfp.b)) { \ + *t = -1; \ + } else if (decNumberIsSNaN(&dfp.b)) { \ + *t = -3; \ + } else if (decNumberIsQNaN(&dfp.b)) { \ + *t = -2; \ + } else { \ + assert(0); \ + } \ + } else { \ + if ((size) == 64) { \ + *t = dfp.b.exponent + 398; \ + } else if ((size) == 128) { \ + *t = dfp.b.exponent + 6176; \ + } else { \ + assert(0); \ + } \ + } \ +} + +DFP_HELPER_XEX(dxex, 64) +DFP_HELPER_XEX(dxexq, 128) + +static void dfp_set_raw_exp_64(uint64_t *t, uint64_t raw) +{ + *t &= 0x8003ffffffffffffULL; + *t |= (raw << (63-13)); +} + +static void dfp_set_raw_exp_128(uint64_t *t, uint64_t raw) +{ + t[HI_IDX] &= 0x80003fffffffffffULL; + t[HI_IDX] |= (raw << (63-17)); +} + +#define DFP_HELPER_IEX(op, size) \ +void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *a, uint64_t *b) \ +{ \ + struct PPC_DFP dfp; \ + uint64_t raw_qnan, raw_snan, raw_inf, max_exp; \ + int bias; \ + int64_t exp = *((int64_t *)a); \ + \ + dfp_prepare_decimal##size(&dfp, 0, b, env); \ + \ + if ((size) == 64) { \ + max_exp = 767; \ + raw_qnan = 0x1F00; \ + raw_snan = 0x1F80; \ + raw_inf = 0x1E00; \ + bias = 398; \ + } else if ((size) == 128) { \ + max_exp = 12287; \ + raw_qnan = 0x1f000; \ + raw_snan = 0x1f800; \ + raw_inf = 0x1e000; \ + bias = 6176; \ + } else { \ + assert(0); \ + } \ + \ + if (unlikely((exp < 0) || (exp > max_exp))) { \ + dfp.t64[0] = dfp.b64[0]; \ + dfp.t64[1] = dfp.b64[1]; \ + if (exp == -1) { \ + dfp_set_raw_exp_##size(dfp.t64, raw_inf); \ + } else if (exp == -3) { \ + dfp_set_raw_exp_##size(dfp.t64, raw_snan); \ + } else { \ + dfp_set_raw_exp_##size(dfp.t64, raw_qnan); \ + } \ + } else { \ + dfp.t = dfp.b; \ + if (unlikely(decNumberIsSpecial(&dfp.t))) { \ + dfp.t.bits &= ~DECSPECIAL; \ + } \ + dfp.t.exponent = exp - bias; \ + decimal##size##FromNumber((decimal##size *)dfp.t64, &dfp.t, \ + &dfp.context); \ + } \ + if (size == 64) { \ + t[0] = dfp.t64[0]; \ + } else if (size == 128) { \ + t[0] = dfp.t64[HI_IDX]; \ + t[1] = dfp.t64[LO_IDX]; \ + } \ +} + +DFP_HELPER_IEX(diex, 64) +DFP_HELPER_IEX(diexq, 128) + +static void dfp_clear_lmd_from_g5msb(uint64_t *t) +{ + + /* The most significant 5 bits of the PowerPC DFP format combine bits */ + /* from the left-most decimal digit (LMD) and the biased exponent. */ + /* This routine clears the LMD bits while preserving the exponent */ + /* bits. See "Figure 80: Encoding of bits 0:4 of the G field for */ + /* Finite Numbers" in the Power ISA for additional details. */ + + uint64_t g5msb = (*t >> 58) & 0x1F; + + if ((g5msb >> 3) < 3) { /* LMD in [0-7] ? */ + *t &= ~(7ULL << 58); + } else { + switch (g5msb & 7) { + case 0: + case 1: + g5msb = 0; + break; + case 2: + case 3: + g5msb = 0x8; + break; + case 4: + case 5: + g5msb = 0x10; + break; + case 6: + g5msb = 0x1E; + break; + case 7: + g5msb = 0x1F; + break; + } + + *t &= ~(0x1fULL << 58); + *t |= (g5msb << 58); + } +} + +#define DFP_HELPER_SHIFT(op, size, shift_left) \ +void helper_##op(CPUPPCState *env, uint64_t *t, uint64_t *a, \ + uint32_t sh) \ +{ \ + struct PPC_DFP dfp; \ + unsigned max_digits = ((size) == 64) ? 16 : 34; \ + \ + dfp_prepare_decimal##size(&dfp, a, 0, env); \ + \ + if (sh <= max_digits) { \ + \ + decNumber shd; \ + unsigned special = dfp.a.bits & DECSPECIAL; \ + \ + if (shift_left) { \ + decNumberFromUInt32(&shd, sh); \ + } else { \ + decNumberFromInt32(&shd, -((int32_t)sh)); \ + } \ + \ + dfp.a.bits &= ~DECSPECIAL; \ + decNumberShift(&dfp.t, &dfp.a, &shd, &dfp.context); \ + \ + dfp.t.bits |= special; \ + if (special && (dfp.t.digits >= max_digits)) { \ + dfp.t.digits = max_digits - 1; \ + } \ + \ + decimal##size##FromNumber((decimal##size *)dfp.t64, &dfp.t, \ + &dfp.context); \ + } else { \ + if ((size) == 64) { \ + dfp.t64[0] = dfp.a64[0] & 0xFFFC000000000000ULL; \ + dfp_clear_lmd_from_g5msb(dfp.t64); \ + } else { \ + dfp.t64[HI_IDX] = dfp.a64[HI_IDX] & \ + 0xFFFFC00000000000ULL; \ + dfp_clear_lmd_from_g5msb(dfp.t64 + HI_IDX); \ + dfp.t64[LO_IDX] = 0; \ + } \ + } \ + \ + if ((size) == 64) { \ + t[0] = dfp.t64[0]; \ + } else { \ + t[0] = dfp.t64[HI_IDX]; \ + t[1] = dfp.t64[LO_IDX]; \ + } \ +} + +DFP_HELPER_SHIFT(dscli, 64, 1) +DFP_HELPER_SHIFT(dscliq, 128, 1) +DFP_HELPER_SHIFT(dscri, 64, 0) +DFP_HELPER_SHIFT(dscriq, 128, 0) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c new file mode 100644 index 0000000000..93369d4fe5 --- /dev/null +++ b/target/ppc/excp_helper.c @@ -0,0 +1,1142 @@ +/* + * PowerPC exception emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * + * 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 "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" + +#include "helper_regs.h" + +//#define DEBUG_OP +//#define DEBUG_SOFTWARE_TLB +//#define DEBUG_EXCEPTIONS + +#ifdef DEBUG_EXCEPTIONS +# define LOG_EXCP(...) qemu_log(__VA_ARGS__) +#else +# define LOG_EXCP(...) do { } while (0) +#endif + +/*****************************************************************************/ +/* PowerPC Hypercall emulation */ + +void (*cpu_ppc_hypercall)(PowerPCCPU *); + +/*****************************************************************************/ +/* Exception processing */ +#if defined(CONFIG_USER_ONLY) +void ppc_cpu_do_interrupt(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + cs->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; +} + +static void ppc_hw_interrupt(CPUPPCState *env) +{ + CPUState *cs = CPU(ppc_env_get_cpu(env)); + + cs->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; +} +#else /* defined(CONFIG_USER_ONLY) */ +static inline void dump_syscall(CPUPPCState *env) +{ + qemu_log_mask(CPU_LOG_INT, "syscall r0=%016" PRIx64 " r3=%016" PRIx64 + " r4=%016" PRIx64 " r5=%016" PRIx64 " r6=%016" PRIx64 + " nip=" TARGET_FMT_lx "\n", + ppc_dump_gpr(env, 0), ppc_dump_gpr(env, 3), + ppc_dump_gpr(env, 4), ppc_dump_gpr(env, 5), + ppc_dump_gpr(env, 6), env->nip); +} + +/* Note that this function should be greatly optimized + * when called with a constant excp, from ppc_hw_interrupt + */ +static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + target_ulong msr, new_msr, vector; + int srr0, srr1, asrr0, asrr1, lev, ail; + bool lpes0; + + qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx + " => %08x (%02x)\n", env->nip, excp, env->error_code); + + /* new srr1 value excluding must-be-zero bits */ + if (excp_model == POWERPC_EXCP_BOOKE) { + msr = env->msr; + } else { + msr = env->msr & ~0x783f0000ULL; + } + + /* new interrupt handler msr preserves existing HV and ME unless + * explicitly overriden + */ + new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB); + + /* target registers */ + srr0 = SPR_SRR0; + srr1 = SPR_SRR1; + asrr0 = -1; + asrr1 = -1; + + /* check for special resume at 0x100 from doze/nap/sleep/winkle on P7/P8 */ + if (env->in_pm_state) { + env->in_pm_state = false; + + /* Pretend to be returning from doze always as we don't lose state */ + msr |= (0x1ull << (63 - 47)); + + /* Non-machine check are routed to 0x100 with a wakeup cause + * encoded in SRR1 + */ + if (excp != POWERPC_EXCP_MCHECK) { + switch (excp) { + case POWERPC_EXCP_RESET: + msr |= 0x4ull << (63 - 45); + break; + case POWERPC_EXCP_EXTERNAL: + msr |= 0x8ull << (63 - 45); + break; + case POWERPC_EXCP_DECR: + msr |= 0x6ull << (63 - 45); + break; + case POWERPC_EXCP_SDOOR: + msr |= 0x5ull << (63 - 45); + break; + case POWERPC_EXCP_SDOOR_HV: + msr |= 0x3ull << (63 - 45); + break; + case POWERPC_EXCP_HV_MAINT: + msr |= 0xaull << (63 - 45); + break; + default: + cpu_abort(cs, "Unsupported exception %d in Power Save mode\n", + excp); + } + excp = POWERPC_EXCP_RESET; + } + } + + /* Exception targetting modifiers + * + * LPES0 is supported on POWER7/8 + * LPES1 is not supported (old iSeries mode) + * + * On anything else, we behave as if LPES0 is 1 + * (externals don't alter MSR:HV) + * + * AIL is initialized here but can be cleared by + * selected exceptions + */ +#if defined(TARGET_PPC64) + if (excp_model == POWERPC_EXCP_POWER7 || + excp_model == POWERPC_EXCP_POWER8) { + lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); + if (excp_model == POWERPC_EXCP_POWER8) { + ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT; + } else { + ail = 0; + } + } else +#endif /* defined(TARGET_PPC64) */ + { + lpes0 = true; + ail = 0; + } + + /* Hypervisor emulation assistance interrupt only exists on server + * arch 2.05 server or later. We also don't want to generate it if + * we don't have HVB in msr_mask (PAPR mode). + */ + if (excp == POWERPC_EXCP_HV_EMU +#if defined(TARGET_PPC64) + && !((env->mmu_model & POWERPC_MMU_64) && (env->msr_mask & MSR_HVB)) +#endif /* defined(TARGET_PPC64) */ + + ) { + excp = POWERPC_EXCP_PROGRAM; + } + + switch (excp) { + case POWERPC_EXCP_NONE: + /* Should never happen */ + return; + case POWERPC_EXCP_CRITICAL: /* Critical input */ + switch (excp_model) { + case POWERPC_EXCP_40x: + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; + break; + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + case POWERPC_EXCP_G2: + break; + default: + goto excp_invalid; + } + break; + case POWERPC_EXCP_MCHECK: /* Machine check exception */ + if (msr_me == 0) { + /* Machine check exception is not enabled. + * Enter checkstop state. + */ + fprintf(stderr, "Machine check while not allowed. " + "Entering checkstop state\n"); + if (qemu_log_separate()) { + qemu_log("Machine check while not allowed. " + "Entering checkstop state\n"); + } + cs->halted = 1; + cs->interrupt_request |= CPU_INTERRUPT_EXITTB; + } + if (env->msr_mask & MSR_HVB) { + /* ISA specifies HV, but can be delivered to guest with HV clear + * (e.g., see FWNMI in PAPR). + */ + new_msr |= (target_ulong)MSR_HVB; + } + ail = 0; + + /* machine check exceptions don't have ME set */ + new_msr &= ~((target_ulong)1 << MSR_ME); + + /* XXX: should also have something loaded in DAR / DSISR */ + switch (excp_model) { + case POWERPC_EXCP_40x: + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; + break; + case POWERPC_EXCP_BOOKE: + /* FIXME: choose one or the other based on CPU type */ + srr0 = SPR_BOOKE_MCSRR0; + srr1 = SPR_BOOKE_MCSRR1; + asrr0 = SPR_BOOKE_CSRR0; + asrr1 = SPR_BOOKE_CSRR1; + break; + default: + break; + } + break; + case POWERPC_EXCP_DSI: /* Data storage exception */ + LOG_EXCP("DSI exception: DSISR=" TARGET_FMT_lx" DAR=" TARGET_FMT_lx + "\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]); + break; + case POWERPC_EXCP_ISI: /* Instruction storage exception */ + LOG_EXCP("ISI exception: msr=" TARGET_FMT_lx ", nip=" TARGET_FMT_lx + "\n", msr, env->nip); + msr |= env->error_code; + break; + case POWERPC_EXCP_EXTERNAL: /* External input */ + cs = CPU(cpu); + + if (!lpes0) { + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + } + if (env->mpic_proxy) { + /* IACK the IRQ on delivery */ + env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack); + } + break; + case POWERPC_EXCP_ALIGN: /* Alignment exception */ + /* Get rS/rD and rA from faulting opcode */ + /* Note: the opcode fields will not be set properly for a direct + * store load/store, but nobody cares as nobody actually uses + * direct store segments. + */ + env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16; + break; + case POWERPC_EXCP_PROGRAM: /* Program exception */ + switch (env->error_code & ~0xF) { + case POWERPC_EXCP_FP: + if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) { + LOG_EXCP("Ignore floating point exception\n"); + cs->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; + return; + } + + /* FP exceptions always have NIP pointing to the faulting + * instruction, so always use store_next and claim we are + * precise in the MSR. + */ + msr |= 0x00100000; + break; + case POWERPC_EXCP_INVAL: + LOG_EXCP("Invalid instruction at " TARGET_FMT_lx "\n", env->nip); + msr |= 0x00080000; + env->spr[SPR_BOOKE_ESR] = ESR_PIL; + break; + case POWERPC_EXCP_PRIV: + msr |= 0x00040000; + env->spr[SPR_BOOKE_ESR] = ESR_PPR; + break; + case POWERPC_EXCP_TRAP: + msr |= 0x00020000; + env->spr[SPR_BOOKE_ESR] = ESR_PTR; + break; + default: + /* Should never occur */ + cpu_abort(cs, "Invalid program exception %d. Aborting\n", + env->error_code); + break; + } + break; + case POWERPC_EXCP_SYSCALL: /* System call exception */ + dump_syscall(env); + lev = env->error_code; + + /* We need to correct the NIP which in this case is supposed + * to point to the next instruction + */ + env->nip += 4; + + /* "PAPR mode" built-in hypercall emulation */ + if ((lev == 1) && cpu_ppc_hypercall) { + cpu_ppc_hypercall(cpu); + return; + } + if (lev == 1) { + new_msr |= (target_ulong)MSR_HVB; + } + break; + case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ + case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ + case POWERPC_EXCP_DECR: /* Decrementer exception */ + break; + case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ + /* FIT on 4xx */ + LOG_EXCP("FIT exception\n"); + break; + case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ + LOG_EXCP("WDT exception\n"); + switch (excp_model) { + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + default: + break; + } + break; + case POWERPC_EXCP_DTLB: /* Data TLB error */ + case POWERPC_EXCP_ITLB: /* Instruction TLB error */ + break; + case POWERPC_EXCP_DEBUG: /* Debug interrupt */ + switch (excp_model) { + case POWERPC_EXCP_BOOKE: + /* FIXME: choose one or the other based on CPU type */ + srr0 = SPR_BOOKE_DSRR0; + srr1 = SPR_BOOKE_DSRR1; + asrr0 = SPR_BOOKE_CSRR0; + asrr1 = SPR_BOOKE_CSRR1; + break; + default: + break; + } + /* XXX: TODO */ + cpu_abort(cs, "Debug exception is not implemented yet !\n"); + break; + case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */ + env->spr[SPR_BOOKE_ESR] = ESR_SPV; + break; + case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ + /* XXX: TODO */ + cpu_abort(cs, "Embedded floating point data exception " + "is not implemented yet !\n"); + env->spr[SPR_BOOKE_ESR] = ESR_SPV; + break; + case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ + /* XXX: TODO */ + cpu_abort(cs, "Embedded floating point round exception " + "is not implemented yet !\n"); + env->spr[SPR_BOOKE_ESR] = ESR_SPV; + break; + case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ + /* XXX: TODO */ + cpu_abort(cs, + "Performance counter exception is not implemented yet !\n"); + break; + case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ + break; + case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + case POWERPC_EXCP_RESET: /* System reset exception */ + /* A power-saving exception sets ME, otherwise it is unchanged */ + if (msr_pow) { + /* indicate that we resumed from power save mode */ + msr |= 0x10000; + new_msr |= ((target_ulong)1 << MSR_ME); + } + if (env->msr_mask & MSR_HVB) { + /* ISA specifies HV, but can be delivered to guest with HV clear + * (e.g., see FWNMI in PAPR, NMI injection in QEMU). + */ + new_msr |= (target_ulong)MSR_HVB; + } else { + if (msr_pow) { + cpu_abort(cs, "Trying to deliver power-saving system reset " + "exception %d with no HV support\n", excp); + } + } + ail = 0; + break; + case POWERPC_EXCP_DSEG: /* Data segment exception */ + case POWERPC_EXCP_ISEG: /* Instruction segment exception */ + case POWERPC_EXCP_TRACE: /* Trace exception */ + break; + case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ + case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ + case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ + case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ + case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ + case POWERPC_EXCP_HV_EMU: + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + break; + case POWERPC_EXCP_VPU: /* Vector unavailable exception */ + case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ + case POWERPC_EXCP_FU: /* Facility unavailable exception */ +#ifdef TARGET_PPC64 + env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56); +#endif + break; + case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ + LOG_EXCP("PIT exception\n"); + break; + case POWERPC_EXCP_IO: /* IO error exception */ + /* XXX: TODO */ + cpu_abort(cs, "601 IO error exception is not implemented yet !\n"); + break; + case POWERPC_EXCP_RUNM: /* Run mode exception */ + /* XXX: TODO */ + cpu_abort(cs, "601 run mode exception is not implemented yet !\n"); + break; + case POWERPC_EXCP_EMUL: /* Emulation trap exception */ + /* XXX: TODO */ + cpu_abort(cs, "602 emulation trap exception " + "is not implemented yet !\n"); + break; + case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ + switch (excp_model) { + case POWERPC_EXCP_602: + case POWERPC_EXCP_603: + case POWERPC_EXCP_603E: + case POWERPC_EXCP_G2: + goto tlb_miss_tgpr; + case POWERPC_EXCP_7x5: + goto tlb_miss; + case POWERPC_EXCP_74xx: + goto tlb_miss_74xx; + default: + cpu_abort(cs, "Invalid instruction TLB miss exception\n"); + break; + } + break; + case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ + switch (excp_model) { + case POWERPC_EXCP_602: + case POWERPC_EXCP_603: + case POWERPC_EXCP_603E: + case POWERPC_EXCP_G2: + goto tlb_miss_tgpr; + case POWERPC_EXCP_7x5: + goto tlb_miss; + case POWERPC_EXCP_74xx: + goto tlb_miss_74xx; + default: + cpu_abort(cs, "Invalid data load TLB miss exception\n"); + break; + } + break; + case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ + switch (excp_model) { + case POWERPC_EXCP_602: + case POWERPC_EXCP_603: + case POWERPC_EXCP_603E: + case POWERPC_EXCP_G2: + tlb_miss_tgpr: + /* Swap temporary saved registers with GPRs */ + if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { + new_msr |= (target_ulong)1 << MSR_TGPR; + hreg_swap_gpr_tgpr(env); + } + goto tlb_miss; + case POWERPC_EXCP_7x5: + tlb_miss: +#if defined(DEBUG_SOFTWARE_TLB) + if (qemu_log_enabled()) { + const char *es; + target_ulong *miss, *cmp; + int en; + + if (excp == POWERPC_EXCP_IFTLB) { + es = "I"; + en = 'I'; + miss = &env->spr[SPR_IMISS]; + cmp = &env->spr[SPR_ICMP]; + } else { + if (excp == POWERPC_EXCP_DLTLB) { + es = "DL"; + } else { + es = "DS"; + } + en = 'D'; + miss = &env->spr[SPR_DMISS]; + cmp = &env->spr[SPR_DCMP]; + } + qemu_log("6xx %sTLB miss: %cM " TARGET_FMT_lx " %cC " + TARGET_FMT_lx " H1 " TARGET_FMT_lx " H2 " + TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp, + env->spr[SPR_HASH1], env->spr[SPR_HASH2], + env->error_code); + } +#endif + msr |= env->crf[0] << 28; + msr |= env->error_code; /* key, D/I, S/L bits */ + /* Set way using a LRU mechanism */ + msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; + break; + case POWERPC_EXCP_74xx: + tlb_miss_74xx: +#if defined(DEBUG_SOFTWARE_TLB) + if (qemu_log_enabled()) { + const char *es; + target_ulong *miss, *cmp; + int en; + + if (excp == POWERPC_EXCP_IFTLB) { + es = "I"; + en = 'I'; + miss = &env->spr[SPR_TLBMISS]; + cmp = &env->spr[SPR_PTEHI]; + } else { + if (excp == POWERPC_EXCP_DLTLB) { + es = "DL"; + } else { + es = "DS"; + } + en = 'D'; + miss = &env->spr[SPR_TLBMISS]; + cmp = &env->spr[SPR_PTEHI]; + } + qemu_log("74xx %sTLB miss: %cM " TARGET_FMT_lx " %cC " + TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp, + env->error_code); + } +#endif + msr |= env->error_code; /* key bit */ + break; + default: + cpu_abort(cs, "Invalid data store TLB miss exception\n"); + break; + } + break; + case POWERPC_EXCP_FPA: /* Floating-point assist exception */ + /* XXX: TODO */ + cpu_abort(cs, "Floating point assist exception " + "is not implemented yet !\n"); + break; + case POWERPC_EXCP_DABR: /* Data address breakpoint */ + /* XXX: TODO */ + cpu_abort(cs, "DABR exception is not implemented yet !\n"); + break; + case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ + /* XXX: TODO */ + cpu_abort(cs, "IABR exception is not implemented yet !\n"); + break; + case POWERPC_EXCP_SMI: /* System management interrupt */ + /* XXX: TODO */ + cpu_abort(cs, "SMI exception is not implemented yet !\n"); + break; + case POWERPC_EXCP_THERM: /* Thermal interrupt */ + /* XXX: TODO */ + cpu_abort(cs, "Thermal management exception " + "is not implemented yet !\n"); + break; + case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ + /* XXX: TODO */ + cpu_abort(cs, + "Performance counter exception is not implemented yet !\n"); + break; + case POWERPC_EXCP_VPUA: /* Vector assist exception */ + /* XXX: TODO */ + cpu_abort(cs, "VPU assist exception is not implemented yet !\n"); + break; + case POWERPC_EXCP_SOFTP: /* Soft patch exception */ + /* XXX: TODO */ + cpu_abort(cs, + "970 soft-patch exception is not implemented yet !\n"); + break; + case POWERPC_EXCP_MAINT: /* Maintenance exception */ + /* XXX: TODO */ + cpu_abort(cs, + "970 maintenance exception is not implemented yet !\n"); + break; + case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ + /* XXX: TODO */ + cpu_abort(cs, "Maskable external exception " + "is not implemented yet !\n"); + break; + case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ + /* XXX: TODO */ + cpu_abort(cs, "Non maskable external exception " + "is not implemented yet !\n"); + break; + default: + excp_invalid: + cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + break; + } + + /* Save PC */ + env->spr[srr0] = env->nip; + + /* Save MSR */ + env->spr[srr1] = msr; + + /* Sanity check */ + if (!(env->msr_mask & MSR_HVB)) { + if (new_msr & MSR_HVB) { + cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " + "no HV support\n", excp); + } + if (srr0 == SPR_HSRR0) { + cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " + "no HV support\n", excp); + } + } + + /* If any alternate SRR register are defined, duplicate saved values */ + if (asrr0 != -1) { + env->spr[asrr0] = env->spr[srr0]; + } + if (asrr1 != -1) { + env->spr[asrr1] = env->spr[srr1]; + } + + /* Sort out endianness of interrupt, this differs depending on the + * CPU, the HV mode, etc... + */ +#ifdef TARGET_PPC64 + if (excp_model == POWERPC_EXCP_POWER7) { + if (!(new_msr & MSR_HVB) && (env->spr[SPR_LPCR] & LPCR_ILE)) { + new_msr |= (target_ulong)1 << MSR_LE; + } + } else if (excp_model == POWERPC_EXCP_POWER8) { + if (new_msr & MSR_HVB) { + if (env->spr[SPR_HID0] & HID0_HILE) { + new_msr |= (target_ulong)1 << MSR_LE; + } + } else if (env->spr[SPR_LPCR] & LPCR_ILE) { + new_msr |= (target_ulong)1 << MSR_LE; + } + } else if (msr_ile) { + new_msr |= (target_ulong)1 << MSR_LE; + } +#else + if (msr_ile) { + new_msr |= (target_ulong)1 << MSR_LE; + } +#endif + + /* Jump to handler */ + vector = env->excp_vectors[excp]; + if (vector == (target_ulong)-1ULL) { + cpu_abort(cs, "Raised an exception without defined vector %d\n", + excp); + } + vector |= env->excp_prefix; + + /* AIL only works if there is no HV transition and we are running with + * translations enabled + */ + if (!((msr >> MSR_IR) & 1) || !((msr >> MSR_DR) & 1) || + ((new_msr & MSR_HVB) && !(msr & MSR_HVB))) { + ail = 0; + } + /* Handle AIL */ + if (ail) { + new_msr |= (1 << MSR_IR) | (1 << MSR_DR); + switch(ail) { + case AIL_0001_8000: + vector |= 0x18000; + break; + case AIL_C000_0000_0000_4000: + vector |= 0xc000000000004000ull; + break; + default: + cpu_abort(cs, "Invalid AIL combination %d\n", ail); + break; + } + } + +#if defined(TARGET_PPC64) + if (excp_model == POWERPC_EXCP_BOOKE) { + if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { + /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ + new_msr |= (target_ulong)1 << MSR_CM; + } else { + vector = (uint32_t)vector; + } + } else { + if (!msr_isf && !(env->mmu_model & POWERPC_MMU_64)) { + vector = (uint32_t)vector; + } else { + new_msr |= (target_ulong)1 << MSR_SF; + } + } +#endif + /* We don't use hreg_store_msr here as already have treated + * any special case that could occur. Just store MSR and update hflags + * + * Note: We *MUST* not use hreg_store_msr() as-is anyway because it + * will prevent setting of the HV bit which some exceptions might need + * to do. + */ + env->msr = new_msr & env->msr_mask; + hreg_compute_hflags(env); + env->nip = vector; + /* Reset exception state */ + cs->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; + + /* Any interrupt is context synchronizing, check if TCG TLB + * needs a delayed flush on ppc64 + */ + check_tlb_flush(env, false); +} + +void ppc_cpu_do_interrupt(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + powerpc_excp(cpu, env->excp_model, cs->exception_index); +} + +static void ppc_hw_interrupt(CPUPPCState *env) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); +#if 0 + CPUState *cs = CPU(cpu); + + qemu_log_mask(CPU_LOG_INT, "%s: %p pending %08x req %08x me %d ee %d\n", + __func__, env, env->pending_interrupts, + cs->interrupt_request, (int)msr_me, (int)msr_ee); +#endif + /* External reset */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET); + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_RESET); + return; + } + /* Machine check exception */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_MCK)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_MCK); + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_MCHECK); + return; + } +#if 0 /* TODO */ + /* External debug exception */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_DEBUG)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DEBUG); + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DEBUG); + return; + } +#endif + /* Hypervisor decrementer exception */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) { + /* LPCR will be clear when not supported so this will work */ + bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE); + if ((msr_ee != 0 || msr_hv == 0) && hdice) { + /* HDEC clears on delivery */ + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HDECR); + return; + } + } + /* Extermal interrupt can ignore MSR:EE under some circumstances */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { + bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); + if (msr_ee != 0 || (env->has_hv_mode && msr_hv == 0 && !lpes0)) { + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_EXTERNAL); + return; + } + } + if (msr_ce != 0) { + /* External critical interrupt */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) { + /* Taking a critical external interrupt does not clear the external + * critical interrupt status + */ +#if 0 + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CEXT); +#endif + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_CRITICAL); + return; + } + } + if (msr_ee != 0) { + /* Watchdog timer on embedded PowerPC */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT); + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_WDT); + return; + } + if (env->pending_interrupts & (1 << PPC_INTERRUPT_CDOORBELL)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CDOORBELL); + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORCI); + return; + } + /* Fixed interval timer on embedded PowerPC */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_FIT)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_FIT); + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_FIT); + return; + } + /* Programmable interval timer on embedded PowerPC */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_PIT)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PIT); + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_PIT); + return; + } + /* Decrementer exception */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_DECR)) { + if (ppc_decr_clear_on_delivery(env)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DECR); + } + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DECR); + return; + } + if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORI); + return; + } + if (env->pending_interrupts & (1 << PPC_INTERRUPT_PERFM)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PERFM); + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_PERFM); + return; + } + /* Thermal interrupt */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_THERM)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_THERM); + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_THERM); + return; + } + } +} + +void ppc_cpu_do_system_reset(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_RESET); +} +#endif /* !CONFIG_USER_ONLY */ + +bool ppc_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + if (interrupt_request & CPU_INTERRUPT_HARD) { + ppc_hw_interrupt(env); + if (env->pending_interrupts == 0) { + cs->interrupt_request &= ~CPU_INTERRUPT_HARD; + } + return true; + } + return false; +} + +#if defined(DEBUG_OP) +static void cpu_dump_rfi(target_ulong RA, target_ulong msr) +{ + qemu_log("Return from exception at " TARGET_FMT_lx " with flags " + TARGET_FMT_lx "\n", RA, msr); +} +#endif + +/*****************************************************************************/ +/* Exceptions processing helpers */ + +void raise_exception_err_ra(CPUPPCState *env, uint32_t exception, + uint32_t error_code, uintptr_t raddr) +{ + CPUState *cs = CPU(ppc_env_get_cpu(env)); + + cs->exception_index = exception; + env->error_code = error_code; + cpu_loop_exit_restore(cs, raddr); +} + +void raise_exception_err(CPUPPCState *env, uint32_t exception, + uint32_t error_code) +{ + raise_exception_err_ra(env, exception, error_code, 0); +} + +void raise_exception(CPUPPCState *env, uint32_t exception) +{ + raise_exception_err_ra(env, exception, 0, 0); +} + +void raise_exception_ra(CPUPPCState *env, uint32_t exception, + uintptr_t raddr) +{ + raise_exception_err_ra(env, exception, 0, raddr); +} + +void helper_raise_exception_err(CPUPPCState *env, uint32_t exception, + uint32_t error_code) +{ + raise_exception_err_ra(env, exception, error_code, 0); +} + +void helper_raise_exception(CPUPPCState *env, uint32_t exception) +{ + raise_exception_err_ra(env, exception, 0, 0); +} + +#if !defined(CONFIG_USER_ONLY) +void helper_store_msr(CPUPPCState *env, target_ulong val) +{ + uint32_t excp = hreg_store_msr(env, val, 0); + + if (excp != 0) { + CPUState *cs = CPU(ppc_env_get_cpu(env)); + cs->interrupt_request |= CPU_INTERRUPT_EXITTB; + raise_exception(env, excp); + } +} + +#if defined(TARGET_PPC64) +void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn) +{ + CPUState *cs; + + cs = CPU(ppc_env_get_cpu(env)); + cs->halted = 1; + env->in_pm_state = true; + + /* The architecture specifies that HDEC interrupts are + * discarded in PM states + */ + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); + + /* Technically, nap doesn't set EE, but if we don't set it + * then ppc_hw_interrupt() won't deliver. We could add some + * other tests there based on LPCR but it's simpler to just + * whack EE in. It will be cleared by the 0x100 at wakeup + * anyway. It will still be observable by the guest in SRR1 + * but this doesn't seem to be a problem. + */ + env->msr |= (1ull << MSR_EE); + raise_exception(env, EXCP_HLT); +} +#endif /* defined(TARGET_PPC64) */ + +static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr) +{ + CPUState *cs = CPU(ppc_env_get_cpu(env)); + + /* MSR:POW cannot be set by any form of rfi */ + msr &= ~(1ULL << MSR_POW); + +#if defined(TARGET_PPC64) + /* Switching to 32-bit ? Crop the nip */ + if (!msr_is_64bit(env, msr)) { + nip = (uint32_t)nip; + } +#else + nip = (uint32_t)nip; +#endif + /* XXX: beware: this is false if VLE is supported */ + env->nip = nip & ~((target_ulong)0x00000003); + hreg_store_msr(env, msr, 1); +#if defined(DEBUG_OP) + cpu_dump_rfi(env->nip, env->msr); +#endif + /* No need to raise an exception here, + * as rfi is always the last insn of a TB + */ + cs->interrupt_request |= CPU_INTERRUPT_EXITTB; + + /* Context synchronizing: check if TCG TLB needs flush */ + check_tlb_flush(env, false); +} + +void helper_rfi(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1] & 0xfffffffful); +} + +#define MSR_BOOK3S_MASK +#if defined(TARGET_PPC64) +void helper_rfid(CPUPPCState *env) +{ + /* The architeture defines a number of rules for which bits + * can change but in practice, we handle this in hreg_store_msr() + * which will be called by do_rfi(), so there is no need to filter + * here + */ + do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1]); +} + +void helper_hrfid(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]); +} +#endif + +/*****************************************************************************/ +/* Embedded PowerPC specific helpers */ +void helper_40x_rfci(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3]); +} + +void helper_rfci(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1]); +} + +void helper_rfdi(CPUPPCState *env) +{ + /* FIXME: choose CSRR1 or DSRR1 based on cpu type */ + do_rfi(env, env->spr[SPR_BOOKE_DSRR0], env->spr[SPR_BOOKE_DSRR1]); +} + +void helper_rfmci(CPUPPCState *env) +{ + /* FIXME: choose CSRR1 or MCSRR1 based on cpu type */ + do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1]); +} +#endif + +void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2, + uint32_t flags) +{ + if (!likely(!(((int32_t)arg1 < (int32_t)arg2 && (flags & 0x10)) || + ((int32_t)arg1 > (int32_t)arg2 && (flags & 0x08)) || + ((int32_t)arg1 == (int32_t)arg2 && (flags & 0x04)) || + ((uint32_t)arg1 < (uint32_t)arg2 && (flags & 0x02)) || + ((uint32_t)arg1 > (uint32_t)arg2 && (flags & 0x01))))) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_TRAP, GETPC()); + } +} + +#if defined(TARGET_PPC64) +void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2, + uint32_t flags) +{ + if (!likely(!(((int64_t)arg1 < (int64_t)arg2 && (flags & 0x10)) || + ((int64_t)arg1 > (int64_t)arg2 && (flags & 0x08)) || + ((int64_t)arg1 == (int64_t)arg2 && (flags & 0x04)) || + ((uint64_t)arg1 < (uint64_t)arg2 && (flags & 0x02)) || + ((uint64_t)arg1 > (uint64_t)arg2 && (flags & 0x01))))) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_TRAP, GETPC()); + } +} +#endif + +#if !defined(CONFIG_USER_ONLY) +/*****************************************************************************/ +/* PowerPC 601 specific instructions (POWER bridge) */ + +void helper_rfsvc(CPUPPCState *env) +{ + do_rfi(env, env->lr, env->ctr & 0x0000FFFF); +} + +/* Embedded.Processor Control */ +static int dbell2irq(target_ulong rb) +{ + int msg = rb & DBELL_TYPE_MASK; + int irq = -1; + + switch (msg) { + case DBELL_TYPE_DBELL: + irq = PPC_INTERRUPT_DOORBELL; + break; + case DBELL_TYPE_DBELL_CRIT: + irq = PPC_INTERRUPT_CDOORBELL; + break; + case DBELL_TYPE_G_DBELL: + case DBELL_TYPE_G_DBELL_CRIT: + case DBELL_TYPE_G_DBELL_MC: + /* XXX implement */ + default: + break; + } + + return irq; +} + +void helper_msgclr(CPUPPCState *env, target_ulong rb) +{ + int irq = dbell2irq(rb); + + if (irq < 0) { + return; + } + + env->pending_interrupts &= ~(1 << irq); +} + +void helper_msgsnd(target_ulong rb) +{ + int irq = dbell2irq(rb); + int pir = rb & DBELL_PIRTAG_MASK; + CPUState *cs; + + if (irq < 0) { + return; + } + + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *cenv = &cpu->env; + + if ((rb & DBELL_BRDCAST) || (cenv->spr[SPR_BOOKE_PIR] == pir)) { + cenv->pending_interrupts |= 1 << irq; + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } + } +} +#endif diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c new file mode 100644 index 0000000000..8a389e19af --- /dev/null +++ b/target/ppc/fpu_helper.c @@ -0,0 +1,2789 @@ +/* + * PowerPC floating point and SPE emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * + * 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 "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "exec/exec-all.h" + +#define float64_snan_to_qnan(x) ((x) | 0x0008000000000000ULL) +#define float32_snan_to_qnan(x) ((x) | 0x00400000) + +/*****************************************************************************/ +/* Floating point operations helpers */ +uint64_t helper_float32_to_float64(CPUPPCState *env, uint32_t arg) +{ + CPU_FloatU f; + CPU_DoubleU d; + + f.l = arg; + d.d = float32_to_float64(f.f, &env->fp_status); + return d.ll; +} + +uint32_t helper_float64_to_float32(CPUPPCState *env, uint64_t arg) +{ + CPU_FloatU f; + CPU_DoubleU d; + + d.ll = arg; + f.f = float64_to_float32(d.d, &env->fp_status); + return f.l; +} + +static inline int isden(float64 d) +{ + CPU_DoubleU u; + + u.d = d; + + return ((u.ll >> 52) & 0x7FF) == 0; +} + +static inline int ppc_float32_get_unbiased_exp(float32 f) +{ + return ((f >> 23) & 0xFF) - 127; +} + +static inline int ppc_float64_get_unbiased_exp(float64 f) +{ + return ((f >> 52) & 0x7FF) - 1023; +} + +void helper_compute_fprf(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + int isneg; + int fprf; + + farg.ll = arg; + isneg = float64_is_neg(farg.d); + if (unlikely(float64_is_any_nan(farg.d))) { + if (float64_is_signaling_nan(farg.d, &env->fp_status)) { + /* Signaling NaN: flags are undefined */ + fprf = 0x00; + } else { + /* Quiet NaN */ + fprf = 0x11; + } + } else if (unlikely(float64_is_infinity(farg.d))) { + /* +/- infinity */ + if (isneg) { + fprf = 0x09; + } else { + fprf = 0x05; + } + } else { + if (float64_is_zero(farg.d)) { + /* +/- zero */ + if (isneg) { + fprf = 0x12; + } else { + fprf = 0x02; + } + } else { + if (isden(farg.d)) { + /* Denormalized numbers */ + fprf = 0x10; + } else { + /* Normalized numbers */ + fprf = 0x00; + } + if (isneg) { + fprf |= 0x08; + } else { + fprf |= 0x04; + } + } + } + /* We update FPSCR_FPRF */ + env->fpscr &= ~(0x1F << FPSCR_FPRF); + env->fpscr |= fprf << FPSCR_FPRF; +} + +/* Floating-point invalid operations exception */ +static inline __attribute__((__always_inline__)) +uint64_t float_invalid_op_excp(CPUPPCState *env, int op, int set_fpcc) +{ + CPUState *cs = CPU(ppc_env_get_cpu(env)); + uint64_t ret = 0; + int ve; + + ve = fpscr_ve; + switch (op) { + case POWERPC_EXCP_FP_VXSNAN: + env->fpscr |= 1 << FPSCR_VXSNAN; + break; + case POWERPC_EXCP_FP_VXSOFT: + env->fpscr |= 1 << FPSCR_VXSOFT; + break; + case POWERPC_EXCP_FP_VXISI: + /* Magnitude subtraction of infinities */ + env->fpscr |= 1 << FPSCR_VXISI; + goto update_arith; + case POWERPC_EXCP_FP_VXIDI: + /* Division of infinity by infinity */ + env->fpscr |= 1 << FPSCR_VXIDI; + goto update_arith; + case POWERPC_EXCP_FP_VXZDZ: + /* Division of zero by zero */ + env->fpscr |= 1 << FPSCR_VXZDZ; + goto update_arith; + case POWERPC_EXCP_FP_VXIMZ: + /* Multiplication of zero by infinity */ + env->fpscr |= 1 << FPSCR_VXIMZ; + goto update_arith; + case POWERPC_EXCP_FP_VXVC: + /* Ordered comparison of NaN */ + env->fpscr |= 1 << FPSCR_VXVC; + if (set_fpcc) { + env->fpscr &= ~(0xF << FPSCR_FPCC); + env->fpscr |= 0x11 << FPSCR_FPCC; + } + /* We must update the target FPR before raising the exception */ + if (ve != 0) { + cs->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_VXVC; + /* Update the floating-point enabled exception summary */ + env->fpscr |= 1 << FPSCR_FEX; + /* Exception is differed */ + ve = 0; + } + break; + case POWERPC_EXCP_FP_VXSQRT: + /* Square root of a negative number */ + env->fpscr |= 1 << FPSCR_VXSQRT; + update_arith: + env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); + if (ve == 0) { + /* Set the result to quiet NaN */ + ret = 0x7FF8000000000000ULL; + if (set_fpcc) { + env->fpscr &= ~(0xF << FPSCR_FPCC); + env->fpscr |= 0x11 << FPSCR_FPCC; + } + } + break; + case POWERPC_EXCP_FP_VXCVI: + /* Invalid conversion */ + env->fpscr |= 1 << FPSCR_VXCVI; + env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); + if (ve == 0) { + /* Set the result to quiet NaN */ + ret = 0x7FF8000000000000ULL; + if (set_fpcc) { + env->fpscr &= ~(0xF << FPSCR_FPCC); + env->fpscr |= 0x11 << FPSCR_FPCC; + } + } + break; + } + /* Update the floating-point invalid operation summary */ + env->fpscr |= 1 << FPSCR_VX; + /* Update the floating-point exception summary */ + env->fpscr |= FP_FX; + if (ve != 0) { + /* Update the floating-point enabled exception summary */ + env->fpscr |= 1 << FPSCR_FEX; + if (msr_fe0 != 0 || msr_fe1 != 0) { + /* GETPC() works here because this is inline */ + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_FP | op, GETPC()); + } + } + return ret; +} + +static inline void float_zero_divide_excp(CPUPPCState *env, uintptr_t raddr) +{ + env->fpscr |= 1 << FPSCR_ZX; + env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); + /* Update the floating-point exception summary */ + env->fpscr |= FP_FX; + if (fpscr_ze != 0) { + /* Update the floating-point enabled exception summary */ + env->fpscr |= 1 << FPSCR_FEX; + if (msr_fe0 != 0 || msr_fe1 != 0) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_FP | POWERPC_EXCP_FP_ZX, + raddr); + } + } +} + +static inline void float_overflow_excp(CPUPPCState *env) +{ + CPUState *cs = CPU(ppc_env_get_cpu(env)); + + env->fpscr |= 1 << FPSCR_OX; + /* Update the floating-point exception summary */ + env->fpscr |= FP_FX; + if (fpscr_oe != 0) { + /* XXX: should adjust the result */ + /* Update the floating-point enabled exception summary */ + env->fpscr |= 1 << FPSCR_FEX; + /* We must update the target FPR before raising the exception */ + cs->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_OX; + } else { + env->fpscr |= 1 << FPSCR_XX; + env->fpscr |= 1 << FPSCR_FI; + } +} + +static inline void float_underflow_excp(CPUPPCState *env) +{ + CPUState *cs = CPU(ppc_env_get_cpu(env)); + + env->fpscr |= 1 << FPSCR_UX; + /* Update the floating-point exception summary */ + env->fpscr |= FP_FX; + if (fpscr_ue != 0) { + /* XXX: should adjust the result */ + /* Update the floating-point enabled exception summary */ + env->fpscr |= 1 << FPSCR_FEX; + /* We must update the target FPR before raising the exception */ + cs->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_UX; + } +} + +static inline void float_inexact_excp(CPUPPCState *env) +{ + CPUState *cs = CPU(ppc_env_get_cpu(env)); + + env->fpscr |= 1 << FPSCR_XX; + /* Update the floating-point exception summary */ + env->fpscr |= FP_FX; + if (fpscr_xe != 0) { + /* Update the floating-point enabled exception summary */ + env->fpscr |= 1 << FPSCR_FEX; + /* We must update the target FPR before raising the exception */ + cs->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_XX; + } +} + +static inline void fpscr_set_rounding_mode(CPUPPCState *env) +{ + int rnd_type; + + /* Set rounding mode */ + switch (fpscr_rn) { + case 0: + /* Best approximation (round to nearest) */ + rnd_type = float_round_nearest_even; + break; + case 1: + /* Smaller magnitude (round toward zero) */ + rnd_type = float_round_to_zero; + break; + case 2: + /* Round toward +infinite */ + rnd_type = float_round_up; + break; + default: + case 3: + /* Round toward -infinite */ + rnd_type = float_round_down; + break; + } + set_float_rounding_mode(rnd_type, &env->fp_status); +} + +void helper_fpscr_clrbit(CPUPPCState *env, uint32_t bit) +{ + int prev; + + prev = (env->fpscr >> bit) & 1; + env->fpscr &= ~(1 << bit); + if (prev == 1) { + switch (bit) { + case FPSCR_RN1: + case FPSCR_RN: + fpscr_set_rounding_mode(env); + break; + default: + break; + } + } +} + +void helper_fpscr_setbit(CPUPPCState *env, uint32_t bit) +{ + CPUState *cs = CPU(ppc_env_get_cpu(env)); + int prev; + + prev = (env->fpscr >> bit) & 1; + env->fpscr |= 1 << bit; + if (prev == 0) { + switch (bit) { + case FPSCR_VX: + env->fpscr |= FP_FX; + if (fpscr_ve) { + goto raise_ve; + } + break; + case FPSCR_OX: + env->fpscr |= FP_FX; + if (fpscr_oe) { + goto raise_oe; + } + break; + case FPSCR_UX: + env->fpscr |= FP_FX; + if (fpscr_ue) { + goto raise_ue; + } + break; + case FPSCR_ZX: + env->fpscr |= FP_FX; + if (fpscr_ze) { + goto raise_ze; + } + break; + case FPSCR_XX: + env->fpscr |= FP_FX; + if (fpscr_xe) { + goto raise_xe; + } + break; + case FPSCR_VXSNAN: + case FPSCR_VXISI: + case FPSCR_VXIDI: + case FPSCR_VXZDZ: + case FPSCR_VXIMZ: + case FPSCR_VXVC: + case FPSCR_VXSOFT: + case FPSCR_VXSQRT: + case FPSCR_VXCVI: + env->fpscr |= 1 << FPSCR_VX; + env->fpscr |= FP_FX; + if (fpscr_ve != 0) { + goto raise_ve; + } + break; + case FPSCR_VE: + if (fpscr_vx != 0) { + raise_ve: + env->error_code = POWERPC_EXCP_FP; + if (fpscr_vxsnan) { + env->error_code |= POWERPC_EXCP_FP_VXSNAN; + } + if (fpscr_vxisi) { + env->error_code |= POWERPC_EXCP_FP_VXISI; + } + if (fpscr_vxidi) { + env->error_code |= POWERPC_EXCP_FP_VXIDI; + } + if (fpscr_vxzdz) { + env->error_code |= POWERPC_EXCP_FP_VXZDZ; + } + if (fpscr_vximz) { + env->error_code |= POWERPC_EXCP_FP_VXIMZ; + } + if (fpscr_vxvc) { + env->error_code |= POWERPC_EXCP_FP_VXVC; + } + if (fpscr_vxsoft) { + env->error_code |= POWERPC_EXCP_FP_VXSOFT; + } + if (fpscr_vxsqrt) { + env->error_code |= POWERPC_EXCP_FP_VXSQRT; + } + if (fpscr_vxcvi) { + env->error_code |= POWERPC_EXCP_FP_VXCVI; + } + goto raise_excp; + } + break; + case FPSCR_OE: + if (fpscr_ox != 0) { + raise_oe: + env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_OX; + goto raise_excp; + } + break; + case FPSCR_UE: + if (fpscr_ux != 0) { + raise_ue: + env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_UX; + goto raise_excp; + } + break; + case FPSCR_ZE: + if (fpscr_zx != 0) { + raise_ze: + env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_ZX; + goto raise_excp; + } + break; + case FPSCR_XE: + if (fpscr_xx != 0) { + raise_xe: + env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_XX; + goto raise_excp; + } + break; + case FPSCR_RN1: + case FPSCR_RN: + fpscr_set_rounding_mode(env); + break; + default: + break; + raise_excp: + /* Update the floating-point enabled exception summary */ + env->fpscr |= 1 << FPSCR_FEX; + /* We have to update Rc1 before raising the exception */ + cs->exception_index = POWERPC_EXCP_PROGRAM; + break; + } + } +} + +void helper_store_fpscr(CPUPPCState *env, uint64_t arg, uint32_t mask) +{ + CPUState *cs = CPU(ppc_env_get_cpu(env)); + target_ulong prev, new; + int i; + + prev = env->fpscr; + new = (target_ulong)arg; + new &= ~0x60000000LL; + new |= prev & 0x60000000LL; + for (i = 0; i < sizeof(target_ulong) * 2; i++) { + if (mask & (1 << i)) { + env->fpscr &= ~(0xFLL << (4 * i)); + env->fpscr |= new & (0xFLL << (4 * i)); + } + } + /* Update VX and FEX */ + if (fpscr_ix != 0) { + env->fpscr |= 1 << FPSCR_VX; + } else { + env->fpscr &= ~(1 << FPSCR_VX); + } + if ((fpscr_ex & fpscr_eex) != 0) { + env->fpscr |= 1 << FPSCR_FEX; + cs->exception_index = POWERPC_EXCP_PROGRAM; + /* XXX: we should compute it properly */ + env->error_code = POWERPC_EXCP_FP; + } else { + env->fpscr &= ~(1 << FPSCR_FEX); + } + fpscr_set_rounding_mode(env); +} + +void store_fpscr(CPUPPCState *env, uint64_t arg, uint32_t mask) +{ + helper_store_fpscr(env, arg, mask); +} + +static void do_float_check_status(CPUPPCState *env, uintptr_t raddr) +{ + CPUState *cs = CPU(ppc_env_get_cpu(env)); + int status = get_float_exception_flags(&env->fp_status); + + if (status & float_flag_divbyzero) { + float_zero_divide_excp(env, raddr); + } else if (status & float_flag_overflow) { + float_overflow_excp(env); + } else if (status & float_flag_underflow) { + float_underflow_excp(env); + } else if (status & float_flag_inexact) { + float_inexact_excp(env); + } + + if (cs->exception_index == POWERPC_EXCP_PROGRAM && + (env->error_code & POWERPC_EXCP_FP)) { + /* Differred floating-point exception after target FPR update */ + if (msr_fe0 != 0 || msr_fe1 != 0) { + raise_exception_err_ra(env, cs->exception_index, + env->error_code, raddr); + } + } +} + +static inline __attribute__((__always_inline__)) +void float_check_status(CPUPPCState *env) +{ + /* GETPC() works here because this is inline */ + do_float_check_status(env, GETPC()); +} + +void helper_float_check_status(CPUPPCState *env) +{ + do_float_check_status(env, GETPC()); +} + +void helper_reset_fpstatus(CPUPPCState *env) +{ + set_float_exception_flags(0, &env->fp_status); +} + +/* fadd - fadd. */ +uint64_t helper_fadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2) +{ + CPU_DoubleU farg1, farg2; + + farg1.ll = arg1; + farg2.ll = arg2; + + if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && + float64_is_neg(farg1.d) != float64_is_neg(farg2.d))) { + /* Magnitude subtraction of infinities */ + farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); + } else { + if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) || + float64_is_signaling_nan(farg2.d, &env->fp_status))) { + /* sNaN addition */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } + farg1.d = float64_add(farg1.d, farg2.d, &env->fp_status); + } + + return farg1.ll; +} + +/* fsub - fsub. */ +uint64_t helper_fsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2) +{ + CPU_DoubleU farg1, farg2; + + farg1.ll = arg1; + farg2.ll = arg2; + + if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && + float64_is_neg(farg1.d) == float64_is_neg(farg2.d))) { + /* Magnitude subtraction of infinities */ + farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); + } else { + if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) || + float64_is_signaling_nan(farg2.d, &env->fp_status))) { + /* sNaN subtraction */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } + farg1.d = float64_sub(farg1.d, farg2.d, &env->fp_status); + } + + return farg1.ll; +} + +/* fmul - fmul. */ +uint64_t helper_fmul(CPUPPCState *env, uint64_t arg1, uint64_t arg2) +{ + CPU_DoubleU farg1, farg2; + + farg1.ll = arg1; + farg2.ll = arg2; + + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { + /* Multiplication of zero by infinity */ + farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); + } else { + if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) || + float64_is_signaling_nan(farg2.d, &env->fp_status))) { + /* sNaN multiplication */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } + farg1.d = float64_mul(farg1.d, farg2.d, &env->fp_status); + } + + return farg1.ll; +} + +/* fdiv - fdiv. */ +uint64_t helper_fdiv(CPUPPCState *env, uint64_t arg1, uint64_t arg2) +{ + CPU_DoubleU farg1, farg2; + + farg1.ll = arg1; + farg2.ll = arg2; + + if (unlikely(float64_is_infinity(farg1.d) && + float64_is_infinity(farg2.d))) { + /* Division of infinity by infinity */ + farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI, 1); + } else if (unlikely(float64_is_zero(farg1.d) && float64_is_zero(farg2.d))) { + /* Division of zero by zero */ + farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ, 1); + } else { + if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) || + float64_is_signaling_nan(farg2.d, &env->fp_status))) { + /* sNaN division */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } + farg1.d = float64_div(farg1.d, farg2.d, &env->fp_status); + } + + return farg1.ll; +} + + +#define FPU_FCTI(op, cvt, nanval) \ +uint64_t helper_##op(CPUPPCState *env, uint64_t arg) \ +{ \ + CPU_DoubleU farg; \ + \ + farg.ll = arg; \ + farg.ll = float64_to_##cvt(farg.d, &env->fp_status); \ + \ + if (unlikely(env->fp_status.float_exception_flags)) { \ + if (float64_is_any_nan(arg)) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 1); \ + if (float64_is_signaling_nan(arg, &env->fp_status)) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); \ + } \ + farg.ll = nanval; \ + } else if (env->fp_status.float_exception_flags & \ + float_flag_invalid) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 1); \ + } \ + float_check_status(env); \ + } \ + return farg.ll; \ + } + +FPU_FCTI(fctiw, int32, 0x80000000U) +FPU_FCTI(fctiwz, int32_round_to_zero, 0x80000000U) +FPU_FCTI(fctiwu, uint32, 0x00000000U) +FPU_FCTI(fctiwuz, uint32_round_to_zero, 0x00000000U) +FPU_FCTI(fctid, int64, 0x8000000000000000ULL) +FPU_FCTI(fctidz, int64_round_to_zero, 0x8000000000000000ULL) +FPU_FCTI(fctidu, uint64, 0x0000000000000000ULL) +FPU_FCTI(fctiduz, uint64_round_to_zero, 0x0000000000000000ULL) + +#define FPU_FCFI(op, cvtr, is_single) \ +uint64_t helper_##op(CPUPPCState *env, uint64_t arg) \ +{ \ + CPU_DoubleU farg; \ + \ + if (is_single) { \ + float32 tmp = cvtr(arg, &env->fp_status); \ + farg.d = float32_to_float64(tmp, &env->fp_status); \ + } else { \ + farg.d = cvtr(arg, &env->fp_status); \ + } \ + float_check_status(env); \ + return farg.ll; \ +} + +FPU_FCFI(fcfid, int64_to_float64, 0) +FPU_FCFI(fcfids, int64_to_float32, 1) +FPU_FCFI(fcfidu, uint64_to_float64, 0) +FPU_FCFI(fcfidus, uint64_to_float32, 1) + +static inline uint64_t do_fri(CPUPPCState *env, uint64_t arg, + int rounding_mode) +{ + CPU_DoubleU farg; + + farg.ll = arg; + + if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) { + /* sNaN round */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + farg.ll = arg | 0x0008000000000000ULL; + } else { + int inexact = get_float_exception_flags(&env->fp_status) & + float_flag_inexact; + set_float_rounding_mode(rounding_mode, &env->fp_status); + farg.ll = float64_round_to_int(farg.d, &env->fp_status); + /* Restore rounding mode from FPSCR */ + fpscr_set_rounding_mode(env); + + /* fri* does not set FPSCR[XX] */ + if (!inexact) { + env->fp_status.float_exception_flags &= ~float_flag_inexact; + } + } + float_check_status(env); + return farg.ll; +} + +uint64_t helper_frin(CPUPPCState *env, uint64_t arg) +{ + return do_fri(env, arg, float_round_ties_away); +} + +uint64_t helper_friz(CPUPPCState *env, uint64_t arg) +{ + return do_fri(env, arg, float_round_to_zero); +} + +uint64_t helper_frip(CPUPPCState *env, uint64_t arg) +{ + return do_fri(env, arg, float_round_up); +} + +uint64_t helper_frim(CPUPPCState *env, uint64_t arg) +{ + return do_fri(env, arg, float_round_down); +} + +/* fmadd - fmadd. */ +uint64_t helper_fmadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2, + uint64_t arg3) +{ + CPU_DoubleU farg1, farg2, farg3; + + farg1.ll = arg1; + farg2.ll = arg2; + farg3.ll = arg3; + + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { + /* Multiplication of zero by infinity */ + farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); + } else { + if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) || + float64_is_signaling_nan(farg2.d, &env->fp_status) || + float64_is_signaling_nan(farg3.d, &env->fp_status))) { + /* sNaN operation */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } + /* This is the way the PowerPC specification defines it */ + float128 ft0_128, ft1_128; + + ft0_128 = float64_to_float128(farg1.d, &env->fp_status); + ft1_128 = float64_to_float128(farg2.d, &env->fp_status); + ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); + if (unlikely(float128_is_infinity(ft0_128) && + float64_is_infinity(farg3.d) && + float128_is_neg(ft0_128) != float64_is_neg(farg3.d))) { + /* Magnitude subtraction of infinities */ + farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); + } else { + ft1_128 = float64_to_float128(farg3.d, &env->fp_status); + ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); + farg1.d = float128_to_float64(ft0_128, &env->fp_status); + } + } + + return farg1.ll; +} + +/* fmsub - fmsub. */ +uint64_t helper_fmsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2, + uint64_t arg3) +{ + CPU_DoubleU farg1, farg2, farg3; + + farg1.ll = arg1; + farg2.ll = arg2; + farg3.ll = arg3; + + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + (float64_is_zero(farg1.d) && + float64_is_infinity(farg2.d)))) { + /* Multiplication of zero by infinity */ + farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); + } else { + if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) || + float64_is_signaling_nan(farg2.d, &env->fp_status) || + float64_is_signaling_nan(farg3.d, &env->fp_status))) { + /* sNaN operation */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } + /* This is the way the PowerPC specification defines it */ + float128 ft0_128, ft1_128; + + ft0_128 = float64_to_float128(farg1.d, &env->fp_status); + ft1_128 = float64_to_float128(farg2.d, &env->fp_status); + ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); + if (unlikely(float128_is_infinity(ft0_128) && + float64_is_infinity(farg3.d) && + float128_is_neg(ft0_128) == float64_is_neg(farg3.d))) { + /* Magnitude subtraction of infinities */ + farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); + } else { + ft1_128 = float64_to_float128(farg3.d, &env->fp_status); + ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); + farg1.d = float128_to_float64(ft0_128, &env->fp_status); + } + } + return farg1.ll; +} + +/* fnmadd - fnmadd. */ +uint64_t helper_fnmadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2, + uint64_t arg3) +{ + CPU_DoubleU farg1, farg2, farg3; + + farg1.ll = arg1; + farg2.ll = arg2; + farg3.ll = arg3; + + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { + /* Multiplication of zero by infinity */ + farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); + } else { + if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) || + float64_is_signaling_nan(farg2.d, &env->fp_status) || + float64_is_signaling_nan(farg3.d, &env->fp_status))) { + /* sNaN operation */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } + /* This is the way the PowerPC specification defines it */ + float128 ft0_128, ft1_128; + + ft0_128 = float64_to_float128(farg1.d, &env->fp_status); + ft1_128 = float64_to_float128(farg2.d, &env->fp_status); + ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); + if (unlikely(float128_is_infinity(ft0_128) && + float64_is_infinity(farg3.d) && + float128_is_neg(ft0_128) != float64_is_neg(farg3.d))) { + /* Magnitude subtraction of infinities */ + farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); + } else { + ft1_128 = float64_to_float128(farg3.d, &env->fp_status); + ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); + farg1.d = float128_to_float64(ft0_128, &env->fp_status); + } + if (likely(!float64_is_any_nan(farg1.d))) { + farg1.d = float64_chs(farg1.d); + } + } + return farg1.ll; +} + +/* fnmsub - fnmsub. */ +uint64_t helper_fnmsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2, + uint64_t arg3) +{ + CPU_DoubleU farg1, farg2, farg3; + + farg1.ll = arg1; + farg2.ll = arg2; + farg3.ll = arg3; + + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + (float64_is_zero(farg1.d) && + float64_is_infinity(farg2.d)))) { + /* Multiplication of zero by infinity */ + farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); + } else { + if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) || + float64_is_signaling_nan(farg2.d, &env->fp_status) || + float64_is_signaling_nan(farg3.d, &env->fp_status))) { + /* sNaN operation */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } + /* This is the way the PowerPC specification defines it */ + float128 ft0_128, ft1_128; + + ft0_128 = float64_to_float128(farg1.d, &env->fp_status); + ft1_128 = float64_to_float128(farg2.d, &env->fp_status); + ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); + if (unlikely(float128_is_infinity(ft0_128) && + float64_is_infinity(farg3.d) && + float128_is_neg(ft0_128) == float64_is_neg(farg3.d))) { + /* Magnitude subtraction of infinities */ + farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); + } else { + ft1_128 = float64_to_float128(farg3.d, &env->fp_status); + ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); + farg1.d = float128_to_float64(ft0_128, &env->fp_status); + } + if (likely(!float64_is_any_nan(farg1.d))) { + farg1.d = float64_chs(farg1.d); + } + } + return farg1.ll; +} + +/* frsp - frsp. */ +uint64_t helper_frsp(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + float32 f32; + + farg.ll = arg; + + if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) { + /* sNaN square root */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } + f32 = float64_to_float32(farg.d, &env->fp_status); + farg.d = float32_to_float64(f32, &env->fp_status); + + return farg.ll; +} + +/* fsqrt - fsqrt. */ +uint64_t helper_fsqrt(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + + farg.ll = arg; + + if (unlikely(float64_is_any_nan(farg.d))) { + if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) { + /* sNaN reciprocal square root */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + farg.ll = float64_snan_to_qnan(farg.ll); + } + } else if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { + /* Square root of a negative nonzero number */ + farg.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); + } else { + farg.d = float64_sqrt(farg.d, &env->fp_status); + } + return farg.ll; +} + +/* fre - fre. */ +uint64_t helper_fre(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + + farg.ll = arg; + + if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) { + /* sNaN reciprocal */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } + farg.d = float64_div(float64_one, farg.d, &env->fp_status); + return farg.d; +} + +/* fres - fres. */ +uint64_t helper_fres(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + float32 f32; + + farg.ll = arg; + + if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) { + /* sNaN reciprocal */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } + farg.d = float64_div(float64_one, farg.d, &env->fp_status); + f32 = float64_to_float32(farg.d, &env->fp_status); + farg.d = float32_to_float64(f32, &env->fp_status); + + return farg.ll; +} + +/* frsqrte - frsqrte. */ +uint64_t helper_frsqrte(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + + farg.ll = arg; + + if (unlikely(float64_is_any_nan(farg.d))) { + if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) { + /* sNaN reciprocal square root */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + farg.ll = float64_snan_to_qnan(farg.ll); + } + } else if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { + /* Reciprocal square root of a negative nonzero number */ + farg.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); + } else { + farg.d = float64_sqrt(farg.d, &env->fp_status); + farg.d = float64_div(float64_one, farg.d, &env->fp_status); + } + + return farg.ll; +} + +/* fsel - fsel. */ +uint64_t helper_fsel(CPUPPCState *env, uint64_t arg1, uint64_t arg2, + uint64_t arg3) +{ + CPU_DoubleU farg1; + + farg1.ll = arg1; + + if ((!float64_is_neg(farg1.d) || float64_is_zero(farg1.d)) && + !float64_is_any_nan(farg1.d)) { + return arg2; + } else { + return arg3; + } +} + +uint32_t helper_ftdiv(uint64_t fra, uint64_t frb) +{ + int fe_flag = 0; + int fg_flag = 0; + + if (unlikely(float64_is_infinity(fra) || + float64_is_infinity(frb) || + float64_is_zero(frb))) { + fe_flag = 1; + fg_flag = 1; + } else { + int e_a = ppc_float64_get_unbiased_exp(fra); + int e_b = ppc_float64_get_unbiased_exp(frb); + + if (unlikely(float64_is_any_nan(fra) || + float64_is_any_nan(frb))) { + fe_flag = 1; + } else if ((e_b <= -1022) || (e_b >= 1021)) { + fe_flag = 1; + } else if (!float64_is_zero(fra) && + (((e_a - e_b) >= 1023) || + ((e_a - e_b) <= -1021) || + (e_a <= -970))) { + fe_flag = 1; + } + + if (unlikely(float64_is_zero_or_denormal(frb))) { + /* XB is not zero because of the above check and */ + /* so must be denormalized. */ + fg_flag = 1; + } + } + + return 0x8 | (fg_flag ? 4 : 0) | (fe_flag ? 2 : 0); +} + +uint32_t helper_ftsqrt(uint64_t frb) +{ + int fe_flag = 0; + int fg_flag = 0; + + if (unlikely(float64_is_infinity(frb) || float64_is_zero(frb))) { + fe_flag = 1; + fg_flag = 1; + } else { + int e_b = ppc_float64_get_unbiased_exp(frb); + + if (unlikely(float64_is_any_nan(frb))) { + fe_flag = 1; + } else if (unlikely(float64_is_zero(frb))) { + fe_flag = 1; + } else if (unlikely(float64_is_neg(frb))) { + fe_flag = 1; + } else if (!float64_is_zero(frb) && (e_b <= (-1022+52))) { + fe_flag = 1; + } + + if (unlikely(float64_is_zero_or_denormal(frb))) { + /* XB is not zero because of the above check and */ + /* therefore must be denormalized. */ + fg_flag = 1; + } + } + + return 0x8 | (fg_flag ? 4 : 0) | (fe_flag ? 2 : 0); +} + +void helper_fcmpu(CPUPPCState *env, uint64_t arg1, uint64_t arg2, + uint32_t crfD) +{ + CPU_DoubleU farg1, farg2; + uint32_t ret = 0; + + farg1.ll = arg1; + farg2.ll = arg2; + + if (unlikely(float64_is_any_nan(farg1.d) || + float64_is_any_nan(farg2.d))) { + ret = 0x01UL; + } else if (float64_lt(farg1.d, farg2.d, &env->fp_status)) { + ret = 0x08UL; + } else if (!float64_le(farg1.d, farg2.d, &env->fp_status)) { + ret = 0x04UL; + } else { + ret = 0x02UL; + } + + env->fpscr &= ~(0x0F << FPSCR_FPRF); + env->fpscr |= ret << FPSCR_FPRF; + env->crf[crfD] = ret; + if (unlikely(ret == 0x01UL + && (float64_is_signaling_nan(farg1.d, &env->fp_status) || + float64_is_signaling_nan(farg2.d, &env->fp_status)))) { + /* sNaN comparison */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } +} + +void helper_fcmpo(CPUPPCState *env, uint64_t arg1, uint64_t arg2, + uint32_t crfD) +{ + CPU_DoubleU farg1, farg2; + uint32_t ret = 0; + + farg1.ll = arg1; + farg2.ll = arg2; + + if (unlikely(float64_is_any_nan(farg1.d) || + float64_is_any_nan(farg2.d))) { + ret = 0x01UL; + } else if (float64_lt(farg1.d, farg2.d, &env->fp_status)) { + ret = 0x08UL; + } else if (!float64_le(farg1.d, farg2.d, &env->fp_status)) { + ret = 0x04UL; + } else { + ret = 0x02UL; + } + + env->fpscr &= ~(0x0F << FPSCR_FPRF); + env->fpscr |= ret << FPSCR_FPRF; + env->crf[crfD] = ret; + if (unlikely(ret == 0x01UL)) { + if (float64_is_signaling_nan(farg1.d, &env->fp_status) || + float64_is_signaling_nan(farg2.d, &env->fp_status)) { + /* sNaN comparison */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | + POWERPC_EXCP_FP_VXVC, 1); + } else { + /* qNaN comparison */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 1); + } + } +} + +/* Single-precision floating-point conversions */ +static inline uint32_t efscfsi(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + + u.f = int32_to_float32(val, &env->vec_status); + + return u.l; +} + +static inline uint32_t efscfui(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + + u.f = uint32_to_float32(val, &env->vec_status); + + return u.l; +} + +static inline int32_t efsctsi(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + + u.l = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float32_is_quiet_nan(u.f, &env->vec_status))) { + return 0; + } + + return float32_to_int32(u.f, &env->vec_status); +} + +static inline uint32_t efsctui(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + + u.l = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float32_is_quiet_nan(u.f, &env->vec_status))) { + return 0; + } + + return float32_to_uint32(u.f, &env->vec_status); +} + +static inline uint32_t efsctsiz(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + + u.l = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float32_is_quiet_nan(u.f, &env->vec_status))) { + return 0; + } + + return float32_to_int32_round_to_zero(u.f, &env->vec_status); +} + +static inline uint32_t efsctuiz(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + + u.l = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float32_is_quiet_nan(u.f, &env->vec_status))) { + return 0; + } + + return float32_to_uint32_round_to_zero(u.f, &env->vec_status); +} + +static inline uint32_t efscfsf(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + float32 tmp; + + u.f = int32_to_float32(val, &env->vec_status); + tmp = int64_to_float32(1ULL << 32, &env->vec_status); + u.f = float32_div(u.f, tmp, &env->vec_status); + + return u.l; +} + +static inline uint32_t efscfuf(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + float32 tmp; + + u.f = uint32_to_float32(val, &env->vec_status); + tmp = uint64_to_float32(1ULL << 32, &env->vec_status); + u.f = float32_div(u.f, tmp, &env->vec_status); + + return u.l; +} + +static inline uint32_t efsctsf(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + float32 tmp; + + u.l = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float32_is_quiet_nan(u.f, &env->vec_status))) { + return 0; + } + tmp = uint64_to_float32(1ULL << 32, &env->vec_status); + u.f = float32_mul(u.f, tmp, &env->vec_status); + + return float32_to_int32(u.f, &env->vec_status); +} + +static inline uint32_t efsctuf(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + float32 tmp; + + u.l = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float32_is_quiet_nan(u.f, &env->vec_status))) { + return 0; + } + tmp = uint64_to_float32(1ULL << 32, &env->vec_status); + u.f = float32_mul(u.f, tmp, &env->vec_status); + + return float32_to_uint32(u.f, &env->vec_status); +} + +#define HELPER_SPE_SINGLE_CONV(name) \ + uint32_t helper_e##name(CPUPPCState *env, uint32_t val) \ + { \ + return e##name(env, val); \ + } +/* efscfsi */ +HELPER_SPE_SINGLE_CONV(fscfsi); +/* efscfui */ +HELPER_SPE_SINGLE_CONV(fscfui); +/* efscfuf */ +HELPER_SPE_SINGLE_CONV(fscfuf); +/* efscfsf */ +HELPER_SPE_SINGLE_CONV(fscfsf); +/* efsctsi */ +HELPER_SPE_SINGLE_CONV(fsctsi); +/* efsctui */ +HELPER_SPE_SINGLE_CONV(fsctui); +/* efsctsiz */ +HELPER_SPE_SINGLE_CONV(fsctsiz); +/* efsctuiz */ +HELPER_SPE_SINGLE_CONV(fsctuiz); +/* efsctsf */ +HELPER_SPE_SINGLE_CONV(fsctsf); +/* efsctuf */ +HELPER_SPE_SINGLE_CONV(fsctuf); + +#define HELPER_SPE_VECTOR_CONV(name) \ + uint64_t helper_ev##name(CPUPPCState *env, uint64_t val) \ + { \ + return ((uint64_t)e##name(env, val >> 32) << 32) | \ + (uint64_t)e##name(env, val); \ + } +/* evfscfsi */ +HELPER_SPE_VECTOR_CONV(fscfsi); +/* evfscfui */ +HELPER_SPE_VECTOR_CONV(fscfui); +/* evfscfuf */ +HELPER_SPE_VECTOR_CONV(fscfuf); +/* evfscfsf */ +HELPER_SPE_VECTOR_CONV(fscfsf); +/* evfsctsi */ +HELPER_SPE_VECTOR_CONV(fsctsi); +/* evfsctui */ +HELPER_SPE_VECTOR_CONV(fsctui); +/* evfsctsiz */ +HELPER_SPE_VECTOR_CONV(fsctsiz); +/* evfsctuiz */ +HELPER_SPE_VECTOR_CONV(fsctuiz); +/* evfsctsf */ +HELPER_SPE_VECTOR_CONV(fsctsf); +/* evfsctuf */ +HELPER_SPE_VECTOR_CONV(fsctuf); + +/* Single-precision floating-point arithmetic */ +static inline uint32_t efsadd(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + CPU_FloatU u1, u2; + + u1.l = op1; + u2.l = op2; + u1.f = float32_add(u1.f, u2.f, &env->vec_status); + return u1.l; +} + +static inline uint32_t efssub(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + CPU_FloatU u1, u2; + + u1.l = op1; + u2.l = op2; + u1.f = float32_sub(u1.f, u2.f, &env->vec_status); + return u1.l; +} + +static inline uint32_t efsmul(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + CPU_FloatU u1, u2; + + u1.l = op1; + u2.l = op2; + u1.f = float32_mul(u1.f, u2.f, &env->vec_status); + return u1.l; +} + +static inline uint32_t efsdiv(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + CPU_FloatU u1, u2; + + u1.l = op1; + u2.l = op2; + u1.f = float32_div(u1.f, u2.f, &env->vec_status); + return u1.l; +} + +#define HELPER_SPE_SINGLE_ARITH(name) \ + uint32_t helper_e##name(CPUPPCState *env, uint32_t op1, uint32_t op2) \ + { \ + return e##name(env, op1, op2); \ + } +/* efsadd */ +HELPER_SPE_SINGLE_ARITH(fsadd); +/* efssub */ +HELPER_SPE_SINGLE_ARITH(fssub); +/* efsmul */ +HELPER_SPE_SINGLE_ARITH(fsmul); +/* efsdiv */ +HELPER_SPE_SINGLE_ARITH(fsdiv); + +#define HELPER_SPE_VECTOR_ARITH(name) \ + uint64_t helper_ev##name(CPUPPCState *env, uint64_t op1, uint64_t op2) \ + { \ + return ((uint64_t)e##name(env, op1 >> 32, op2 >> 32) << 32) | \ + (uint64_t)e##name(env, op1, op2); \ + } +/* evfsadd */ +HELPER_SPE_VECTOR_ARITH(fsadd); +/* evfssub */ +HELPER_SPE_VECTOR_ARITH(fssub); +/* evfsmul */ +HELPER_SPE_VECTOR_ARITH(fsmul); +/* evfsdiv */ +HELPER_SPE_VECTOR_ARITH(fsdiv); + +/* Single-precision floating-point comparisons */ +static inline uint32_t efscmplt(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + CPU_FloatU u1, u2; + + u1.l = op1; + u2.l = op2; + return float32_lt(u1.f, u2.f, &env->vec_status) ? 4 : 0; +} + +static inline uint32_t efscmpgt(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + CPU_FloatU u1, u2; + + u1.l = op1; + u2.l = op2; + return float32_le(u1.f, u2.f, &env->vec_status) ? 0 : 4; +} + +static inline uint32_t efscmpeq(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + CPU_FloatU u1, u2; + + u1.l = op1; + u2.l = op2; + return float32_eq(u1.f, u2.f, &env->vec_status) ? 4 : 0; +} + +static inline uint32_t efststlt(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + /* XXX: TODO: ignore special values (NaN, infinites, ...) */ + return efscmplt(env, op1, op2); +} + +static inline uint32_t efststgt(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + /* XXX: TODO: ignore special values (NaN, infinites, ...) */ + return efscmpgt(env, op1, op2); +} + +static inline uint32_t efststeq(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + /* XXX: TODO: ignore special values (NaN, infinites, ...) */ + return efscmpeq(env, op1, op2); +} + +#define HELPER_SINGLE_SPE_CMP(name) \ + uint32_t helper_e##name(CPUPPCState *env, uint32_t op1, uint32_t op2) \ + { \ + return e##name(env, op1, op2); \ + } +/* efststlt */ +HELPER_SINGLE_SPE_CMP(fststlt); +/* efststgt */ +HELPER_SINGLE_SPE_CMP(fststgt); +/* efststeq */ +HELPER_SINGLE_SPE_CMP(fststeq); +/* efscmplt */ +HELPER_SINGLE_SPE_CMP(fscmplt); +/* efscmpgt */ +HELPER_SINGLE_SPE_CMP(fscmpgt); +/* efscmpeq */ +HELPER_SINGLE_SPE_CMP(fscmpeq); + +static inline uint32_t evcmp_merge(int t0, int t1) +{ + return (t0 << 3) | (t1 << 2) | ((t0 | t1) << 1) | (t0 & t1); +} + +#define HELPER_VECTOR_SPE_CMP(name) \ + uint32_t helper_ev##name(CPUPPCState *env, uint64_t op1, uint64_t op2) \ + { \ + return evcmp_merge(e##name(env, op1 >> 32, op2 >> 32), \ + e##name(env, op1, op2)); \ + } +/* evfststlt */ +HELPER_VECTOR_SPE_CMP(fststlt); +/* evfststgt */ +HELPER_VECTOR_SPE_CMP(fststgt); +/* evfststeq */ +HELPER_VECTOR_SPE_CMP(fststeq); +/* evfscmplt */ +HELPER_VECTOR_SPE_CMP(fscmplt); +/* evfscmpgt */ +HELPER_VECTOR_SPE_CMP(fscmpgt); +/* evfscmpeq */ +HELPER_VECTOR_SPE_CMP(fscmpeq); + +/* Double-precision floating-point conversion */ +uint64_t helper_efdcfsi(CPUPPCState *env, uint32_t val) +{ + CPU_DoubleU u; + + u.d = int32_to_float64(val, &env->vec_status); + + return u.ll; +} + +uint64_t helper_efdcfsid(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + + u.d = int64_to_float64(val, &env->vec_status); + + return u.ll; +} + +uint64_t helper_efdcfui(CPUPPCState *env, uint32_t val) +{ + CPU_DoubleU u; + + u.d = uint32_to_float64(val, &env->vec_status); + + return u.ll; +} + +uint64_t helper_efdcfuid(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + + u.d = uint64_to_float64(val, &env->vec_status); + + return u.ll; +} + +uint32_t helper_efdctsi(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + + u.ll = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float64_is_any_nan(u.d))) { + return 0; + } + + return float64_to_int32(u.d, &env->vec_status); +} + +uint32_t helper_efdctui(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + + u.ll = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float64_is_any_nan(u.d))) { + return 0; + } + + return float64_to_uint32(u.d, &env->vec_status); +} + +uint32_t helper_efdctsiz(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + + u.ll = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float64_is_any_nan(u.d))) { + return 0; + } + + return float64_to_int32_round_to_zero(u.d, &env->vec_status); +} + +uint64_t helper_efdctsidz(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + + u.ll = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float64_is_any_nan(u.d))) { + return 0; + } + + return float64_to_int64_round_to_zero(u.d, &env->vec_status); +} + +uint32_t helper_efdctuiz(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + + u.ll = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float64_is_any_nan(u.d))) { + return 0; + } + + return float64_to_uint32_round_to_zero(u.d, &env->vec_status); +} + +uint64_t helper_efdctuidz(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + + u.ll = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float64_is_any_nan(u.d))) { + return 0; + } + + return float64_to_uint64_round_to_zero(u.d, &env->vec_status); +} + +uint64_t helper_efdcfsf(CPUPPCState *env, uint32_t val) +{ + CPU_DoubleU u; + float64 tmp; + + u.d = int32_to_float64(val, &env->vec_status); + tmp = int64_to_float64(1ULL << 32, &env->vec_status); + u.d = float64_div(u.d, tmp, &env->vec_status); + + return u.ll; +} + +uint64_t helper_efdcfuf(CPUPPCState *env, uint32_t val) +{ + CPU_DoubleU u; + float64 tmp; + + u.d = uint32_to_float64(val, &env->vec_status); + tmp = int64_to_float64(1ULL << 32, &env->vec_status); + u.d = float64_div(u.d, tmp, &env->vec_status); + + return u.ll; +} + +uint32_t helper_efdctsf(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + float64 tmp; + + u.ll = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float64_is_any_nan(u.d))) { + return 0; + } + tmp = uint64_to_float64(1ULL << 32, &env->vec_status); + u.d = float64_mul(u.d, tmp, &env->vec_status); + + return float64_to_int32(u.d, &env->vec_status); +} + +uint32_t helper_efdctuf(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + float64 tmp; + + u.ll = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float64_is_any_nan(u.d))) { + return 0; + } + tmp = uint64_to_float64(1ULL << 32, &env->vec_status); + u.d = float64_mul(u.d, tmp, &env->vec_status); + + return float64_to_uint32(u.d, &env->vec_status); +} + +uint32_t helper_efscfd(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u1; + CPU_FloatU u2; + + u1.ll = val; + u2.f = float64_to_float32(u1.d, &env->vec_status); + + return u2.l; +} + +uint64_t helper_efdcfs(CPUPPCState *env, uint32_t val) +{ + CPU_DoubleU u2; + CPU_FloatU u1; + + u1.l = val; + u2.d = float32_to_float64(u1.f, &env->vec_status); + + return u2.ll; +} + +/* Double precision fixed-point arithmetic */ +uint64_t helper_efdadd(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + CPU_DoubleU u1, u2; + + u1.ll = op1; + u2.ll = op2; + u1.d = float64_add(u1.d, u2.d, &env->vec_status); + return u1.ll; +} + +uint64_t helper_efdsub(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + CPU_DoubleU u1, u2; + + u1.ll = op1; + u2.ll = op2; + u1.d = float64_sub(u1.d, u2.d, &env->vec_status); + return u1.ll; +} + +uint64_t helper_efdmul(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + CPU_DoubleU u1, u2; + + u1.ll = op1; + u2.ll = op2; + u1.d = float64_mul(u1.d, u2.d, &env->vec_status); + return u1.ll; +} + +uint64_t helper_efddiv(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + CPU_DoubleU u1, u2; + + u1.ll = op1; + u2.ll = op2; + u1.d = float64_div(u1.d, u2.d, &env->vec_status); + return u1.ll; +} + +/* Double precision floating point helpers */ +uint32_t helper_efdtstlt(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + CPU_DoubleU u1, u2; + + u1.ll = op1; + u2.ll = op2; + return float64_lt(u1.d, u2.d, &env->vec_status) ? 4 : 0; +} + +uint32_t helper_efdtstgt(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + CPU_DoubleU u1, u2; + + u1.ll = op1; + u2.ll = op2; + return float64_le(u1.d, u2.d, &env->vec_status) ? 0 : 4; +} + +uint32_t helper_efdtsteq(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + CPU_DoubleU u1, u2; + + u1.ll = op1; + u2.ll = op2; + return float64_eq_quiet(u1.d, u2.d, &env->vec_status) ? 4 : 0; +} + +uint32_t helper_efdcmplt(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + /* XXX: TODO: test special values (NaN, infinites, ...) */ + return helper_efdtstlt(env, op1, op2); +} + +uint32_t helper_efdcmpgt(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + /* XXX: TODO: test special values (NaN, infinites, ...) */ + return helper_efdtstgt(env, op1, op2); +} + +uint32_t helper_efdcmpeq(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + /* XXX: TODO: test special values (NaN, infinites, ...) */ + return helper_efdtsteq(env, op1, op2); +} + +#define DECODE_SPLIT(opcode, shift1, nb1, shift2, nb2) \ + (((((opcode) >> (shift1)) & ((1 << (nb1)) - 1)) << nb2) | \ + (((opcode) >> (shift2)) & ((1 << (nb2)) - 1))) + +#define xT(opcode) DECODE_SPLIT(opcode, 0, 1, 21, 5) +#define xA(opcode) DECODE_SPLIT(opcode, 2, 1, 16, 5) +#define xB(opcode) DECODE_SPLIT(opcode, 1, 1, 11, 5) +#define xC(opcode) DECODE_SPLIT(opcode, 3, 1, 6, 5) +#define BF(opcode) (((opcode) >> (31-8)) & 7) + +typedef union _ppc_vsr_t { + uint64_t u64[2]; + uint32_t u32[4]; + float32 f32[4]; + float64 f64[2]; +} ppc_vsr_t; + +#if defined(HOST_WORDS_BIGENDIAN) +#define VsrW(i) u32[i] +#define VsrD(i) u64[i] +#else +#define VsrW(i) u32[3-(i)] +#define VsrD(i) u64[1-(i)] +#endif + +static void getVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env) +{ + if (n < 32) { + vsr->VsrD(0) = env->fpr[n]; + vsr->VsrD(1) = env->vsr[n]; + } else { + vsr->u64[0] = env->avr[n-32].u64[0]; + vsr->u64[1] = env->avr[n-32].u64[1]; + } +} + +static void putVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env) +{ + if (n < 32) { + env->fpr[n] = vsr->VsrD(0); + env->vsr[n] = vsr->VsrD(1); + } else { + env->avr[n-32].u64[0] = vsr->u64[0]; + env->avr[n-32].u64[1] = vsr->u64[1]; + } +} + +#define float64_to_float64(x, env) x + + +/* VSX_ADD_SUB - VSX floating point add/subract + * name - instruction mnemonic + * op - operation (add or sub) + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (VsrD(*) or VsrW(*)) + * sfprf - set FPRF + */ +#define VSX_ADD_SUB(name, op, nels, tp, fld, sfprf, r2sp) \ +void helper_##name(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + int i; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + xt.fld = tp##_##op(xa.fld, xb.fld, &tstat); \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if (tp##_is_infinity(xa.fld) && tp##_is_infinity(xb.fld)) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, sfprf); \ + } else if (tp##_is_signaling_nan(xa.fld, &tstat) || \ + tp##_is_signaling_nan(xb.fld, &tstat)) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt.fld = helper_frsp(env, xt.fld); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld); \ + } \ + } \ + putVSR(xT(opcode), &xt, env); \ + float_check_status(env); \ +} + +VSX_ADD_SUB(xsadddp, add, 1, float64, VsrD(0), 1, 0) +VSX_ADD_SUB(xsaddsp, add, 1, float64, VsrD(0), 1, 1) +VSX_ADD_SUB(xvadddp, add, 2, float64, VsrD(i), 0, 0) +VSX_ADD_SUB(xvaddsp, add, 4, float32, VsrW(i), 0, 0) +VSX_ADD_SUB(xssubdp, sub, 1, float64, VsrD(0), 1, 0) +VSX_ADD_SUB(xssubsp, sub, 1, float64, VsrD(0), 1, 1) +VSX_ADD_SUB(xvsubdp, sub, 2, float64, VsrD(i), 0, 0) +VSX_ADD_SUB(xvsubsp, sub, 4, float32, VsrW(i), 0, 0) + +/* VSX_MUL - VSX floating point multiply + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (VsrD(*) or VsrW(*)) + * sfprf - set FPRF + */ +#define VSX_MUL(op, nels, tp, fld, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + int i; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + xt.fld = tp##_mul(xa.fld, xb.fld, &tstat); \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if ((tp##_is_infinity(xa.fld) && tp##_is_zero(xb.fld)) || \ + (tp##_is_infinity(xb.fld) && tp##_is_zero(xa.fld))) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, sfprf); \ + } else if (tp##_is_signaling_nan(xa.fld, &tstat) || \ + tp##_is_signaling_nan(xb.fld, &tstat)) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt.fld = helper_frsp(env, xt.fld); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + float_check_status(env); \ +} + +VSX_MUL(xsmuldp, 1, float64, VsrD(0), 1, 0) +VSX_MUL(xsmulsp, 1, float64, VsrD(0), 1, 1) +VSX_MUL(xvmuldp, 2, float64, VsrD(i), 0, 0) +VSX_MUL(xvmulsp, 4, float32, VsrW(i), 0, 0) + +/* VSX_DIV - VSX floating point divide + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (VsrD(*) or VsrW(*)) + * sfprf - set FPRF + */ +#define VSX_DIV(op, nels, tp, fld, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + int i; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + xt.fld = tp##_div(xa.fld, xb.fld, &tstat); \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if (tp##_is_infinity(xa.fld) && tp##_is_infinity(xb.fld)) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI, sfprf); \ + } else if (tp##_is_zero(xa.fld) && \ + tp##_is_zero(xb.fld)) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ, sfprf); \ + } else if (tp##_is_signaling_nan(xa.fld, &tstat) || \ + tp##_is_signaling_nan(xb.fld, &tstat)) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt.fld = helper_frsp(env, xt.fld); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + float_check_status(env); \ +} + +VSX_DIV(xsdivdp, 1, float64, VsrD(0), 1, 0) +VSX_DIV(xsdivsp, 1, float64, VsrD(0), 1, 1) +VSX_DIV(xvdivdp, 2, float64, VsrD(i), 0, 0) +VSX_DIV(xvdivsp, 4, float32, VsrW(i), 0, 0) + +/* VSX_RE - VSX floating point reciprocal estimate + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (VsrD(*) or VsrW(*)) + * sfprf - set FPRF + */ +#define VSX_RE(op, nels, tp, fld, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + if (unlikely(tp##_is_signaling_nan(xb.fld, &env->fp_status))) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + xt.fld = tp##_div(tp##_one, xb.fld, &env->fp_status); \ + \ + if (r2sp) { \ + xt.fld = helper_frsp(env, xt.fld); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + float_check_status(env); \ +} + +VSX_RE(xsredp, 1, float64, VsrD(0), 1, 0) +VSX_RE(xsresp, 1, float64, VsrD(0), 1, 1) +VSX_RE(xvredp, 2, float64, VsrD(i), 0, 0) +VSX_RE(xvresp, 4, float32, VsrW(i), 0, 0) + +/* VSX_SQRT - VSX floating point square root + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (VsrD(*) or VsrW(*)) + * sfprf - set FPRF + */ +#define VSX_SQRT(op, nels, tp, fld, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + xt.fld = tp##_sqrt(xb.fld, &tstat); \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if (tp##_is_neg(xb.fld) && !tp##_is_zero(xb.fld)) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, sfprf); \ + } else if (tp##_is_signaling_nan(xb.fld, &tstat)) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt.fld = helper_frsp(env, xt.fld); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + float_check_status(env); \ +} + +VSX_SQRT(xssqrtdp, 1, float64, VsrD(0), 1, 0) +VSX_SQRT(xssqrtsp, 1, float64, VsrD(0), 1, 1) +VSX_SQRT(xvsqrtdp, 2, float64, VsrD(i), 0, 0) +VSX_SQRT(xvsqrtsp, 4, float32, VsrW(i), 0, 0) + +/* VSX_RSQRTE - VSX floating point reciprocal square root estimate + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (VsrD(*) or VsrW(*)) + * sfprf - set FPRF + */ +#define VSX_RSQRTE(op, nels, tp, fld, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + xt.fld = tp##_sqrt(xb.fld, &tstat); \ + xt.fld = tp##_div(tp##_one, xt.fld, &tstat); \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if (tp##_is_neg(xb.fld) && !tp##_is_zero(xb.fld)) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, sfprf); \ + } else if (tp##_is_signaling_nan(xb.fld, &tstat)) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt.fld = helper_frsp(env, xt.fld); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + float_check_status(env); \ +} + +VSX_RSQRTE(xsrsqrtedp, 1, float64, VsrD(0), 1, 0) +VSX_RSQRTE(xsrsqrtesp, 1, float64, VsrD(0), 1, 1) +VSX_RSQRTE(xvrsqrtedp, 2, float64, VsrD(i), 0, 0) +VSX_RSQRTE(xvrsqrtesp, 4, float32, VsrW(i), 0, 0) + +/* VSX_TDIV - VSX floating point test for divide + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (VsrD(*) or VsrW(*)) + * emin - minimum unbiased exponent + * emax - maximum unbiased exponent + * nbits - number of fraction bits + */ +#define VSX_TDIV(op, nels, tp, fld, emin, emax, nbits) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xa, xb; \ + int i; \ + int fe_flag = 0; \ + int fg_flag = 0; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + \ + for (i = 0; i < nels; i++) { \ + if (unlikely(tp##_is_infinity(xa.fld) || \ + tp##_is_infinity(xb.fld) || \ + tp##_is_zero(xb.fld))) { \ + fe_flag = 1; \ + fg_flag = 1; \ + } else { \ + int e_a = ppc_##tp##_get_unbiased_exp(xa.fld); \ + int e_b = ppc_##tp##_get_unbiased_exp(xb.fld); \ + \ + if (unlikely(tp##_is_any_nan(xa.fld) || \ + tp##_is_any_nan(xb.fld))) { \ + fe_flag = 1; \ + } else if ((e_b <= emin) || (e_b >= (emax-2))) { \ + fe_flag = 1; \ + } else if (!tp##_is_zero(xa.fld) && \ + (((e_a - e_b) >= emax) || \ + ((e_a - e_b) <= (emin+1)) || \ + (e_a <= (emin+nbits)))) { \ + fe_flag = 1; \ + } \ + \ + if (unlikely(tp##_is_zero_or_denormal(xb.fld))) { \ + /* XB is not zero because of the above check and */ \ + /* so must be denormalized. */ \ + fg_flag = 1; \ + } \ + } \ + } \ + \ + env->crf[BF(opcode)] = 0x8 | (fg_flag ? 4 : 0) | (fe_flag ? 2 : 0); \ +} + +VSX_TDIV(xstdivdp, 1, float64, VsrD(0), -1022, 1023, 52) +VSX_TDIV(xvtdivdp, 2, float64, VsrD(i), -1022, 1023, 52) +VSX_TDIV(xvtdivsp, 4, float32, VsrW(i), -126, 127, 23) + +/* VSX_TSQRT - VSX floating point test for square root + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (VsrD(*) or VsrW(*)) + * emin - minimum unbiased exponent + * emax - maximum unbiased exponent + * nbits - number of fraction bits + */ +#define VSX_TSQRT(op, nels, tp, fld, emin, nbits) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xa, xb; \ + int i; \ + int fe_flag = 0; \ + int fg_flag = 0; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + \ + for (i = 0; i < nels; i++) { \ + if (unlikely(tp##_is_infinity(xb.fld) || \ + tp##_is_zero(xb.fld))) { \ + fe_flag = 1; \ + fg_flag = 1; \ + } else { \ + int e_b = ppc_##tp##_get_unbiased_exp(xb.fld); \ + \ + if (unlikely(tp##_is_any_nan(xb.fld))) { \ + fe_flag = 1; \ + } else if (unlikely(tp##_is_zero(xb.fld))) { \ + fe_flag = 1; \ + } else if (unlikely(tp##_is_neg(xb.fld))) { \ + fe_flag = 1; \ + } else if (!tp##_is_zero(xb.fld) && \ + (e_b <= (emin+nbits))) { \ + fe_flag = 1; \ + } \ + \ + if (unlikely(tp##_is_zero_or_denormal(xb.fld))) { \ + /* XB is not zero because of the above check and */ \ + /* therefore must be denormalized. */ \ + fg_flag = 1; \ + } \ + } \ + } \ + \ + env->crf[BF(opcode)] = 0x8 | (fg_flag ? 4 : 0) | (fe_flag ? 2 : 0); \ +} + +VSX_TSQRT(xstsqrtdp, 1, float64, VsrD(0), -1022, 52) +VSX_TSQRT(xvtsqrtdp, 2, float64, VsrD(i), -1022, 52) +VSX_TSQRT(xvtsqrtsp, 4, float32, VsrW(i), -126, 23) + +/* VSX_MADD - VSX floating point muliply/add variations + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (VsrD(*) or VsrW(*)) + * maddflgs - flags for the float*muladd routine that control the + * various forms (madd, msub, nmadd, nmsub) + * afrm - A form (1=A, 0=M) + * sfprf - set FPRF + */ +#define VSX_MADD(op, nels, tp, fld, maddflgs, afrm, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt_in, xa, xb, xt_out; \ + ppc_vsr_t *b, *c; \ + int i; \ + \ + if (afrm) { /* AxB + T */ \ + b = &xb; \ + c = &xt_in; \ + } else { /* AxT + B */ \ + b = &xt_in; \ + c = &xb; \ + } \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt_in, env); \ + \ + xt_out = xt_in; \ + \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + if (r2sp && (tstat.float_rounding_mode == float_round_nearest_even)) {\ + /* Avoid double rounding errors by rounding the intermediate */ \ + /* result to odd. */ \ + set_float_rounding_mode(float_round_to_zero, &tstat); \ + xt_out.fld = tp##_muladd(xa.fld, b->fld, c->fld, \ + maddflgs, &tstat); \ + xt_out.fld |= (get_float_exception_flags(&tstat) & \ + float_flag_inexact) != 0; \ + } else { \ + xt_out.fld = tp##_muladd(xa.fld, b->fld, c->fld, \ + maddflgs, &tstat); \ + } \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if (tp##_is_signaling_nan(xa.fld, &tstat) || \ + tp##_is_signaling_nan(b->fld, &tstat) || \ + tp##_is_signaling_nan(c->fld, &tstat)) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + tstat.float_exception_flags &= ~float_flag_invalid; \ + } \ + if ((tp##_is_infinity(xa.fld) && tp##_is_zero(b->fld)) || \ + (tp##_is_zero(xa.fld) && tp##_is_infinity(b->fld))) { \ + xt_out.fld = float64_to_##tp(float_invalid_op_excp(env, \ + POWERPC_EXCP_FP_VXIMZ, sfprf), &env->fp_status); \ + tstat.float_exception_flags &= ~float_flag_invalid; \ + } \ + if ((tstat.float_exception_flags & float_flag_invalid) && \ + ((tp##_is_infinity(xa.fld) || \ + tp##_is_infinity(b->fld)) && \ + tp##_is_infinity(c->fld))) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt_out.fld = helper_frsp(env, xt_out.fld); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt_out.fld); \ + } \ + } \ + putVSR(xT(opcode), &xt_out, env); \ + float_check_status(env); \ +} + +#define MADD_FLGS 0 +#define MSUB_FLGS float_muladd_negate_c +#define NMADD_FLGS float_muladd_negate_result +#define NMSUB_FLGS (float_muladd_negate_c | float_muladd_negate_result) + +VSX_MADD(xsmaddadp, 1, float64, VsrD(0), MADD_FLGS, 1, 1, 0) +VSX_MADD(xsmaddmdp, 1, float64, VsrD(0), MADD_FLGS, 0, 1, 0) +VSX_MADD(xsmsubadp, 1, float64, VsrD(0), MSUB_FLGS, 1, 1, 0) +VSX_MADD(xsmsubmdp, 1, float64, VsrD(0), MSUB_FLGS, 0, 1, 0) +VSX_MADD(xsnmaddadp, 1, float64, VsrD(0), NMADD_FLGS, 1, 1, 0) +VSX_MADD(xsnmaddmdp, 1, float64, VsrD(0), NMADD_FLGS, 0, 1, 0) +VSX_MADD(xsnmsubadp, 1, float64, VsrD(0), NMSUB_FLGS, 1, 1, 0) +VSX_MADD(xsnmsubmdp, 1, float64, VsrD(0), NMSUB_FLGS, 0, 1, 0) + +VSX_MADD(xsmaddasp, 1, float64, VsrD(0), MADD_FLGS, 1, 1, 1) +VSX_MADD(xsmaddmsp, 1, float64, VsrD(0), MADD_FLGS, 0, 1, 1) +VSX_MADD(xsmsubasp, 1, float64, VsrD(0), MSUB_FLGS, 1, 1, 1) +VSX_MADD(xsmsubmsp, 1, float64, VsrD(0), MSUB_FLGS, 0, 1, 1) +VSX_MADD(xsnmaddasp, 1, float64, VsrD(0), NMADD_FLGS, 1, 1, 1) +VSX_MADD(xsnmaddmsp, 1, float64, VsrD(0), NMADD_FLGS, 0, 1, 1) +VSX_MADD(xsnmsubasp, 1, float64, VsrD(0), NMSUB_FLGS, 1, 1, 1) +VSX_MADD(xsnmsubmsp, 1, float64, VsrD(0), NMSUB_FLGS, 0, 1, 1) + +VSX_MADD(xvmaddadp, 2, float64, VsrD(i), MADD_FLGS, 1, 0, 0) +VSX_MADD(xvmaddmdp, 2, float64, VsrD(i), MADD_FLGS, 0, 0, 0) +VSX_MADD(xvmsubadp, 2, float64, VsrD(i), MSUB_FLGS, 1, 0, 0) +VSX_MADD(xvmsubmdp, 2, float64, VsrD(i), MSUB_FLGS, 0, 0, 0) +VSX_MADD(xvnmaddadp, 2, float64, VsrD(i), NMADD_FLGS, 1, 0, 0) +VSX_MADD(xvnmaddmdp, 2, float64, VsrD(i), NMADD_FLGS, 0, 0, 0) +VSX_MADD(xvnmsubadp, 2, float64, VsrD(i), NMSUB_FLGS, 1, 0, 0) +VSX_MADD(xvnmsubmdp, 2, float64, VsrD(i), NMSUB_FLGS, 0, 0, 0) + +VSX_MADD(xvmaddasp, 4, float32, VsrW(i), MADD_FLGS, 1, 0, 0) +VSX_MADD(xvmaddmsp, 4, float32, VsrW(i), MADD_FLGS, 0, 0, 0) +VSX_MADD(xvmsubasp, 4, float32, VsrW(i), MSUB_FLGS, 1, 0, 0) +VSX_MADD(xvmsubmsp, 4, float32, VsrW(i), MSUB_FLGS, 0, 0, 0) +VSX_MADD(xvnmaddasp, 4, float32, VsrW(i), NMADD_FLGS, 1, 0, 0) +VSX_MADD(xvnmaddmsp, 4, float32, VsrW(i), NMADD_FLGS, 0, 0, 0) +VSX_MADD(xvnmsubasp, 4, float32, VsrW(i), NMSUB_FLGS, 1, 0, 0) +VSX_MADD(xvnmsubmsp, 4, float32, VsrW(i), NMSUB_FLGS, 0, 0, 0) + +/* VSX_SCALAR_CMP_DP - VSX scalar floating point compare double precision + * op - instruction mnemonic + * cmp - comparison operation + * exp - expected result of comparison + * svxvc - set VXVC bit + */ +#define VSX_SCALAR_CMP_DP(op, cmp, exp, svxvc) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + bool vxsnan_flag = false, vxvc_flag = false, vex_flag = false; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + if (float64_is_signaling_nan(xa.VsrD(0), &env->fp_status) || \ + float64_is_signaling_nan(xb.VsrD(0), &env->fp_status)) { \ + vxsnan_flag = true; \ + if (fpscr_ve == 0 && svxvc) { \ + vxvc_flag = true; \ + } \ + } else if (svxvc) { \ + vxvc_flag = float64_is_quiet_nan(xa.VsrD(0), &env->fp_status) || \ + float64_is_quiet_nan(xb.VsrD(0), &env->fp_status); \ + } \ + if (vxsnan_flag) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + if (vxvc_flag) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \ + } \ + vex_flag = fpscr_ve && (vxvc_flag || vxsnan_flag); \ + \ + if (!vex_flag) { \ + if (float64_##cmp(xb.VsrD(0), xa.VsrD(0), &env->fp_status) == exp) { \ + xt.VsrD(0) = -1; \ + xt.VsrD(1) = 0; \ + } else { \ + xt.VsrD(0) = 0; \ + xt.VsrD(1) = 0; \ + } \ + } \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_SCALAR_CMP_DP(xscmpeqdp, eq, 1, 0) +VSX_SCALAR_CMP_DP(xscmpgedp, le, 1, 1) +VSX_SCALAR_CMP_DP(xscmpgtdp, lt, 1, 1) +VSX_SCALAR_CMP_DP(xscmpnedp, eq, 0, 0) + +#define VSX_SCALAR_CMP(op, ordered) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xa, xb; \ + uint32_t cc = 0; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + \ + if (unlikely(float64_is_any_nan(xa.VsrD(0)) || \ + float64_is_any_nan(xb.VsrD(0)))) { \ + if (float64_is_signaling_nan(xa.VsrD(0), &env->fp_status) || \ + float64_is_signaling_nan(xb.VsrD(0), &env->fp_status)) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + if (ordered) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \ + } \ + cc = 1; \ + } else { \ + if (float64_lt(xa.VsrD(0), xb.VsrD(0), &env->fp_status)) { \ + cc = 8; \ + } else if (!float64_le(xa.VsrD(0), xb.VsrD(0), \ + &env->fp_status)) { \ + cc = 4; \ + } else { \ + cc = 2; \ + } \ + } \ + \ + env->fpscr &= ~(0x0F << FPSCR_FPRF); \ + env->fpscr |= cc << FPSCR_FPRF; \ + env->crf[BF(opcode)] = cc; \ + \ + float_check_status(env); \ +} + +VSX_SCALAR_CMP(xscmpodp, 1) +VSX_SCALAR_CMP(xscmpudp, 0) + +/* VSX_MAX_MIN - VSX floating point maximum/minimum + * name - instruction mnemonic + * op - operation (max or min) + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (VsrD(*) or VsrW(*)) + */ +#define VSX_MAX_MIN(name, op, nels, tp, fld) \ +void helper_##name(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + int i; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + for (i = 0; i < nels; i++) { \ + xt.fld = tp##_##op(xa.fld, xb.fld, &env->fp_status); \ + if (unlikely(tp##_is_signaling_nan(xa.fld, &env->fp_status) || \ + tp##_is_signaling_nan(xb.fld, &env->fp_status))) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + float_check_status(env); \ +} + +VSX_MAX_MIN(xsmaxdp, maxnum, 1, float64, VsrD(0)) +VSX_MAX_MIN(xvmaxdp, maxnum, 2, float64, VsrD(i)) +VSX_MAX_MIN(xvmaxsp, maxnum, 4, float32, VsrW(i)) +VSX_MAX_MIN(xsmindp, minnum, 1, float64, VsrD(0)) +VSX_MAX_MIN(xvmindp, minnum, 2, float64, VsrD(i)) +VSX_MAX_MIN(xvminsp, minnum, 4, float32, VsrW(i)) + +/* VSX_CMP - VSX floating point compare + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (VsrD(*) or VsrW(*)) + * cmp - comparison operation + * svxvc - set VXVC bit + * exp - expected result of comparison + */ +#define VSX_CMP(op, nels, tp, fld, cmp, svxvc, exp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + int i; \ + int all_true = 1; \ + int all_false = 1; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + for (i = 0; i < nels; i++) { \ + if (unlikely(tp##_is_any_nan(xa.fld) || \ + tp##_is_any_nan(xb.fld))) { \ + if (tp##_is_signaling_nan(xa.fld, &env->fp_status) || \ + tp##_is_signaling_nan(xb.fld, &env->fp_status)) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + if (svxvc) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \ + } \ + xt.fld = 0; \ + all_true = 0; \ + } else { \ + if (tp##_##cmp(xb.fld, xa.fld, &env->fp_status) == exp) { \ + xt.fld = -1; \ + all_false = 0; \ + } else { \ + xt.fld = 0; \ + all_true = 0; \ + } \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + if ((opcode >> (31-21)) & 1) { \ + env->crf[6] = (all_true ? 0x8 : 0) | (all_false ? 0x2 : 0); \ + } \ + float_check_status(env); \ + } + +VSX_CMP(xvcmpeqdp, 2, float64, VsrD(i), eq, 0, 1) +VSX_CMP(xvcmpgedp, 2, float64, VsrD(i), le, 1, 1) +VSX_CMP(xvcmpgtdp, 2, float64, VsrD(i), lt, 1, 1) +VSX_CMP(xvcmpnedp, 2, float64, VsrD(i), eq, 0, 0) +VSX_CMP(xvcmpeqsp, 4, float32, VsrW(i), eq, 0, 1) +VSX_CMP(xvcmpgesp, 4, float32, VsrW(i), le, 1, 1) +VSX_CMP(xvcmpgtsp, 4, float32, VsrW(i), lt, 1, 1) +VSX_CMP(xvcmpnesp, 4, float32, VsrW(i), eq, 0, 0) + +/* VSX_CVT_FP_TO_FP - VSX floating point/floating point conversion + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * stp - source type (float32 or float64) + * ttp - target type (float32 or float64) + * sfld - source vsr_t field + * tfld - target vsr_t field (f32 or f64) + * sfprf - set FPRF + */ +#define VSX_CVT_FP_TO_FP(op, nels, stp, ttp, sfld, tfld, sfprf) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + for (i = 0; i < nels; i++) { \ + xt.tfld = stp##_to_##ttp(xb.sfld, &env->fp_status); \ + if (unlikely(stp##_is_signaling_nan(xb.sfld, \ + &env->fp_status))) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + xt.tfld = ttp##_snan_to_qnan(xt.tfld); \ + } \ + if (sfprf) { \ + helper_compute_fprf(env, ttp##_to_float64(xt.tfld, \ + &env->fp_status)); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + float_check_status(env); \ +} + +VSX_CVT_FP_TO_FP(xscvdpsp, 1, float64, float32, VsrD(0), VsrW(0), 1) +VSX_CVT_FP_TO_FP(xscvspdp, 1, float32, float64, VsrW(0), VsrD(0), 1) +VSX_CVT_FP_TO_FP(xvcvdpsp, 2, float64, float32, VsrD(i), VsrW(2*i), 0) +VSX_CVT_FP_TO_FP(xvcvspdp, 2, float32, float64, VsrW(2*i), VsrD(i), 0) + +uint64_t helper_xscvdpspn(CPUPPCState *env, uint64_t xb) +{ + float_status tstat = env->fp_status; + set_float_exception_flags(0, &tstat); + + return (uint64_t)float64_to_float32(xb, &tstat) << 32; +} + +uint64_t helper_xscvspdpn(CPUPPCState *env, uint64_t xb) +{ + float_status tstat = env->fp_status; + set_float_exception_flags(0, &tstat); + + return float32_to_float64(xb >> 32, &tstat); +} + +/* VSX_CVT_FP_TO_INT - VSX floating point to integer conversion + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * stp - source type (float32 or float64) + * ttp - target type (int32, uint32, int64 or uint64) + * sfld - source vsr_t field + * tfld - target vsr_t field + * rnan - resulting NaN + */ +#define VSX_CVT_FP_TO_INT(op, nels, stp, ttp, sfld, tfld, rnan) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + for (i = 0; i < nels; i++) { \ + if (unlikely(stp##_is_any_nan(xb.sfld))) { \ + if (stp##_is_signaling_nan(xb.sfld, &env->fp_status)) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 0); \ + xt.tfld = rnan; \ + } else { \ + xt.tfld = stp##_to_##ttp##_round_to_zero(xb.sfld, \ + &env->fp_status); \ + if (env->fp_status.float_exception_flags & float_flag_invalid) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 0); \ + } \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + float_check_status(env); \ +} + +VSX_CVT_FP_TO_INT(xscvdpsxds, 1, float64, int64, VsrD(0), VsrD(0), \ + 0x8000000000000000ULL) +VSX_CVT_FP_TO_INT(xscvdpsxws, 1, float64, int32, VsrD(0), VsrW(1), \ + 0x80000000U) +VSX_CVT_FP_TO_INT(xscvdpuxds, 1, float64, uint64, VsrD(0), VsrD(0), 0ULL) +VSX_CVT_FP_TO_INT(xscvdpuxws, 1, float64, uint32, VsrD(0), VsrW(1), 0U) +VSX_CVT_FP_TO_INT(xvcvdpsxds, 2, float64, int64, VsrD(i), VsrD(i), \ + 0x8000000000000000ULL) +VSX_CVT_FP_TO_INT(xvcvdpsxws, 2, float64, int32, VsrD(i), VsrW(2*i), \ + 0x80000000U) +VSX_CVT_FP_TO_INT(xvcvdpuxds, 2, float64, uint64, VsrD(i), VsrD(i), 0ULL) +VSX_CVT_FP_TO_INT(xvcvdpuxws, 2, float64, uint32, VsrD(i), VsrW(2*i), 0U) +VSX_CVT_FP_TO_INT(xvcvspsxds, 2, float32, int64, VsrW(2*i), VsrD(i), \ + 0x8000000000000000ULL) +VSX_CVT_FP_TO_INT(xvcvspsxws, 4, float32, int32, VsrW(i), VsrW(i), 0x80000000U) +VSX_CVT_FP_TO_INT(xvcvspuxds, 2, float32, uint64, VsrW(2*i), VsrD(i), 0ULL) +VSX_CVT_FP_TO_INT(xvcvspuxws, 4, float32, uint32, VsrW(i), VsrW(i), 0U) + +/* VSX_CVT_INT_TO_FP - VSX integer to floating point conversion + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * stp - source type (int32, uint32, int64 or uint64) + * ttp - target type (float32 or float64) + * sfld - source vsr_t field + * tfld - target vsr_t field + * jdef - definition of the j index (i or 2*i) + * sfprf - set FPRF + */ +#define VSX_CVT_INT_TO_FP(op, nels, stp, ttp, sfld, tfld, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + for (i = 0; i < nels; i++) { \ + xt.tfld = stp##_to_##ttp(xb.sfld, &env->fp_status); \ + if (r2sp) { \ + xt.tfld = helper_frsp(env, xt.tfld); \ + } \ + if (sfprf) { \ + helper_compute_fprf(env, xt.tfld); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + float_check_status(env); \ +} + +VSX_CVT_INT_TO_FP(xscvsxddp, 1, int64, float64, VsrD(0), VsrD(0), 1, 0) +VSX_CVT_INT_TO_FP(xscvuxddp, 1, uint64, float64, VsrD(0), VsrD(0), 1, 0) +VSX_CVT_INT_TO_FP(xscvsxdsp, 1, int64, float64, VsrD(0), VsrD(0), 1, 1) +VSX_CVT_INT_TO_FP(xscvuxdsp, 1, uint64, float64, VsrD(0), VsrD(0), 1, 1) +VSX_CVT_INT_TO_FP(xvcvsxddp, 2, int64, float64, VsrD(i), VsrD(i), 0, 0) +VSX_CVT_INT_TO_FP(xvcvuxddp, 2, uint64, float64, VsrD(i), VsrD(i), 0, 0) +VSX_CVT_INT_TO_FP(xvcvsxwdp, 2, int32, float64, VsrW(2*i), VsrD(i), 0, 0) +VSX_CVT_INT_TO_FP(xvcvuxwdp, 2, uint64, float64, VsrW(2*i), VsrD(i), 0, 0) +VSX_CVT_INT_TO_FP(xvcvsxdsp, 2, int64, float32, VsrD(i), VsrW(2*i), 0, 0) +VSX_CVT_INT_TO_FP(xvcvuxdsp, 2, uint64, float32, VsrD(i), VsrW(2*i), 0, 0) +VSX_CVT_INT_TO_FP(xvcvsxwsp, 4, int32, float32, VsrW(i), VsrW(i), 0, 0) +VSX_CVT_INT_TO_FP(xvcvuxwsp, 4, uint32, float32, VsrW(i), VsrW(i), 0, 0) + +/* For "use current rounding mode", define a value that will not be one of + * the existing rounding model enums. + */ +#define FLOAT_ROUND_CURRENT (float_round_nearest_even + float_round_down + \ + float_round_up + float_round_to_zero) + +/* VSX_ROUND - VSX floating point round + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (VsrD(*) or VsrW(*)) + * rmode - rounding mode + * sfprf - set FPRF + */ +#define VSX_ROUND(op, nels, tp, fld, rmode, sfprf) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + if (rmode != FLOAT_ROUND_CURRENT) { \ + set_float_rounding_mode(rmode, &env->fp_status); \ + } \ + \ + for (i = 0; i < nels; i++) { \ + if (unlikely(tp##_is_signaling_nan(xb.fld, \ + &env->fp_status))) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + xt.fld = tp##_snan_to_qnan(xb.fld); \ + } else { \ + xt.fld = tp##_round_to_int(xb.fld, &env->fp_status); \ + } \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld); \ + } \ + } \ + \ + /* If this is not a "use current rounding mode" instruction, \ + * then inhibit setting of the XX bit and restore rounding \ + * mode from FPSCR */ \ + if (rmode != FLOAT_ROUND_CURRENT) { \ + fpscr_set_rounding_mode(env); \ + env->fp_status.float_exception_flags &= ~float_flag_inexact; \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + float_check_status(env); \ +} + +VSX_ROUND(xsrdpi, 1, float64, VsrD(0), float_round_ties_away, 1) +VSX_ROUND(xsrdpic, 1, float64, VsrD(0), FLOAT_ROUND_CURRENT, 1) +VSX_ROUND(xsrdpim, 1, float64, VsrD(0), float_round_down, 1) +VSX_ROUND(xsrdpip, 1, float64, VsrD(0), float_round_up, 1) +VSX_ROUND(xsrdpiz, 1, float64, VsrD(0), float_round_to_zero, 1) + +VSX_ROUND(xvrdpi, 2, float64, VsrD(i), float_round_ties_away, 0) +VSX_ROUND(xvrdpic, 2, float64, VsrD(i), FLOAT_ROUND_CURRENT, 0) +VSX_ROUND(xvrdpim, 2, float64, VsrD(i), float_round_down, 0) +VSX_ROUND(xvrdpip, 2, float64, VsrD(i), float_round_up, 0) +VSX_ROUND(xvrdpiz, 2, float64, VsrD(i), float_round_to_zero, 0) + +VSX_ROUND(xvrspi, 4, float32, VsrW(i), float_round_ties_away, 0) +VSX_ROUND(xvrspic, 4, float32, VsrW(i), FLOAT_ROUND_CURRENT, 0) +VSX_ROUND(xvrspim, 4, float32, VsrW(i), float_round_down, 0) +VSX_ROUND(xvrspip, 4, float32, VsrW(i), float_round_up, 0) +VSX_ROUND(xvrspiz, 4, float32, VsrW(i), float_round_to_zero, 0) + +uint64_t helper_xsrsp(CPUPPCState *env, uint64_t xb) +{ + helper_reset_fpstatus(env); + + uint64_t xt = helper_frsp(env, xb); + + helper_compute_fprf(env, xt); + float_check_status(env); + return xt; +} diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c new file mode 100644 index 0000000000..7a338136a8 --- /dev/null +++ b/target/ppc/gdbstub.c @@ -0,0 +1,321 @@ +/* + * PowerPC 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 "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" +#include "exec/gdbstub.h" + +static int ppc_gdb_register_len_apple(int n) +{ + switch (n) { + case 0 ... 31: + /* gprs */ + return 8; + case 32 ... 63: + /* fprs */ + return 8; + case 64 ... 95: + return 16; + case 64+32: /* nip */ + case 65+32: /* msr */ + case 67+32: /* lr */ + case 68+32: /* ctr */ + case 69+32: /* xer */ + case 70+32: /* fpscr */ + return 8; + case 66+32: /* cr */ + return 4; + default: + return 0; + } +} + +static int ppc_gdb_register_len(int n) +{ + switch (n) { + case 0 ... 31: + /* gprs */ + return sizeof(target_ulong); + case 32 ... 63: + /* fprs */ + if (gdb_has_xml) { + return 0; + } + return 8; + case 66: + /* cr */ + return 4; + case 64: + /* nip */ + case 65: + /* msr */ + case 67: + /* lr */ + case 68: + /* ctr */ + case 69: + /* xer */ + return sizeof(target_ulong); + case 70: + /* fpscr */ + if (gdb_has_xml) { + return 0; + } + return sizeof(target_ulong); + default: + return 0; + } +} + +/* We need to present the registers to gdb in the "current" memory ordering. + For user-only mode we get this for free; TARGET_WORDS_BIGENDIAN is set to + the proper ordering for the binary, and cannot be changed. + For system mode, TARGET_WORDS_BIGENDIAN is always set, and we must check + the current mode of the chip to see if we're running in little-endian. */ +void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len) +{ +#ifndef CONFIG_USER_ONLY + if (!msr_le) { + /* do nothing */ + } else if (len == 4) { + bswap32s((uint32_t *)mem_buf); + } else if (len == 8) { + bswap64s((uint64_t *)mem_buf); + } else { + g_assert_not_reached(); + } +#endif +} + +/* Old gdb always expects FP registers. Newer (xml-aware) gdb only + * expects whatever the target description contains. Due to a + * historical mishap the FP registers appear in between core integer + * regs and PC, MSR, CR, and so forth. We hack round this by giving the + * FP regs zero size when talking to a newer gdb. + */ + +int ppc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + int r = ppc_gdb_register_len(n); + + if (!r) { + return r; + } + + if (n < 32) { + /* gprs */ + gdb_get_regl(mem_buf, env->gpr[n]); + } else if (n < 64) { + /* fprs */ + stfq_p(mem_buf, env->fpr[n-32]); + } else { + switch (n) { + case 64: + gdb_get_regl(mem_buf, env->nip); + break; + case 65: + gdb_get_regl(mem_buf, env->msr); + break; + case 66: + { + uint32_t cr = 0; + int i; + for (i = 0; i < 8; i++) { + cr |= env->crf[i] << (32 - ((i + 1) * 4)); + } + gdb_get_reg32(mem_buf, cr); + break; + } + case 67: + gdb_get_regl(mem_buf, env->lr); + break; + case 68: + gdb_get_regl(mem_buf, env->ctr); + break; + case 69: + gdb_get_regl(mem_buf, env->xer); + break; + case 70: + gdb_get_reg32(mem_buf, env->fpscr); + break; + } + } + ppc_maybe_bswap_register(env, mem_buf, r); + return r; +} + +int ppc_cpu_gdb_read_register_apple(CPUState *cs, uint8_t *mem_buf, int n) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + int r = ppc_gdb_register_len_apple(n); + + if (!r) { + return r; + } + + if (n < 32) { + /* gprs */ + gdb_get_reg64(mem_buf, env->gpr[n]); + } else if (n < 64) { + /* fprs */ + stfq_p(mem_buf, env->fpr[n-32]); + } else if (n < 96) { + /* Altivec */ + stq_p(mem_buf, n - 64); + stq_p(mem_buf + 8, 0); + } else { + switch (n) { + case 64 + 32: + gdb_get_reg64(mem_buf, env->nip); + break; + case 65 + 32: + gdb_get_reg64(mem_buf, env->msr); + break; + case 66 + 32: + { + uint32_t cr = 0; + int i; + for (i = 0; i < 8; i++) { + cr |= env->crf[i] << (32 - ((i + 1) * 4)); + } + gdb_get_reg32(mem_buf, cr); + break; + } + case 67 + 32: + gdb_get_reg64(mem_buf, env->lr); + break; + case 68 + 32: + gdb_get_reg64(mem_buf, env->ctr); + break; + case 69 + 32: + gdb_get_reg64(mem_buf, env->xer); + break; + case 70 + 32: + gdb_get_reg64(mem_buf, env->fpscr); + break; + } + } + ppc_maybe_bswap_register(env, mem_buf, r); + return r; +} + +int ppc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + int r = ppc_gdb_register_len(n); + + if (!r) { + return r; + } + ppc_maybe_bswap_register(env, mem_buf, r); + if (n < 32) { + /* gprs */ + env->gpr[n] = ldtul_p(mem_buf); + } else if (n < 64) { + /* fprs */ + env->fpr[n-32] = ldfq_p(mem_buf); + } else { + switch (n) { + case 64: + env->nip = ldtul_p(mem_buf); + break; + case 65: + ppc_store_msr(env, ldtul_p(mem_buf)); + break; + case 66: + { + uint32_t cr = ldl_p(mem_buf); + int i; + for (i = 0; i < 8; i++) { + env->crf[i] = (cr >> (32 - ((i + 1) * 4))) & 0xF; + } + break; + } + case 67: + env->lr = ldtul_p(mem_buf); + break; + case 68: + env->ctr = ldtul_p(mem_buf); + break; + case 69: + env->xer = ldtul_p(mem_buf); + break; + case 70: + /* fpscr */ + store_fpscr(env, ldtul_p(mem_buf), 0xffffffff); + break; + } + } + return r; +} +int ppc_cpu_gdb_write_register_apple(CPUState *cs, uint8_t *mem_buf, int n) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + int r = ppc_gdb_register_len_apple(n); + + if (!r) { + return r; + } + ppc_maybe_bswap_register(env, mem_buf, r); + if (n < 32) { + /* gprs */ + env->gpr[n] = ldq_p(mem_buf); + } else if (n < 64) { + /* fprs */ + env->fpr[n-32] = ldfq_p(mem_buf); + } else { + switch (n) { + case 64 + 32: + env->nip = ldq_p(mem_buf); + break; + case 65 + 32: + ppc_store_msr(env, ldq_p(mem_buf)); + break; + case 66 + 32: + { + uint32_t cr = ldl_p(mem_buf); + int i; + for (i = 0; i < 8; i++) { + env->crf[i] = (cr >> (32 - ((i + 1) * 4))) & 0xF; + } + break; + } + case 67 + 32: + env->lr = ldq_p(mem_buf); + break; + case 68 + 32: + env->ctr = ldq_p(mem_buf); + break; + case 69 + 32: + env->xer = ldq_p(mem_buf); + break; + case 70 + 32: + /* fpscr */ + store_fpscr(env, ldq_p(mem_buf), 0xffffffff); + break; + } + } + return r; +} diff --git a/target/ppc/helper.h b/target/ppc/helper.h new file mode 100644 index 0000000000..da00f0ab49 --- /dev/null +++ b/target/ppc/helper.h @@ -0,0 +1,742 @@ +DEF_HELPER_FLAGS_3(raise_exception_err, TCG_CALL_NO_WG, void, env, i32, i32) +DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_FLAGS_4(tw, TCG_CALL_NO_WG, void, env, tl, tl, i32) +#if defined(TARGET_PPC64) +DEF_HELPER_FLAGS_4(td, TCG_CALL_NO_WG, void, env, tl, tl, i32) +#endif +#if !defined(CONFIG_USER_ONLY) +DEF_HELPER_2(store_msr, void, env, tl) +DEF_HELPER_1(rfi, void, env) +DEF_HELPER_1(rfsvc, void, env) +DEF_HELPER_1(40x_rfci, void, env) +DEF_HELPER_1(rfci, void, env) +DEF_HELPER_1(rfdi, void, env) +DEF_HELPER_1(rfmci, void, env) +#if defined(TARGET_PPC64) +DEF_HELPER_2(pminsn, void, env, i32) +DEF_HELPER_1(rfid, void, env) +DEF_HELPER_1(hrfid, void, env) +DEF_HELPER_2(store_lpcr, void, env, tl) +#endif +DEF_HELPER_1(check_tlb_flush_local, void, env) +DEF_HELPER_1(check_tlb_flush_global, void, env) +#endif + +DEF_HELPER_3(lmw, void, env, tl, i32) +DEF_HELPER_FLAGS_3(stmw, TCG_CALL_NO_WG, void, env, tl, i32) +DEF_HELPER_4(lsw, void, env, tl, i32, i32) +DEF_HELPER_5(lswx, void, env, tl, i32, i32, i32) +DEF_HELPER_FLAGS_4(stsw, TCG_CALL_NO_WG, void, env, tl, i32, i32) +DEF_HELPER_FLAGS_3(dcbz, TCG_CALL_NO_WG, void, env, tl, i32) +DEF_HELPER_FLAGS_2(icbi, TCG_CALL_NO_WG, void, env, tl) +DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32) + +#if defined(TARGET_PPC64) +DEF_HELPER_4(divdeu, i64, env, i64, i64, i32) +DEF_HELPER_4(divde, i64, env, i64, i64, i32) +#endif +DEF_HELPER_4(divweu, tl, env, tl, tl, i32) +DEF_HELPER_4(divwe, tl, env, tl, tl, i32) + +DEF_HELPER_FLAGS_1(cntlzw, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(cnttzw, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_2(cmpb, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_3(sraw, tl, env, tl, tl) +#if defined(TARGET_PPC64) +DEF_HELPER_FLAGS_2(cmpeqb, TCG_CALL_NO_RWG_SE, i32, tl, tl) +DEF_HELPER_FLAGS_1(cntlzd, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(cnttzd, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(popcntd, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_2(bpermd, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_3(srad, tl, env, tl, tl) +DEF_HELPER_0(darn32, tl) +DEF_HELPER_0(darn64, tl) +#endif + +DEF_HELPER_FLAGS_1(cntlsw32, TCG_CALL_NO_RWG_SE, i32, i32) +DEF_HELPER_FLAGS_1(cntlzw32, TCG_CALL_NO_RWG_SE, i32, i32) +DEF_HELPER_FLAGS_2(brinc, TCG_CALL_NO_RWG_SE, tl, tl, tl) + +DEF_HELPER_1(float_check_status, void, env) +DEF_HELPER_1(reset_fpstatus, void, env) +DEF_HELPER_2(compute_fprf, void, env, i64) +DEF_HELPER_3(store_fpscr, void, env, i64, i32) +DEF_HELPER_2(fpscr_clrbit, void, env, i32) +DEF_HELPER_2(fpscr_setbit, void, env, i32) +DEF_HELPER_2(float64_to_float32, i32, env, i64) +DEF_HELPER_2(float32_to_float64, i64, env, i32) + +DEF_HELPER_4(fcmpo, void, env, i64, i64, i32) +DEF_HELPER_4(fcmpu, void, env, i64, i64, i32) + +DEF_HELPER_2(fctiw, i64, env, i64) +DEF_HELPER_2(fctiwu, i64, env, i64) +DEF_HELPER_2(fctiwz, i64, env, i64) +DEF_HELPER_2(fctiwuz, i64, env, i64) +DEF_HELPER_2(fcfid, i64, env, i64) +DEF_HELPER_2(fcfidu, i64, env, i64) +DEF_HELPER_2(fcfids, i64, env, i64) +DEF_HELPER_2(fcfidus, i64, env, i64) +DEF_HELPER_2(fctid, i64, env, i64) +DEF_HELPER_2(fctidu, i64, env, i64) +DEF_HELPER_2(fctidz, i64, env, i64) +DEF_HELPER_2(fctiduz, i64, env, i64) +DEF_HELPER_2(frsp, i64, env, i64) +DEF_HELPER_2(frin, i64, env, i64) +DEF_HELPER_2(friz, i64, env, i64) +DEF_HELPER_2(frip, i64, env, i64) +DEF_HELPER_2(frim, i64, env, i64) + +DEF_HELPER_3(fadd, i64, env, i64, i64) +DEF_HELPER_3(fsub, i64, env, i64, i64) +DEF_HELPER_3(fmul, i64, env, i64, i64) +DEF_HELPER_3(fdiv, i64, env, i64, i64) +DEF_HELPER_4(fmadd, i64, env, i64, i64, i64) +DEF_HELPER_4(fmsub, i64, env, i64, i64, i64) +DEF_HELPER_4(fnmadd, i64, env, i64, i64, i64) +DEF_HELPER_4(fnmsub, i64, env, i64, i64, i64) +DEF_HELPER_2(fsqrt, i64, env, i64) +DEF_HELPER_2(fre, i64, env, i64) +DEF_HELPER_2(fres, i64, env, i64) +DEF_HELPER_2(frsqrte, i64, env, i64) +DEF_HELPER_4(fsel, i64, env, i64, i64, i64) + +DEF_HELPER_FLAGS_2(ftdiv, TCG_CALL_NO_RWG_SE, i32, i64, i64) +DEF_HELPER_FLAGS_1(ftsqrt, TCG_CALL_NO_RWG_SE, i32, i64) + +#define dh_alias_avr ptr +#define dh_ctype_avr ppc_avr_t * +#define dh_is_signed_avr dh_is_signed_ptr + +DEF_HELPER_3(vaddubm, void, avr, avr, avr) +DEF_HELPER_3(vadduhm, void, avr, avr, avr) +DEF_HELPER_3(vadduwm, void, avr, avr, avr) +DEF_HELPER_3(vaddudm, void, avr, avr, avr) +DEF_HELPER_3(vsububm, void, avr, avr, avr) +DEF_HELPER_3(vsubuhm, void, avr, avr, avr) +DEF_HELPER_3(vsubuwm, void, avr, avr, avr) +DEF_HELPER_3(vsubudm, void, avr, avr, avr) +DEF_HELPER_3(vavgub, void, avr, avr, avr) +DEF_HELPER_3(vavguh, void, avr, avr, avr) +DEF_HELPER_3(vavguw, void, avr, avr, avr) +DEF_HELPER_3(vabsdub, void, avr, avr, avr) +DEF_HELPER_3(vabsduh, void, avr, avr, avr) +DEF_HELPER_3(vabsduw, void, avr, avr, avr) +DEF_HELPER_3(vavgsb, void, avr, avr, avr) +DEF_HELPER_3(vavgsh, void, avr, avr, avr) +DEF_HELPER_3(vavgsw, void, avr, avr, avr) +DEF_HELPER_3(vminsb, void, avr, avr, avr) +DEF_HELPER_3(vminsh, void, avr, avr, avr) +DEF_HELPER_3(vminsw, void, avr, avr, avr) +DEF_HELPER_3(vminsd, void, avr, avr, avr) +DEF_HELPER_3(vmaxsb, void, avr, avr, avr) +DEF_HELPER_3(vmaxsh, void, avr, avr, avr) +DEF_HELPER_3(vmaxsw, void, avr, avr, avr) +DEF_HELPER_3(vmaxsd, void, avr, avr, avr) +DEF_HELPER_3(vminub, void, avr, avr, avr) +DEF_HELPER_3(vminuh, void, avr, avr, avr) +DEF_HELPER_3(vminuw, void, avr, avr, avr) +DEF_HELPER_3(vminud, void, avr, avr, avr) +DEF_HELPER_3(vmaxub, void, avr, avr, avr) +DEF_HELPER_3(vmaxuh, void, avr, avr, avr) +DEF_HELPER_3(vmaxuw, void, avr, avr, avr) +DEF_HELPER_3(vmaxud, void, avr, avr, avr) +DEF_HELPER_4(vcmpequb, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpequh, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpequw, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpequd, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpneb, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpneh, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpnew, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpnezb, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpnezh, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpnezw, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtub, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtuh, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtuw, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtud, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsb, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsh, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsw, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsd, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpeqfp, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgefp, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtfp, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpbfp, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpequb_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpequh_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpequw_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpequd_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpneb_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpneh_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpnew_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpnezb_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpnezh_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpnezw_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtub_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtuh_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtuw_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtud_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsb_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsh_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsw_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsd_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpeqfp_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgefp_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtfp_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpbfp_dot, void, env, avr, avr, avr) +DEF_HELPER_3(vmrglb, void, avr, avr, avr) +DEF_HELPER_3(vmrglh, void, avr, avr, avr) +DEF_HELPER_3(vmrglw, void, avr, avr, avr) +DEF_HELPER_3(vmrghb, void, avr, avr, avr) +DEF_HELPER_3(vmrghh, void, avr, avr, avr) +DEF_HELPER_3(vmrghw, void, avr, avr, avr) +DEF_HELPER_3(vmulesb, void, avr, avr, avr) +DEF_HELPER_3(vmulesh, void, avr, avr, avr) +DEF_HELPER_3(vmulesw, void, avr, avr, avr) +DEF_HELPER_3(vmuleub, void, avr, avr, avr) +DEF_HELPER_3(vmuleuh, void, avr, avr, avr) +DEF_HELPER_3(vmuleuw, void, avr, avr, avr) +DEF_HELPER_3(vmulosb, void, avr, avr, avr) +DEF_HELPER_3(vmulosh, void, avr, avr, avr) +DEF_HELPER_3(vmulosw, void, avr, avr, avr) +DEF_HELPER_3(vmuloub, void, avr, avr, avr) +DEF_HELPER_3(vmulouh, void, avr, avr, avr) +DEF_HELPER_3(vmulouw, void, avr, avr, avr) +DEF_HELPER_3(vmuluwm, void, avr, avr, avr) +DEF_HELPER_3(vsrab, void, avr, avr, avr) +DEF_HELPER_3(vsrah, void, avr, avr, avr) +DEF_HELPER_3(vsraw, void, avr, avr, avr) +DEF_HELPER_3(vsrad, void, avr, avr, avr) +DEF_HELPER_3(vsrb, void, avr, avr, avr) +DEF_HELPER_3(vsrh, void, avr, avr, avr) +DEF_HELPER_3(vsrw, void, avr, avr, avr) +DEF_HELPER_3(vsrd, void, avr, avr, avr) +DEF_HELPER_3(vslb, void, avr, avr, avr) +DEF_HELPER_3(vslh, void, avr, avr, avr) +DEF_HELPER_3(vslw, void, avr, avr, avr) +DEF_HELPER_3(vsld, void, avr, avr, avr) +DEF_HELPER_3(vslo, void, avr, avr, avr) +DEF_HELPER_3(vsro, void, avr, avr, avr) +DEF_HELPER_3(vsrv, void, avr, avr, avr) +DEF_HELPER_3(vslv, void, avr, avr, avr) +DEF_HELPER_3(vaddcuw, void, avr, avr, avr) +DEF_HELPER_2(vprtybw, void, avr, avr) +DEF_HELPER_2(vprtybd, void, avr, avr) +DEF_HELPER_2(vprtybq, void, avr, avr) +DEF_HELPER_3(vsubcuw, void, avr, avr, avr) +DEF_HELPER_2(lvsl, void, avr, tl) +DEF_HELPER_2(lvsr, void, avr, tl) +DEF_HELPER_4(vaddsbs, void, env, avr, avr, avr) +DEF_HELPER_4(vaddshs, void, env, avr, avr, avr) +DEF_HELPER_4(vaddsws, void, env, avr, avr, avr) +DEF_HELPER_4(vsubsbs, void, env, avr, avr, avr) +DEF_HELPER_4(vsubshs, void, env, avr, avr, avr) +DEF_HELPER_4(vsubsws, void, env, avr, avr, avr) +DEF_HELPER_4(vaddubs, void, env, avr, avr, avr) +DEF_HELPER_4(vadduhs, void, env, avr, avr, avr) +DEF_HELPER_4(vadduws, void, env, avr, avr, avr) +DEF_HELPER_4(vsububs, void, env, avr, avr, avr) +DEF_HELPER_4(vsubuhs, void, env, avr, avr, avr) +DEF_HELPER_4(vsubuws, void, env, avr, avr, avr) +DEF_HELPER_3(vadduqm, void, avr, avr, avr) +DEF_HELPER_4(vaddecuq, void, avr, avr, avr, avr) +DEF_HELPER_4(vaddeuqm, void, avr, avr, avr, avr) +DEF_HELPER_3(vaddcuq, void, avr, avr, avr) +DEF_HELPER_3(vsubuqm, void, avr, avr, avr) +DEF_HELPER_4(vsubecuq, void, avr, avr, avr, avr) +DEF_HELPER_4(vsubeuqm, void, avr, avr, avr, avr) +DEF_HELPER_3(vsubcuq, void, avr, avr, avr) +DEF_HELPER_3(vrlb, void, avr, avr, avr) +DEF_HELPER_3(vrlh, void, avr, avr, avr) +DEF_HELPER_3(vrlw, void, avr, avr, avr) +DEF_HELPER_3(vrld, void, avr, avr, avr) +DEF_HELPER_3(vsl, void, avr, avr, avr) +DEF_HELPER_3(vsr, void, avr, avr, avr) +DEF_HELPER_4(vsldoi, void, avr, avr, avr, i32) +DEF_HELPER_2(vspltisb, void, avr, i32) +DEF_HELPER_2(vspltish, void, avr, i32) +DEF_HELPER_2(vspltisw, void, avr, i32) +DEF_HELPER_3(vspltb, void, avr, avr, i32) +DEF_HELPER_3(vsplth, void, avr, avr, i32) +DEF_HELPER_3(vspltw, void, avr, avr, i32) +DEF_HELPER_3(vextractub, void, avr, avr, i32) +DEF_HELPER_3(vextractuh, void, avr, avr, i32) +DEF_HELPER_3(vextractuw, void, avr, avr, i32) +DEF_HELPER_3(vextractd, void, avr, avr, i32) +DEF_HELPER_3(vinsertb, void, avr, avr, i32) +DEF_HELPER_3(vinserth, void, avr, avr, i32) +DEF_HELPER_3(vinsertw, void, avr, avr, i32) +DEF_HELPER_3(vinsertd, void, avr, avr, i32) +DEF_HELPER_2(vextsb2w, void, avr, avr) +DEF_HELPER_2(vextsh2w, void, avr, avr) +DEF_HELPER_2(vextsb2d, void, avr, avr) +DEF_HELPER_2(vextsh2d, void, avr, avr) +DEF_HELPER_2(vextsw2d, void, avr, avr) +DEF_HELPER_2(vnegw, void, avr, avr) +DEF_HELPER_2(vnegd, void, avr, avr) +DEF_HELPER_2(vupkhpx, void, avr, avr) +DEF_HELPER_2(vupklpx, void, avr, avr) +DEF_HELPER_2(vupkhsb, void, avr, avr) +DEF_HELPER_2(vupkhsh, void, avr, avr) +DEF_HELPER_2(vupkhsw, void, avr, avr) +DEF_HELPER_2(vupklsb, void, avr, avr) +DEF_HELPER_2(vupklsh, void, avr, avr) +DEF_HELPER_2(vupklsw, void, avr, avr) +DEF_HELPER_5(vmsumubm, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vmsummbm, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vsel, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vperm, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vpermr, void, env, avr, avr, avr, avr) +DEF_HELPER_4(vpkshss, void, env, avr, avr, avr) +DEF_HELPER_4(vpkshus, void, env, avr, avr, avr) +DEF_HELPER_4(vpkswss, void, env, avr, avr, avr) +DEF_HELPER_4(vpkswus, void, env, avr, avr, avr) +DEF_HELPER_4(vpksdss, void, env, avr, avr, avr) +DEF_HELPER_4(vpksdus, void, env, avr, avr, avr) +DEF_HELPER_4(vpkuhus, void, env, avr, avr, avr) +DEF_HELPER_4(vpkuwus, void, env, avr, avr, avr) +DEF_HELPER_4(vpkudus, void, env, avr, avr, avr) +DEF_HELPER_4(vpkuhum, void, env, avr, avr, avr) +DEF_HELPER_4(vpkuwum, void, env, avr, avr, avr) +DEF_HELPER_4(vpkudum, void, env, avr, avr, avr) +DEF_HELPER_3(vpkpx, void, avr, avr, avr) +DEF_HELPER_5(vmhaddshs, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vmhraddshs, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vmsumuhm, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vmsumuhs, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vmsumshm, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vmsumshs, void, env, avr, avr, avr, avr) +DEF_HELPER_4(vmladduhm, void, avr, avr, avr, avr) +DEF_HELPER_2(mtvscr, void, env, avr) +DEF_HELPER_3(lvebx, void, env, avr, tl) +DEF_HELPER_3(lvehx, void, env, avr, tl) +DEF_HELPER_3(lvewx, void, env, avr, tl) +DEF_HELPER_3(stvebx, void, env, avr, tl) +DEF_HELPER_3(stvehx, void, env, avr, tl) +DEF_HELPER_3(stvewx, void, env, avr, tl) +DEF_HELPER_4(vsumsws, void, env, avr, avr, avr) +DEF_HELPER_4(vsum2sws, void, env, avr, avr, avr) +DEF_HELPER_4(vsum4sbs, void, env, avr, avr, avr) +DEF_HELPER_4(vsum4shs, void, env, avr, avr, avr) +DEF_HELPER_4(vsum4ubs, void, env, avr, avr, avr) +DEF_HELPER_4(vaddfp, void, env, avr, avr, avr) +DEF_HELPER_4(vsubfp, void, env, avr, avr, avr) +DEF_HELPER_4(vmaxfp, void, env, avr, avr, avr) +DEF_HELPER_4(vminfp, void, env, avr, avr, avr) +DEF_HELPER_3(vrefp, void, env, avr, avr) +DEF_HELPER_3(vrsqrtefp, void, env, avr, avr) +DEF_HELPER_3(vrlwmi, void, avr, avr, avr) +DEF_HELPER_3(vrldmi, void, avr, avr, avr) +DEF_HELPER_3(vrldnm, void, avr, avr, avr) +DEF_HELPER_3(vrlwnm, void, avr, avr, avr) +DEF_HELPER_5(vmaddfp, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vnmsubfp, void, env, avr, avr, avr, avr) +DEF_HELPER_3(vexptefp, void, env, avr, avr) +DEF_HELPER_3(vlogefp, void, env, avr, avr) +DEF_HELPER_3(vrfim, void, env, avr, avr) +DEF_HELPER_3(vrfin, void, env, avr, avr) +DEF_HELPER_3(vrfip, void, env, avr, avr) +DEF_HELPER_3(vrfiz, void, env, avr, avr) +DEF_HELPER_4(vcfux, void, env, avr, avr, i32) +DEF_HELPER_4(vcfsx, void, env, avr, avr, i32) +DEF_HELPER_4(vctuxs, void, env, avr, avr, i32) +DEF_HELPER_4(vctsxs, void, env, avr, avr, i32) + +DEF_HELPER_2(vclzb, void, avr, avr) +DEF_HELPER_2(vclzh, void, avr, avr) +DEF_HELPER_2(vclzw, void, avr, avr) +DEF_HELPER_2(vclzd, void, avr, avr) +DEF_HELPER_2(vctzb, void, avr, avr) +DEF_HELPER_2(vctzh, void, avr, avr) +DEF_HELPER_2(vctzw, void, avr, avr) +DEF_HELPER_2(vctzd, void, avr, avr) +DEF_HELPER_2(vpopcntb, void, avr, avr) +DEF_HELPER_2(vpopcnth, void, avr, avr) +DEF_HELPER_2(vpopcntw, void, avr, avr) +DEF_HELPER_2(vpopcntd, void, avr, avr) +DEF_HELPER_1(vclzlsbb, tl, avr) +DEF_HELPER_1(vctzlsbb, tl, avr) +DEF_HELPER_3(vbpermd, void, avr, avr, avr) +DEF_HELPER_3(vbpermq, void, avr, avr, avr) +DEF_HELPER_2(vgbbd, void, avr, avr) +DEF_HELPER_3(vpmsumb, void, avr, avr, avr) +DEF_HELPER_3(vpmsumh, void, avr, avr, avr) +DEF_HELPER_3(vpmsumw, void, avr, avr, avr) +DEF_HELPER_3(vpmsumd, void, avr, avr, avr) + +DEF_HELPER_2(vsbox, void, avr, avr) +DEF_HELPER_3(vcipher, void, avr, avr, avr) +DEF_HELPER_3(vcipherlast, void, avr, avr, avr) +DEF_HELPER_3(vncipher, void, avr, avr, avr) +DEF_HELPER_3(vncipherlast, void, avr, avr, avr) +DEF_HELPER_3(vshasigmaw, void, avr, avr, i32) +DEF_HELPER_3(vshasigmad, void, avr, avr, i32) +DEF_HELPER_4(vpermxor, void, avr, avr, avr, avr) + +DEF_HELPER_4(bcdadd, i32, avr, avr, avr, i32) +DEF_HELPER_4(bcdsub, i32, avr, avr, avr, i32) +DEF_HELPER_3(bcdcfn, i32, avr, avr, i32) +DEF_HELPER_3(bcdctn, i32, avr, avr, i32) +DEF_HELPER_3(bcdcfz, i32, avr, avr, i32) +DEF_HELPER_3(bcdctz, i32, avr, avr, i32) + +DEF_HELPER_2(xsadddp, void, env, i32) +DEF_HELPER_2(xssubdp, void, env, i32) +DEF_HELPER_2(xsmuldp, void, env, i32) +DEF_HELPER_2(xsdivdp, void, env, i32) +DEF_HELPER_2(xsredp, void, env, i32) +DEF_HELPER_2(xssqrtdp, void, env, i32) +DEF_HELPER_2(xsrsqrtedp, void, env, i32) +DEF_HELPER_2(xstdivdp, void, env, i32) +DEF_HELPER_2(xstsqrtdp, void, env, i32) +DEF_HELPER_2(xsmaddadp, void, env, i32) +DEF_HELPER_2(xsmaddmdp, void, env, i32) +DEF_HELPER_2(xsmsubadp, void, env, i32) +DEF_HELPER_2(xsmsubmdp, void, env, i32) +DEF_HELPER_2(xsnmaddadp, void, env, i32) +DEF_HELPER_2(xsnmaddmdp, void, env, i32) +DEF_HELPER_2(xsnmsubadp, void, env, i32) +DEF_HELPER_2(xsnmsubmdp, void, env, i32) +DEF_HELPER_2(xscmpeqdp, void, env, i32) +DEF_HELPER_2(xscmpgtdp, void, env, i32) +DEF_HELPER_2(xscmpgedp, void, env, i32) +DEF_HELPER_2(xscmpnedp, void, env, i32) +DEF_HELPER_2(xscmpodp, void, env, i32) +DEF_HELPER_2(xscmpudp, void, env, i32) +DEF_HELPER_2(xsmaxdp, void, env, i32) +DEF_HELPER_2(xsmindp, void, env, i32) +DEF_HELPER_2(xscvdpsp, void, env, i32) +DEF_HELPER_2(xscvdpspn, i64, env, i64) +DEF_HELPER_2(xscvspdp, void, env, i32) +DEF_HELPER_2(xscvspdpn, i64, env, i64) +DEF_HELPER_2(xscvdpsxds, void, env, i32) +DEF_HELPER_2(xscvdpsxws, void, env, i32) +DEF_HELPER_2(xscvdpuxds, void, env, i32) +DEF_HELPER_2(xscvdpuxws, void, env, i32) +DEF_HELPER_2(xscvsxddp, void, env, i32) +DEF_HELPER_2(xscvuxdsp, void, env, i32) +DEF_HELPER_2(xscvsxdsp, void, env, i32) +DEF_HELPER_2(xscvuxddp, void, env, i32) +DEF_HELPER_2(xsrdpi, void, env, i32) +DEF_HELPER_2(xsrdpic, void, env, i32) +DEF_HELPER_2(xsrdpim, void, env, i32) +DEF_HELPER_2(xsrdpip, void, env, i32) +DEF_HELPER_2(xsrdpiz, void, env, i32) + +DEF_HELPER_2(xsaddsp, void, env, i32) +DEF_HELPER_2(xssubsp, void, env, i32) +DEF_HELPER_2(xsmulsp, void, env, i32) +DEF_HELPER_2(xsdivsp, void, env, i32) +DEF_HELPER_2(xsresp, void, env, i32) +DEF_HELPER_2(xsrsp, i64, env, i64) +DEF_HELPER_2(xssqrtsp, void, env, i32) +DEF_HELPER_2(xsrsqrtesp, void, env, i32) +DEF_HELPER_2(xsmaddasp, void, env, i32) +DEF_HELPER_2(xsmaddmsp, void, env, i32) +DEF_HELPER_2(xsmsubasp, void, env, i32) +DEF_HELPER_2(xsmsubmsp, void, env, i32) +DEF_HELPER_2(xsnmaddasp, void, env, i32) +DEF_HELPER_2(xsnmaddmsp, void, env, i32) +DEF_HELPER_2(xsnmsubasp, void, env, i32) +DEF_HELPER_2(xsnmsubmsp, void, env, i32) + +DEF_HELPER_2(xvadddp, void, env, i32) +DEF_HELPER_2(xvsubdp, void, env, i32) +DEF_HELPER_2(xvmuldp, void, env, i32) +DEF_HELPER_2(xvdivdp, void, env, i32) +DEF_HELPER_2(xvredp, void, env, i32) +DEF_HELPER_2(xvsqrtdp, void, env, i32) +DEF_HELPER_2(xvrsqrtedp, void, env, i32) +DEF_HELPER_2(xvtdivdp, void, env, i32) +DEF_HELPER_2(xvtsqrtdp, void, env, i32) +DEF_HELPER_2(xvmaddadp, void, env, i32) +DEF_HELPER_2(xvmaddmdp, void, env, i32) +DEF_HELPER_2(xvmsubadp, void, env, i32) +DEF_HELPER_2(xvmsubmdp, void, env, i32) +DEF_HELPER_2(xvnmaddadp, void, env, i32) +DEF_HELPER_2(xvnmaddmdp, void, env, i32) +DEF_HELPER_2(xvnmsubadp, void, env, i32) +DEF_HELPER_2(xvnmsubmdp, void, env, i32) +DEF_HELPER_2(xvmaxdp, void, env, i32) +DEF_HELPER_2(xvmindp, void, env, i32) +DEF_HELPER_2(xvcmpeqdp, void, env, i32) +DEF_HELPER_2(xvcmpgedp, void, env, i32) +DEF_HELPER_2(xvcmpgtdp, void, env, i32) +DEF_HELPER_2(xvcmpnedp, void, env, i32) +DEF_HELPER_2(xvcvdpsp, void, env, i32) +DEF_HELPER_2(xvcvdpsxds, void, env, i32) +DEF_HELPER_2(xvcvdpsxws, void, env, i32) +DEF_HELPER_2(xvcvdpuxds, void, env, i32) +DEF_HELPER_2(xvcvdpuxws, void, env, i32) +DEF_HELPER_2(xvcvsxddp, void, env, i32) +DEF_HELPER_2(xvcvuxddp, void, env, i32) +DEF_HELPER_2(xvcvsxwdp, void, env, i32) +DEF_HELPER_2(xvcvuxwdp, void, env, i32) +DEF_HELPER_2(xvrdpi, void, env, i32) +DEF_HELPER_2(xvrdpic, void, env, i32) +DEF_HELPER_2(xvrdpim, void, env, i32) +DEF_HELPER_2(xvrdpip, void, env, i32) +DEF_HELPER_2(xvrdpiz, void, env, i32) + +DEF_HELPER_2(xvaddsp, void, env, i32) +DEF_HELPER_2(xvsubsp, void, env, i32) +DEF_HELPER_2(xvmulsp, void, env, i32) +DEF_HELPER_2(xvdivsp, void, env, i32) +DEF_HELPER_2(xvresp, void, env, i32) +DEF_HELPER_2(xvsqrtsp, void, env, i32) +DEF_HELPER_2(xvrsqrtesp, void, env, i32) +DEF_HELPER_2(xvtdivsp, void, env, i32) +DEF_HELPER_2(xvtsqrtsp, void, env, i32) +DEF_HELPER_2(xvmaddasp, void, env, i32) +DEF_HELPER_2(xvmaddmsp, void, env, i32) +DEF_HELPER_2(xvmsubasp, void, env, i32) +DEF_HELPER_2(xvmsubmsp, void, env, i32) +DEF_HELPER_2(xvnmaddasp, void, env, i32) +DEF_HELPER_2(xvnmaddmsp, void, env, i32) +DEF_HELPER_2(xvnmsubasp, void, env, i32) +DEF_HELPER_2(xvnmsubmsp, void, env, i32) +DEF_HELPER_2(xvmaxsp, void, env, i32) +DEF_HELPER_2(xvminsp, void, env, i32) +DEF_HELPER_2(xvcmpeqsp, void, env, i32) +DEF_HELPER_2(xvcmpgesp, void, env, i32) +DEF_HELPER_2(xvcmpgtsp, void, env, i32) +DEF_HELPER_2(xvcmpnesp, void, env, i32) +DEF_HELPER_2(xvcvspdp, void, env, i32) +DEF_HELPER_2(xvcvspsxds, void, env, i32) +DEF_HELPER_2(xvcvspsxws, void, env, i32) +DEF_HELPER_2(xvcvspuxds, void, env, i32) +DEF_HELPER_2(xvcvspuxws, void, env, i32) +DEF_HELPER_2(xvcvsxdsp, void, env, i32) +DEF_HELPER_2(xvcvuxdsp, void, env, i32) +DEF_HELPER_2(xvcvsxwsp, void, env, i32) +DEF_HELPER_2(xvcvuxwsp, void, env, i32) +DEF_HELPER_2(xvrspi, void, env, i32) +DEF_HELPER_2(xvrspic, void, env, i32) +DEF_HELPER_2(xvrspim, void, env, i32) +DEF_HELPER_2(xvrspip, void, env, i32) +DEF_HELPER_2(xvrspiz, void, env, i32) + +DEF_HELPER_2(efscfsi, i32, env, i32) +DEF_HELPER_2(efscfui, i32, env, i32) +DEF_HELPER_2(efscfuf, i32, env, i32) +DEF_HELPER_2(efscfsf, i32, env, i32) +DEF_HELPER_2(efsctsi, i32, env, i32) +DEF_HELPER_2(efsctui, i32, env, i32) +DEF_HELPER_2(efsctsiz, i32, env, i32) +DEF_HELPER_2(efsctuiz, i32, env, i32) +DEF_HELPER_2(efsctsf, i32, env, i32) +DEF_HELPER_2(efsctuf, i32, env, i32) +DEF_HELPER_2(evfscfsi, i64, env, i64) +DEF_HELPER_2(evfscfui, i64, env, i64) +DEF_HELPER_2(evfscfuf, i64, env, i64) +DEF_HELPER_2(evfscfsf, i64, env, i64) +DEF_HELPER_2(evfsctsi, i64, env, i64) +DEF_HELPER_2(evfsctui, i64, env, i64) +DEF_HELPER_2(evfsctsiz, i64, env, i64) +DEF_HELPER_2(evfsctuiz, i64, env, i64) +DEF_HELPER_2(evfsctsf, i64, env, i64) +DEF_HELPER_2(evfsctuf, i64, env, i64) +DEF_HELPER_3(efsadd, i32, env, i32, i32) +DEF_HELPER_3(efssub, i32, env, i32, i32) +DEF_HELPER_3(efsmul, i32, env, i32, i32) +DEF_HELPER_3(efsdiv, i32, env, i32, i32) +DEF_HELPER_3(evfsadd, i64, env, i64, i64) +DEF_HELPER_3(evfssub, i64, env, i64, i64) +DEF_HELPER_3(evfsmul, i64, env, i64, i64) +DEF_HELPER_3(evfsdiv, i64, env, i64, i64) +DEF_HELPER_3(efststlt, i32, env, i32, i32) +DEF_HELPER_3(efststgt, i32, env, i32, i32) +DEF_HELPER_3(efststeq, i32, env, i32, i32) +DEF_HELPER_3(efscmplt, i32, env, i32, i32) +DEF_HELPER_3(efscmpgt, i32, env, i32, i32) +DEF_HELPER_3(efscmpeq, i32, env, i32, i32) +DEF_HELPER_3(evfststlt, i32, env, i64, i64) +DEF_HELPER_3(evfststgt, i32, env, i64, i64) +DEF_HELPER_3(evfststeq, i32, env, i64, i64) +DEF_HELPER_3(evfscmplt, i32, env, i64, i64) +DEF_HELPER_3(evfscmpgt, i32, env, i64, i64) +DEF_HELPER_3(evfscmpeq, i32, env, i64, i64) +DEF_HELPER_2(efdcfsi, i64, env, i32) +DEF_HELPER_2(efdcfsid, i64, env, i64) +DEF_HELPER_2(efdcfui, i64, env, i32) +DEF_HELPER_2(efdcfuid, i64, env, i64) +DEF_HELPER_2(efdctsi, i32, env, i64) +DEF_HELPER_2(efdctui, i32, env, i64) +DEF_HELPER_2(efdctsiz, i32, env, i64) +DEF_HELPER_2(efdctsidz, i64, env, i64) +DEF_HELPER_2(efdctuiz, i32, env, i64) +DEF_HELPER_2(efdctuidz, i64, env, i64) +DEF_HELPER_2(efdcfsf, i64, env, i32) +DEF_HELPER_2(efdcfuf, i64, env, i32) +DEF_HELPER_2(efdctsf, i32, env, i64) +DEF_HELPER_2(efdctuf, i32, env, i64) +DEF_HELPER_2(efscfd, i32, env, i64) +DEF_HELPER_2(efdcfs, i64, env, i32) +DEF_HELPER_3(efdadd, i64, env, i64, i64) +DEF_HELPER_3(efdsub, i64, env, i64, i64) +DEF_HELPER_3(efdmul, i64, env, i64, i64) +DEF_HELPER_3(efddiv, i64, env, i64, i64) +DEF_HELPER_3(efdtstlt, i32, env, i64, i64) +DEF_HELPER_3(efdtstgt, i32, env, i64, i64) +DEF_HELPER_3(efdtsteq, i32, env, i64, i64) +DEF_HELPER_3(efdcmplt, i32, env, i64, i64) +DEF_HELPER_3(efdcmpgt, i32, env, i64, i64) +DEF_HELPER_3(efdcmpeq, i32, env, i64, i64) + +#if !defined(CONFIG_USER_ONLY) +DEF_HELPER_2(4xx_tlbre_hi, tl, env, tl) +DEF_HELPER_2(4xx_tlbre_lo, tl, env, tl) +DEF_HELPER_3(4xx_tlbwe_hi, void, env, tl, tl) +DEF_HELPER_3(4xx_tlbwe_lo, void, env, tl, tl) +DEF_HELPER_2(4xx_tlbsx, tl, env, tl) +DEF_HELPER_3(440_tlbre, tl, env, i32, tl) +DEF_HELPER_4(440_tlbwe, void, env, i32, tl, tl) +DEF_HELPER_2(440_tlbsx, tl, env, tl) +DEF_HELPER_1(booke206_tlbre, void, env) +DEF_HELPER_1(booke206_tlbwe, void, env) +DEF_HELPER_2(booke206_tlbsx, void, env, tl) +DEF_HELPER_2(booke206_tlbivax, void, env, tl) +DEF_HELPER_2(booke206_tlbilx0, void, env, tl) +DEF_HELPER_2(booke206_tlbilx1, void, env, tl) +DEF_HELPER_2(booke206_tlbilx3, void, env, tl) +DEF_HELPER_2(booke206_tlbflush, void, env, tl) +DEF_HELPER_3(booke_setpid, void, env, i32, tl) +DEF_HELPER_2(6xx_tlbd, void, env, tl) +DEF_HELPER_2(6xx_tlbi, void, env, tl) +DEF_HELPER_2(74xx_tlbd, void, env, tl) +DEF_HELPER_2(74xx_tlbi, void, env, tl) +DEF_HELPER_FLAGS_1(tlbia, TCG_CALL_NO_RWG, void, env) +DEF_HELPER_FLAGS_2(tlbie, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(tlbiva, TCG_CALL_NO_RWG, void, env, tl) +#if defined(TARGET_PPC64) +DEF_HELPER_FLAGS_3(store_slb, TCG_CALL_NO_RWG, void, env, tl, tl) +DEF_HELPER_2(load_slb_esid, tl, env, tl) +DEF_HELPER_2(load_slb_vsid, tl, env, tl) +DEF_HELPER_2(find_slb_vsid, tl, env, tl) +DEF_HELPER_FLAGS_1(slbia, TCG_CALL_NO_RWG, void, env) +DEF_HELPER_FLAGS_2(slbie, TCG_CALL_NO_RWG, void, env, tl) +#endif +DEF_HELPER_FLAGS_2(load_sr, TCG_CALL_NO_RWG, tl, env, tl) +DEF_HELPER_FLAGS_3(store_sr, TCG_CALL_NO_RWG, void, env, tl, tl) + +DEF_HELPER_FLAGS_1(602_mfrom, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_1(msgsnd, void, tl) +DEF_HELPER_2(msgclr, void, env, tl) +#endif + +DEF_HELPER_4(dlmzb, tl, env, tl, tl, i32) +DEF_HELPER_FLAGS_2(clcs, TCG_CALL_NO_RWG_SE, tl, env, i32) +#if !defined(CONFIG_USER_ONLY) +DEF_HELPER_2(rac, tl, env, tl) +#endif +DEF_HELPER_3(div, tl, env, tl, tl) +DEF_HELPER_3(divo, tl, env, tl, tl) +DEF_HELPER_3(divs, tl, env, tl, tl) +DEF_HELPER_3(divso, tl, env, tl, tl) + +DEF_HELPER_2(load_dcr, tl, env, tl) +DEF_HELPER_3(store_dcr, void, env, tl, tl) + +DEF_HELPER_2(load_dump_spr, void, env, i32) +DEF_HELPER_2(store_dump_spr, void, env, i32) +DEF_HELPER_4(fscr_facility_check, void, env, i32, i32, i32) +DEF_HELPER_4(msr_facility_check, void, env, i32, i32, i32) +DEF_HELPER_FLAGS_1(load_tbl, TCG_CALL_NO_RWG, tl, env) +DEF_HELPER_FLAGS_1(load_tbu, TCG_CALL_NO_RWG, tl, env) +DEF_HELPER_FLAGS_1(load_atbl, TCG_CALL_NO_RWG, tl, env) +DEF_HELPER_FLAGS_1(load_atbu, TCG_CALL_NO_RWG, tl, env) +DEF_HELPER_FLAGS_1(load_601_rtcl, TCG_CALL_NO_RWG, tl, env) +DEF_HELPER_FLAGS_1(load_601_rtcu, TCG_CALL_NO_RWG, tl, env) +#if !defined(CONFIG_USER_ONLY) +#if defined(TARGET_PPC64) +DEF_HELPER_FLAGS_1(load_purr, TCG_CALL_NO_RWG, tl, env) +#endif +DEF_HELPER_2(store_sdr1, void, env, tl) +DEF_HELPER_FLAGS_2(store_tbl, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(store_tbu, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(store_atbl, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(store_atbu, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(store_601_rtcl, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(store_601_rtcu, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_1(load_decr, TCG_CALL_NO_RWG, tl, env) +DEF_HELPER_FLAGS_2(store_decr, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_1(load_hdecr, TCG_CALL_NO_RWG, tl, env) +DEF_HELPER_FLAGS_2(store_hdecr, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_2(store_hid0_601, void, env, tl) +DEF_HELPER_3(store_403_pbr, void, env, i32, tl) +DEF_HELPER_FLAGS_1(load_40x_pit, TCG_CALL_NO_RWG, tl, env) +DEF_HELPER_FLAGS_2(store_40x_pit, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_2(store_40x_dbcr0, void, env, tl) +DEF_HELPER_2(store_40x_sler, void, env, tl) +DEF_HELPER_FLAGS_2(store_booke_tcr, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(store_booke_tsr, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_3(store_ibatl, void, env, i32, tl) +DEF_HELPER_3(store_ibatu, void, env, i32, tl) +DEF_HELPER_3(store_dbatl, void, env, i32, tl) +DEF_HELPER_3(store_dbatu, void, env, i32, tl) +DEF_HELPER_3(store_601_batl, void, env, i32, tl) +DEF_HELPER_3(store_601_batu, void, env, i32, tl) +#endif + +#define dh_alias_fprp ptr +#define dh_ctype_fprp uint64_t * +#define dh_is_signed_fprp dh_is_signed_ptr + +DEF_HELPER_4(dadd, void, env, fprp, fprp, fprp) +DEF_HELPER_4(daddq, void, env, fprp, fprp, fprp) +DEF_HELPER_4(dsub, void, env, fprp, fprp, fprp) +DEF_HELPER_4(dsubq, void, env, fprp, fprp, fprp) +DEF_HELPER_4(dmul, void, env, fprp, fprp, fprp) +DEF_HELPER_4(dmulq, void, env, fprp, fprp, fprp) +DEF_HELPER_4(ddiv, void, env, fprp, fprp, fprp) +DEF_HELPER_4(ddivq, void, env, fprp, fprp, fprp) +DEF_HELPER_3(dcmpo, i32, env, fprp, fprp) +DEF_HELPER_3(dcmpoq, i32, env, fprp, fprp) +DEF_HELPER_3(dcmpu, i32, env, fprp, fprp) +DEF_HELPER_3(dcmpuq, i32, env, fprp, fprp) +DEF_HELPER_3(dtstdc, i32, env, fprp, i32) +DEF_HELPER_3(dtstdcq, i32, env, fprp, i32) +DEF_HELPER_3(dtstdg, i32, env, fprp, i32) +DEF_HELPER_3(dtstdgq, i32, env, fprp, i32) +DEF_HELPER_3(dtstex, i32, env, fprp, fprp) +DEF_HELPER_3(dtstexq, i32, env, fprp, fprp) +DEF_HELPER_3(dtstsf, i32, env, fprp, fprp) +DEF_HELPER_3(dtstsfq, i32, env, fprp, fprp) +DEF_HELPER_3(dtstsfi, i32, env, i32, fprp) +DEF_HELPER_3(dtstsfiq, i32, env, i32, fprp) +DEF_HELPER_5(dquai, void, env, fprp, fprp, i32, i32) +DEF_HELPER_5(dquaiq, void, env, fprp, fprp, i32, i32) +DEF_HELPER_5(dqua, void, env, fprp, fprp, fprp, i32) +DEF_HELPER_5(dquaq, void, env, fprp, fprp, fprp, i32) +DEF_HELPER_5(drrnd, void, env, fprp, fprp, fprp, i32) +DEF_HELPER_5(drrndq, void, env, fprp, fprp, fprp, i32) +DEF_HELPER_5(drintx, void, env, fprp, fprp, i32, i32) +DEF_HELPER_5(drintxq, void, env, fprp, fprp, i32, i32) +DEF_HELPER_5(drintn, void, env, fprp, fprp, i32, i32) +DEF_HELPER_5(drintnq, void, env, fprp, fprp, i32, i32) +DEF_HELPER_3(dctdp, void, env, fprp, fprp) +DEF_HELPER_3(dctqpq, void, env, fprp, fprp) +DEF_HELPER_3(drsp, void, env, fprp, fprp) +DEF_HELPER_3(drdpq, void, env, fprp, fprp) +DEF_HELPER_3(dcffix, void, env, fprp, fprp) +DEF_HELPER_3(dcffixq, void, env, fprp, fprp) +DEF_HELPER_3(dctfix, void, env, fprp, fprp) +DEF_HELPER_3(dctfixq, void, env, fprp, fprp) +DEF_HELPER_4(ddedpd, void, env, fprp, fprp, i32) +DEF_HELPER_4(ddedpdq, void, env, fprp, fprp, i32) +DEF_HELPER_4(denbcd, void, env, fprp, fprp, i32) +DEF_HELPER_4(denbcdq, void, env, fprp, fprp, i32) +DEF_HELPER_3(dxex, void, env, fprp, fprp) +DEF_HELPER_3(dxexq, void, env, fprp, fprp) +DEF_HELPER_4(diex, void, env, fprp, fprp, fprp) +DEF_HELPER_4(diexq, void, env, fprp, fprp, fprp) +DEF_HELPER_4(dscri, void, env, fprp, fprp, i32) +DEF_HELPER_4(dscriq, void, env, fprp, fprp, i32) +DEF_HELPER_4(dscli, void, env, fprp, fprp, i32) +DEF_HELPER_4(dscliq, void, env, fprp, fprp, i32) + +DEF_HELPER_1(tbegin, void, env) +DEF_HELPER_FLAGS_1(fixup_thrm, TCG_CALL_NO_RWG, void, env) diff --git a/target/ppc/helper_regs.h b/target/ppc/helper_regs.h new file mode 100644 index 0000000000..62138163a5 --- /dev/null +++ b/target/ppc/helper_regs.h @@ -0,0 +1,189 @@ +/* + * PowerPC emulation special registers manipulation helpers for qemu. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * + * 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 HELPER_REGS_H +#define HELPER_REGS_H + +/* Swap temporary saved registers with GPRs */ +static inline void hreg_swap_gpr_tgpr(CPUPPCState *env) +{ + target_ulong tmp; + + tmp = env->gpr[0]; + env->gpr[0] = env->tgpr[0]; + env->tgpr[0] = tmp; + tmp = env->gpr[1]; + env->gpr[1] = env->tgpr[1]; + env->tgpr[1] = tmp; + tmp = env->gpr[2]; + env->gpr[2] = env->tgpr[2]; + env->tgpr[2] = tmp; + tmp = env->gpr[3]; + env->gpr[3] = env->tgpr[3]; + env->tgpr[3] = tmp; +} + +static inline void hreg_compute_mem_idx(CPUPPCState *env) +{ + /* This is our encoding for server processors. The architecture + * specifies that there is no such thing as userspace with + * translation off, however it appears that MacOS does it and + * some 32-bit CPUs support it. Weird... + * + * 0 = Guest User space virtual mode + * 1 = Guest Kernel space virtual mode + * 2 = Guest User space real mode + * 3 = Guest Kernel space real mode + * 4 = HV User space virtual mode + * 5 = HV Kernel space virtual mode + * 6 = HV User space real mode + * 7 = HV Kernel space real mode + * + * For BookE, we need 8 MMU modes as follow: + * + * 0 = AS 0 HV User space + * 1 = AS 0 HV Kernel space + * 2 = AS 1 HV User space + * 3 = AS 1 HV Kernel space + * 4 = AS 0 Guest User space + * 5 = AS 0 Guest Kernel space + * 6 = AS 1 Guest User space + * 7 = AS 1 Guest Kernel space + */ + if (env->mmu_model & POWERPC_MMU_BOOKE) { + env->immu_idx = env->dmmu_idx = msr_pr ? 0 : 1; + env->immu_idx += msr_is ? 2 : 0; + env->dmmu_idx += msr_ds ? 2 : 0; + env->immu_idx += msr_gs ? 4 : 0; + env->dmmu_idx += msr_gs ? 4 : 0; + } else { + env->immu_idx = env->dmmu_idx = msr_pr ? 0 : 1; + env->immu_idx += msr_ir ? 0 : 2; + env->dmmu_idx += msr_dr ? 0 : 2; + env->immu_idx += msr_hv ? 4 : 0; + env->dmmu_idx += msr_hv ? 4 : 0; + } +} + +static inline void hreg_compute_hflags(CPUPPCState *env) +{ + target_ulong hflags_mask; + + /* We 'forget' FE0 & FE1: we'll never generate imprecise exceptions */ + hflags_mask = (1 << MSR_VR) | (1 << MSR_AP) | (1 << MSR_SA) | + (1 << MSR_PR) | (1 << MSR_FP) | (1 << MSR_SE) | (1 << MSR_BE) | + (1 << MSR_LE) | (1 << MSR_VSX) | (1 << MSR_IR) | (1 << MSR_DR); + hflags_mask |= (1ULL << MSR_CM) | (1ULL << MSR_SF) | MSR_HVB; + hreg_compute_mem_idx(env); + env->hflags = env->msr & hflags_mask; + /* Merge with hflags coming from other registers */ + env->hflags |= env->hflags_nmsr; +} + +static inline int hreg_store_msr(CPUPPCState *env, target_ulong value, + int alter_hv) +{ + int excp; +#if !defined(CONFIG_USER_ONLY) + CPUState *cs = CPU(ppc_env_get_cpu(env)); +#endif + + excp = 0; + value &= env->msr_mask; +#if !defined(CONFIG_USER_ONLY) + /* Neither mtmsr nor guest state can alter HV */ + if (!alter_hv || !(env->msr & MSR_HVB)) { + value &= ~MSR_HVB; + value |= env->msr & MSR_HVB; + } + if (((value >> MSR_IR) & 1) != msr_ir || + ((value >> MSR_DR) & 1) != msr_dr) { + cs->interrupt_request |= CPU_INTERRUPT_EXITTB; + } + if ((env->mmu_model & POWERPC_MMU_BOOKE) && + ((value >> MSR_GS) & 1) != msr_gs) { + cs->interrupt_request |= CPU_INTERRUPT_EXITTB; + } + if (unlikely((env->flags & POWERPC_FLAG_TGPR) && + ((value ^ env->msr) & (1 << MSR_TGPR)))) { + /* Swap temporary saved registers with GPRs */ + hreg_swap_gpr_tgpr(env); + } + if (unlikely((value >> MSR_EP) & 1) != msr_ep) { + /* Change the exception prefix on PowerPC 601 */ + env->excp_prefix = ((value >> MSR_EP) & 1) * 0xFFF00000; + } + /* If PR=1 then EE, IR and DR must be 1 + * + * Note: We only enforce this on 64-bit server processors. + * It appears that: + * - 32-bit implementations supports PR=1 and EE/DR/IR=0 and MacOS + * exploits it. + * - 64-bit embedded implementations do not need any operation to be + * performed when PR is set. + */ + if ((env->insns_flags & PPC_SEGMENT_64B) && ((value >> MSR_PR) & 1)) { + value |= (1 << MSR_EE) | (1 << MSR_DR) | (1 << MSR_IR); + } +#endif + env->msr = value; + hreg_compute_hflags(env); +#if !defined(CONFIG_USER_ONLY) + if (unlikely(msr_pow == 1)) { + if (!env->pending_interrupts && (*env->check_pow)(env)) { + cs->halted = 1; + excp = EXCP_HALTED; + } + } +#endif + + return excp; +} + +#if !defined(CONFIG_USER_ONLY) +static inline void check_tlb_flush(CPUPPCState *env, bool global) +{ + CPUState *cs = CPU(ppc_env_get_cpu(env)); + if (env->tlb_need_flush & TLB_NEED_LOCAL_FLUSH) { + tlb_flush(cs, 1); + env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH; + } + + /* Propagate TLB invalidations to other CPUs when the guest uses broadcast + * TLB invalidation instructions. + */ + if (global && (env->tlb_need_flush & TLB_NEED_GLOBAL_FLUSH)) { + CPUState *other_cs; + CPU_FOREACH(other_cs) { + if (other_cs != cs) { + PowerPCCPU *cpu = POWERPC_CPU(other_cs); + CPUPPCState *other_env = &cpu->env; + + other_env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH; + tlb_flush(other_cs, 1); + } + } + env->tlb_need_flush &= ~TLB_NEED_GLOBAL_FLUSH; + } +} +#else +static inline void check_tlb_flush(CPUPPCState *env, bool global) { } +#endif + +#endif /* HELPER_REGS_H */ diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c new file mode 100644 index 0000000000..2d57c9a1c2 --- /dev/null +++ b/target/ppc/int_helper.c @@ -0,0 +1,3126 @@ +/* + * PowerPC integer and vector emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * + * 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 "qemu/osdep.h" +#include "cpu.h" +#include "internal.h" +#include "exec/exec-all.h" +#include "qemu/host-utils.h" +#include "exec/helper-proto.h" +#include "crypto/aes.h" + +#include "helper_regs.h" +/*****************************************************************************/ +/* Fixed point operations helpers */ + +target_ulong helper_divweu(CPUPPCState *env, target_ulong ra, target_ulong rb, + uint32_t oe) +{ + uint64_t rt = 0; + int overflow = 0; + + uint64_t dividend = (uint64_t)ra << 32; + uint64_t divisor = (uint32_t)rb; + + if (unlikely(divisor == 0)) { + overflow = 1; + } else { + rt = dividend / divisor; + overflow = rt > UINT32_MAX; + } + + if (unlikely(overflow)) { + rt = 0; /* Undefined */ + } + + if (oe) { + if (unlikely(overflow)) { + env->so = env->ov = 1; + } else { + env->ov = 0; + } + } + + return (target_ulong)rt; +} + +target_ulong helper_divwe(CPUPPCState *env, target_ulong ra, target_ulong rb, + uint32_t oe) +{ + int64_t rt = 0; + int overflow = 0; + + int64_t dividend = (int64_t)ra << 32; + int64_t divisor = (int64_t)((int32_t)rb); + + if (unlikely((divisor == 0) || + ((divisor == -1ull) && (dividend == INT64_MIN)))) { + overflow = 1; + } else { + rt = dividend / divisor; + overflow = rt != (int32_t)rt; + } + + if (unlikely(overflow)) { + rt = 0; /* Undefined */ + } + + if (oe) { + if (unlikely(overflow)) { + env->so = env->ov = 1; + } else { + env->ov = 0; + } + } + + return (target_ulong)rt; +} + +#if defined(TARGET_PPC64) + +uint64_t helper_divdeu(CPUPPCState *env, uint64_t ra, uint64_t rb, uint32_t oe) +{ + uint64_t rt = 0; + int overflow = 0; + + overflow = divu128(&rt, &ra, rb); + + if (unlikely(overflow)) { + rt = 0; /* Undefined */ + } + + if (oe) { + if (unlikely(overflow)) { + env->so = env->ov = 1; + } else { + env->ov = 0; + } + } + + return rt; +} + +uint64_t helper_divde(CPUPPCState *env, uint64_t rau, uint64_t rbu, uint32_t oe) +{ + int64_t rt = 0; + int64_t ra = (int64_t)rau; + int64_t rb = (int64_t)rbu; + int overflow = divs128(&rt, &ra, rb); + + if (unlikely(overflow)) { + rt = 0; /* Undefined */ + } + + if (oe) { + + if (unlikely(overflow)) { + env->so = env->ov = 1; + } else { + env->ov = 0; + } + } + + return rt; +} + +#endif + + +target_ulong helper_cntlzw(target_ulong t) +{ + return clz32(t); +} + +target_ulong helper_cnttzw(target_ulong t) +{ + return ctz32(t); +} + +#if defined(TARGET_PPC64) +/* if x = 0xab, returns 0xababababababababa */ +#define pattern(x) (((x) & 0xff) * (~(target_ulong)0 / 0xff)) + +/* substract 1 from each byte, and with inverse, check if MSB is set at each + * byte. + * i.e. ((0x00 - 0x01) & ~(0x00)) & 0x80 + * (0xFF & 0xFF) & 0x80 = 0x80 (zero found) + */ +#define haszero(v) (((v) - pattern(0x01)) & ~(v) & pattern(0x80)) + +/* When you XOR the pattern and there is a match, that byte will be zero */ +#define hasvalue(x, n) (haszero((x) ^ pattern(n))) + +uint32_t helper_cmpeqb(target_ulong ra, target_ulong rb) +{ + return hasvalue(rb, ra) ? 1 << CRF_GT : 0; +} + +#undef pattern +#undef haszero +#undef hasvalue + +target_ulong helper_cntlzd(target_ulong t) +{ + return clz64(t); +} + +target_ulong helper_cnttzd(target_ulong t) +{ + return ctz64(t); +} + +/* Return invalid random number. + * + * FIXME: Add rng backend or other mechanism to get cryptographically suitable + * random number + */ +target_ulong helper_darn32(void) +{ + return -1; +} + +target_ulong helper_darn64(void) +{ + return -1; +} + +#endif + +#if defined(TARGET_PPC64) + +uint64_t helper_bpermd(uint64_t rs, uint64_t rb) +{ + int i; + uint64_t ra = 0; + + for (i = 0; i < 8; i++) { + int index = (rs >> (i*8)) & 0xFF; + if (index < 64) { + if (rb & (1ull << (63-index))) { + ra |= 1 << i; + } + } + } + return ra; +} + +#endif + +target_ulong helper_cmpb(target_ulong rs, target_ulong rb) +{ + target_ulong mask = 0xff; + target_ulong ra = 0; + int i; + + for (i = 0; i < sizeof(target_ulong); i++) { + if ((rs & mask) == (rb & mask)) { + ra |= mask; + } + mask <<= 8; + } + return ra; +} + +/* shift right arithmetic helper */ +target_ulong helper_sraw(CPUPPCState *env, target_ulong value, + target_ulong shift) +{ + int32_t ret; + + if (likely(!(shift & 0x20))) { + if (likely((uint32_t)shift != 0)) { + shift &= 0x1f; + ret = (int32_t)value >> shift; + if (likely(ret >= 0 || (value & ((1 << shift) - 1)) == 0)) { + env->ca = 0; + } else { + env->ca = 1; + } + } else { + ret = (int32_t)value; + env->ca = 0; + } + } else { + ret = (int32_t)value >> 31; + env->ca = (ret != 0); + } + return (target_long)ret; +} + +#if defined(TARGET_PPC64) +target_ulong helper_srad(CPUPPCState *env, target_ulong value, + target_ulong shift) +{ + int64_t ret; + + if (likely(!(shift & 0x40))) { + if (likely((uint64_t)shift != 0)) { + shift &= 0x3f; + ret = (int64_t)value >> shift; + if (likely(ret >= 0 || (value & ((1ULL << shift) - 1)) == 0)) { + env->ca = 0; + } else { + env->ca = 1; + } + } else { + ret = (int64_t)value; + env->ca = 0; + } + } else { + ret = (int64_t)value >> 63; + env->ca = (ret != 0); + } + return ret; +} +#endif + +#if defined(TARGET_PPC64) +target_ulong helper_popcntb(target_ulong val) +{ + val = (val & 0x5555555555555555ULL) + ((val >> 1) & + 0x5555555555555555ULL); + val = (val & 0x3333333333333333ULL) + ((val >> 2) & + 0x3333333333333333ULL); + val = (val & 0x0f0f0f0f0f0f0f0fULL) + ((val >> 4) & + 0x0f0f0f0f0f0f0f0fULL); + return val; +} + +target_ulong helper_popcntw(target_ulong val) +{ + val = (val & 0x5555555555555555ULL) + ((val >> 1) & + 0x5555555555555555ULL); + val = (val & 0x3333333333333333ULL) + ((val >> 2) & + 0x3333333333333333ULL); + val = (val & 0x0f0f0f0f0f0f0f0fULL) + ((val >> 4) & + 0x0f0f0f0f0f0f0f0fULL); + val = (val & 0x00ff00ff00ff00ffULL) + ((val >> 8) & + 0x00ff00ff00ff00ffULL); + val = (val & 0x0000ffff0000ffffULL) + ((val >> 16) & + 0x0000ffff0000ffffULL); + return val; +} + +target_ulong helper_popcntd(target_ulong val) +{ + return ctpop64(val); +} +#else +target_ulong helper_popcntb(target_ulong val) +{ + val = (val & 0x55555555) + ((val >> 1) & 0x55555555); + val = (val & 0x33333333) + ((val >> 2) & 0x33333333); + val = (val & 0x0f0f0f0f) + ((val >> 4) & 0x0f0f0f0f); + return val; +} + +target_ulong helper_popcntw(target_ulong val) +{ + val = (val & 0x55555555) + ((val >> 1) & 0x55555555); + val = (val & 0x33333333) + ((val >> 2) & 0x33333333); + val = (val & 0x0f0f0f0f) + ((val >> 4) & 0x0f0f0f0f); + val = (val & 0x00ff00ff) + ((val >> 8) & 0x00ff00ff); + val = (val & 0x0000ffff) + ((val >> 16) & 0x0000ffff); + return val; +} +#endif + +/*****************************************************************************/ +/* PowerPC 601 specific instructions (POWER bridge) */ +target_ulong helper_div(CPUPPCState *env, target_ulong arg1, target_ulong arg2) +{ + uint64_t tmp = (uint64_t)arg1 << 32 | env->spr[SPR_MQ]; + + if (((int32_t)tmp == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || + (int32_t)arg2 == 0) { + env->spr[SPR_MQ] = 0; + return INT32_MIN; + } else { + env->spr[SPR_MQ] = tmp % arg2; + return tmp / (int32_t)arg2; + } +} + +target_ulong helper_divo(CPUPPCState *env, target_ulong arg1, + target_ulong arg2) +{ + uint64_t tmp = (uint64_t)arg1 << 32 | env->spr[SPR_MQ]; + + if (((int32_t)tmp == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || + (int32_t)arg2 == 0) { + env->so = env->ov = 1; + env->spr[SPR_MQ] = 0; + return INT32_MIN; + } else { + env->spr[SPR_MQ] = tmp % arg2; + tmp /= (int32_t)arg2; + if ((int32_t)tmp != tmp) { + env->so = env->ov = 1; + } else { + env->ov = 0; + } + return tmp; + } +} + +target_ulong helper_divs(CPUPPCState *env, target_ulong arg1, + target_ulong arg2) +{ + if (((int32_t)arg1 == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || + (int32_t)arg2 == 0) { + env->spr[SPR_MQ] = 0; + return INT32_MIN; + } else { + env->spr[SPR_MQ] = (int32_t)arg1 % (int32_t)arg2; + return (int32_t)arg1 / (int32_t)arg2; + } +} + +target_ulong helper_divso(CPUPPCState *env, target_ulong arg1, + target_ulong arg2) +{ + if (((int32_t)arg1 == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || + (int32_t)arg2 == 0) { + env->so = env->ov = 1; + env->spr[SPR_MQ] = 0; + return INT32_MIN; + } else { + env->ov = 0; + env->spr[SPR_MQ] = (int32_t)arg1 % (int32_t)arg2; + return (int32_t)arg1 / (int32_t)arg2; + } +} + +/*****************************************************************************/ +/* 602 specific instructions */ +/* mfrom is the most crazy instruction ever seen, imho ! */ +/* Real implementation uses a ROM table. Do the same */ +/* Extremely decomposed: + * -arg / 256 + * return 256 * log10(10 + 1.0) + 0.5 + */ +#if !defined(CONFIG_USER_ONLY) +target_ulong helper_602_mfrom(target_ulong arg) +{ + if (likely(arg < 602)) { +#include "mfrom_table.c" + return mfrom_ROM_table[arg]; + } else { + return 0; + } +} +#endif + +/*****************************************************************************/ +/* Altivec extension helpers */ +#if defined(HOST_WORDS_BIGENDIAN) +#define HI_IDX 0 +#define LO_IDX 1 +#define AVRB(i) u8[i] +#define AVRW(i) u32[i] +#else +#define HI_IDX 1 +#define LO_IDX 0 +#define AVRB(i) u8[15-(i)] +#define AVRW(i) u32[3-(i)] +#endif + +#if defined(HOST_WORDS_BIGENDIAN) +#define VECTOR_FOR_INORDER_I(index, element) \ + for (index = 0; index < ARRAY_SIZE(r->element); index++) +#else +#define VECTOR_FOR_INORDER_I(index, element) \ + for (index = ARRAY_SIZE(r->element)-1; index >= 0; index--) +#endif + +/* Saturating arithmetic helpers. */ +#define SATCVT(from, to, from_type, to_type, min, max) \ + static inline to_type cvt##from##to(from_type x, int *sat) \ + { \ + to_type r; \ + \ + if (x < (from_type)min) { \ + r = min; \ + *sat = 1; \ + } else if (x > (from_type)max) { \ + r = max; \ + *sat = 1; \ + } else { \ + r = x; \ + } \ + return r; \ + } +#define SATCVTU(from, to, from_type, to_type, min, max) \ + static inline to_type cvt##from##to(from_type x, int *sat) \ + { \ + to_type r; \ + \ + if (x > (from_type)max) { \ + r = max; \ + *sat = 1; \ + } else { \ + r = x; \ + } \ + return r; \ + } +SATCVT(sh, sb, int16_t, int8_t, INT8_MIN, INT8_MAX) +SATCVT(sw, sh, int32_t, int16_t, INT16_MIN, INT16_MAX) +SATCVT(sd, sw, int64_t, int32_t, INT32_MIN, INT32_MAX) + +SATCVTU(uh, ub, uint16_t, uint8_t, 0, UINT8_MAX) +SATCVTU(uw, uh, uint32_t, uint16_t, 0, UINT16_MAX) +SATCVTU(ud, uw, uint64_t, uint32_t, 0, UINT32_MAX) +SATCVT(sh, ub, int16_t, uint8_t, 0, UINT8_MAX) +SATCVT(sw, uh, int32_t, uint16_t, 0, UINT16_MAX) +SATCVT(sd, uw, int64_t, uint32_t, 0, UINT32_MAX) +#undef SATCVT +#undef SATCVTU + +void helper_lvsl(ppc_avr_t *r, target_ulong sh) +{ + int i, j = (sh & 0xf); + + VECTOR_FOR_INORDER_I(i, u8) { + r->u8[i] = j++; + } +} + +void helper_lvsr(ppc_avr_t *r, target_ulong sh) +{ + int i, j = 0x10 - (sh & 0xf); + + VECTOR_FOR_INORDER_I(i, u8) { + r->u8[i] = j++; + } +} + +void helper_mtvscr(CPUPPCState *env, ppc_avr_t *r) +{ +#if defined(HOST_WORDS_BIGENDIAN) + env->vscr = r->u32[3]; +#else + env->vscr = r->u32[0]; +#endif + set_flush_to_zero(vscr_nj, &env->vec_status); +} + +void helper_vaddcuw(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(r->u32); i++) { + r->u32[i] = ~a->u32[i] < b->u32[i]; + } +} + +/* vprtybw */ +void helper_vprtybw(ppc_avr_t *r, ppc_avr_t *b) +{ + int i; + for (i = 0; i < ARRAY_SIZE(r->u32); i++) { + uint64_t res = b->u32[i] ^ (b->u32[i] >> 16); + res ^= res >> 8; + r->u32[i] = res & 1; + } +} + +/* vprtybd */ +void helper_vprtybd(ppc_avr_t *r, ppc_avr_t *b) +{ + int i; + for (i = 0; i < ARRAY_SIZE(r->u64); i++) { + uint64_t res = b->u64[i] ^ (b->u64[i] >> 32); + res ^= res >> 16; + res ^= res >> 8; + r->u64[i] = res & 1; + } +} + +/* vprtybq */ +void helper_vprtybq(ppc_avr_t *r, ppc_avr_t *b) +{ + uint64_t res = b->u64[0] ^ b->u64[1]; + res ^= res >> 32; + res ^= res >> 16; + res ^= res >> 8; + r->u64[LO_IDX] = res & 1; + r->u64[HI_IDX] = 0; +} + +#define VARITH_DO(name, op, element) \ + void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + r->element[i] = a->element[i] op b->element[i]; \ + } \ + } +#define VARITH(suffix, element) \ + VARITH_DO(add##suffix, +, element) \ + VARITH_DO(sub##suffix, -, element) +VARITH(ubm, u8) +VARITH(uhm, u16) +VARITH(uwm, u32) +VARITH(udm, u64) +VARITH_DO(muluwm, *, u32) +#undef VARITH_DO +#undef VARITH + +#define VARITHFP(suffix, func) \ + void helper_v##suffix(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, \ + ppc_avr_t *b) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ + r->f[i] = func(a->f[i], b->f[i], &env->vec_status); \ + } \ + } +VARITHFP(addfp, float32_add) +VARITHFP(subfp, float32_sub) +VARITHFP(minfp, float32_min) +VARITHFP(maxfp, float32_max) +#undef VARITHFP + +#define VARITHFPFMA(suffix, type) \ + void helper_v##suffix(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, \ + ppc_avr_t *b, ppc_avr_t *c) \ + { \ + int i; \ + for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ + r->f[i] = float32_muladd(a->f[i], c->f[i], b->f[i], \ + type, &env->vec_status); \ + } \ + } +VARITHFPFMA(maddfp, 0); +VARITHFPFMA(nmsubfp, float_muladd_negate_result | float_muladd_negate_c); +#undef VARITHFPFMA + +#define VARITHSAT_CASE(type, op, cvt, element) \ + { \ + type result = (type)a->element[i] op (type)b->element[i]; \ + r->element[i] = cvt(result, &sat); \ + } + +#define VARITHSAT_DO(name, op, optype, cvt, element) \ + void helper_v##name(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, \ + ppc_avr_t *b) \ + { \ + int sat = 0; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + switch (sizeof(r->element[0])) { \ + case 1: \ + VARITHSAT_CASE(optype, op, cvt, element); \ + break; \ + case 2: \ + VARITHSAT_CASE(optype, op, cvt, element); \ + break; \ + case 4: \ + VARITHSAT_CASE(optype, op, cvt, element); \ + break; \ + } \ + } \ + if (sat) { \ + env->vscr |= (1 << VSCR_SAT); \ + } \ + } +#define VARITHSAT_SIGNED(suffix, element, optype, cvt) \ + VARITHSAT_DO(adds##suffix##s, +, optype, cvt, element) \ + VARITHSAT_DO(subs##suffix##s, -, optype, cvt, element) +#define VARITHSAT_UNSIGNED(suffix, element, optype, cvt) \ + VARITHSAT_DO(addu##suffix##s, +, optype, cvt, element) \ + VARITHSAT_DO(subu##suffix##s, -, optype, cvt, element) +VARITHSAT_SIGNED(b, s8, int16_t, cvtshsb) +VARITHSAT_SIGNED(h, s16, int32_t, cvtswsh) +VARITHSAT_SIGNED(w, s32, int64_t, cvtsdsw) +VARITHSAT_UNSIGNED(b, u8, uint16_t, cvtshub) +VARITHSAT_UNSIGNED(h, u16, uint32_t, cvtswuh) +VARITHSAT_UNSIGNED(w, u32, uint64_t, cvtsduw) +#undef VARITHSAT_CASE +#undef VARITHSAT_DO +#undef VARITHSAT_SIGNED +#undef VARITHSAT_UNSIGNED + +#define VAVG_DO(name, element, etype) \ + void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + etype x = (etype)a->element[i] + (etype)b->element[i] + 1; \ + r->element[i] = x >> 1; \ + } \ + } + +#define VAVG(type, signed_element, signed_type, unsigned_element, \ + unsigned_type) \ + VAVG_DO(avgs##type, signed_element, signed_type) \ + VAVG_DO(avgu##type, unsigned_element, unsigned_type) +VAVG(b, s8, int16_t, u8, uint16_t) +VAVG(h, s16, int32_t, u16, uint32_t) +VAVG(w, s32, int64_t, u32, uint64_t) +#undef VAVG_DO +#undef VAVG + +#define VABSDU_DO(name, element) \ +void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ +{ \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + r->element[i] = (a->element[i] > b->element[i]) ? \ + (a->element[i] - b->element[i]) : \ + (b->element[i] - a->element[i]); \ + } \ +} + +/* VABSDU - Vector absolute difference unsigned + * name - instruction mnemonic suffix (b: byte, h: halfword, w: word) + * element - element type to access from vector + */ +#define VABSDU(type, element) \ + VABSDU_DO(absdu##type, element) +VABSDU(b, u8) +VABSDU(h, u16) +VABSDU(w, u32) +#undef VABSDU_DO +#undef VABSDU + +#define VCF(suffix, cvt, element) \ + void helper_vcf##suffix(CPUPPCState *env, ppc_avr_t *r, \ + ppc_avr_t *b, uint32_t uim) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ + float32 t = cvt(b->element[i], &env->vec_status); \ + r->f[i] = float32_scalbn(t, -uim, &env->vec_status); \ + } \ + } +VCF(ux, uint32_to_float32, u32) +VCF(sx, int32_to_float32, s32) +#undef VCF + +#define VCMP_DO(suffix, compare, element, record) \ + void helper_vcmp##suffix(CPUPPCState *env, ppc_avr_t *r, \ + ppc_avr_t *a, ppc_avr_t *b) \ + { \ + uint64_t ones = (uint64_t)-1; \ + uint64_t all = ones; \ + uint64_t none = 0; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + uint64_t result = (a->element[i] compare b->element[i] ? \ + ones : 0x0); \ + switch (sizeof(a->element[0])) { \ + case 8: \ + r->u64[i] = result; \ + break; \ + case 4: \ + r->u32[i] = result; \ + break; \ + case 2: \ + r->u16[i] = result; \ + break; \ + case 1: \ + r->u8[i] = result; \ + break; \ + } \ + all &= result; \ + none |= result; \ + } \ + if (record) { \ + env->crf[6] = ((all != 0) << 3) | ((none == 0) << 1); \ + } \ + } +#define VCMP(suffix, compare, element) \ + VCMP_DO(suffix, compare, element, 0) \ + VCMP_DO(suffix##_dot, compare, element, 1) +VCMP(equb, ==, u8) +VCMP(equh, ==, u16) +VCMP(equw, ==, u32) +VCMP(equd, ==, u64) +VCMP(gtub, >, u8) +VCMP(gtuh, >, u16) +VCMP(gtuw, >, u32) +VCMP(gtud, >, u64) +VCMP(gtsb, >, s8) +VCMP(gtsh, >, s16) +VCMP(gtsw, >, s32) +VCMP(gtsd, >, s64) +#undef VCMP_DO +#undef VCMP + +#define VCMPNE_DO(suffix, element, etype, cmpzero, record) \ +void helper_vcmpne##suffix(CPUPPCState *env, ppc_avr_t *r, \ + ppc_avr_t *a, ppc_avr_t *b) \ +{ \ + etype ones = (etype)-1; \ + etype all = ones; \ + etype result, none = 0; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + if (cmpzero) { \ + result = ((a->element[i] == 0) \ + || (b->element[i] == 0) \ + || (a->element[i] != b->element[i]) ? \ + ones : 0x0); \ + } else { \ + result = (a->element[i] != b->element[i]) ? ones : 0x0; \ + } \ + r->element[i] = result; \ + all &= result; \ + none |= result; \ + } \ + if (record) { \ + env->crf[6] = ((all != 0) << 3) | ((none == 0) << 1); \ + } \ +} + +/* VCMPNEZ - Vector compare not equal to zero + * suffix - instruction mnemonic suffix (b: byte, h: halfword, w: word) + * element - element type to access from vector + */ +#define VCMPNE(suffix, element, etype, cmpzero) \ + VCMPNE_DO(suffix, element, etype, cmpzero, 0) \ + VCMPNE_DO(suffix##_dot, element, etype, cmpzero, 1) +VCMPNE(zb, u8, uint8_t, 1) +VCMPNE(zh, u16, uint16_t, 1) +VCMPNE(zw, u32, uint32_t, 1) +VCMPNE(b, u8, uint8_t, 0) +VCMPNE(h, u16, uint16_t, 0) +VCMPNE(w, u32, uint32_t, 0) +#undef VCMPNE_DO +#undef VCMPNE + +#define VCMPFP_DO(suffix, compare, order, record) \ + void helper_vcmp##suffix(CPUPPCState *env, ppc_avr_t *r, \ + ppc_avr_t *a, ppc_avr_t *b) \ + { \ + uint32_t ones = (uint32_t)-1; \ + uint32_t all = ones; \ + uint32_t none = 0; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ + uint32_t result; \ + int rel = float32_compare_quiet(a->f[i], b->f[i], \ + &env->vec_status); \ + if (rel == float_relation_unordered) { \ + result = 0; \ + } else if (rel compare order) { \ + result = ones; \ + } else { \ + result = 0; \ + } \ + r->u32[i] = result; \ + all &= result; \ + none |= result; \ + } \ + if (record) { \ + env->crf[6] = ((all != 0) << 3) | ((none == 0) << 1); \ + } \ + } +#define VCMPFP(suffix, compare, order) \ + VCMPFP_DO(suffix, compare, order, 0) \ + VCMPFP_DO(suffix##_dot, compare, order, 1) +VCMPFP(eqfp, ==, float_relation_equal) +VCMPFP(gefp, !=, float_relation_less) +VCMPFP(gtfp, ==, float_relation_greater) +#undef VCMPFP_DO +#undef VCMPFP + +static inline void vcmpbfp_internal(CPUPPCState *env, ppc_avr_t *r, + ppc_avr_t *a, ppc_avr_t *b, int record) +{ + int i; + int all_in = 0; + + for (i = 0; i < ARRAY_SIZE(r->f); i++) { + int le_rel = float32_compare_quiet(a->f[i], b->f[i], &env->vec_status); + if (le_rel == float_relation_unordered) { + r->u32[i] = 0xc0000000; + all_in = 1; + } else { + float32 bneg = float32_chs(b->f[i]); + int ge_rel = float32_compare_quiet(a->f[i], bneg, &env->vec_status); + int le = le_rel != float_relation_greater; + int ge = ge_rel != float_relation_less; + + r->u32[i] = ((!le) << 31) | ((!ge) << 30); + all_in |= (!le | !ge); + } + } + if (record) { + env->crf[6] = (all_in == 0) << 1; + } +} + +void helper_vcmpbfp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + vcmpbfp_internal(env, r, a, b, 0); +} + +void helper_vcmpbfp_dot(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b) +{ + vcmpbfp_internal(env, r, a, b, 1); +} + +#define VCT(suffix, satcvt, element) \ + void helper_vct##suffix(CPUPPCState *env, ppc_avr_t *r, \ + ppc_avr_t *b, uint32_t uim) \ + { \ + int i; \ + int sat = 0; \ + float_status s = env->vec_status; \ + \ + set_float_rounding_mode(float_round_to_zero, &s); \ + for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ + if (float32_is_any_nan(b->f[i])) { \ + r->element[i] = 0; \ + } else { \ + float64 t = float32_to_float64(b->f[i], &s); \ + int64_t j; \ + \ + t = float64_scalbn(t, uim, &s); \ + j = float64_to_int64(t, &s); \ + r->element[i] = satcvt(j, &sat); \ + } \ + } \ + if (sat) { \ + env->vscr |= (1 << VSCR_SAT); \ + } \ + } +VCT(uxs, cvtsduw, u32) +VCT(sxs, cvtsdsw, s32) +#undef VCT + +target_ulong helper_vclzlsbb(ppc_avr_t *r) +{ + target_ulong count = 0; + int i; + VECTOR_FOR_INORDER_I(i, u8) { + if (r->u8[i] & 0x01) { + break; + } + count++; + } + return count; +} + +target_ulong helper_vctzlsbb(ppc_avr_t *r) +{ + target_ulong count = 0; + int i; +#if defined(HOST_WORDS_BIGENDIAN) + for (i = ARRAY_SIZE(r->u8) - 1; i >= 0; i--) { +#else + for (i = 0; i < ARRAY_SIZE(r->u8); i++) { +#endif + if (r->u8[i] & 0x01) { + break; + } + count++; + } + return count; +} + +void helper_vmhaddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b, ppc_avr_t *c) +{ + int sat = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(r->s16); i++) { + int32_t prod = a->s16[i] * b->s16[i]; + int32_t t = (int32_t)c->s16[i] + (prod >> 15); + + r->s16[i] = cvtswsh(t, &sat); + } + + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +void helper_vmhraddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b, ppc_avr_t *c) +{ + int sat = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(r->s16); i++) { + int32_t prod = a->s16[i] * b->s16[i] + 0x00004000; + int32_t t = (int32_t)c->s16[i] + (prod >> 15); + r->s16[i] = cvtswsh(t, &sat); + } + + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +#define VMINMAX_DO(name, compare, element) \ + void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + if (a->element[i] compare b->element[i]) { \ + r->element[i] = b->element[i]; \ + } else { \ + r->element[i] = a->element[i]; \ + } \ + } \ + } +#define VMINMAX(suffix, element) \ + VMINMAX_DO(min##suffix, >, element) \ + VMINMAX_DO(max##suffix, <, element) +VMINMAX(sb, s8) +VMINMAX(sh, s16) +VMINMAX(sw, s32) +VMINMAX(sd, s64) +VMINMAX(ub, u8) +VMINMAX(uh, u16) +VMINMAX(uw, u32) +VMINMAX(ud, u64) +#undef VMINMAX_DO +#undef VMINMAX + +void helper_vmladduhm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(r->s16); i++) { + int32_t prod = a->s16[i] * b->s16[i]; + r->s16[i] = (int16_t) (prod + c->s16[i]); + } +} + +#define VMRG_DO(name, element, highp) \ + void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + ppc_avr_t result; \ + int i; \ + size_t n_elems = ARRAY_SIZE(r->element); \ + \ + for (i = 0; i < n_elems / 2; i++) { \ + if (highp) { \ + result.element[i*2+HI_IDX] = a->element[i]; \ + result.element[i*2+LO_IDX] = b->element[i]; \ + } else { \ + result.element[n_elems - i * 2 - (1 + HI_IDX)] = \ + b->element[n_elems - i - 1]; \ + result.element[n_elems - i * 2 - (1 + LO_IDX)] = \ + a->element[n_elems - i - 1]; \ + } \ + } \ + *r = result; \ + } +#if defined(HOST_WORDS_BIGENDIAN) +#define MRGHI 0 +#define MRGLO 1 +#else +#define MRGHI 1 +#define MRGLO 0 +#endif +#define VMRG(suffix, element) \ + VMRG_DO(mrgl##suffix, element, MRGHI) \ + VMRG_DO(mrgh##suffix, element, MRGLO) +VMRG(b, u8) +VMRG(h, u16) +VMRG(w, u32) +#undef VMRG_DO +#undef VMRG +#undef MRGHI +#undef MRGLO + +void helper_vmsummbm(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b, ppc_avr_t *c) +{ + int32_t prod[16]; + int i; + + for (i = 0; i < ARRAY_SIZE(r->s8); i++) { + prod[i] = (int32_t)a->s8[i] * b->u8[i]; + } + + VECTOR_FOR_INORDER_I(i, s32) { + r->s32[i] = c->s32[i] + prod[4 * i] + prod[4 * i + 1] + + prod[4 * i + 2] + prod[4 * i + 3]; + } +} + +void helper_vmsumshm(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b, ppc_avr_t *c) +{ + int32_t prod[8]; + int i; + + for (i = 0; i < ARRAY_SIZE(r->s16); i++) { + prod[i] = a->s16[i] * b->s16[i]; + } + + VECTOR_FOR_INORDER_I(i, s32) { + r->s32[i] = c->s32[i] + prod[2 * i] + prod[2 * i + 1]; + } +} + +void helper_vmsumshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b, ppc_avr_t *c) +{ + int32_t prod[8]; + int i; + int sat = 0; + + for (i = 0; i < ARRAY_SIZE(r->s16); i++) { + prod[i] = (int32_t)a->s16[i] * b->s16[i]; + } + + VECTOR_FOR_INORDER_I(i, s32) { + int64_t t = (int64_t)c->s32[i] + prod[2 * i] + prod[2 * i + 1]; + + r->u32[i] = cvtsdsw(t, &sat); + } + + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +void helper_vmsumubm(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b, ppc_avr_t *c) +{ + uint16_t prod[16]; + int i; + + for (i = 0; i < ARRAY_SIZE(r->u8); i++) { + prod[i] = a->u8[i] * b->u8[i]; + } + + VECTOR_FOR_INORDER_I(i, u32) { + r->u32[i] = c->u32[i] + prod[4 * i] + prod[4 * i + 1] + + prod[4 * i + 2] + prod[4 * i + 3]; + } +} + +void helper_vmsumuhm(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b, ppc_avr_t *c) +{ + uint32_t prod[8]; + int i; + + for (i = 0; i < ARRAY_SIZE(r->u16); i++) { + prod[i] = a->u16[i] * b->u16[i]; + } + + VECTOR_FOR_INORDER_I(i, u32) { + r->u32[i] = c->u32[i] + prod[2 * i] + prod[2 * i + 1]; + } +} + +void helper_vmsumuhs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b, ppc_avr_t *c) +{ + uint32_t prod[8]; + int i; + int sat = 0; + + for (i = 0; i < ARRAY_SIZE(r->u16); i++) { + prod[i] = a->u16[i] * b->u16[i]; + } + + VECTOR_FOR_INORDER_I(i, s32) { + uint64_t t = (uint64_t)c->u32[i] + prod[2 * i] + prod[2 * i + 1]; + + r->u32[i] = cvtuduw(t, &sat); + } + + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +#define VMUL_DO(name, mul_element, prod_element, cast, evenp) \ + void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int i; \ + \ + VECTOR_FOR_INORDER_I(i, prod_element) { \ + if (evenp) { \ + r->prod_element[i] = \ + (cast)a->mul_element[i * 2 + HI_IDX] * \ + (cast)b->mul_element[i * 2 + HI_IDX]; \ + } else { \ + r->prod_element[i] = \ + (cast)a->mul_element[i * 2 + LO_IDX] * \ + (cast)b->mul_element[i * 2 + LO_IDX]; \ + } \ + } \ + } +#define VMUL(suffix, mul_element, prod_element, cast) \ + VMUL_DO(mule##suffix, mul_element, prod_element, cast, 1) \ + VMUL_DO(mulo##suffix, mul_element, prod_element, cast, 0) +VMUL(sb, s8, s16, int16_t) +VMUL(sh, s16, s32, int32_t) +VMUL(sw, s32, s64, int64_t) +VMUL(ub, u8, u16, uint16_t) +VMUL(uh, u16, u32, uint32_t) +VMUL(uw, u32, u64, uint64_t) +#undef VMUL_DO +#undef VMUL + +void helper_vperm(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, + ppc_avr_t *c) +{ + ppc_avr_t result; + int i; + + VECTOR_FOR_INORDER_I(i, u8) { + int s = c->u8[i] & 0x1f; +#if defined(HOST_WORDS_BIGENDIAN) + int index = s & 0xf; +#else + int index = 15 - (s & 0xf); +#endif + + if (s & 0x10) { + result.u8[i] = b->u8[index]; + } else { + result.u8[i] = a->u8[index]; + } + } + *r = result; +} + +void helper_vpermr(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, + ppc_avr_t *c) +{ + ppc_avr_t result; + int i; + + VECTOR_FOR_INORDER_I(i, u8) { + int s = c->u8[i] & 0x1f; +#if defined(HOST_WORDS_BIGENDIAN) + int index = 15 - (s & 0xf); +#else + int index = s & 0xf; +#endif + + if (s & 0x10) { + result.u8[i] = a->u8[index]; + } else { + result.u8[i] = b->u8[index]; + } + } + *r = result; +} + +#if defined(HOST_WORDS_BIGENDIAN) +#define VBPERMQ_INDEX(avr, i) ((avr)->u8[(i)]) +#define VBPERMD_INDEX(i) (i) +#define VBPERMQ_DW(index) (((index) & 0x40) != 0) +#define EXTRACT_BIT(avr, i, index) (extract64((avr)->u64[i], index, 1)) +#else +#define VBPERMQ_INDEX(avr, i) ((avr)->u8[15-(i)]) +#define VBPERMD_INDEX(i) (1 - i) +#define VBPERMQ_DW(index) (((index) & 0x40) == 0) +#define EXTRACT_BIT(avr, i, index) \ + (extract64((avr)->u64[1 - i], 63 - index, 1)) +#endif + +void helper_vbpermd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i, j; + ppc_avr_t result = { .u64 = { 0, 0 } }; + VECTOR_FOR_INORDER_I(i, u64) { + for (j = 0; j < 8; j++) { + int index = VBPERMQ_INDEX(b, (i * 8) + j); + if (index < 64 && EXTRACT_BIT(a, i, index)) { + result.u64[VBPERMD_INDEX(i)] |= (0x80 >> j); + } + } + } + *r = result; +} + +void helper_vbpermq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i; + uint64_t perm = 0; + + VECTOR_FOR_INORDER_I(i, u8) { + int index = VBPERMQ_INDEX(b, i); + + if (index < 128) { + uint64_t mask = (1ull << (63-(index & 0x3F))); + if (a->u64[VBPERMQ_DW(index)] & mask) { + perm |= (0x8000 >> i); + } + } + } + + r->u64[HI_IDX] = perm; + r->u64[LO_IDX] = 0; +} + +#undef VBPERMQ_INDEX +#undef VBPERMQ_DW + +static const uint64_t VGBBD_MASKS[256] = { + 0x0000000000000000ull, /* 00 */ + 0x0000000000000080ull, /* 01 */ + 0x0000000000008000ull, /* 02 */ + 0x0000000000008080ull, /* 03 */ + 0x0000000000800000ull, /* 04 */ + 0x0000000000800080ull, /* 05 */ + 0x0000000000808000ull, /* 06 */ + 0x0000000000808080ull, /* 07 */ + 0x0000000080000000ull, /* 08 */ + 0x0000000080000080ull, /* 09 */ + 0x0000000080008000ull, /* 0A */ + 0x0000000080008080ull, /* 0B */ + 0x0000000080800000ull, /* 0C */ + 0x0000000080800080ull, /* 0D */ + 0x0000000080808000ull, /* 0E */ + 0x0000000080808080ull, /* 0F */ + 0x0000008000000000ull, /* 10 */ + 0x0000008000000080ull, /* 11 */ + 0x0000008000008000ull, /* 12 */ + 0x0000008000008080ull, /* 13 */ + 0x0000008000800000ull, /* 14 */ + 0x0000008000800080ull, /* 15 */ + 0x0000008000808000ull, /* 16 */ + 0x0000008000808080ull, /* 17 */ + 0x0000008080000000ull, /* 18 */ + 0x0000008080000080ull, /* 19 */ + 0x0000008080008000ull, /* 1A */ + 0x0000008080008080ull, /* 1B */ + 0x0000008080800000ull, /* 1C */ + 0x0000008080800080ull, /* 1D */ + 0x0000008080808000ull, /* 1E */ + 0x0000008080808080ull, /* 1F */ + 0x0000800000000000ull, /* 20 */ + 0x0000800000000080ull, /* 21 */ + 0x0000800000008000ull, /* 22 */ + 0x0000800000008080ull, /* 23 */ + 0x0000800000800000ull, /* 24 */ + 0x0000800000800080ull, /* 25 */ + 0x0000800000808000ull, /* 26 */ + 0x0000800000808080ull, /* 27 */ + 0x0000800080000000ull, /* 28 */ + 0x0000800080000080ull, /* 29 */ + 0x0000800080008000ull, /* 2A */ + 0x0000800080008080ull, /* 2B */ + 0x0000800080800000ull, /* 2C */ + 0x0000800080800080ull, /* 2D */ + 0x0000800080808000ull, /* 2E */ + 0x0000800080808080ull, /* 2F */ + 0x0000808000000000ull, /* 30 */ + 0x0000808000000080ull, /* 31 */ + 0x0000808000008000ull, /* 32 */ + 0x0000808000008080ull, /* 33 */ + 0x0000808000800000ull, /* 34 */ + 0x0000808000800080ull, /* 35 */ + 0x0000808000808000ull, /* 36 */ + 0x0000808000808080ull, /* 37 */ + 0x0000808080000000ull, /* 38 */ + 0x0000808080000080ull, /* 39 */ + 0x0000808080008000ull, /* 3A */ + 0x0000808080008080ull, /* 3B */ + 0x0000808080800000ull, /* 3C */ + 0x0000808080800080ull, /* 3D */ + 0x0000808080808000ull, /* 3E */ + 0x0000808080808080ull, /* 3F */ + 0x0080000000000000ull, /* 40 */ + 0x0080000000000080ull, /* 41 */ + 0x0080000000008000ull, /* 42 */ + 0x0080000000008080ull, /* 43 */ + 0x0080000000800000ull, /* 44 */ + 0x0080000000800080ull, /* 45 */ + 0x0080000000808000ull, /* 46 */ + 0x0080000000808080ull, /* 47 */ + 0x0080000080000000ull, /* 48 */ + 0x0080000080000080ull, /* 49 */ + 0x0080000080008000ull, /* 4A */ + 0x0080000080008080ull, /* 4B */ + 0x0080000080800000ull, /* 4C */ + 0x0080000080800080ull, /* 4D */ + 0x0080000080808000ull, /* 4E */ + 0x0080000080808080ull, /* 4F */ + 0x0080008000000000ull, /* 50 */ + 0x0080008000000080ull, /* 51 */ + 0x0080008000008000ull, /* 52 */ + 0x0080008000008080ull, /* 53 */ + 0x0080008000800000ull, /* 54 */ + 0x0080008000800080ull, /* 55 */ + 0x0080008000808000ull, /* 56 */ + 0x0080008000808080ull, /* 57 */ + 0x0080008080000000ull, /* 58 */ + 0x0080008080000080ull, /* 59 */ + 0x0080008080008000ull, /* 5A */ + 0x0080008080008080ull, /* 5B */ + 0x0080008080800000ull, /* 5C */ + 0x0080008080800080ull, /* 5D */ + 0x0080008080808000ull, /* 5E */ + 0x0080008080808080ull, /* 5F */ + 0x0080800000000000ull, /* 60 */ + 0x0080800000000080ull, /* 61 */ + 0x0080800000008000ull, /* 62 */ + 0x0080800000008080ull, /* 63 */ + 0x0080800000800000ull, /* 64 */ + 0x0080800000800080ull, /* 65 */ + 0x0080800000808000ull, /* 66 */ + 0x0080800000808080ull, /* 67 */ + 0x0080800080000000ull, /* 68 */ + 0x0080800080000080ull, /* 69 */ + 0x0080800080008000ull, /* 6A */ + 0x0080800080008080ull, /* 6B */ + 0x0080800080800000ull, /* 6C */ + 0x0080800080800080ull, /* 6D */ + 0x0080800080808000ull, /* 6E */ + 0x0080800080808080ull, /* 6F */ + 0x0080808000000000ull, /* 70 */ + 0x0080808000000080ull, /* 71 */ + 0x0080808000008000ull, /* 72 */ + 0x0080808000008080ull, /* 73 */ + 0x0080808000800000ull, /* 74 */ + 0x0080808000800080ull, /* 75 */ + 0x0080808000808000ull, /* 76 */ + 0x0080808000808080ull, /* 77 */ + 0x0080808080000000ull, /* 78 */ + 0x0080808080000080ull, /* 79 */ + 0x0080808080008000ull, /* 7A */ + 0x0080808080008080ull, /* 7B */ + 0x0080808080800000ull, /* 7C */ + 0x0080808080800080ull, /* 7D */ + 0x0080808080808000ull, /* 7E */ + 0x0080808080808080ull, /* 7F */ + 0x8000000000000000ull, /* 80 */ + 0x8000000000000080ull, /* 81 */ + 0x8000000000008000ull, /* 82 */ + 0x8000000000008080ull, /* 83 */ + 0x8000000000800000ull, /* 84 */ + 0x8000000000800080ull, /* 85 */ + 0x8000000000808000ull, /* 86 */ + 0x8000000000808080ull, /* 87 */ + 0x8000000080000000ull, /* 88 */ + 0x8000000080000080ull, /* 89 */ + 0x8000000080008000ull, /* 8A */ + 0x8000000080008080ull, /* 8B */ + 0x8000000080800000ull, /* 8C */ + 0x8000000080800080ull, /* 8D */ + 0x8000000080808000ull, /* 8E */ + 0x8000000080808080ull, /* 8F */ + 0x8000008000000000ull, /* 90 */ + 0x8000008000000080ull, /* 91 */ + 0x8000008000008000ull, /* 92 */ + 0x8000008000008080ull, /* 93 */ + 0x8000008000800000ull, /* 94 */ + 0x8000008000800080ull, /* 95 */ + 0x8000008000808000ull, /* 96 */ + 0x8000008000808080ull, /* 97 */ + 0x8000008080000000ull, /* 98 */ + 0x8000008080000080ull, /* 99 */ + 0x8000008080008000ull, /* 9A */ + 0x8000008080008080ull, /* 9B */ + 0x8000008080800000ull, /* 9C */ + 0x8000008080800080ull, /* 9D */ + 0x8000008080808000ull, /* 9E */ + 0x8000008080808080ull, /* 9F */ + 0x8000800000000000ull, /* A0 */ + 0x8000800000000080ull, /* A1 */ + 0x8000800000008000ull, /* A2 */ + 0x8000800000008080ull, /* A3 */ + 0x8000800000800000ull, /* A4 */ + 0x8000800000800080ull, /* A5 */ + 0x8000800000808000ull, /* A6 */ + 0x8000800000808080ull, /* A7 */ + 0x8000800080000000ull, /* A8 */ + 0x8000800080000080ull, /* A9 */ + 0x8000800080008000ull, /* AA */ + 0x8000800080008080ull, /* AB */ + 0x8000800080800000ull, /* AC */ + 0x8000800080800080ull, /* AD */ + 0x8000800080808000ull, /* AE */ + 0x8000800080808080ull, /* AF */ + 0x8000808000000000ull, /* B0 */ + 0x8000808000000080ull, /* B1 */ + 0x8000808000008000ull, /* B2 */ + 0x8000808000008080ull, /* B3 */ + 0x8000808000800000ull, /* B4 */ + 0x8000808000800080ull, /* B5 */ + 0x8000808000808000ull, /* B6 */ + 0x8000808000808080ull, /* B7 */ + 0x8000808080000000ull, /* B8 */ + 0x8000808080000080ull, /* B9 */ + 0x8000808080008000ull, /* BA */ + 0x8000808080008080ull, /* BB */ + 0x8000808080800000ull, /* BC */ + 0x8000808080800080ull, /* BD */ + 0x8000808080808000ull, /* BE */ + 0x8000808080808080ull, /* BF */ + 0x8080000000000000ull, /* C0 */ + 0x8080000000000080ull, /* C1 */ + 0x8080000000008000ull, /* C2 */ + 0x8080000000008080ull, /* C3 */ + 0x8080000000800000ull, /* C4 */ + 0x8080000000800080ull, /* C5 */ + 0x8080000000808000ull, /* C6 */ + 0x8080000000808080ull, /* C7 */ + 0x8080000080000000ull, /* C8 */ + 0x8080000080000080ull, /* C9 */ + 0x8080000080008000ull, /* CA */ + 0x8080000080008080ull, /* CB */ + 0x8080000080800000ull, /* CC */ + 0x8080000080800080ull, /* CD */ + 0x8080000080808000ull, /* CE */ + 0x8080000080808080ull, /* CF */ + 0x8080008000000000ull, /* D0 */ + 0x8080008000000080ull, /* D1 */ + 0x8080008000008000ull, /* D2 */ + 0x8080008000008080ull, /* D3 */ + 0x8080008000800000ull, /* D4 */ + 0x8080008000800080ull, /* D5 */ + 0x8080008000808000ull, /* D6 */ + 0x8080008000808080ull, /* D7 */ + 0x8080008080000000ull, /* D8 */ + 0x8080008080000080ull, /* D9 */ + 0x8080008080008000ull, /* DA */ + 0x8080008080008080ull, /* DB */ + 0x8080008080800000ull, /* DC */ + 0x8080008080800080ull, /* DD */ + 0x8080008080808000ull, /* DE */ + 0x8080008080808080ull, /* DF */ + 0x8080800000000000ull, /* E0 */ + 0x8080800000000080ull, /* E1 */ + 0x8080800000008000ull, /* E2 */ + 0x8080800000008080ull, /* E3 */ + 0x8080800000800000ull, /* E4 */ + 0x8080800000800080ull, /* E5 */ + 0x8080800000808000ull, /* E6 */ + 0x8080800000808080ull, /* E7 */ + 0x8080800080000000ull, /* E8 */ + 0x8080800080000080ull, /* E9 */ + 0x8080800080008000ull, /* EA */ + 0x8080800080008080ull, /* EB */ + 0x8080800080800000ull, /* EC */ + 0x8080800080800080ull, /* ED */ + 0x8080800080808000ull, /* EE */ + 0x8080800080808080ull, /* EF */ + 0x8080808000000000ull, /* F0 */ + 0x8080808000000080ull, /* F1 */ + 0x8080808000008000ull, /* F2 */ + 0x8080808000008080ull, /* F3 */ + 0x8080808000800000ull, /* F4 */ + 0x8080808000800080ull, /* F5 */ + 0x8080808000808000ull, /* F6 */ + 0x8080808000808080ull, /* F7 */ + 0x8080808080000000ull, /* F8 */ + 0x8080808080000080ull, /* F9 */ + 0x8080808080008000ull, /* FA */ + 0x8080808080008080ull, /* FB */ + 0x8080808080800000ull, /* FC */ + 0x8080808080800080ull, /* FD */ + 0x8080808080808000ull, /* FE */ + 0x8080808080808080ull, /* FF */ +}; + +void helper_vgbbd(ppc_avr_t *r, ppc_avr_t *b) +{ + int i; + uint64_t t[2] = { 0, 0 }; + + VECTOR_FOR_INORDER_I(i, u8) { +#if defined(HOST_WORDS_BIGENDIAN) + t[i>>3] |= VGBBD_MASKS[b->u8[i]] >> (i & 7); +#else + t[i>>3] |= VGBBD_MASKS[b->u8[i]] >> (7-(i & 7)); +#endif + } + + r->u64[0] = t[0]; + r->u64[1] = t[1]; +} + +#define PMSUM(name, srcfld, trgfld, trgtyp) \ +void helper_##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ +{ \ + int i, j; \ + trgtyp prod[sizeof(ppc_avr_t)/sizeof(a->srcfld[0])]; \ + \ + VECTOR_FOR_INORDER_I(i, srcfld) { \ + prod[i] = 0; \ + for (j = 0; j < sizeof(a->srcfld[0]) * 8; j++) { \ + if (a->srcfld[i] & (1ull<<j)) { \ + prod[i] ^= ((trgtyp)b->srcfld[i] << j); \ + } \ + } \ + } \ + \ + VECTOR_FOR_INORDER_I(i, trgfld) { \ + r->trgfld[i] = prod[2*i] ^ prod[2*i+1]; \ + } \ +} + +PMSUM(vpmsumb, u8, u16, uint16_t) +PMSUM(vpmsumh, u16, u32, uint32_t) +PMSUM(vpmsumw, u32, u64, uint64_t) + +void helper_vpmsumd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + +#ifdef CONFIG_INT128 + int i, j; + __uint128_t prod[2]; + + VECTOR_FOR_INORDER_I(i, u64) { + prod[i] = 0; + for (j = 0; j < 64; j++) { + if (a->u64[i] & (1ull<<j)) { + prod[i] ^= (((__uint128_t)b->u64[i]) << j); + } + } + } + + r->u128 = prod[0] ^ prod[1]; + +#else + int i, j; + ppc_avr_t prod[2]; + + VECTOR_FOR_INORDER_I(i, u64) { + prod[i].u64[LO_IDX] = prod[i].u64[HI_IDX] = 0; + for (j = 0; j < 64; j++) { + if (a->u64[i] & (1ull<<j)) { + ppc_avr_t bshift; + if (j == 0) { + bshift.u64[HI_IDX] = 0; + bshift.u64[LO_IDX] = b->u64[i]; + } else { + bshift.u64[HI_IDX] = b->u64[i] >> (64-j); + bshift.u64[LO_IDX] = b->u64[i] << j; + } + prod[i].u64[LO_IDX] ^= bshift.u64[LO_IDX]; + prod[i].u64[HI_IDX] ^= bshift.u64[HI_IDX]; + } + } + } + + r->u64[LO_IDX] = prod[0].u64[LO_IDX] ^ prod[1].u64[LO_IDX]; + r->u64[HI_IDX] = prod[0].u64[HI_IDX] ^ prod[1].u64[HI_IDX]; +#endif +} + + +#if defined(HOST_WORDS_BIGENDIAN) +#define PKBIG 1 +#else +#define PKBIG 0 +#endif +void helper_vpkpx(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i, j; + ppc_avr_t result; +#if defined(HOST_WORDS_BIGENDIAN) + const ppc_avr_t *x[2] = { a, b }; +#else + const ppc_avr_t *x[2] = { b, a }; +#endif + + VECTOR_FOR_INORDER_I(i, u64) { + VECTOR_FOR_INORDER_I(j, u32) { + uint32_t e = x[i]->u32[j]; + + result.u16[4*i+j] = (((e >> 9) & 0xfc00) | + ((e >> 6) & 0x3e0) | + ((e >> 3) & 0x1f)); + } + } + *r = result; +} + +#define VPK(suffix, from, to, cvt, dosat) \ + void helper_vpk##suffix(CPUPPCState *env, ppc_avr_t *r, \ + ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int i; \ + int sat = 0; \ + ppc_avr_t result; \ + ppc_avr_t *a0 = PKBIG ? a : b; \ + ppc_avr_t *a1 = PKBIG ? b : a; \ + \ + VECTOR_FOR_INORDER_I(i, from) { \ + result.to[i] = cvt(a0->from[i], &sat); \ + result.to[i+ARRAY_SIZE(r->from)] = cvt(a1->from[i], &sat); \ + } \ + *r = result; \ + if (dosat && sat) { \ + env->vscr |= (1 << VSCR_SAT); \ + } \ + } +#define I(x, y) (x) +VPK(shss, s16, s8, cvtshsb, 1) +VPK(shus, s16, u8, cvtshub, 1) +VPK(swss, s32, s16, cvtswsh, 1) +VPK(swus, s32, u16, cvtswuh, 1) +VPK(sdss, s64, s32, cvtsdsw, 1) +VPK(sdus, s64, u32, cvtsduw, 1) +VPK(uhus, u16, u8, cvtuhub, 1) +VPK(uwus, u32, u16, cvtuwuh, 1) +VPK(udus, u64, u32, cvtuduw, 1) +VPK(uhum, u16, u8, I, 0) +VPK(uwum, u32, u16, I, 0) +VPK(udum, u64, u32, I, 0) +#undef I +#undef VPK +#undef PKBIG + +void helper_vrefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(r->f); i++) { + r->f[i] = float32_div(float32_one, b->f[i], &env->vec_status); + } +} + +#define VRFI(suffix, rounding) \ + void helper_vrfi##suffix(CPUPPCState *env, ppc_avr_t *r, \ + ppc_avr_t *b) \ + { \ + int i; \ + float_status s = env->vec_status; \ + \ + set_float_rounding_mode(rounding, &s); \ + for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ + r->f[i] = float32_round_to_int (b->f[i], &s); \ + } \ + } +VRFI(n, float_round_nearest_even) +VRFI(m, float_round_down) +VRFI(p, float_round_up) +VRFI(z, float_round_to_zero) +#undef VRFI + +#define VROTATE(suffix, element, mask) \ + void helper_vrl##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + unsigned int shift = b->element[i] & mask; \ + r->element[i] = (a->element[i] << shift) | \ + (a->element[i] >> (sizeof(a->element[0]) * 8 - shift)); \ + } \ + } +VROTATE(b, u8, 0x7) +VROTATE(h, u16, 0xF) +VROTATE(w, u32, 0x1F) +VROTATE(d, u64, 0x3F) +#undef VROTATE + +void helper_vrsqrtefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(r->f); i++) { + float32 t = float32_sqrt(b->f[i], &env->vec_status); + + r->f[i] = float32_div(float32_one, t, &env->vec_status); + } +} + +#define VRLMI(name, size, element, insert) \ +void helper_##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ +{ \ + int i; \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + uint##size##_t src1 = a->element[i]; \ + uint##size##_t src2 = b->element[i]; \ + uint##size##_t src3 = r->element[i]; \ + uint##size##_t begin, end, shift, mask, rot_val; \ + \ + shift = extract##size(src2, 0, 6); \ + end = extract##size(src2, 8, 6); \ + begin = extract##size(src2, 16, 6); \ + rot_val = rol##size(src1, shift); \ + mask = mask_u##size(begin, end); \ + if (insert) { \ + r->element[i] = (rot_val & mask) | (src3 & ~mask); \ + } else { \ + r->element[i] = (rot_val & mask); \ + } \ + } \ +} + +VRLMI(vrldmi, 64, u64, 1); +VRLMI(vrlwmi, 32, u32, 1); +VRLMI(vrldnm, 64, u64, 0); +VRLMI(vrlwnm, 32, u32, 0); + +void helper_vsel(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, + ppc_avr_t *c) +{ + r->u64[0] = (a->u64[0] & ~c->u64[0]) | (b->u64[0] & c->u64[0]); + r->u64[1] = (a->u64[1] & ~c->u64[1]) | (b->u64[1] & c->u64[1]); +} + +void helper_vexptefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(r->f); i++) { + r->f[i] = float32_exp2(b->f[i], &env->vec_status); + } +} + +void helper_vlogefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(r->f); i++) { + r->f[i] = float32_log2(b->f[i], &env->vec_status); + } +} + +/* The specification says that the results are undefined if all of the + * shift counts are not identical. We check to make sure that they are + * to conform to what real hardware appears to do. */ +#define VSHIFT(suffix, leftp) \ + void helper_vs##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int shift = b->u8[LO_IDX*15] & 0x7; \ + int doit = 1; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->u8); i++) { \ + doit = doit && ((b->u8[i] & 0x7) == shift); \ + } \ + if (doit) { \ + if (shift == 0) { \ + *r = *a; \ + } else if (leftp) { \ + uint64_t carry = a->u64[LO_IDX] >> (64 - shift); \ + \ + r->u64[HI_IDX] = (a->u64[HI_IDX] << shift) | carry; \ + r->u64[LO_IDX] = a->u64[LO_IDX] << shift; \ + } else { \ + uint64_t carry = a->u64[HI_IDX] << (64 - shift); \ + \ + r->u64[LO_IDX] = (a->u64[LO_IDX] >> shift) | carry; \ + r->u64[HI_IDX] = a->u64[HI_IDX] >> shift; \ + } \ + } \ + } +VSHIFT(l, 1) +VSHIFT(r, 0) +#undef VSHIFT + +#define VSL(suffix, element, mask) \ + void helper_vsl##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + unsigned int shift = b->element[i] & mask; \ + \ + r->element[i] = a->element[i] << shift; \ + } \ + } +VSL(b, u8, 0x7) +VSL(h, u16, 0x0F) +VSL(w, u32, 0x1F) +VSL(d, u64, 0x3F) +#undef VSL + +void helper_vslv(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i; + unsigned int shift, bytes, size; + + size = ARRAY_SIZE(r->u8); + for (i = 0; i < size; i++) { + shift = b->u8[i] & 0x7; /* extract shift value */ + bytes = (a->u8[i] << 8) + /* extract adjacent bytes */ + (((i + 1) < size) ? a->u8[i + 1] : 0); + r->u8[i] = (bytes << shift) >> 8; /* shift and store result */ + } +} + +void helper_vsrv(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i; + unsigned int shift, bytes; + + /* Use reverse order, as destination and source register can be same. Its + * being modified in place saving temporary, reverse order will guarantee + * that computed result is not fed back. + */ + for (i = ARRAY_SIZE(r->u8) - 1; i >= 0; i--) { + shift = b->u8[i] & 0x7; /* extract shift value */ + bytes = ((i ? a->u8[i - 1] : 0) << 8) + a->u8[i]; + /* extract adjacent bytes */ + r->u8[i] = (bytes >> shift) & 0xFF; /* shift and store result */ + } +} + +void helper_vsldoi(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t shift) +{ + int sh = shift & 0xf; + int i; + ppc_avr_t result; + +#if defined(HOST_WORDS_BIGENDIAN) + for (i = 0; i < ARRAY_SIZE(r->u8); i++) { + int index = sh + i; + if (index > 0xf) { + result.u8[i] = b->u8[index - 0x10]; + } else { + result.u8[i] = a->u8[index]; + } + } +#else + for (i = 0; i < ARRAY_SIZE(r->u8); i++) { + int index = (16 - sh) + i; + if (index > 0xf) { + result.u8[i] = a->u8[index - 0x10]; + } else { + result.u8[i] = b->u8[index]; + } + } +#endif + *r = result; +} + +void helper_vslo(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int sh = (b->u8[LO_IDX*0xf] >> 3) & 0xf; + +#if defined(HOST_WORDS_BIGENDIAN) + memmove(&r->u8[0], &a->u8[sh], 16 - sh); + memset(&r->u8[16-sh], 0, sh); +#else + memmove(&r->u8[sh], &a->u8[0], 16 - sh); + memset(&r->u8[0], 0, sh); +#endif +} + +/* Experimental testing shows that hardware masks the immediate. */ +#define _SPLAT_MASKED(element) (splat & (ARRAY_SIZE(r->element) - 1)) +#if defined(HOST_WORDS_BIGENDIAN) +#define SPLAT_ELEMENT(element) _SPLAT_MASKED(element) +#else +#define SPLAT_ELEMENT(element) \ + (ARRAY_SIZE(r->element) - 1 - _SPLAT_MASKED(element)) +#endif +#define VSPLT(suffix, element) \ + void helper_vsplt##suffix(ppc_avr_t *r, ppc_avr_t *b, uint32_t splat) \ + { \ + uint32_t s = b->element[SPLAT_ELEMENT(element)]; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + r->element[i] = s; \ + } \ + } +VSPLT(b, u8) +VSPLT(h, u16) +VSPLT(w, u32) +#undef VSPLT +#undef SPLAT_ELEMENT +#undef _SPLAT_MASKED +#if defined(HOST_WORDS_BIGENDIAN) +#define VINSERT(suffix, element) \ + void helper_vinsert##suffix(ppc_avr_t *r, ppc_avr_t *b, uint32_t index) \ + { \ + memmove(&r->u8[index], &b->u8[8 - sizeof(r->element)], \ + sizeof(r->element[0])); \ + } +#else +#define VINSERT(suffix, element) \ + void helper_vinsert##suffix(ppc_avr_t *r, ppc_avr_t *b, uint32_t index) \ + { \ + uint32_t d = (16 - index) - sizeof(r->element[0]); \ + memmove(&r->u8[d], &b->u8[8], sizeof(r->element[0])); \ + } +#endif +VINSERT(b, u8) +VINSERT(h, u16) +VINSERT(w, u32) +VINSERT(d, u64) +#undef VINSERT +#if defined(HOST_WORDS_BIGENDIAN) +#define VEXTRACT(suffix, element) \ + void helper_vextract##suffix(ppc_avr_t *r, ppc_avr_t *b, uint32_t index) \ + { \ + uint32_t es = sizeof(r->element[0]); \ + memmove(&r->u8[8 - es], &b->u8[index], es); \ + memset(&r->u8[8], 0, 8); \ + memset(&r->u8[0], 0, 8 - es); \ + } +#else +#define VEXTRACT(suffix, element) \ + void helper_vextract##suffix(ppc_avr_t *r, ppc_avr_t *b, uint32_t index) \ + { \ + uint32_t es = sizeof(r->element[0]); \ + uint32_t s = (16 - index) - es; \ + memmove(&r->u8[8], &b->u8[s], es); \ + memset(&r->u8[0], 0, 8); \ + memset(&r->u8[8 + es], 0, 8 - es); \ + } +#endif +VEXTRACT(ub, u8) +VEXTRACT(uh, u16) +VEXTRACT(uw, u32) +VEXTRACT(d, u64) +#undef VEXTRACT + +#define VEXT_SIGNED(name, element, mask, cast, recast) \ +void helper_##name(ppc_avr_t *r, ppc_avr_t *b) \ +{ \ + int i; \ + VECTOR_FOR_INORDER_I(i, element) { \ + r->element[i] = (recast)((cast)(b->element[i] & mask)); \ + } \ +} +VEXT_SIGNED(vextsb2w, s32, UINT8_MAX, int8_t, int32_t) +VEXT_SIGNED(vextsb2d, s64, UINT8_MAX, int8_t, int64_t) +VEXT_SIGNED(vextsh2w, s32, UINT16_MAX, int16_t, int32_t) +VEXT_SIGNED(vextsh2d, s64, UINT16_MAX, int16_t, int64_t) +VEXT_SIGNED(vextsw2d, s64, UINT32_MAX, int32_t, int64_t) +#undef VEXT_SIGNED + +#define VNEG(name, element) \ +void helper_##name(ppc_avr_t *r, ppc_avr_t *b) \ +{ \ + int i; \ + VECTOR_FOR_INORDER_I(i, element) { \ + r->element[i] = -b->element[i]; \ + } \ +} +VNEG(vnegw, s32) +VNEG(vnegd, s64) +#undef VNEG + +#define VSPLTI(suffix, element, splat_type) \ + void helper_vspltis##suffix(ppc_avr_t *r, uint32_t splat) \ + { \ + splat_type x = (int8_t)(splat << 3) >> 3; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + r->element[i] = x; \ + } \ + } +VSPLTI(b, s8, int8_t) +VSPLTI(h, s16, int16_t) +VSPLTI(w, s32, int32_t) +#undef VSPLTI + +#define VSR(suffix, element, mask) \ + void helper_vsr##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + unsigned int shift = b->element[i] & mask; \ + r->element[i] = a->element[i] >> shift; \ + } \ + } +VSR(ab, s8, 0x7) +VSR(ah, s16, 0xF) +VSR(aw, s32, 0x1F) +VSR(ad, s64, 0x3F) +VSR(b, u8, 0x7) +VSR(h, u16, 0xF) +VSR(w, u32, 0x1F) +VSR(d, u64, 0x3F) +#undef VSR + +void helper_vsro(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int sh = (b->u8[LO_IDX * 0xf] >> 3) & 0xf; + +#if defined(HOST_WORDS_BIGENDIAN) + memmove(&r->u8[sh], &a->u8[0], 16 - sh); + memset(&r->u8[0], 0, sh); +#else + memmove(&r->u8[0], &a->u8[sh], 16 - sh); + memset(&r->u8[16 - sh], 0, sh); +#endif +} + +void helper_vsubcuw(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(r->u32); i++) { + r->u32[i] = a->u32[i] >= b->u32[i]; + } +} + +void helper_vsumsws(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int64_t t; + int i, upper; + ppc_avr_t result; + int sat = 0; + +#if defined(HOST_WORDS_BIGENDIAN) + upper = ARRAY_SIZE(r->s32)-1; +#else + upper = 0; +#endif + t = (int64_t)b->s32[upper]; + for (i = 0; i < ARRAY_SIZE(r->s32); i++) { + t += a->s32[i]; + result.s32[i] = 0; + } + result.s32[upper] = cvtsdsw(t, &sat); + *r = result; + + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +void helper_vsum2sws(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i, j, upper; + ppc_avr_t result; + int sat = 0; + +#if defined(HOST_WORDS_BIGENDIAN) + upper = 1; +#else + upper = 0; +#endif + for (i = 0; i < ARRAY_SIZE(r->u64); i++) { + int64_t t = (int64_t)b->s32[upper + i * 2]; + + result.u64[i] = 0; + for (j = 0; j < ARRAY_SIZE(r->u64); j++) { + t += a->s32[2 * i + j]; + } + result.s32[upper + i * 2] = cvtsdsw(t, &sat); + } + + *r = result; + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +void helper_vsum4sbs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i, j; + int sat = 0; + + for (i = 0; i < ARRAY_SIZE(r->s32); i++) { + int64_t t = (int64_t)b->s32[i]; + + for (j = 0; j < ARRAY_SIZE(r->s32); j++) { + t += a->s8[4 * i + j]; + } + r->s32[i] = cvtsdsw(t, &sat); + } + + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +void helper_vsum4shs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int sat = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(r->s32); i++) { + int64_t t = (int64_t)b->s32[i]; + + t += a->s16[2 * i] + a->s16[2 * i + 1]; + r->s32[i] = cvtsdsw(t, &sat); + } + + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +void helper_vsum4ubs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i, j; + int sat = 0; + + for (i = 0; i < ARRAY_SIZE(r->u32); i++) { + uint64_t t = (uint64_t)b->u32[i]; + + for (j = 0; j < ARRAY_SIZE(r->u32); j++) { + t += a->u8[4 * i + j]; + } + r->u32[i] = cvtuduw(t, &sat); + } + + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +#if defined(HOST_WORDS_BIGENDIAN) +#define UPKHI 1 +#define UPKLO 0 +#else +#define UPKHI 0 +#define UPKLO 1 +#endif +#define VUPKPX(suffix, hi) \ + void helper_vupk##suffix(ppc_avr_t *r, ppc_avr_t *b) \ + { \ + int i; \ + ppc_avr_t result; \ + \ + for (i = 0; i < ARRAY_SIZE(r->u32); i++) { \ + uint16_t e = b->u16[hi ? i : i+4]; \ + uint8_t a = (e >> 15) ? 0xff : 0; \ + uint8_t r = (e >> 10) & 0x1f; \ + uint8_t g = (e >> 5) & 0x1f; \ + uint8_t b = e & 0x1f; \ + \ + result.u32[i] = (a << 24) | (r << 16) | (g << 8) | b; \ + } \ + *r = result; \ + } +VUPKPX(lpx, UPKLO) +VUPKPX(hpx, UPKHI) +#undef VUPKPX + +#define VUPK(suffix, unpacked, packee, hi) \ + void helper_vupk##suffix(ppc_avr_t *r, ppc_avr_t *b) \ + { \ + int i; \ + ppc_avr_t result; \ + \ + if (hi) { \ + for (i = 0; i < ARRAY_SIZE(r->unpacked); i++) { \ + result.unpacked[i] = b->packee[i]; \ + } \ + } else { \ + for (i = ARRAY_SIZE(r->unpacked); i < ARRAY_SIZE(r->packee); \ + i++) { \ + result.unpacked[i - ARRAY_SIZE(r->unpacked)] = b->packee[i]; \ + } \ + } \ + *r = result; \ + } +VUPK(hsb, s16, s8, UPKHI) +VUPK(hsh, s32, s16, UPKHI) +VUPK(hsw, s64, s32, UPKHI) +VUPK(lsb, s16, s8, UPKLO) +VUPK(lsh, s32, s16, UPKLO) +VUPK(lsw, s64, s32, UPKLO) +#undef VUPK +#undef UPKHI +#undef UPKLO + +#define VGENERIC_DO(name, element) \ + void helper_v##name(ppc_avr_t *r, ppc_avr_t *b) \ + { \ + int i; \ + \ + VECTOR_FOR_INORDER_I(i, element) { \ + r->element[i] = name(b->element[i]); \ + } \ + } + +#define clzb(v) ((v) ? clz32((uint32_t)(v) << 24) : 8) +#define clzh(v) ((v) ? clz32((uint32_t)(v) << 16) : 16) +#define clzw(v) clz32((v)) +#define clzd(v) clz64((v)) + +VGENERIC_DO(clzb, u8) +VGENERIC_DO(clzh, u16) +VGENERIC_DO(clzw, u32) +VGENERIC_DO(clzd, u64) + +#undef clzb +#undef clzh +#undef clzw +#undef clzd + +#define ctzb(v) ((v) ? ctz32(v) : 8) +#define ctzh(v) ((v) ? ctz32(v) : 16) +#define ctzw(v) ctz32((v)) +#define ctzd(v) ctz64((v)) + +VGENERIC_DO(ctzb, u8) +VGENERIC_DO(ctzh, u16) +VGENERIC_DO(ctzw, u32) +VGENERIC_DO(ctzd, u64) + +#undef ctzb +#undef ctzh +#undef ctzw +#undef ctzd + +#define popcntb(v) ctpop8(v) +#define popcnth(v) ctpop16(v) +#define popcntw(v) ctpop32(v) +#define popcntd(v) ctpop64(v) + +VGENERIC_DO(popcntb, u8) +VGENERIC_DO(popcnth, u16) +VGENERIC_DO(popcntw, u32) +VGENERIC_DO(popcntd, u64) + +#undef popcntb +#undef popcnth +#undef popcntw +#undef popcntd + +#undef VGENERIC_DO + +#if defined(HOST_WORDS_BIGENDIAN) +#define QW_ONE { .u64 = { 0, 1 } } +#else +#define QW_ONE { .u64 = { 1, 0 } } +#endif + +#ifndef CONFIG_INT128 + +static inline void avr_qw_not(ppc_avr_t *t, ppc_avr_t a) +{ + t->u64[0] = ~a.u64[0]; + t->u64[1] = ~a.u64[1]; +} + +static int avr_qw_cmpu(ppc_avr_t a, ppc_avr_t b) +{ + if (a.u64[HI_IDX] < b.u64[HI_IDX]) { + return -1; + } else if (a.u64[HI_IDX] > b.u64[HI_IDX]) { + return 1; + } else if (a.u64[LO_IDX] < b.u64[LO_IDX]) { + return -1; + } else if (a.u64[LO_IDX] > b.u64[LO_IDX]) { + return 1; + } else { + return 0; + } +} + +static void avr_qw_add(ppc_avr_t *t, ppc_avr_t a, ppc_avr_t b) +{ + t->u64[LO_IDX] = a.u64[LO_IDX] + b.u64[LO_IDX]; + t->u64[HI_IDX] = a.u64[HI_IDX] + b.u64[HI_IDX] + + (~a.u64[LO_IDX] < b.u64[LO_IDX]); +} + +static int avr_qw_addc(ppc_avr_t *t, ppc_avr_t a, ppc_avr_t b) +{ + ppc_avr_t not_a; + t->u64[LO_IDX] = a.u64[LO_IDX] + b.u64[LO_IDX]; + t->u64[HI_IDX] = a.u64[HI_IDX] + b.u64[HI_IDX] + + (~a.u64[LO_IDX] < b.u64[LO_IDX]); + avr_qw_not(¬_a, a); + return avr_qw_cmpu(not_a, b) < 0; +} + +#endif + +void helper_vadduqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ +#ifdef CONFIG_INT128 + r->u128 = a->u128 + b->u128; +#else + avr_qw_add(r, *a, *b); +#endif +} + +void helper_vaddeuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ +#ifdef CONFIG_INT128 + r->u128 = a->u128 + b->u128 + (c->u128 & 1); +#else + + if (c->u64[LO_IDX] & 1) { + ppc_avr_t tmp; + + tmp.u64[HI_IDX] = 0; + tmp.u64[LO_IDX] = c->u64[LO_IDX] & 1; + avr_qw_add(&tmp, *a, tmp); + avr_qw_add(r, tmp, *b); + } else { + avr_qw_add(r, *a, *b); + } +#endif +} + +void helper_vaddcuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ +#ifdef CONFIG_INT128 + r->u128 = (~a->u128 < b->u128); +#else + ppc_avr_t not_a; + + avr_qw_not(¬_a, *a); + + r->u64[HI_IDX] = 0; + r->u64[LO_IDX] = (avr_qw_cmpu(not_a, *b) < 0); +#endif +} + +void helper_vaddecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ +#ifdef CONFIG_INT128 + int carry_out = (~a->u128 < b->u128); + if (!carry_out && (c->u128 & 1)) { + carry_out = ((a->u128 + b->u128 + 1) == 0) && + ((a->u128 != 0) || (b->u128 != 0)); + } + r->u128 = carry_out; +#else + + int carry_in = c->u64[LO_IDX] & 1; + int carry_out = 0; + ppc_avr_t tmp; + + carry_out = avr_qw_addc(&tmp, *a, *b); + + if (!carry_out && carry_in) { + ppc_avr_t one = QW_ONE; + carry_out = avr_qw_addc(&tmp, tmp, one); + } + r->u64[HI_IDX] = 0; + r->u64[LO_IDX] = carry_out; +#endif +} + +void helper_vsubuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ +#ifdef CONFIG_INT128 + r->u128 = a->u128 - b->u128; +#else + ppc_avr_t tmp; + ppc_avr_t one = QW_ONE; + + avr_qw_not(&tmp, *b); + avr_qw_add(&tmp, *a, tmp); + avr_qw_add(r, tmp, one); +#endif +} + +void helper_vsubeuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ +#ifdef CONFIG_INT128 + r->u128 = a->u128 + ~b->u128 + (c->u128 & 1); +#else + ppc_avr_t tmp, sum; + + avr_qw_not(&tmp, *b); + avr_qw_add(&sum, *a, tmp); + + tmp.u64[HI_IDX] = 0; + tmp.u64[LO_IDX] = c->u64[LO_IDX] & 1; + avr_qw_add(r, sum, tmp); +#endif +} + +void helper_vsubcuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ +#ifdef CONFIG_INT128 + r->u128 = (~a->u128 < ~b->u128) || + (a->u128 + ~b->u128 == (__uint128_t)-1); +#else + int carry = (avr_qw_cmpu(*a, *b) > 0); + if (!carry) { + ppc_avr_t tmp; + avr_qw_not(&tmp, *b); + avr_qw_add(&tmp, *a, tmp); + carry = ((tmp.s64[HI_IDX] == -1ull) && (tmp.s64[LO_IDX] == -1ull)); + } + r->u64[HI_IDX] = 0; + r->u64[LO_IDX] = carry; +#endif +} + +void helper_vsubecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ +#ifdef CONFIG_INT128 + r->u128 = + (~a->u128 < ~b->u128) || + ((c->u128 & 1) && (a->u128 + ~b->u128 == (__uint128_t)-1)); +#else + int carry_in = c->u64[LO_IDX] & 1; + int carry_out = (avr_qw_cmpu(*a, *b) > 0); + if (!carry_out && carry_in) { + ppc_avr_t tmp; + avr_qw_not(&tmp, *b); + avr_qw_add(&tmp, *a, tmp); + carry_out = ((tmp.u64[HI_IDX] == -1ull) && (tmp.u64[LO_IDX] == -1ull)); + } + + r->u64[HI_IDX] = 0; + r->u64[LO_IDX] = carry_out; +#endif +} + +#define BCD_PLUS_PREF_1 0xC +#define BCD_PLUS_PREF_2 0xF +#define BCD_PLUS_ALT_1 0xA +#define BCD_NEG_PREF 0xD +#define BCD_NEG_ALT 0xB +#define BCD_PLUS_ALT_2 0xE +#define NATIONAL_PLUS 0x2B +#define NATIONAL_NEG 0x2D + +#if defined(HOST_WORDS_BIGENDIAN) +#define BCD_DIG_BYTE(n) (15 - (n/2)) +#else +#define BCD_DIG_BYTE(n) (n/2) +#endif + +static int bcd_get_sgn(ppc_avr_t *bcd) +{ + switch (bcd->u8[BCD_DIG_BYTE(0)] & 0xF) { + case BCD_PLUS_PREF_1: + case BCD_PLUS_PREF_2: + case BCD_PLUS_ALT_1: + case BCD_PLUS_ALT_2: + { + return 1; + } + + case BCD_NEG_PREF: + case BCD_NEG_ALT: + { + return -1; + } + + default: + { + return 0; + } + } +} + +static int bcd_preferred_sgn(int sgn, int ps) +{ + if (sgn >= 0) { + return (ps == 0) ? BCD_PLUS_PREF_1 : BCD_PLUS_PREF_2; + } else { + return BCD_NEG_PREF; + } +} + +static uint8_t bcd_get_digit(ppc_avr_t *bcd, int n, int *invalid) +{ + uint8_t result; + if (n & 1) { + result = bcd->u8[BCD_DIG_BYTE(n)] >> 4; + } else { + result = bcd->u8[BCD_DIG_BYTE(n)] & 0xF; + } + + if (unlikely(result > 9)) { + *invalid = true; + } + return result; +} + +static void bcd_put_digit(ppc_avr_t *bcd, uint8_t digit, int n) +{ + if (n & 1) { + bcd->u8[BCD_DIG_BYTE(n)] &= 0x0F; + bcd->u8[BCD_DIG_BYTE(n)] |= (digit<<4); + } else { + bcd->u8[BCD_DIG_BYTE(n)] &= 0xF0; + bcd->u8[BCD_DIG_BYTE(n)] |= digit; + } +} + +static int bcd_cmp_zero(ppc_avr_t *bcd) +{ + if (bcd->u64[HI_IDX] == 0 && (bcd->u64[LO_IDX] >> 4) == 0) { + return 1 << CRF_EQ; + } else { + return (bcd_get_sgn(bcd) == 1) ? 1 << CRF_GT : 1 << CRF_LT; + } +} + +static uint16_t get_national_digit(ppc_avr_t *reg, int n) +{ +#if defined(HOST_WORDS_BIGENDIAN) + return reg->u16[7 - n]; +#else + return reg->u16[n]; +#endif +} + +static void set_national_digit(ppc_avr_t *reg, uint8_t val, int n) +{ +#if defined(HOST_WORDS_BIGENDIAN) + reg->u16[7 - n] = val; +#else + reg->u16[n] = val; +#endif +} + +static int bcd_cmp_mag(ppc_avr_t *a, ppc_avr_t *b) +{ + int i; + int invalid = 0; + for (i = 31; i > 0; i--) { + uint8_t dig_a = bcd_get_digit(a, i, &invalid); + uint8_t dig_b = bcd_get_digit(b, i, &invalid); + if (unlikely(invalid)) { + return 0; /* doesn't matter */ + } else if (dig_a > dig_b) { + return 1; + } else if (dig_a < dig_b) { + return -1; + } + } + + return 0; +} + +static int bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, + int *overflow) +{ + int carry = 0; + int i; + int is_zero = 1; + for (i = 1; i <= 31; i++) { + uint8_t digit = bcd_get_digit(a, i, invalid) + + bcd_get_digit(b, i, invalid) + carry; + is_zero &= (digit == 0); + if (digit > 9) { + carry = 1; + digit -= 10; + } else { + carry = 0; + } + + bcd_put_digit(t, digit, i); + + if (unlikely(*invalid)) { + return -1; + } + } + + *overflow = carry; + return is_zero; +} + +static int bcd_sub_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, + int *overflow) +{ + int carry = 0; + int i; + int is_zero = 1; + for (i = 1; i <= 31; i++) { + uint8_t digit = bcd_get_digit(a, i, invalid) - + bcd_get_digit(b, i, invalid) + carry; + is_zero &= (digit == 0); + if (digit & 0x80) { + carry = -1; + digit += 10; + } else { + carry = 0; + } + + bcd_put_digit(t, digit, i); + + if (unlikely(*invalid)) { + return -1; + } + } + + *overflow = carry; + return is_zero; +} + +uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) +{ + + int sgna = bcd_get_sgn(a); + int sgnb = bcd_get_sgn(b); + int invalid = (sgna == 0) || (sgnb == 0); + int overflow = 0; + int zero = 0; + uint32_t cr = 0; + ppc_avr_t result = { .u64 = { 0, 0 } }; + + if (!invalid) { + if (sgna == sgnb) { + result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps); + zero = bcd_add_mag(&result, a, b, &invalid, &overflow); + cr = (sgna > 0) ? 1 << CRF_GT : 1 << CRF_LT; + } else if (bcd_cmp_mag(a, b) > 0) { + result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps); + zero = bcd_sub_mag(&result, a, b, &invalid, &overflow); + cr = (sgna > 0) ? 1 << CRF_GT : 1 << CRF_LT; + } else { + result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgnb, ps); + zero = bcd_sub_mag(&result, b, a, &invalid, &overflow); + cr = (sgnb > 0) ? 1 << CRF_GT : 1 << CRF_LT; + } + } + + if (unlikely(invalid)) { + result.u64[HI_IDX] = result.u64[LO_IDX] = -1; + cr = 1 << CRF_SO; + } else if (overflow) { + cr |= 1 << CRF_SO; + } else if (zero) { + cr = 1 << CRF_EQ; + } + + *r = result; + + return cr; +} + +uint32_t helper_bcdsub(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) +{ + ppc_avr_t bcopy = *b; + int sgnb = bcd_get_sgn(b); + if (sgnb < 0) { + bcd_put_digit(&bcopy, BCD_PLUS_PREF_1, 0); + } else if (sgnb > 0) { + bcd_put_digit(&bcopy, BCD_NEG_PREF, 0); + } + /* else invalid ... defer to bcdadd code for proper handling */ + + return helper_bcdadd(r, a, &bcopy, ps); +} + +uint32_t helper_bcdcfn(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) +{ + int i; + int cr = 0; + uint16_t national = 0; + uint16_t sgnb = get_national_digit(b, 0); + ppc_avr_t ret = { .u64 = { 0, 0 } }; + int invalid = (sgnb != NATIONAL_PLUS && sgnb != NATIONAL_NEG); + + for (i = 1; i < 8; i++) { + national = get_national_digit(b, i); + if (unlikely(national < 0x30 || national > 0x39)) { + invalid = 1; + break; + } + + bcd_put_digit(&ret, national & 0xf, i); + } + + if (sgnb == NATIONAL_PLUS) { + bcd_put_digit(&ret, (ps == 0) ? BCD_PLUS_PREF_1 : BCD_PLUS_PREF_2, 0); + } else { + bcd_put_digit(&ret, BCD_NEG_PREF, 0); + } + + cr = bcd_cmp_zero(&ret); + + if (unlikely(invalid)) { + cr = 1 << CRF_SO; + } + + *r = ret; + + return cr; +} + +uint32_t helper_bcdctn(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) +{ + int i; + int cr = 0; + int sgnb = bcd_get_sgn(b); + int invalid = (sgnb == 0); + ppc_avr_t ret = { .u64 = { 0, 0 } }; + + int ox_flag = (b->u64[HI_IDX] != 0) || ((b->u64[LO_IDX] >> 32) != 0); + + for (i = 1; i < 8; i++) { + set_national_digit(&ret, 0x30 + bcd_get_digit(b, i, &invalid), i); + + if (unlikely(invalid)) { + break; + } + } + set_national_digit(&ret, (sgnb == -1) ? NATIONAL_NEG : NATIONAL_PLUS, 0); + + cr = bcd_cmp_zero(b); + + if (ox_flag) { + cr |= 1 << CRF_SO; + } + + if (unlikely(invalid)) { + cr = 1 << CRF_SO; + } + + *r = ret; + + return cr; +} + +uint32_t helper_bcdcfz(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) +{ + int i; + int cr = 0; + int invalid = 0; + int zone_digit = 0; + int zone_lead = ps ? 0xF : 0x3; + int digit = 0; + ppc_avr_t ret = { .u64 = { 0, 0 } }; + int sgnb = b->u8[BCD_DIG_BYTE(0)] >> 4; + + if (unlikely((sgnb < 0xA) && ps)) { + invalid = 1; + } + + for (i = 0; i < 16; i++) { + zone_digit = (i * 2) ? b->u8[BCD_DIG_BYTE(i * 2)] >> 4 : zone_lead; + digit = b->u8[BCD_DIG_BYTE(i * 2)] & 0xF; + if (unlikely(zone_digit != zone_lead || digit > 0x9)) { + invalid = 1; + break; + } + + bcd_put_digit(&ret, digit, i + 1); + } + + if ((ps && (sgnb == 0xB || sgnb == 0xD)) || + (!ps && (sgnb & 0x4))) { + bcd_put_digit(&ret, BCD_NEG_PREF, 0); + } else { + bcd_put_digit(&ret, BCD_PLUS_PREF_1, 0); + } + + cr = bcd_cmp_zero(&ret); + + if (unlikely(invalid)) { + cr = 1 << CRF_SO; + } + + *r = ret; + + return cr; +} + +uint32_t helper_bcdctz(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) +{ + int i; + int cr = 0; + uint8_t digit = 0; + int sgnb = bcd_get_sgn(b); + int zone_lead = (ps) ? 0xF0 : 0x30; + int invalid = (sgnb == 0); + ppc_avr_t ret = { .u64 = { 0, 0 } }; + + int ox_flag = ((b->u64[HI_IDX] >> 4) != 0); + + for (i = 0; i < 16; i++) { + digit = bcd_get_digit(b, i + 1, &invalid); + + if (unlikely(invalid)) { + break; + } + + ret.u8[BCD_DIG_BYTE(i * 2)] = zone_lead + digit; + } + + if (ps) { + bcd_put_digit(&ret, (sgnb == 1) ? 0xC : 0xD, 1); + } else { + bcd_put_digit(&ret, (sgnb == 1) ? 0x3 : 0x7, 1); + } + + cr = bcd_cmp_zero(b); + + if (ox_flag) { + cr |= 1 << CRF_SO; + } + + if (unlikely(invalid)) { + cr = 1 << CRF_SO; + } + + *r = ret; + + return cr; +} + +void helper_vsbox(ppc_avr_t *r, ppc_avr_t *a) +{ + int i; + VECTOR_FOR_INORDER_I(i, u8) { + r->u8[i] = AES_sbox[a->u8[i]]; + } +} + +void helper_vcipher(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + ppc_avr_t result; + int i; + + VECTOR_FOR_INORDER_I(i, u32) { + result.AVRW(i) = b->AVRW(i) ^ + (AES_Te0[a->AVRB(AES_shifts[4*i + 0])] ^ + AES_Te1[a->AVRB(AES_shifts[4*i + 1])] ^ + AES_Te2[a->AVRB(AES_shifts[4*i + 2])] ^ + AES_Te3[a->AVRB(AES_shifts[4*i + 3])]); + } + *r = result; +} + +void helper_vcipherlast(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + ppc_avr_t result; + int i; + + VECTOR_FOR_INORDER_I(i, u8) { + result.AVRB(i) = b->AVRB(i) ^ (AES_sbox[a->AVRB(AES_shifts[i])]); + } + *r = result; +} + +void helper_vncipher(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + /* This differs from what is written in ISA V2.07. The RTL is */ + /* incorrect and will be fixed in V2.07B. */ + int i; + ppc_avr_t tmp; + + VECTOR_FOR_INORDER_I(i, u8) { + tmp.AVRB(i) = b->AVRB(i) ^ AES_isbox[a->AVRB(AES_ishifts[i])]; + } + + VECTOR_FOR_INORDER_I(i, u32) { + r->AVRW(i) = + AES_imc[tmp.AVRB(4*i + 0)][0] ^ + AES_imc[tmp.AVRB(4*i + 1)][1] ^ + AES_imc[tmp.AVRB(4*i + 2)][2] ^ + AES_imc[tmp.AVRB(4*i + 3)][3]; + } +} + +void helper_vncipherlast(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + ppc_avr_t result; + int i; + + VECTOR_FOR_INORDER_I(i, u8) { + result.AVRB(i) = b->AVRB(i) ^ (AES_isbox[a->AVRB(AES_ishifts[i])]); + } + *r = result; +} + +#define ROTRu32(v, n) (((v) >> (n)) | ((v) << (32-n))) +#if defined(HOST_WORDS_BIGENDIAN) +#define EL_IDX(i) (i) +#else +#define EL_IDX(i) (3 - (i)) +#endif + +void helper_vshasigmaw(ppc_avr_t *r, ppc_avr_t *a, uint32_t st_six) +{ + int st = (st_six & 0x10) != 0; + int six = st_six & 0xF; + int i; + + VECTOR_FOR_INORDER_I(i, u32) { + if (st == 0) { + if ((six & (0x8 >> i)) == 0) { + r->u32[EL_IDX(i)] = ROTRu32(a->u32[EL_IDX(i)], 7) ^ + ROTRu32(a->u32[EL_IDX(i)], 18) ^ + (a->u32[EL_IDX(i)] >> 3); + } else { /* six.bit[i] == 1 */ + r->u32[EL_IDX(i)] = ROTRu32(a->u32[EL_IDX(i)], 17) ^ + ROTRu32(a->u32[EL_IDX(i)], 19) ^ + (a->u32[EL_IDX(i)] >> 10); + } + } else { /* st == 1 */ + if ((six & (0x8 >> i)) == 0) { + r->u32[EL_IDX(i)] = ROTRu32(a->u32[EL_IDX(i)], 2) ^ + ROTRu32(a->u32[EL_IDX(i)], 13) ^ + ROTRu32(a->u32[EL_IDX(i)], 22); + } else { /* six.bit[i] == 1 */ + r->u32[EL_IDX(i)] = ROTRu32(a->u32[EL_IDX(i)], 6) ^ + ROTRu32(a->u32[EL_IDX(i)], 11) ^ + ROTRu32(a->u32[EL_IDX(i)], 25); + } + } + } +} + +#undef ROTRu32 +#undef EL_IDX + +#define ROTRu64(v, n) (((v) >> (n)) | ((v) << (64-n))) +#if defined(HOST_WORDS_BIGENDIAN) +#define EL_IDX(i) (i) +#else +#define EL_IDX(i) (1 - (i)) +#endif + +void helper_vshasigmad(ppc_avr_t *r, ppc_avr_t *a, uint32_t st_six) +{ + int st = (st_six & 0x10) != 0; + int six = st_six & 0xF; + int i; + + VECTOR_FOR_INORDER_I(i, u64) { + if (st == 0) { + if ((six & (0x8 >> (2*i))) == 0) { + r->u64[EL_IDX(i)] = ROTRu64(a->u64[EL_IDX(i)], 1) ^ + ROTRu64(a->u64[EL_IDX(i)], 8) ^ + (a->u64[EL_IDX(i)] >> 7); + } else { /* six.bit[2*i] == 1 */ + r->u64[EL_IDX(i)] = ROTRu64(a->u64[EL_IDX(i)], 19) ^ + ROTRu64(a->u64[EL_IDX(i)], 61) ^ + (a->u64[EL_IDX(i)] >> 6); + } + } else { /* st == 1 */ + if ((six & (0x8 >> (2*i))) == 0) { + r->u64[EL_IDX(i)] = ROTRu64(a->u64[EL_IDX(i)], 28) ^ + ROTRu64(a->u64[EL_IDX(i)], 34) ^ + ROTRu64(a->u64[EL_IDX(i)], 39); + } else { /* six.bit[2*i] == 1 */ + r->u64[EL_IDX(i)] = ROTRu64(a->u64[EL_IDX(i)], 14) ^ + ROTRu64(a->u64[EL_IDX(i)], 18) ^ + ROTRu64(a->u64[EL_IDX(i)], 41); + } + } + } +} + +#undef ROTRu64 +#undef EL_IDX + +void helper_vpermxor(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ + ppc_avr_t result; + int i; + + VECTOR_FOR_INORDER_I(i, u8) { + int indexA = c->u8[i] >> 4; + int indexB = c->u8[i] & 0xF; +#if defined(HOST_WORDS_BIGENDIAN) + result.u8[i] = a->u8[indexA] ^ b->u8[indexB]; +#else + result.u8[i] = a->u8[15-indexA] ^ b->u8[15-indexB]; +#endif + } + *r = result; +} + +#undef VECTOR_FOR_INORDER_I +#undef HI_IDX +#undef LO_IDX + +/*****************************************************************************/ +/* SPE extension helpers */ +/* Use a table to make this quicker */ +static const uint8_t hbrev[16] = { + 0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE, + 0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF, +}; + +static inline uint8_t byte_reverse(uint8_t val) +{ + return hbrev[val >> 4] | (hbrev[val & 0xF] << 4); +} + +static inline uint32_t word_reverse(uint32_t val) +{ + return byte_reverse(val >> 24) | (byte_reverse(val >> 16) << 8) | + (byte_reverse(val >> 8) << 16) | (byte_reverse(val) << 24); +} + +#define MASKBITS 16 /* Random value - to be fixed (implementation dependent) */ +target_ulong helper_brinc(target_ulong arg1, target_ulong arg2) +{ + uint32_t a, b, d, mask; + + mask = UINT32_MAX >> (32 - MASKBITS); + a = arg1 & mask; + b = arg2 & mask; + d = word_reverse(1 + word_reverse(a | ~b)); + return (arg1 & ~mask) | (d & b); +} + +uint32_t helper_cntlsw32(uint32_t val) +{ + if (val & 0x80000000) { + return clz32(~val); + } else { + return clz32(val); + } +} + +uint32_t helper_cntlzw32(uint32_t val) +{ + return clz32(val); +} + +/* 440 specific */ +target_ulong helper_dlmzb(CPUPPCState *env, target_ulong high, + target_ulong low, uint32_t update_Rc) +{ + target_ulong mask; + int i; + + i = 1; + for (mask = 0xFF000000; mask != 0; mask = mask >> 8) { + if ((high & mask) == 0) { + if (update_Rc) { + env->crf[0] = 0x4; + } + goto done; + } + i++; + } + for (mask = 0xFF000000; mask != 0; mask = mask >> 8) { + if ((low & mask) == 0) { + if (update_Rc) { + env->crf[0] = 0x8; + } + goto done; + } + i++; + } + i = 8; + if (update_Rc) { + env->crf[0] = 0x2; + } + done: + env->xer = (env->xer & ~0x7F) | i; + if (update_Rc) { + env->crf[0] |= xer_so; + } + return i; +} diff --git a/target/ppc/internal.h b/target/ppc/internal.h new file mode 100644 index 0000000000..1ff4896c45 --- /dev/null +++ b/target/ppc/internal.h @@ -0,0 +1,50 @@ +/* + * PowerPC interal definitions for qemu. + * + * 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 PPC_INTERNAL_H +#define PPC_INTERNAL_H + +#define FUNC_MASK(name, ret_type, size, max_val) \ +static inline ret_type name(uint##size##_t start, \ + uint##size##_t end) \ +{ \ + ret_type ret, max_bit = size - 1; \ + \ + if (likely(start == 0)) { \ + ret = max_val << (max_bit - end); \ + } else if (likely(end == max_bit)) { \ + ret = max_val >> start; \ + } else { \ + ret = (((uint##size##_t)(-1ULL)) >> (start)) ^ \ + (((uint##size##_t)(-1ULL) >> (end)) >> 1); \ + if (unlikely(start > end)) { \ + return ~ret; \ + } \ + } \ + \ + return ret; \ +} + +#if defined(TARGET_PPC64) +FUNC_MASK(MASK, target_ulong, 64, UINT64_MAX); +#else +FUNC_MASK(MASK, target_ulong, 32, UINT32_MAX); +#endif +FUNC_MASK(mask_u32, uint32_t, 32, UINT32_MAX); +FUNC_MASK(mask_u64, uint64_t, 64, UINT64_MAX); + +#endif /* PPC_INTERNAL_H */ diff --git a/target/ppc/kvm-stub.c b/target/ppc/kvm-stub.c new file mode 100644 index 0000000000..efeafca1df --- /dev/null +++ b/target/ppc/kvm-stub.c @@ -0,0 +1,20 @@ +/* + * QEMU KVM PPC specific function stubs + * + * Copyright Freescale Inc. 2013 + * + * Author: Alexander Graf <agraf@suse.de> + * + * 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/osdep.h" +#include "qemu-common.h" +#include "cpu.h" +#include "hw/ppc/openpic.h" + +int kvm_openpic_connect_vcpu(DeviceState *d, CPUState *cs) +{ + return -EINVAL; +} diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c new file mode 100644 index 0000000000..9c4834c4fc --- /dev/null +++ b/target/ppc/kvm.c @@ -0,0 +1,2674 @@ +/* + * PowerPC implementation of KVM hooks + * + * Copyright IBM Corp. 2007 + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * + * Authors: + * Jerone Young <jyoung5@us.ibm.com> + * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com> + * Hollis Blanchard <hollisb@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 "qemu/osdep.h" +#include <dirent.h> +#include <sys/ioctl.h> +#include <sys/vfs.h> + +#include <linux/kvm.h> + +#include "qemu-common.h" +#include "qemu/error-report.h" +#include "cpu.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "sysemu/numa.h" +#include "kvm_ppc.h" +#include "sysemu/cpus.h" +#include "sysemu/device_tree.h" +#include "mmu-hash64.h" + +#include "hw/sysbus.h" +#include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_vio.h" +#include "hw/ppc/spapr_cpu_core.h" +#include "hw/ppc/ppc.h" +#include "sysemu/watchdog.h" +#include "trace.h" +#include "exec/gdbstub.h" +#include "exec/memattrs.h" +#include "sysemu/hostmem.h" +#include "qemu/cutils.h" +#if defined(TARGET_PPC64) +#include "hw/ppc/spapr_cpu_core.h" +#endif + +//#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 PROC_DEVTREE_CPU "/proc/device-tree/cpus/" + +const KVMCapabilityInfo kvm_arch_required_capabilities[] = { + KVM_CAP_LAST_INFO +}; + +static int cap_interrupt_unset = false; +static int cap_interrupt_level = false; +static int cap_segstate; +static int cap_booke_sregs; +static int cap_ppc_smt; +static int cap_ppc_rma; +static int cap_spapr_tce; +static int cap_spapr_multitce; +static int cap_spapr_vfio; +static int cap_hior; +static int cap_one_reg; +static int cap_epr; +static int cap_ppc_watchdog; +static int cap_papr; +static int cap_htab_fd; +static int cap_fixup_hcalls; +static int cap_htm; /* Hardware transactional memory support */ + +static uint32_t debug_inst_opcode; + +/* XXX We have a race condition where we actually have a level triggered + * interrupt, but the infrastructure can't expose that yet, so the guest + * takes but ignores it, goes to sleep and never gets notified that there's + * still an interrupt pending. + * + * As a quick workaround, let's just wake up again 20 ms after we injected + * an interrupt. That way we can assure that we're always reinjecting + * interrupts in case the guest swallowed them. + */ +static QEMUTimer *idle_timer; + +static void kvm_kick_cpu(void *opaque) +{ + PowerPCCPU *cpu = opaque; + + qemu_cpu_kick(CPU(cpu)); +} + +/* Check whether we are running with KVM-PR (instead of KVM-HV). This + * should only be used for fallback tests - generally we should use + * explicit capabilities for the features we want, rather than + * assuming what is/isn't available depending on the KVM variant. */ +static bool kvmppc_is_pr(KVMState *ks) +{ + /* Assume KVM-PR if the GET_PVINFO capability is available */ + return kvm_check_extension(ks, KVM_CAP_PPC_GET_PVINFO) != 0; +} + +static int kvm_ppc_register_host_cpu_type(void); + +int kvm_arch_init(MachineState *ms, KVMState *s) +{ + cap_interrupt_unset = kvm_check_extension(s, KVM_CAP_PPC_UNSET_IRQ); + cap_interrupt_level = kvm_check_extension(s, KVM_CAP_PPC_IRQ_LEVEL); + cap_segstate = kvm_check_extension(s, KVM_CAP_PPC_SEGSTATE); + cap_booke_sregs = kvm_check_extension(s, KVM_CAP_PPC_BOOKE_SREGS); + cap_ppc_smt = kvm_check_extension(s, KVM_CAP_PPC_SMT); + cap_ppc_rma = kvm_check_extension(s, KVM_CAP_PPC_RMA); + cap_spapr_tce = kvm_check_extension(s, KVM_CAP_SPAPR_TCE); + cap_spapr_multitce = kvm_check_extension(s, KVM_CAP_SPAPR_MULTITCE); + cap_spapr_vfio = false; + cap_one_reg = kvm_check_extension(s, KVM_CAP_ONE_REG); + cap_hior = kvm_check_extension(s, KVM_CAP_PPC_HIOR); + cap_epr = kvm_check_extension(s, KVM_CAP_PPC_EPR); + cap_ppc_watchdog = kvm_check_extension(s, KVM_CAP_PPC_BOOKE_WATCHDOG); + /* Note: we don't set cap_papr here, because this capability is + * only activated after this by kvmppc_set_papr() */ + cap_htab_fd = kvm_check_extension(s, KVM_CAP_PPC_HTAB_FD); + cap_fixup_hcalls = kvm_check_extension(s, KVM_CAP_PPC_FIXUP_HCALL); + cap_htm = kvm_vm_check_extension(s, KVM_CAP_PPC_HTM); + + if (!cap_interrupt_level) { + fprintf(stderr, "KVM: Couldn't find level irq capability. Expect the " + "VM to stall at times!\n"); + } + + kvm_ppc_register_host_cpu_type(); + + return 0; +} + +static int kvm_arch_sync_sregs(PowerPCCPU *cpu) +{ + CPUPPCState *cenv = &cpu->env; + CPUState *cs = CPU(cpu); + struct kvm_sregs sregs; + int ret; + + if (cenv->excp_model == POWERPC_EXCP_BOOKE) { + /* What we're really trying to say is "if we're on BookE, we use + the native PVR for now". This is the only sane way to check + it though, so we potentially confuse users that they can run + BookE guests on BookS. Let's hope nobody dares enough :) */ + return 0; + } else { + if (!cap_segstate) { + fprintf(stderr, "kvm error: missing PVR setting capability\n"); + return -ENOSYS; + } + } + + ret = kvm_vcpu_ioctl(cs, KVM_GET_SREGS, &sregs); + if (ret) { + return ret; + } + + sregs.pvr = cenv->spr[SPR_PVR]; + return kvm_vcpu_ioctl(cs, KVM_SET_SREGS, &sregs); +} + +/* Set up a shared TLB array with KVM */ +static int kvm_booke206_tlb_init(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + CPUState *cs = CPU(cpu); + struct kvm_book3e_206_tlb_params params = {}; + struct kvm_config_tlb cfg = {}; + unsigned int entries = 0; + int ret, i; + + if (!kvm_enabled() || + !kvm_check_extension(cs->kvm_state, KVM_CAP_SW_TLB)) { + return 0; + } + + assert(ARRAY_SIZE(params.tlb_sizes) == BOOKE206_MAX_TLBN); + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + params.tlb_sizes[i] = booke206_tlb_size(env, i); + params.tlb_ways[i] = booke206_tlb_ways(env, i); + entries += params.tlb_sizes[i]; + } + + assert(entries == env->nb_tlb); + assert(sizeof(struct kvm_book3e_206_tlb_entry) == sizeof(ppcmas_tlb_t)); + + env->tlb_dirty = true; + + cfg.array = (uintptr_t)env->tlb.tlbm; + cfg.array_len = sizeof(ppcmas_tlb_t) * entries; + cfg.params = (uintptr_t)¶ms; + cfg.mmu_type = KVM_MMU_FSL_BOOKE_NOHV; + + ret = kvm_vcpu_enable_cap(cs, KVM_CAP_SW_TLB, 0, (uintptr_t)&cfg); + if (ret < 0) { + fprintf(stderr, "%s: couldn't enable KVM_CAP_SW_TLB: %s\n", + __func__, strerror(-ret)); + return ret; + } + + env->kvm_sw_tlb = true; + return 0; +} + + +#if defined(TARGET_PPC64) +static void kvm_get_fallback_smmu_info(PowerPCCPU *cpu, + struct kvm_ppc_smmu_info *info) +{ + CPUPPCState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + memset(info, 0, sizeof(*info)); + + /* We don't have the new KVM_PPC_GET_SMMU_INFO ioctl, so + * need to "guess" what the supported page sizes are. + * + * For that to work we make a few assumptions: + * + * - Check whether we are running "PR" KVM which only supports 4K + * and 16M pages, but supports them regardless of the backing + * store characteritics. We also don't support 1T segments. + * + * This is safe as if HV KVM ever supports that capability or PR + * KVM grows supports for more page/segment sizes, those versions + * will have implemented KVM_CAP_PPC_GET_SMMU_INFO and thus we + * will not hit this fallback + * + * - Else we are running HV KVM. This means we only support page + * sizes that fit in the backing store. Additionally we only + * advertize 64K pages if the processor is ARCH 2.06 and we assume + * P7 encodings for the SLB and hash table. Here too, we assume + * support for any newer processor will mean a kernel that + * implements KVM_CAP_PPC_GET_SMMU_INFO and thus doesn't hit + * this fallback. + */ + if (kvmppc_is_pr(cs->kvm_state)) { + /* No flags */ + info->flags = 0; + info->slb_size = 64; + + /* Standard 4k base page size segment */ + info->sps[0].page_shift = 12; + info->sps[0].slb_enc = 0; + info->sps[0].enc[0].page_shift = 12; + info->sps[0].enc[0].pte_enc = 0; + + /* Standard 16M large page size segment */ + info->sps[1].page_shift = 24; + info->sps[1].slb_enc = SLB_VSID_L; + info->sps[1].enc[0].page_shift = 24; + info->sps[1].enc[0].pte_enc = 0; + } else { + int i = 0; + + /* HV KVM has backing store size restrictions */ + info->flags = KVM_PPC_PAGE_SIZES_REAL; + + if (env->mmu_model & POWERPC_MMU_1TSEG) { + info->flags |= KVM_PPC_1T_SEGMENTS; + } + + if (env->mmu_model == POWERPC_MMU_2_06 || + env->mmu_model == POWERPC_MMU_2_07) { + info->slb_size = 32; + } else { + info->slb_size = 64; + } + + /* Standard 4k base page size segment */ + info->sps[i].page_shift = 12; + info->sps[i].slb_enc = 0; + info->sps[i].enc[0].page_shift = 12; + info->sps[i].enc[0].pte_enc = 0; + i++; + + /* 64K on MMU 2.06 and later */ + if (env->mmu_model == POWERPC_MMU_2_06 || + env->mmu_model == POWERPC_MMU_2_07) { + info->sps[i].page_shift = 16; + info->sps[i].slb_enc = 0x110; + info->sps[i].enc[0].page_shift = 16; + info->sps[i].enc[0].pte_enc = 1; + i++; + } + + /* Standard 16M large page size segment */ + info->sps[i].page_shift = 24; + info->sps[i].slb_enc = SLB_VSID_L; + info->sps[i].enc[0].page_shift = 24; + info->sps[i].enc[0].pte_enc = 0; + } +} + +static void kvm_get_smmu_info(PowerPCCPU *cpu, struct kvm_ppc_smmu_info *info) +{ + CPUState *cs = CPU(cpu); + int ret; + + if (kvm_check_extension(cs->kvm_state, KVM_CAP_PPC_GET_SMMU_INFO)) { + ret = kvm_vm_ioctl(cs->kvm_state, KVM_PPC_GET_SMMU_INFO, info); + if (ret == 0) { + return; + } + } + + kvm_get_fallback_smmu_info(cpu, info); +} + +static long gethugepagesize(const char *mem_path) +{ + struct statfs fs; + int ret; + + do { + ret = statfs(mem_path, &fs); + } while (ret != 0 && errno == EINTR); + + if (ret != 0) { + fprintf(stderr, "Couldn't statfs() memory path: %s\n", + strerror(errno)); + exit(1); + } + +#define HUGETLBFS_MAGIC 0x958458f6 + + if (fs.f_type != HUGETLBFS_MAGIC) { + /* Explicit mempath, but it's ordinary pages */ + return getpagesize(); + } + + /* It's hugepage, return the huge page size */ + return fs.f_bsize; +} + +/* + * FIXME TOCTTOU: this iterates over memory backends' mem-path, which + * may or may not name the same files / on the same filesystem now as + * when we actually open and map them. Iterate over the file + * descriptors instead, and use qemu_fd_getpagesize(). + */ +static int find_max_supported_pagesize(Object *obj, void *opaque) +{ + char *mem_path; + long *hpsize_min = opaque; + + if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) { + mem_path = object_property_get_str(obj, "mem-path", NULL); + if (mem_path) { + long hpsize = gethugepagesize(mem_path); + if (hpsize < *hpsize_min) { + *hpsize_min = hpsize; + } + } else { + *hpsize_min = getpagesize(); + } + } + + return 0; +} + +static long getrampagesize(void) +{ + long hpsize = LONG_MAX; + long mainrampagesize; + Object *memdev_root; + + if (mem_path) { + mainrampagesize = gethugepagesize(mem_path); + } else { + mainrampagesize = getpagesize(); + } + + /* it's possible we have memory-backend objects with + * hugepage-backed RAM. these may get mapped into system + * address space via -numa parameters or memory hotplug + * hooks. we want to take these into account, but we + * also want to make sure these supported hugepage + * sizes are applicable across the entire range of memory + * we may boot from, so we take the min across all + * backends, and assume normal pages in cases where a + * backend isn't backed by hugepages. + */ + memdev_root = object_resolve_path("/objects", NULL); + if (memdev_root) { + object_child_foreach(memdev_root, find_max_supported_pagesize, &hpsize); + } + if (hpsize == LONG_MAX) { + /* No additional memory regions found ==> Report main RAM page size */ + return mainrampagesize; + } + + /* If NUMA is disabled or the NUMA nodes are not backed with a + * memory-backend, then there is at least one node using "normal" RAM, + * so if its page size is smaller we have got to report that size instead. + */ + if (hpsize > mainrampagesize && + (nb_numa_nodes == 0 || numa_info[0].node_memdev == NULL)) { + static bool warned; + if (!warned) { + error_report("Huge page support disabled (n/a for main memory)."); + warned = true; + } + return mainrampagesize; + } + + return hpsize; +} + +static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift) +{ + if (!(flags & KVM_PPC_PAGE_SIZES_REAL)) { + return true; + } + + return (1ul << shift) <= rampgsize; +} + +static void kvm_fixup_page_sizes(PowerPCCPU *cpu) +{ + static struct kvm_ppc_smmu_info smmu_info; + static bool has_smmu_info; + CPUPPCState *env = &cpu->env; + long rampagesize; + int iq, ik, jq, jk; + bool has_64k_pages = false; + + /* We only handle page sizes for 64-bit server guests for now */ + if (!(env->mmu_model & POWERPC_MMU_64)) { + return; + } + + /* Collect MMU info from kernel if not already */ + if (!has_smmu_info) { + kvm_get_smmu_info(cpu, &smmu_info); + has_smmu_info = true; + } + + rampagesize = getrampagesize(); + + /* Convert to QEMU form */ + memset(&env->sps, 0, sizeof(env->sps)); + + /* If we have HV KVM, we need to forbid CI large pages if our + * host page size is smaller than 64K. + */ + if (smmu_info.flags & KVM_PPC_PAGE_SIZES_REAL) { + env->ci_large_pages = getpagesize() >= 0x10000; + } + + /* + * XXX This loop should be an entry wide AND of the capabilities that + * the selected CPU has with the capabilities that KVM supports. + */ + for (ik = iq = 0; ik < KVM_PPC_PAGE_SIZES_MAX_SZ; ik++) { + struct ppc_one_seg_page_size *qsps = &env->sps.sps[iq]; + struct kvm_ppc_one_seg_page_size *ksps = &smmu_info.sps[ik]; + + if (!kvm_valid_page_size(smmu_info.flags, rampagesize, + ksps->page_shift)) { + continue; + } + qsps->page_shift = ksps->page_shift; + qsps->slb_enc = ksps->slb_enc; + for (jk = jq = 0; jk < KVM_PPC_PAGE_SIZES_MAX_SZ; jk++) { + if (!kvm_valid_page_size(smmu_info.flags, rampagesize, + ksps->enc[jk].page_shift)) { + continue; + } + if (ksps->enc[jk].page_shift == 16) { + has_64k_pages = true; + } + qsps->enc[jq].page_shift = ksps->enc[jk].page_shift; + qsps->enc[jq].pte_enc = ksps->enc[jk].pte_enc; + if (++jq >= PPC_PAGE_SIZES_MAX_SZ) { + break; + } + } + if (++iq >= PPC_PAGE_SIZES_MAX_SZ) { + break; + } + } + env->slb_nr = smmu_info.slb_size; + if (!(smmu_info.flags & KVM_PPC_1T_SEGMENTS)) { + env->mmu_model &= ~POWERPC_MMU_1TSEG; + } + if (!has_64k_pages) { + env->mmu_model &= ~POWERPC_MMU_64K; + } +} +#else /* defined (TARGET_PPC64) */ + +static inline void kvm_fixup_page_sizes(PowerPCCPU *cpu) +{ +} + +#endif /* !defined (TARGET_PPC64) */ + +unsigned long kvm_arch_vcpu_id(CPUState *cpu) +{ + return ppc_get_vcpu_dt_id(POWERPC_CPU(cpu)); +} + +/* e500 supports 2 h/w breakpoint and 2 watchpoint. + * book3s supports only 1 watchpoint, so array size + * of 4 is sufficient for now. + */ +#define MAX_HW_BKPTS 4 + +static struct HWBreakpoint { + target_ulong addr; + int type; +} hw_debug_points[MAX_HW_BKPTS]; + +static CPUWatchpoint hw_watchpoint; + +/* Default there is no breakpoint and watchpoint supported */ +static int max_hw_breakpoint; +static int max_hw_watchpoint; +static int nb_hw_breakpoint; +static int nb_hw_watchpoint; + +static void kvmppc_hw_debug_points_init(CPUPPCState *cenv) +{ + if (cenv->excp_model == POWERPC_EXCP_BOOKE) { + max_hw_breakpoint = 2; + max_hw_watchpoint = 2; + } + + if ((max_hw_breakpoint + max_hw_watchpoint) > MAX_HW_BKPTS) { + fprintf(stderr, "Error initializing h/w breakpoints\n"); + return; + } +} + +int kvm_arch_init_vcpu(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *cenv = &cpu->env; + int ret; + + /* Gather server mmu info from KVM and update the CPU state */ + kvm_fixup_page_sizes(cpu); + + /* Synchronize sregs with kvm */ + ret = kvm_arch_sync_sregs(cpu); + if (ret) { + if (ret == -EINVAL) { + error_report("Register sync failed... If you're using kvm-hv.ko," + " only \"-cpu host\" is possible"); + } + return ret; + } + + idle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, kvm_kick_cpu, cpu); + + switch (cenv->mmu_model) { + case POWERPC_MMU_BOOKE206: + /* This target supports access to KVM's guest TLB */ + ret = kvm_booke206_tlb_init(cpu); + break; + case POWERPC_MMU_2_07: + if (!cap_htm && !kvmppc_is_pr(cs->kvm_state)) { + /* KVM-HV has transactional memory on POWER8 also without the + * KVM_CAP_PPC_HTM extension, so enable it here instead. */ + cap_htm = true; + } + break; + default: + break; + } + + kvm_get_one_reg(cs, KVM_REG_PPC_DEBUG_INST, &debug_inst_opcode); + kvmppc_hw_debug_points_init(cenv); + + return ret; +} + +static void kvm_sw_tlb_put(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + CPUState *cs = CPU(cpu); + struct kvm_dirty_tlb dirty_tlb; + unsigned char *bitmap; + int ret; + + if (!env->kvm_sw_tlb) { + return; + } + + bitmap = g_malloc((env->nb_tlb + 7) / 8); + memset(bitmap, 0xFF, (env->nb_tlb + 7) / 8); + + dirty_tlb.bitmap = (uintptr_t)bitmap; + dirty_tlb.num_dirty = env->nb_tlb; + + ret = kvm_vcpu_ioctl(cs, KVM_DIRTY_TLB, &dirty_tlb); + if (ret) { + fprintf(stderr, "%s: KVM_DIRTY_TLB: %s\n", + __func__, strerror(-ret)); + } + + g_free(bitmap); +} + +static void kvm_get_one_spr(CPUState *cs, uint64_t id, int spr) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + union { + uint32_t u32; + uint64_t u64; + } val; + struct kvm_one_reg reg = { + .id = id, + .addr = (uintptr_t) &val, + }; + int ret; + + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret != 0) { + trace_kvm_failed_spr_get(spr, strerror(errno)); + } else { + switch (id & KVM_REG_SIZE_MASK) { + case KVM_REG_SIZE_U32: + env->spr[spr] = val.u32; + break; + + case KVM_REG_SIZE_U64: + env->spr[spr] = val.u64; + break; + + default: + /* Don't handle this size yet */ + abort(); + } + } +} + +static void kvm_put_one_spr(CPUState *cs, uint64_t id, int spr) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + union { + uint32_t u32; + uint64_t u64; + } val; + struct kvm_one_reg reg = { + .id = id, + .addr = (uintptr_t) &val, + }; + int ret; + + switch (id & KVM_REG_SIZE_MASK) { + case KVM_REG_SIZE_U32: + val.u32 = env->spr[spr]; + break; + + case KVM_REG_SIZE_U64: + val.u64 = env->spr[spr]; + break; + + default: + /* Don't handle this size yet */ + abort(); + } + + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret != 0) { + trace_kvm_failed_spr_set(spr, strerror(errno)); + } +} + +static int kvm_put_fp(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + struct kvm_one_reg reg; + int i; + int ret; + + if (env->insns_flags & PPC_FLOAT) { + uint64_t fpscr = env->fpscr; + bool vsx = !!(env->insns_flags2 & PPC2_VSX); + + reg.id = KVM_REG_PPC_FPSCR; + reg.addr = (uintptr_t)&fpscr; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + DPRINTF("Unable to set FPSCR to KVM: %s\n", strerror(errno)); + return ret; + } + + for (i = 0; i < 32; i++) { + uint64_t vsr[2]; + +#ifdef HOST_WORDS_BIGENDIAN + vsr[0] = float64_val(env->fpr[i]); + vsr[1] = env->vsr[i]; +#else + vsr[0] = env->vsr[i]; + vsr[1] = float64_val(env->fpr[i]); +#endif + reg.addr = (uintptr_t) &vsr; + reg.id = vsx ? KVM_REG_PPC_VSR(i) : KVM_REG_PPC_FPR(i); + + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + DPRINTF("Unable to set %s%d to KVM: %s\n", vsx ? "VSR" : "FPR", + i, strerror(errno)); + return ret; + } + } + } + + if (env->insns_flags & PPC_ALTIVEC) { + reg.id = KVM_REG_PPC_VSCR; + reg.addr = (uintptr_t)&env->vscr; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + DPRINTF("Unable to set VSCR to KVM: %s\n", strerror(errno)); + return ret; + } + + for (i = 0; i < 32; i++) { + reg.id = KVM_REG_PPC_VR(i); + reg.addr = (uintptr_t)&env->avr[i]; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + DPRINTF("Unable to set VR%d to KVM: %s\n", i, strerror(errno)); + return ret; + } + } + } + + return 0; +} + +static int kvm_get_fp(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + struct kvm_one_reg reg; + int i; + int ret; + + if (env->insns_flags & PPC_FLOAT) { + uint64_t fpscr; + bool vsx = !!(env->insns_flags2 & PPC2_VSX); + + reg.id = KVM_REG_PPC_FPSCR; + reg.addr = (uintptr_t)&fpscr; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret < 0) { + DPRINTF("Unable to get FPSCR from KVM: %s\n", strerror(errno)); + return ret; + } else { + env->fpscr = fpscr; + } + + for (i = 0; i < 32; i++) { + uint64_t vsr[2]; + + reg.addr = (uintptr_t) &vsr; + reg.id = vsx ? KVM_REG_PPC_VSR(i) : KVM_REG_PPC_FPR(i); + + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret < 0) { + DPRINTF("Unable to get %s%d from KVM: %s\n", + vsx ? "VSR" : "FPR", i, strerror(errno)); + return ret; + } else { +#ifdef HOST_WORDS_BIGENDIAN + env->fpr[i] = vsr[0]; + if (vsx) { + env->vsr[i] = vsr[1]; + } +#else + env->fpr[i] = vsr[1]; + if (vsx) { + env->vsr[i] = vsr[0]; + } +#endif + } + } + } + + if (env->insns_flags & PPC_ALTIVEC) { + reg.id = KVM_REG_PPC_VSCR; + reg.addr = (uintptr_t)&env->vscr; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret < 0) { + DPRINTF("Unable to get VSCR from KVM: %s\n", strerror(errno)); + return ret; + } + + for (i = 0; i < 32; i++) { + reg.id = KVM_REG_PPC_VR(i); + reg.addr = (uintptr_t)&env->avr[i]; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret < 0) { + DPRINTF("Unable to get VR%d from KVM: %s\n", + i, strerror(errno)); + return ret; + } + } + } + + return 0; +} + +#if defined(TARGET_PPC64) +static int kvm_get_vpa(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + struct kvm_one_reg reg; + int ret; + + reg.id = KVM_REG_PPC_VPA_ADDR; + reg.addr = (uintptr_t)&env->vpa_addr; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret < 0) { + DPRINTF("Unable to get VPA address from KVM: %s\n", strerror(errno)); + return ret; + } + + assert((uintptr_t)&env->slb_shadow_size + == ((uintptr_t)&env->slb_shadow_addr + 8)); + reg.id = KVM_REG_PPC_VPA_SLB; + reg.addr = (uintptr_t)&env->slb_shadow_addr; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret < 0) { + DPRINTF("Unable to get SLB shadow state from KVM: %s\n", + strerror(errno)); + return ret; + } + + assert((uintptr_t)&env->dtl_size == ((uintptr_t)&env->dtl_addr + 8)); + reg.id = KVM_REG_PPC_VPA_DTL; + reg.addr = (uintptr_t)&env->dtl_addr; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret < 0) { + DPRINTF("Unable to get dispatch trace log state from KVM: %s\n", + strerror(errno)); + return ret; + } + + return 0; +} + +static int kvm_put_vpa(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + struct kvm_one_reg reg; + int ret; + + /* SLB shadow or DTL can't be registered unless a master VPA is + * registered. That means when restoring state, if a VPA *is* + * registered, we need to set that up first. If not, we need to + * deregister the others before deregistering the master VPA */ + assert(env->vpa_addr || !(env->slb_shadow_addr || env->dtl_addr)); + + if (env->vpa_addr) { + reg.id = KVM_REG_PPC_VPA_ADDR; + reg.addr = (uintptr_t)&env->vpa_addr; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + DPRINTF("Unable to set VPA address to KVM: %s\n", strerror(errno)); + return ret; + } + } + + assert((uintptr_t)&env->slb_shadow_size + == ((uintptr_t)&env->slb_shadow_addr + 8)); + reg.id = KVM_REG_PPC_VPA_SLB; + reg.addr = (uintptr_t)&env->slb_shadow_addr; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + DPRINTF("Unable to set SLB shadow state to KVM: %s\n", strerror(errno)); + return ret; + } + + assert((uintptr_t)&env->dtl_size == ((uintptr_t)&env->dtl_addr + 8)); + reg.id = KVM_REG_PPC_VPA_DTL; + reg.addr = (uintptr_t)&env->dtl_addr; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + DPRINTF("Unable to set dispatch trace log state to KVM: %s\n", + strerror(errno)); + return ret; + } + + if (!env->vpa_addr) { + reg.id = KVM_REG_PPC_VPA_ADDR; + reg.addr = (uintptr_t)&env->vpa_addr; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + DPRINTF("Unable to set VPA address to KVM: %s\n", strerror(errno)); + return ret; + } + } + + return 0; +} +#endif /* TARGET_PPC64 */ + +int kvmppc_put_books_sregs(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + struct kvm_sregs sregs; + int i; + + sregs.pvr = env->spr[SPR_PVR]; + + sregs.u.s.sdr1 = env->spr[SPR_SDR1]; + + /* Sync SLB */ +#ifdef TARGET_PPC64 + for (i = 0; i < ARRAY_SIZE(env->slb); i++) { + sregs.u.s.ppc64.slb[i].slbe = env->slb[i].esid; + if (env->slb[i].esid & SLB_ESID_V) { + sregs.u.s.ppc64.slb[i].slbe |= i; + } + sregs.u.s.ppc64.slb[i].slbv = env->slb[i].vsid; + } +#endif + + /* Sync SRs */ + for (i = 0; i < 16; i++) { + sregs.u.s.ppc32.sr[i] = env->sr[i]; + } + + /* Sync BATs */ + for (i = 0; i < 8; i++) { + /* Beware. We have to swap upper and lower bits here */ + sregs.u.s.ppc32.dbat[i] = ((uint64_t)env->DBAT[0][i] << 32) + | env->DBAT[1][i]; + sregs.u.s.ppc32.ibat[i] = ((uint64_t)env->IBAT[0][i] << 32) + | env->IBAT[1][i]; + } + + return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_SREGS, &sregs); +} + +int kvm_arch_put_registers(CPUState *cs, int level) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + struct kvm_regs regs; + int ret; + int i; + + ret = kvm_vcpu_ioctl(cs, KVM_GET_REGS, ®s); + if (ret < 0) { + return ret; + } + + regs.ctr = env->ctr; + regs.lr = env->lr; + regs.xer = cpu_read_xer(env); + regs.msr = env->msr; + regs.pc = env->nip; + + regs.srr0 = env->spr[SPR_SRR0]; + regs.srr1 = env->spr[SPR_SRR1]; + + regs.sprg0 = env->spr[SPR_SPRG0]; + regs.sprg1 = env->spr[SPR_SPRG1]; + regs.sprg2 = env->spr[SPR_SPRG2]; + regs.sprg3 = env->spr[SPR_SPRG3]; + regs.sprg4 = env->spr[SPR_SPRG4]; + regs.sprg5 = env->spr[SPR_SPRG5]; + regs.sprg6 = env->spr[SPR_SPRG6]; + regs.sprg7 = env->spr[SPR_SPRG7]; + + regs.pid = env->spr[SPR_BOOKE_PID]; + + for (i = 0;i < 32; i++) + regs.gpr[i] = env->gpr[i]; + + regs.cr = 0; + for (i = 0; i < 8; i++) { + regs.cr |= (env->crf[i] & 15) << (4 * (7 - i)); + } + + ret = kvm_vcpu_ioctl(cs, KVM_SET_REGS, ®s); + if (ret < 0) + return ret; + + kvm_put_fp(cs); + + if (env->tlb_dirty) { + kvm_sw_tlb_put(cpu); + env->tlb_dirty = false; + } + + if (cap_segstate && (level >= KVM_PUT_RESET_STATE)) { + ret = kvmppc_put_books_sregs(cpu); + if (ret < 0) { + return ret; + } + } + + if (cap_hior && (level >= KVM_PUT_RESET_STATE)) { + kvm_put_one_spr(cs, KVM_REG_PPC_HIOR, SPR_HIOR); + } + + if (cap_one_reg) { + int i; + + /* We deliberately ignore errors here, for kernels which have + * the ONE_REG calls, but don't support the specific + * registers, there's a reasonable chance things will still + * work, at least until we try to migrate. */ + for (i = 0; i < 1024; i++) { + uint64_t id = env->spr_cb[i].one_reg_id; + + if (id != 0) { + kvm_put_one_spr(cs, id, i); + } + } + +#ifdef TARGET_PPC64 + if (msr_ts) { + for (i = 0; i < ARRAY_SIZE(env->tm_gpr); i++) { + kvm_set_one_reg(cs, KVM_REG_PPC_TM_GPR(i), &env->tm_gpr[i]); + } + for (i = 0; i < ARRAY_SIZE(env->tm_vsr); i++) { + kvm_set_one_reg(cs, KVM_REG_PPC_TM_VSR(i), &env->tm_vsr[i]); + } + kvm_set_one_reg(cs, KVM_REG_PPC_TM_CR, &env->tm_cr); + kvm_set_one_reg(cs, KVM_REG_PPC_TM_LR, &env->tm_lr); + kvm_set_one_reg(cs, KVM_REG_PPC_TM_CTR, &env->tm_ctr); + kvm_set_one_reg(cs, KVM_REG_PPC_TM_FPSCR, &env->tm_fpscr); + kvm_set_one_reg(cs, KVM_REG_PPC_TM_AMR, &env->tm_amr); + kvm_set_one_reg(cs, KVM_REG_PPC_TM_PPR, &env->tm_ppr); + kvm_set_one_reg(cs, KVM_REG_PPC_TM_VRSAVE, &env->tm_vrsave); + kvm_set_one_reg(cs, KVM_REG_PPC_TM_VSCR, &env->tm_vscr); + kvm_set_one_reg(cs, KVM_REG_PPC_TM_DSCR, &env->tm_dscr); + kvm_set_one_reg(cs, KVM_REG_PPC_TM_TAR, &env->tm_tar); + } + + if (cap_papr) { + if (kvm_put_vpa(cs) < 0) { + DPRINTF("Warning: Unable to set VPA information to KVM\n"); + } + } + + kvm_set_one_reg(cs, KVM_REG_PPC_TB_OFFSET, &env->tb_env->tb_offset); +#endif /* TARGET_PPC64 */ + } + + return ret; +} + +static void kvm_sync_excp(CPUPPCState *env, int vector, int ivor) +{ + env->excp_vectors[vector] = env->spr[ivor] + env->spr[SPR_BOOKE_IVPR]; +} + +static int kvmppc_get_booke_sregs(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + struct kvm_sregs sregs; + int ret; + + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_SREGS, &sregs); + if (ret < 0) { + return ret; + } + + if (sregs.u.e.features & KVM_SREGS_E_BASE) { + env->spr[SPR_BOOKE_CSRR0] = sregs.u.e.csrr0; + env->spr[SPR_BOOKE_CSRR1] = sregs.u.e.csrr1; + env->spr[SPR_BOOKE_ESR] = sregs.u.e.esr; + env->spr[SPR_BOOKE_DEAR] = sregs.u.e.dear; + env->spr[SPR_BOOKE_MCSR] = sregs.u.e.mcsr; + env->spr[SPR_BOOKE_TSR] = sregs.u.e.tsr; + env->spr[SPR_BOOKE_TCR] = sregs.u.e.tcr; + env->spr[SPR_DECR] = sregs.u.e.dec; + env->spr[SPR_TBL] = sregs.u.e.tb & 0xffffffff; + env->spr[SPR_TBU] = sregs.u.e.tb >> 32; + env->spr[SPR_VRSAVE] = sregs.u.e.vrsave; + } + + if (sregs.u.e.features & KVM_SREGS_E_ARCH206) { + env->spr[SPR_BOOKE_PIR] = sregs.u.e.pir; + env->spr[SPR_BOOKE_MCSRR0] = sregs.u.e.mcsrr0; + env->spr[SPR_BOOKE_MCSRR1] = sregs.u.e.mcsrr1; + env->spr[SPR_BOOKE_DECAR] = sregs.u.e.decar; + env->spr[SPR_BOOKE_IVPR] = sregs.u.e.ivpr; + } + + if (sregs.u.e.features & KVM_SREGS_E_64) { + env->spr[SPR_BOOKE_EPCR] = sregs.u.e.epcr; + } + + if (sregs.u.e.features & KVM_SREGS_E_SPRG8) { + env->spr[SPR_BOOKE_SPRG8] = sregs.u.e.sprg8; + } + + if (sregs.u.e.features & KVM_SREGS_E_IVOR) { + env->spr[SPR_BOOKE_IVOR0] = sregs.u.e.ivor_low[0]; + kvm_sync_excp(env, POWERPC_EXCP_CRITICAL, SPR_BOOKE_IVOR0); + env->spr[SPR_BOOKE_IVOR1] = sregs.u.e.ivor_low[1]; + kvm_sync_excp(env, POWERPC_EXCP_MCHECK, SPR_BOOKE_IVOR1); + env->spr[SPR_BOOKE_IVOR2] = sregs.u.e.ivor_low[2]; + kvm_sync_excp(env, POWERPC_EXCP_DSI, SPR_BOOKE_IVOR2); + env->spr[SPR_BOOKE_IVOR3] = sregs.u.e.ivor_low[3]; + kvm_sync_excp(env, POWERPC_EXCP_ISI, SPR_BOOKE_IVOR3); + env->spr[SPR_BOOKE_IVOR4] = sregs.u.e.ivor_low[4]; + kvm_sync_excp(env, POWERPC_EXCP_EXTERNAL, SPR_BOOKE_IVOR4); + env->spr[SPR_BOOKE_IVOR5] = sregs.u.e.ivor_low[5]; + kvm_sync_excp(env, POWERPC_EXCP_ALIGN, SPR_BOOKE_IVOR5); + env->spr[SPR_BOOKE_IVOR6] = sregs.u.e.ivor_low[6]; + kvm_sync_excp(env, POWERPC_EXCP_PROGRAM, SPR_BOOKE_IVOR6); + env->spr[SPR_BOOKE_IVOR7] = sregs.u.e.ivor_low[7]; + kvm_sync_excp(env, POWERPC_EXCP_FPU, SPR_BOOKE_IVOR7); + env->spr[SPR_BOOKE_IVOR8] = sregs.u.e.ivor_low[8]; + kvm_sync_excp(env, POWERPC_EXCP_SYSCALL, SPR_BOOKE_IVOR8); + env->spr[SPR_BOOKE_IVOR9] = sregs.u.e.ivor_low[9]; + kvm_sync_excp(env, POWERPC_EXCP_APU, SPR_BOOKE_IVOR9); + env->spr[SPR_BOOKE_IVOR10] = sregs.u.e.ivor_low[10]; + kvm_sync_excp(env, POWERPC_EXCP_DECR, SPR_BOOKE_IVOR10); + env->spr[SPR_BOOKE_IVOR11] = sregs.u.e.ivor_low[11]; + kvm_sync_excp(env, POWERPC_EXCP_FIT, SPR_BOOKE_IVOR11); + env->spr[SPR_BOOKE_IVOR12] = sregs.u.e.ivor_low[12]; + kvm_sync_excp(env, POWERPC_EXCP_WDT, SPR_BOOKE_IVOR12); + env->spr[SPR_BOOKE_IVOR13] = sregs.u.e.ivor_low[13]; + kvm_sync_excp(env, POWERPC_EXCP_DTLB, SPR_BOOKE_IVOR13); + env->spr[SPR_BOOKE_IVOR14] = sregs.u.e.ivor_low[14]; + kvm_sync_excp(env, POWERPC_EXCP_ITLB, SPR_BOOKE_IVOR14); + env->spr[SPR_BOOKE_IVOR15] = sregs.u.e.ivor_low[15]; + kvm_sync_excp(env, POWERPC_EXCP_DEBUG, SPR_BOOKE_IVOR15); + + if (sregs.u.e.features & KVM_SREGS_E_SPE) { + env->spr[SPR_BOOKE_IVOR32] = sregs.u.e.ivor_high[0]; + kvm_sync_excp(env, POWERPC_EXCP_SPEU, SPR_BOOKE_IVOR32); + env->spr[SPR_BOOKE_IVOR33] = sregs.u.e.ivor_high[1]; + kvm_sync_excp(env, POWERPC_EXCP_EFPDI, SPR_BOOKE_IVOR33); + env->spr[SPR_BOOKE_IVOR34] = sregs.u.e.ivor_high[2]; + kvm_sync_excp(env, POWERPC_EXCP_EFPRI, SPR_BOOKE_IVOR34); + } + + if (sregs.u.e.features & KVM_SREGS_E_PM) { + env->spr[SPR_BOOKE_IVOR35] = sregs.u.e.ivor_high[3]; + kvm_sync_excp(env, POWERPC_EXCP_EPERFM, SPR_BOOKE_IVOR35); + } + + if (sregs.u.e.features & KVM_SREGS_E_PC) { + env->spr[SPR_BOOKE_IVOR36] = sregs.u.e.ivor_high[4]; + kvm_sync_excp(env, POWERPC_EXCP_DOORI, SPR_BOOKE_IVOR36); + env->spr[SPR_BOOKE_IVOR37] = sregs.u.e.ivor_high[5]; + kvm_sync_excp(env, POWERPC_EXCP_DOORCI, SPR_BOOKE_IVOR37); + } + } + + if (sregs.u.e.features & KVM_SREGS_E_ARCH206_MMU) { + env->spr[SPR_BOOKE_MAS0] = sregs.u.e.mas0; + env->spr[SPR_BOOKE_MAS1] = sregs.u.e.mas1; + env->spr[SPR_BOOKE_MAS2] = sregs.u.e.mas2; + env->spr[SPR_BOOKE_MAS3] = sregs.u.e.mas7_3 & 0xffffffff; + env->spr[SPR_BOOKE_MAS4] = sregs.u.e.mas4; + env->spr[SPR_BOOKE_MAS6] = sregs.u.e.mas6; + env->spr[SPR_BOOKE_MAS7] = sregs.u.e.mas7_3 >> 32; + env->spr[SPR_MMUCFG] = sregs.u.e.mmucfg; + env->spr[SPR_BOOKE_TLB0CFG] = sregs.u.e.tlbcfg[0]; + env->spr[SPR_BOOKE_TLB1CFG] = sregs.u.e.tlbcfg[1]; + } + + if (sregs.u.e.features & KVM_SREGS_EXP) { + env->spr[SPR_BOOKE_EPR] = sregs.u.e.epr; + } + + if (sregs.u.e.features & KVM_SREGS_E_PD) { + env->spr[SPR_BOOKE_EPLC] = sregs.u.e.eplc; + env->spr[SPR_BOOKE_EPSC] = sregs.u.e.epsc; + } + + if (sregs.u.e.impl_id == KVM_SREGS_E_IMPL_FSL) { + env->spr[SPR_E500_SVR] = sregs.u.e.impl.fsl.svr; + env->spr[SPR_Exxx_MCAR] = sregs.u.e.impl.fsl.mcar; + env->spr[SPR_HID0] = sregs.u.e.impl.fsl.hid0; + + if (sregs.u.e.impl.fsl.features & KVM_SREGS_E_FSL_PIDn) { + env->spr[SPR_BOOKE_PID1] = sregs.u.e.impl.fsl.pid1; + env->spr[SPR_BOOKE_PID2] = sregs.u.e.impl.fsl.pid2; + } + } + + return 0; +} + +static int kvmppc_get_books_sregs(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + struct kvm_sregs sregs; + int ret; + int i; + + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_SREGS, &sregs); + if (ret < 0) { + return ret; + } + + if (!env->external_htab) { + ppc_store_sdr1(env, sregs.u.s.sdr1); + } + + /* Sync SLB */ +#ifdef TARGET_PPC64 + /* + * The packed SLB array we get from KVM_GET_SREGS only contains + * information about valid entries. So we flush our internal copy + * to get rid of stale ones, then put all valid SLB entries back + * in. + */ + memset(env->slb, 0, sizeof(env->slb)); + for (i = 0; i < ARRAY_SIZE(env->slb); i++) { + target_ulong rb = sregs.u.s.ppc64.slb[i].slbe; + target_ulong rs = sregs.u.s.ppc64.slb[i].slbv; + /* + * Only restore valid entries + */ + if (rb & SLB_ESID_V) { + ppc_store_slb(cpu, rb & 0xfff, rb & ~0xfffULL, rs); + } + } +#endif + + /* Sync SRs */ + for (i = 0; i < 16; i++) { + env->sr[i] = sregs.u.s.ppc32.sr[i]; + } + + /* Sync BATs */ + for (i = 0; i < 8; i++) { + env->DBAT[0][i] = sregs.u.s.ppc32.dbat[i] & 0xffffffff; + env->DBAT[1][i] = sregs.u.s.ppc32.dbat[i] >> 32; + env->IBAT[0][i] = sregs.u.s.ppc32.ibat[i] & 0xffffffff; + env->IBAT[1][i] = sregs.u.s.ppc32.ibat[i] >> 32; + } + + return 0; +} + +int kvm_arch_get_registers(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + struct kvm_regs regs; + uint32_t cr; + int i, ret; + + ret = kvm_vcpu_ioctl(cs, KVM_GET_REGS, ®s); + if (ret < 0) + return ret; + + cr = regs.cr; + for (i = 7; i >= 0; i--) { + env->crf[i] = cr & 15; + cr >>= 4; + } + + env->ctr = regs.ctr; + env->lr = regs.lr; + cpu_write_xer(env, regs.xer); + env->msr = regs.msr; + env->nip = regs.pc; + + env->spr[SPR_SRR0] = regs.srr0; + env->spr[SPR_SRR1] = regs.srr1; + + env->spr[SPR_SPRG0] = regs.sprg0; + env->spr[SPR_SPRG1] = regs.sprg1; + env->spr[SPR_SPRG2] = regs.sprg2; + env->spr[SPR_SPRG3] = regs.sprg3; + env->spr[SPR_SPRG4] = regs.sprg4; + env->spr[SPR_SPRG5] = regs.sprg5; + env->spr[SPR_SPRG6] = regs.sprg6; + env->spr[SPR_SPRG7] = regs.sprg7; + + env->spr[SPR_BOOKE_PID] = regs.pid; + + for (i = 0;i < 32; i++) + env->gpr[i] = regs.gpr[i]; + + kvm_get_fp(cs); + + if (cap_booke_sregs) { + ret = kvmppc_get_booke_sregs(cpu); + if (ret < 0) { + return ret; + } + } + + if (cap_segstate) { + ret = kvmppc_get_books_sregs(cpu); + if (ret < 0) { + return ret; + } + } + + if (cap_hior) { + kvm_get_one_spr(cs, KVM_REG_PPC_HIOR, SPR_HIOR); + } + + if (cap_one_reg) { + int i; + + /* We deliberately ignore errors here, for kernels which have + * the ONE_REG calls, but don't support the specific + * registers, there's a reasonable chance things will still + * work, at least until we try to migrate. */ + for (i = 0; i < 1024; i++) { + uint64_t id = env->spr_cb[i].one_reg_id; + + if (id != 0) { + kvm_get_one_spr(cs, id, i); + } + } + +#ifdef TARGET_PPC64 + if (msr_ts) { + for (i = 0; i < ARRAY_SIZE(env->tm_gpr); i++) { + kvm_get_one_reg(cs, KVM_REG_PPC_TM_GPR(i), &env->tm_gpr[i]); + } + for (i = 0; i < ARRAY_SIZE(env->tm_vsr); i++) { + kvm_get_one_reg(cs, KVM_REG_PPC_TM_VSR(i), &env->tm_vsr[i]); + } + kvm_get_one_reg(cs, KVM_REG_PPC_TM_CR, &env->tm_cr); + kvm_get_one_reg(cs, KVM_REG_PPC_TM_LR, &env->tm_lr); + kvm_get_one_reg(cs, KVM_REG_PPC_TM_CTR, &env->tm_ctr); + kvm_get_one_reg(cs, KVM_REG_PPC_TM_FPSCR, &env->tm_fpscr); + kvm_get_one_reg(cs, KVM_REG_PPC_TM_AMR, &env->tm_amr); + kvm_get_one_reg(cs, KVM_REG_PPC_TM_PPR, &env->tm_ppr); + kvm_get_one_reg(cs, KVM_REG_PPC_TM_VRSAVE, &env->tm_vrsave); + kvm_get_one_reg(cs, KVM_REG_PPC_TM_VSCR, &env->tm_vscr); + kvm_get_one_reg(cs, KVM_REG_PPC_TM_DSCR, &env->tm_dscr); + kvm_get_one_reg(cs, KVM_REG_PPC_TM_TAR, &env->tm_tar); + } + + if (cap_papr) { + if (kvm_get_vpa(cs) < 0) { + DPRINTF("Warning: Unable to get VPA information from KVM\n"); + } + } + + kvm_get_one_reg(cs, KVM_REG_PPC_TB_OFFSET, &env->tb_env->tb_offset); +#endif + } + + return 0; +} + +int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level) +{ + unsigned virq = level ? KVM_INTERRUPT_SET_LEVEL : KVM_INTERRUPT_UNSET; + + if (irq != PPC_INTERRUPT_EXT) { + return 0; + } + + if (!kvm_enabled() || !cap_interrupt_unset || !cap_interrupt_level) { + return 0; + } + + kvm_vcpu_ioctl(CPU(cpu), KVM_INTERRUPT, &virq); + + return 0; +} + +#if defined(TARGET_PPCEMB) +#define PPC_INPUT_INT PPC40x_INPUT_INT +#elif defined(TARGET_PPC64) +#define PPC_INPUT_INT PPC970_INPUT_INT +#else +#define PPC_INPUT_INT PPC6xx_INPUT_INT +#endif + +void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + int r; + unsigned irq; + + qemu_mutex_lock_iothread(); + + /* PowerPC QEMU tracks the various core input pins (interrupt, critical + * interrupt, reset, etc) in PPC-specific env->irq_input_state. */ + if (!cap_interrupt_level && + run->ready_for_interrupt_injection && + (cs->interrupt_request & CPU_INTERRUPT_HARD) && + (env->irq_input_state & (1<<PPC_INPUT_INT))) + { + /* For now KVM disregards the 'irq' argument. However, in the + * future KVM could cache it in-kernel to avoid a heavyweight exit + * when reading the UIC. + */ + irq = KVM_INTERRUPT_SET; + + DPRINTF("injected interrupt %d\n", irq); + r = kvm_vcpu_ioctl(cs, KVM_INTERRUPT, &irq); + if (r < 0) { + printf("cpu %d fail inject %x\n", cs->cpu_index, irq); + } + + /* Always wake up soon in case the interrupt was level based */ + timer_mod(idle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (NANOSECONDS_PER_SECOND / 50)); + } + + /* We don't know if there are more interrupts pending after this. However, + * the guest will return to userspace in the course of handling this one + * anyways, so we will get a chance to deliver the rest. */ + + qemu_mutex_unlock_iothread(); +} + +MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) +{ + return MEMTXATTRS_UNSPECIFIED; +} + +int kvm_arch_process_async_events(CPUState *cs) +{ + return cs->halted; +} + +static int kvmppc_handle_halt(PowerPCCPU *cpu) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + + if (!(cs->interrupt_request & CPU_INTERRUPT_HARD) && (msr_ee)) { + cs->halted = 1; + cs->exception_index = EXCP_HLT; + } + + return 0; +} + +/* map dcr access to existing qemu dcr emulation */ +static int kvmppc_handle_dcr_read(CPUPPCState *env, uint32_t dcrn, uint32_t *data) +{ + if (ppc_dcr_read(env->dcr_env, dcrn, data) < 0) + fprintf(stderr, "Read to unhandled DCR (0x%x)\n", dcrn); + + return 0; +} + +static int kvmppc_handle_dcr_write(CPUPPCState *env, uint32_t dcrn, uint32_t data) +{ + if (ppc_dcr_write(env->dcr_env, dcrn, data) < 0) + fprintf(stderr, "Write to unhandled DCR (0x%x)\n", dcrn); + + return 0; +} + +int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ + /* Mixed endian case is not handled */ + uint32_t sc = debug_inst_opcode; + + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, + sizeof(sc), 0) || + cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&sc, sizeof(sc), 1)) { + return -EINVAL; + } + + return 0; +} + +int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ + uint32_t sc; + + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&sc, sizeof(sc), 0) || + sc != debug_inst_opcode || + cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, + sizeof(sc), 1)) { + return -EINVAL; + } + + return 0; +} + +static int find_hw_breakpoint(target_ulong addr, int type) +{ + int n; + + assert((nb_hw_breakpoint + nb_hw_watchpoint) + <= ARRAY_SIZE(hw_debug_points)); + + for (n = 0; n < nb_hw_breakpoint + nb_hw_watchpoint; n++) { + if (hw_debug_points[n].addr == addr && + hw_debug_points[n].type == type) { + return n; + } + } + + return -1; +} + +static int find_hw_watchpoint(target_ulong addr, int *flag) +{ + int n; + + n = find_hw_breakpoint(addr, GDB_WATCHPOINT_ACCESS); + if (n >= 0) { + *flag = BP_MEM_ACCESS; + return n; + } + + n = find_hw_breakpoint(addr, GDB_WATCHPOINT_WRITE); + if (n >= 0) { + *flag = BP_MEM_WRITE; + return n; + } + + n = find_hw_breakpoint(addr, GDB_WATCHPOINT_READ); + if (n >= 0) { + *flag = BP_MEM_READ; + return n; + } + + return -1; +} + +int kvm_arch_insert_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + if ((nb_hw_breakpoint + nb_hw_watchpoint) >= ARRAY_SIZE(hw_debug_points)) { + return -ENOBUFS; + } + + hw_debug_points[nb_hw_breakpoint + nb_hw_watchpoint].addr = addr; + hw_debug_points[nb_hw_breakpoint + nb_hw_watchpoint].type = type; + + switch (type) { + case GDB_BREAKPOINT_HW: + if (nb_hw_breakpoint >= max_hw_breakpoint) { + return -ENOBUFS; + } + + if (find_hw_breakpoint(addr, type) >= 0) { + return -EEXIST; + } + + nb_hw_breakpoint++; + break; + + case GDB_WATCHPOINT_WRITE: + case GDB_WATCHPOINT_READ: + case GDB_WATCHPOINT_ACCESS: + if (nb_hw_watchpoint >= max_hw_watchpoint) { + return -ENOBUFS; + } + + if (find_hw_breakpoint(addr, type) >= 0) { + return -EEXIST; + } + + nb_hw_watchpoint++; + break; + + default: + return -ENOSYS; + } + + return 0; +} + +int kvm_arch_remove_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + int n; + + n = find_hw_breakpoint(addr, type); + if (n < 0) { + return -ENOENT; + } + + switch (type) { + case GDB_BREAKPOINT_HW: + nb_hw_breakpoint--; + break; + + case GDB_WATCHPOINT_WRITE: + case GDB_WATCHPOINT_READ: + case GDB_WATCHPOINT_ACCESS: + nb_hw_watchpoint--; + break; + + default: + return -ENOSYS; + } + hw_debug_points[n] = hw_debug_points[nb_hw_breakpoint + nb_hw_watchpoint]; + + return 0; +} + +void kvm_arch_remove_all_hw_breakpoints(void) +{ + nb_hw_breakpoint = nb_hw_watchpoint = 0; +} + +void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg) +{ + int n; + + /* Software Breakpoint updates */ + if (kvm_sw_breakpoints_active(cs)) { + dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; + } + + assert((nb_hw_breakpoint + nb_hw_watchpoint) + <= ARRAY_SIZE(hw_debug_points)); + assert((nb_hw_breakpoint + nb_hw_watchpoint) <= ARRAY_SIZE(dbg->arch.bp)); + + if (nb_hw_breakpoint + nb_hw_watchpoint > 0) { + dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; + memset(dbg->arch.bp, 0, sizeof(dbg->arch.bp)); + for (n = 0; n < nb_hw_breakpoint + nb_hw_watchpoint; n++) { + switch (hw_debug_points[n].type) { + case GDB_BREAKPOINT_HW: + dbg->arch.bp[n].type = KVMPPC_DEBUG_BREAKPOINT; + break; + case GDB_WATCHPOINT_WRITE: + dbg->arch.bp[n].type = KVMPPC_DEBUG_WATCH_WRITE; + break; + case GDB_WATCHPOINT_READ: + dbg->arch.bp[n].type = KVMPPC_DEBUG_WATCH_READ; + break; + case GDB_WATCHPOINT_ACCESS: + dbg->arch.bp[n].type = KVMPPC_DEBUG_WATCH_WRITE | + KVMPPC_DEBUG_WATCH_READ; + break; + default: + cpu_abort(cs, "Unsupported breakpoint type\n"); + } + dbg->arch.bp[n].addr = hw_debug_points[n].addr; + } + } +} + +static int kvm_handle_debug(PowerPCCPU *cpu, struct kvm_run *run) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + struct kvm_debug_exit_arch *arch_info = &run->debug.arch; + int handle = 0; + int n; + int flag = 0; + + if (cs->singlestep_enabled) { + handle = 1; + } else if (arch_info->status) { + if (nb_hw_breakpoint + nb_hw_watchpoint > 0) { + if (arch_info->status & KVMPPC_DEBUG_BREAKPOINT) { + n = find_hw_breakpoint(arch_info->address, GDB_BREAKPOINT_HW); + if (n >= 0) { + handle = 1; + } + } else if (arch_info->status & (KVMPPC_DEBUG_WATCH_READ | + KVMPPC_DEBUG_WATCH_WRITE)) { + n = find_hw_watchpoint(arch_info->address, &flag); + if (n >= 0) { + handle = 1; + cs->watchpoint_hit = &hw_watchpoint; + hw_watchpoint.vaddr = hw_debug_points[n].addr; + hw_watchpoint.flags = flag; + } + } + } + } else if (kvm_find_sw_breakpoint(cs, arch_info->address)) { + handle = 1; + } else { + /* QEMU is not able to handle debug exception, so inject + * program exception to guest; + * Yes program exception NOT debug exception !! + * When QEMU is using debug resources then debug exception must + * be always set. To achieve this we set MSR_DE and also set + * MSRP_DEP so guest cannot change MSR_DE. + * When emulating debug resource for guest we want guest + * to control MSR_DE (enable/disable debug interrupt on need). + * Supporting both configurations are NOT possible. + * So the result is that we cannot share debug resources + * between QEMU and Guest on BOOKE architecture. + * In the current design QEMU gets the priority over guest, + * this means that if QEMU is using debug resources then guest + * cannot use them; + * For software breakpoint QEMU uses a privileged instruction; + * So there cannot be any reason that we are here for guest + * set debug exception, only possibility is guest executed a + * privileged / illegal instruction and that's why we are + * injecting a program interrupt. + */ + + cpu_synchronize_state(cs); + /* env->nip is PC, so increment this by 4 to use + * ppc_cpu_do_interrupt(), which set srr0 = env->nip - 4. + */ + env->nip += 4; + cs->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = POWERPC_EXCP_INVAL; + ppc_cpu_do_interrupt(cs); + } + + return handle; +} + +int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + int ret; + + qemu_mutex_lock_iothread(); + + switch (run->exit_reason) { + case KVM_EXIT_DCR: + if (run->dcr.is_write) { + DPRINTF("handle dcr write\n"); + ret = kvmppc_handle_dcr_write(env, run->dcr.dcrn, run->dcr.data); + } else { + DPRINTF("handle dcr read\n"); + ret = kvmppc_handle_dcr_read(env, run->dcr.dcrn, &run->dcr.data); + } + break; + case KVM_EXIT_HLT: + DPRINTF("handle halt\n"); + ret = kvmppc_handle_halt(cpu); + break; +#if defined(TARGET_PPC64) + case KVM_EXIT_PAPR_HCALL: + DPRINTF("handle PAPR hypercall\n"); + run->papr_hcall.ret = spapr_hypercall(cpu, + run->papr_hcall.nr, + run->papr_hcall.args); + ret = 0; + break; +#endif + case KVM_EXIT_EPR: + DPRINTF("handle epr\n"); + run->epr.epr = ldl_phys(cs->as, env->mpic_iack); + ret = 0; + break; + case KVM_EXIT_WATCHDOG: + DPRINTF("handle watchdog expiry\n"); + watchdog_perform_action(); + ret = 0; + break; + + case KVM_EXIT_DEBUG: + DPRINTF("handle debug exception\n"); + if (kvm_handle_debug(cpu, run)) { + ret = EXCP_DEBUG; + break; + } + /* re-enter, this exception was guest-internal */ + ret = 0; + break; + + default: + fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); + ret = -1; + break; + } + + qemu_mutex_unlock_iothread(); + return ret; +} + +int kvmppc_or_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits) +{ + CPUState *cs = CPU(cpu); + uint32_t bits = tsr_bits; + struct kvm_one_reg reg = { + .id = KVM_REG_PPC_OR_TSR, + .addr = (uintptr_t) &bits, + }; + + return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); +} + +int kvmppc_clear_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits) +{ + + CPUState *cs = CPU(cpu); + uint32_t bits = tsr_bits; + struct kvm_one_reg reg = { + .id = KVM_REG_PPC_CLEAR_TSR, + .addr = (uintptr_t) &bits, + }; + + return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); +} + +int kvmppc_set_tcr(PowerPCCPU *cpu) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + uint32_t tcr = env->spr[SPR_BOOKE_TCR]; + + struct kvm_one_reg reg = { + .id = KVM_REG_PPC_TCR, + .addr = (uintptr_t) &tcr, + }; + + return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); +} + +int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu) +{ + CPUState *cs = CPU(cpu); + int ret; + + if (!kvm_enabled()) { + return -1; + } + + if (!cap_ppc_watchdog) { + printf("warning: KVM does not support watchdog"); + return -1; + } + + ret = kvm_vcpu_enable_cap(cs, KVM_CAP_PPC_BOOKE_WATCHDOG, 0); + if (ret < 0) { + fprintf(stderr, "%s: couldn't enable KVM_CAP_PPC_BOOKE_WATCHDOG: %s\n", + __func__, strerror(-ret)); + return ret; + } + + return ret; +} + +static int read_cpuinfo(const char *field, char *value, int len) +{ + FILE *f; + int ret = -1; + int field_len = strlen(field); + char line[512]; + + f = fopen("/proc/cpuinfo", "r"); + if (!f) { + return -1; + } + + do { + if (!fgets(line, sizeof(line), f)) { + break; + } + if (!strncmp(line, field, field_len)) { + pstrcpy(value, len, line); + ret = 0; + break; + } + } while(*line); + + fclose(f); + + return ret; +} + +uint32_t kvmppc_get_tbfreq(void) +{ + char line[512]; + char *ns; + uint32_t retval = NANOSECONDS_PER_SECOND; + + if (read_cpuinfo("timebase", line, sizeof(line))) { + return retval; + } + + if (!(ns = strchr(line, ':'))) { + return retval; + } + + ns++; + + return atoi(ns); +} + +bool kvmppc_get_host_serial(char **value) +{ + return g_file_get_contents("/proc/device-tree/system-id", value, NULL, + NULL); +} + +bool kvmppc_get_host_model(char **value) +{ + return g_file_get_contents("/proc/device-tree/model", value, NULL, NULL); +} + +/* Try to find a device tree node for a CPU with clock-frequency property */ +static int kvmppc_find_cpu_dt(char *buf, int buf_len) +{ + struct dirent *dirp; + DIR *dp; + + if ((dp = opendir(PROC_DEVTREE_CPU)) == NULL) { + printf("Can't open directory " PROC_DEVTREE_CPU "\n"); + return -1; + } + + buf[0] = '\0'; + while ((dirp = readdir(dp)) != NULL) { + FILE *f; + snprintf(buf, buf_len, "%s%s/clock-frequency", PROC_DEVTREE_CPU, + dirp->d_name); + f = fopen(buf, "r"); + if (f) { + snprintf(buf, buf_len, "%s%s", PROC_DEVTREE_CPU, dirp->d_name); + fclose(f); + break; + } + buf[0] = '\0'; + } + closedir(dp); + if (buf[0] == '\0') { + printf("Unknown host!\n"); + return -1; + } + + return 0; +} + +static uint64_t kvmppc_read_int_dt(const char *filename) +{ + union { + uint32_t v32; + uint64_t v64; + } u; + FILE *f; + int len; + + f = fopen(filename, "rb"); + if (!f) { + return -1; + } + + len = fread(&u, 1, sizeof(u), f); + fclose(f); + switch (len) { + case 4: + /* property is a 32-bit quantity */ + return be32_to_cpu(u.v32); + case 8: + return be64_to_cpu(u.v64); + } + + return 0; +} + +/* Read a CPU node property from the host device tree that's a single + * integer (32-bit or 64-bit). Returns 0 if anything goes wrong + * (can't find or open the property, or doesn't understand the + * format) */ +static uint64_t kvmppc_read_int_cpu_dt(const char *propname) +{ + char buf[PATH_MAX], *tmp; + uint64_t val; + + if (kvmppc_find_cpu_dt(buf, sizeof(buf))) { + return -1; + } + + tmp = g_strdup_printf("%s/%s", buf, propname); + val = kvmppc_read_int_dt(tmp); + g_free(tmp); + + return val; +} + +uint64_t kvmppc_get_clockfreq(void) +{ + return kvmppc_read_int_cpu_dt("clock-frequency"); +} + +uint32_t kvmppc_get_vmx(void) +{ + return kvmppc_read_int_cpu_dt("ibm,vmx"); +} + +uint32_t kvmppc_get_dfp(void) +{ + return kvmppc_read_int_cpu_dt("ibm,dfp"); +} + +static int kvmppc_get_pvinfo(CPUPPCState *env, struct kvm_ppc_pvinfo *pvinfo) + { + PowerPCCPU *cpu = ppc_env_get_cpu(env); + CPUState *cs = CPU(cpu); + + if (kvm_vm_check_extension(cs->kvm_state, KVM_CAP_PPC_GET_PVINFO) && + !kvm_vm_ioctl(cs->kvm_state, KVM_PPC_GET_PVINFO, pvinfo)) { + return 0; + } + + return 1; +} + +int kvmppc_get_hasidle(CPUPPCState *env) +{ + struct kvm_ppc_pvinfo pvinfo; + + if (!kvmppc_get_pvinfo(env, &pvinfo) && + (pvinfo.flags & KVM_PPC_PVINFO_FLAGS_EV_IDLE)) { + return 1; + } + + return 0; +} + +int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_len) +{ + uint32_t *hc = (uint32_t*)buf; + struct kvm_ppc_pvinfo pvinfo; + + if (!kvmppc_get_pvinfo(env, &pvinfo)) { + memcpy(buf, pvinfo.hcall, buf_len); + return 0; + } + + /* + * Fallback to always fail hypercalls regardless of endianness: + * + * tdi 0,r0,72 (becomes b .+8 in wrong endian, nop in good endian) + * li r3, -1 + * b .+8 (becomes nop in wrong endian) + * bswap32(li r3, -1) + */ + + hc[0] = cpu_to_be32(0x08000048); + hc[1] = cpu_to_be32(0x3860ffff); + hc[2] = cpu_to_be32(0x48000008); + hc[3] = cpu_to_be32(bswap32(0x3860ffff)); + + return 1; +} + +static inline int kvmppc_enable_hcall(KVMState *s, target_ulong hcall) +{ + return kvm_vm_enable_cap(s, KVM_CAP_PPC_ENABLE_HCALL, 0, hcall, 1); +} + +void kvmppc_enable_logical_ci_hcalls(void) +{ + /* + * FIXME: it would be nice if we could detect the cases where + * we're using a device which requires the in kernel + * implementation of these hcalls, but the kernel lacks them and + * produce a warning. + */ + kvmppc_enable_hcall(kvm_state, H_LOGICAL_CI_LOAD); + kvmppc_enable_hcall(kvm_state, H_LOGICAL_CI_STORE); +} + +void kvmppc_enable_set_mode_hcall(void) +{ + kvmppc_enable_hcall(kvm_state, H_SET_MODE); +} + +void kvmppc_enable_clear_ref_mod_hcalls(void) +{ + kvmppc_enable_hcall(kvm_state, H_CLEAR_REF); + kvmppc_enable_hcall(kvm_state, H_CLEAR_MOD); +} + +void kvmppc_set_papr(PowerPCCPU *cpu) +{ + CPUState *cs = CPU(cpu); + int ret; + + ret = kvm_vcpu_enable_cap(cs, KVM_CAP_PPC_PAPR, 0); + if (ret) { + error_report("This vCPU type or KVM version does not support PAPR"); + exit(1); + } + + /* Update the capability flag so we sync the right information + * with kvm */ + cap_papr = 1; +} + +int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version) +{ + return kvm_set_one_reg(CPU(cpu), KVM_REG_PPC_ARCH_COMPAT, &cpu_version); +} + +void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy) +{ + CPUState *cs = CPU(cpu); + int ret; + + ret = kvm_vcpu_enable_cap(cs, KVM_CAP_PPC_EPR, 0, mpic_proxy); + if (ret && mpic_proxy) { + error_report("This KVM version does not support EPR"); + exit(1); + } +} + +int kvmppc_smt_threads(void) +{ + return cap_ppc_smt ? cap_ppc_smt : 1; +} + +#ifdef TARGET_PPC64 +off_t kvmppc_alloc_rma(void **rma) +{ + off_t size; + int fd; + struct kvm_allocate_rma ret; + + /* If cap_ppc_rma == 0, contiguous RMA allocation is not supported + * if cap_ppc_rma == 1, contiguous RMA allocation is supported, but + * not necessary on this hardware + * if cap_ppc_rma == 2, contiguous RMA allocation is needed on this hardware + * + * FIXME: We should allow the user to force contiguous RMA + * allocation in the cap_ppc_rma==1 case. + */ + if (cap_ppc_rma < 2) { + return 0; + } + + fd = kvm_vm_ioctl(kvm_state, KVM_ALLOCATE_RMA, &ret); + if (fd < 0) { + fprintf(stderr, "KVM: Error on KVM_ALLOCATE_RMA: %s\n", + strerror(errno)); + return -1; + } + + size = MIN(ret.rma_size, 256ul << 20); + + *rma = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (*rma == MAP_FAILED) { + fprintf(stderr, "KVM: Error mapping RMA: %s\n", strerror(errno)); + return -1; + }; + + return size; +} + +uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift) +{ + struct kvm_ppc_smmu_info info; + long rampagesize, best_page_shift; + int i; + + if (cap_ppc_rma >= 2) { + return current_size; + } + + /* Find the largest hardware supported page size that's less than + * or equal to the (logical) backing page size of guest RAM */ + kvm_get_smmu_info(POWERPC_CPU(first_cpu), &info); + rampagesize = getrampagesize(); + best_page_shift = 0; + + for (i = 0; i < KVM_PPC_PAGE_SIZES_MAX_SZ; i++) { + struct kvm_ppc_one_seg_page_size *sps = &info.sps[i]; + + if (!sps->page_shift) { + continue; + } + + if ((sps->page_shift > best_page_shift) + && ((1UL << sps->page_shift) <= rampagesize)) { + best_page_shift = sps->page_shift; + } + } + + return MIN(current_size, + 1ULL << (best_page_shift + hash_shift - 7)); +} +#endif + +bool kvmppc_spapr_use_multitce(void) +{ + return cap_spapr_multitce; +} + +void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd, + bool need_vfio) +{ + struct kvm_create_spapr_tce args = { + .liobn = liobn, + .window_size = window_size, + }; + long len; + int fd; + void *table; + + /* Must set fd to -1 so we don't try to munmap when called for + * destroying the table, which the upper layers -will- do + */ + *pfd = -1; + if (!cap_spapr_tce || (need_vfio && !cap_spapr_vfio)) { + return NULL; + } + + fd = kvm_vm_ioctl(kvm_state, KVM_CREATE_SPAPR_TCE, &args); + if (fd < 0) { + fprintf(stderr, "KVM: Failed to create TCE table for liobn 0x%x\n", + liobn); + return NULL; + } + + len = (window_size / SPAPR_TCE_PAGE_SIZE) * sizeof(uint64_t); + /* FIXME: round this up to page size */ + + table = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (table == MAP_FAILED) { + fprintf(stderr, "KVM: Failed to map TCE table for liobn 0x%x\n", + liobn); + close(fd); + return NULL; + } + + *pfd = fd; + return table; +} + +int kvmppc_remove_spapr_tce(void *table, int fd, uint32_t nb_table) +{ + long len; + + if (fd < 0) { + return -1; + } + + len = nb_table * sizeof(uint64_t); + if ((munmap(table, len) < 0) || + (close(fd) < 0)) { + fprintf(stderr, "KVM: Unexpected error removing TCE table: %s", + strerror(errno)); + /* Leak the table */ + } + + return 0; +} + +int kvmppc_reset_htab(int shift_hint) +{ + uint32_t shift = shift_hint; + + if (!kvm_enabled()) { + /* Full emulation, tell caller to allocate htab itself */ + return 0; + } + if (kvm_check_extension(kvm_state, KVM_CAP_PPC_ALLOC_HTAB)) { + int ret; + ret = kvm_vm_ioctl(kvm_state, KVM_PPC_ALLOCATE_HTAB, &shift); + if (ret == -ENOTTY) { + /* At least some versions of PR KVM advertise the + * capability, but don't implement the ioctl(). Oops. + * Return 0 so that we allocate the htab in qemu, as is + * correct for PR. */ + return 0; + } else if (ret < 0) { + return ret; + } + return shift; + } + + /* We have a kernel that predates the htab reset calls. For PR + * KVM, we need to allocate the htab ourselves, for an HV KVM of + * this era, it has allocated a 16MB fixed size hash table already. */ + if (kvmppc_is_pr(kvm_state)) { + /* PR - tell caller to allocate htab */ + return 0; + } else { + /* HV - assume 16MB kernel allocated htab */ + return 24; + } +} + +static inline uint32_t mfpvr(void) +{ + uint32_t pvr; + + asm ("mfpvr %0" + : "=r"(pvr)); + return pvr; +} + +static void alter_insns(uint64_t *word, uint64_t flags, bool on) +{ + if (on) { + *word |= flags; + } else { + *word &= ~flags; + } +} + +static void kvmppc_host_cpu_initfn(Object *obj) +{ + assert(kvm_enabled()); +} + +static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + uint32_t vmx = kvmppc_get_vmx(); + uint32_t dfp = kvmppc_get_dfp(); + uint32_t dcache_size = kvmppc_read_int_cpu_dt("d-cache-size"); + uint32_t icache_size = kvmppc_read_int_cpu_dt("i-cache-size"); + + /* Now fix up the class with information we can query from the host */ + pcc->pvr = mfpvr(); + + if (vmx != -1) { + /* Only override when we know what the host supports */ + alter_insns(&pcc->insns_flags, PPC_ALTIVEC, vmx > 0); + alter_insns(&pcc->insns_flags2, PPC2_VSX, vmx > 1); + } + if (dfp != -1) { + /* Only override when we know what the host supports */ + alter_insns(&pcc->insns_flags2, PPC2_DFP, dfp); + } + + if (dcache_size != -1) { + pcc->l1_dcache_size = dcache_size; + } + + if (icache_size != -1) { + pcc->l1_icache_size = icache_size; + } + + /* Reason: kvmppc_host_cpu_initfn() dies when !kvm_enabled() */ + dc->cannot_destroy_with_object_finalize_yet = true; +} + +bool kvmppc_has_cap_epr(void) +{ + return cap_epr; +} + +bool kvmppc_has_cap_htab_fd(void) +{ + return cap_htab_fd; +} + +bool kvmppc_has_cap_fixup_hcalls(void) +{ + return cap_fixup_hcalls; +} + +bool kvmppc_has_cap_htm(void) +{ + return cap_htm; +} + +static PowerPCCPUClass *ppc_cpu_get_family_class(PowerPCCPUClass *pcc) +{ + ObjectClass *oc = OBJECT_CLASS(pcc); + + while (oc && !object_class_is_abstract(oc)) { + oc = object_class_get_parent(oc); + } + assert(oc); + + return POWERPC_CPU_CLASS(oc); +} + +PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void) +{ + uint32_t host_pvr = mfpvr(); + PowerPCCPUClass *pvr_pcc; + + pvr_pcc = ppc_cpu_class_by_pvr(host_pvr); + if (pvr_pcc == NULL) { + pvr_pcc = ppc_cpu_class_by_pvr_mask(host_pvr); + } + + return pvr_pcc; +} + +static int kvm_ppc_register_host_cpu_type(void) +{ + TypeInfo type_info = { + .name = TYPE_HOST_POWERPC_CPU, + .instance_init = kvmppc_host_cpu_initfn, + .class_init = kvmppc_host_cpu_class_init, + }; + PowerPCCPUClass *pvr_pcc; + DeviceClass *dc; + + pvr_pcc = kvm_ppc_get_host_cpu_class(); + if (pvr_pcc == NULL) { + return -1; + } + type_info.parent = object_class_get_name(OBJECT_CLASS(pvr_pcc)); + type_register(&type_info); + + /* Register generic family CPU class for a family */ + pvr_pcc = ppc_cpu_get_family_class(pvr_pcc); + dc = DEVICE_CLASS(pvr_pcc); + type_info.parent = object_class_get_name(OBJECT_CLASS(pvr_pcc)); + type_info.name = g_strdup_printf("%s-"TYPE_POWERPC_CPU, dc->desc); + type_register(&type_info); + +#if defined(TARGET_PPC64) + type_info.name = g_strdup_printf("%s-"TYPE_SPAPR_CPU_CORE, "host"); + type_info.parent = TYPE_SPAPR_CPU_CORE, + type_info.instance_size = sizeof(sPAPRCPUCore); + type_info.instance_init = NULL; + type_info.class_init = spapr_cpu_core_class_init; + type_info.class_data = (void *) "host"; + type_register(&type_info); + g_free((void *)type_info.name); + + /* Register generic spapr CPU family class for current host CPU type */ + type_info.name = g_strdup_printf("%s-"TYPE_SPAPR_CPU_CORE, dc->desc); + type_info.class_data = (void *) dc->desc; + type_register(&type_info); + g_free((void *)type_info.name); +#endif + + return 0; +} + +int kvmppc_define_rtas_kernel_token(uint32_t token, const char *function) +{ + struct kvm_rtas_token_args args = { + .token = token, + }; + + if (!kvm_check_extension(kvm_state, KVM_CAP_PPC_RTAS)) { + return -ENOENT; + } + + strncpy(args.name, function, sizeof(args.name)); + + return kvm_vm_ioctl(kvm_state, KVM_PPC_RTAS_DEFINE_TOKEN, &args); +} + +int kvmppc_get_htab_fd(bool write) +{ + struct kvm_get_htab_fd s = { + .flags = write ? KVM_GET_HTAB_WRITE : 0, + .start_index = 0, + }; + + if (!cap_htab_fd) { + fprintf(stderr, "KVM version doesn't support saving the hash table\n"); + return -1; + } + + return kvm_vm_ioctl(kvm_state, KVM_PPC_GET_HTAB_FD, &s); +} + +int kvmppc_save_htab(QEMUFile *f, int fd, size_t bufsize, int64_t max_ns) +{ + int64_t starttime = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + uint8_t buf[bufsize]; + ssize_t rc; + + do { + rc = read(fd, buf, bufsize); + if (rc < 0) { + fprintf(stderr, "Error reading data from KVM HTAB fd: %s\n", + strerror(errno)); + return rc; + } else if (rc) { + uint8_t *buffer = buf; + ssize_t n = rc; + while (n) { + struct kvm_get_htab_header *head = + (struct kvm_get_htab_header *) buffer; + size_t chunksize = sizeof(*head) + + HASH_PTE_SIZE_64 * head->n_valid; + + qemu_put_be32(f, head->index); + qemu_put_be16(f, head->n_valid); + qemu_put_be16(f, head->n_invalid); + qemu_put_buffer(f, (void *)(head + 1), + HASH_PTE_SIZE_64 * head->n_valid); + + buffer += chunksize; + n -= chunksize; + } + } + } while ((rc != 0) + && ((max_ns < 0) + || ((qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - starttime) < max_ns))); + + return (rc == 0) ? 1 : 0; +} + +int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index, + uint16_t n_valid, uint16_t n_invalid) +{ + struct kvm_get_htab_header *buf; + size_t chunksize = sizeof(*buf) + n_valid*HASH_PTE_SIZE_64; + ssize_t rc; + + buf = alloca(chunksize); + buf->index = index; + buf->n_valid = n_valid; + buf->n_invalid = n_invalid; + + qemu_get_buffer(f, (void *)(buf + 1), HASH_PTE_SIZE_64*n_valid); + + rc = write(fd, buf, chunksize); + if (rc < 0) { + fprintf(stderr, "Error writing KVM hash table: %s\n", + strerror(errno)); + return rc; + } + if (rc != chunksize) { + /* We should never get a short write on a single chunk */ + fprintf(stderr, "Short write, restoring KVM hash table\n"); + return -1; + } + return 0; +} + +bool kvm_arch_stop_on_emulation_error(CPUState *cpu) +{ + return true; +} + +int kvm_arch_on_sigbus_vcpu(CPUState *cpu, int code, void *addr) +{ + return 1; +} + +int kvm_arch_on_sigbus(int code, void *addr) +{ + return 1; +} + +void kvm_arch_init_irq_routing(KVMState *s) +{ +} + +struct kvm_get_htab_buf { + struct kvm_get_htab_header header; + /* + * We require one extra byte for read + */ + target_ulong hpte[(HPTES_PER_GROUP * 2) + 1]; +}; + +uint64_t kvmppc_hash64_read_pteg(PowerPCCPU *cpu, target_ulong pte_index) +{ + int htab_fd; + struct kvm_get_htab_fd ghf; + struct kvm_get_htab_buf *hpte_buf; + + ghf.flags = 0; + ghf.start_index = pte_index; + htab_fd = kvm_vm_ioctl(kvm_state, KVM_PPC_GET_HTAB_FD, &ghf); + if (htab_fd < 0) { + goto error_out; + } + + hpte_buf = g_malloc0(sizeof(*hpte_buf)); + /* + * Read the hpte group + */ + if (read(htab_fd, hpte_buf, sizeof(*hpte_buf)) < 0) { + goto out_close; + } + + close(htab_fd); + return (uint64_t)(uintptr_t) hpte_buf->hpte; + +out_close: + g_free(hpte_buf); + close(htab_fd); +error_out: + return 0; +} + +void kvmppc_hash64_free_pteg(uint64_t token) +{ + struct kvm_get_htab_buf *htab_buf; + + htab_buf = container_of((void *)(uintptr_t) token, struct kvm_get_htab_buf, + hpte); + g_free(htab_buf); + return; +} + +void kvmppc_hash64_write_pte(CPUPPCState *env, target_ulong pte_index, + target_ulong pte0, target_ulong pte1) +{ + int htab_fd; + struct kvm_get_htab_fd ghf; + struct kvm_get_htab_buf hpte_buf; + + ghf.flags = 0; + ghf.start_index = 0; /* Ignored */ + htab_fd = kvm_vm_ioctl(kvm_state, KVM_PPC_GET_HTAB_FD, &ghf); + if (htab_fd < 0) { + goto error_out; + } + + hpte_buf.header.n_valid = 1; + hpte_buf.header.n_invalid = 0; + hpte_buf.header.index = pte_index; + hpte_buf.hpte[0] = pte0; + hpte_buf.hpte[1] = pte1; + /* + * Write the hpte entry. + * CAUTION: write() has the warn_unused_result attribute. Hence we + * need to check the return value, even though we do nothing. + */ + if (write(htab_fd, &hpte_buf, sizeof(hpte_buf)) < 0) { + goto out_close; + } + +out_close: + close(htab_fd); + return; + +error_out: + return; +} + +int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, + uint64_t address, uint32_t data, PCIDevice *dev) +{ + return 0; +} + +int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route, + int vector, PCIDevice *dev) +{ + return 0; +} + +int kvm_arch_release_virq_post(int virq) +{ + return 0; +} + +int kvm_arch_msi_data_to_gsi(uint32_t data) +{ + return data & 0xffff; +} + +int kvmppc_enable_hwrng(void) +{ + if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_PPC_HWRNG)) { + return -1; + } + + return kvmppc_enable_hcall(kvm_state, H_RANDOM); +} diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h new file mode 100644 index 0000000000..bd1d78bfbe --- /dev/null +++ b/target/ppc/kvm_ppc.h @@ -0,0 +1,330 @@ +/* + * Copyright 2008 IBM Corporation. + * Authors: Hollis Blanchard <hollisb@us.ibm.com> + * + * This work is licensed under the GNU GPL license version 2 or later. + * + */ + +#ifndef KVM_PPC_H +#define KVM_PPC_H + +#define TYPE_HOST_POWERPC_CPU "host-" TYPE_POWERPC_CPU + +#ifdef CONFIG_KVM + +uint32_t kvmppc_get_tbfreq(void); +uint64_t kvmppc_get_clockfreq(void); +uint32_t kvmppc_get_vmx(void); +uint32_t kvmppc_get_dfp(void); +bool kvmppc_get_host_model(char **buf); +bool kvmppc_get_host_serial(char **buf); +int kvmppc_get_hasidle(CPUPPCState *env); +int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_len); +int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level); +void kvmppc_enable_logical_ci_hcalls(void); +void kvmppc_enable_set_mode_hcall(void); +void kvmppc_enable_clear_ref_mod_hcalls(void); +void kvmppc_set_papr(PowerPCCPU *cpu); +int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version); +void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy); +int kvmppc_smt_threads(void); +int kvmppc_clear_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits); +int kvmppc_or_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits); +int kvmppc_set_tcr(PowerPCCPU *cpu); +int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu); +#ifndef CONFIG_USER_ONLY +off_t kvmppc_alloc_rma(void **rma); +bool kvmppc_spapr_use_multitce(void); +void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd, + bool need_vfio); +int kvmppc_remove_spapr_tce(void *table, int pfd, uint32_t window_size); +int kvmppc_reset_htab(int shift_hint); +uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift); +#endif /* !CONFIG_USER_ONLY */ +bool kvmppc_has_cap_epr(void); +int kvmppc_define_rtas_kernel_token(uint32_t token, const char *function); +bool kvmppc_has_cap_htab_fd(void); +int kvmppc_get_htab_fd(bool write); +int kvmppc_save_htab(QEMUFile *f, int fd, size_t bufsize, int64_t max_ns); +int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index, + uint16_t n_valid, uint16_t n_invalid); +uint64_t kvmppc_hash64_read_pteg(PowerPCCPU *cpu, target_ulong pte_index); +void kvmppc_hash64_free_pteg(uint64_t token); + +void kvmppc_hash64_write_pte(CPUPPCState *env, target_ulong pte_index, + target_ulong pte0, target_ulong pte1); +bool kvmppc_has_cap_fixup_hcalls(void); +bool kvmppc_has_cap_htm(void); +int kvmppc_enable_hwrng(void); +int kvmppc_put_books_sregs(PowerPCCPU *cpu); +PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void); + +#else + +static inline uint32_t kvmppc_get_tbfreq(void) +{ + return 0; +} + +static inline bool kvmppc_get_host_model(char **buf) +{ + return false; +} + +static inline bool kvmppc_get_host_serial(char **buf) +{ + return false; +} + +static inline uint64_t kvmppc_get_clockfreq(void) +{ + return 0; +} + +static inline uint32_t kvmppc_get_vmx(void) +{ + return 0; +} + +static inline uint32_t kvmppc_get_dfp(void) +{ + return 0; +} + +static inline int kvmppc_get_hasidle(CPUPPCState *env) +{ + return 0; +} + +static inline int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_len) +{ + return -1; +} + +static inline int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level) +{ + return -1; +} + +static inline void kvmppc_enable_logical_ci_hcalls(void) +{ +} + +static inline void kvmppc_enable_set_mode_hcall(void) +{ +} + +static inline void kvmppc_enable_clear_ref_mod_hcalls(void) +{ +} + +static inline void kvmppc_set_papr(PowerPCCPU *cpu) +{ +} + +static inline int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version) +{ + return 0; +} + +static inline void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy) +{ +} + +static inline int kvmppc_smt_threads(void) +{ + return 1; +} + +static inline int kvmppc_or_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits) +{ + return 0; +} + +static inline int kvmppc_clear_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits) +{ + return 0; +} + +static inline int kvmppc_set_tcr(PowerPCCPU *cpu) +{ + return 0; +} + +static inline int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu) +{ + return -1; +} + +#ifndef CONFIG_USER_ONLY +static inline off_t kvmppc_alloc_rma(void **rma) +{ + return 0; +} + +static inline bool kvmppc_spapr_use_multitce(void) +{ + return false; +} + +static inline void *kvmppc_create_spapr_tce(uint32_t liobn, + uint32_t window_size, int *fd, + bool need_vfio) +{ + return NULL; +} + +static inline int kvmppc_remove_spapr_tce(void *table, int pfd, + uint32_t nb_table) +{ + return -1; +} + +static inline int kvmppc_reset_htab(int shift_hint) +{ + return 0; +} + +static inline uint64_t kvmppc_rma_size(uint64_t current_size, + unsigned int hash_shift) +{ + return ram_size; +} + +#endif /* !CONFIG_USER_ONLY */ + +static inline bool kvmppc_has_cap_epr(void) +{ + return false; +} + +static inline int kvmppc_define_rtas_kernel_token(uint32_t token, + const char *function) +{ + return -1; +} + +static inline bool kvmppc_has_cap_htab_fd(void) +{ + return false; +} + +static inline int kvmppc_get_htab_fd(bool write) +{ + return -1; +} + +static inline int kvmppc_save_htab(QEMUFile *f, int fd, size_t bufsize, + int64_t max_ns) +{ + abort(); +} + +static inline int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index, + uint16_t n_valid, uint16_t n_invalid) +{ + abort(); +} + +static inline uint64_t kvmppc_hash64_read_pteg(PowerPCCPU *cpu, + target_ulong pte_index) +{ + abort(); +} + +static inline void kvmppc_hash64_free_pteg(uint64_t token) +{ + abort(); +} + +static inline void kvmppc_hash64_write_pte(CPUPPCState *env, + target_ulong pte_index, + target_ulong pte0, target_ulong pte1) +{ + abort(); +} + +static inline bool kvmppc_has_cap_fixup_hcalls(void) +{ + abort(); +} + +static inline bool kvmppc_has_cap_htm(void) +{ + return false; +} + +static inline int kvmppc_enable_hwrng(void) +{ + return -1; +} + +static inline int kvmppc_put_books_sregs(PowerPCCPU *cpu) +{ + abort(); +} + +static inline PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void) +{ + return NULL; +} + +#endif + +#ifndef CONFIG_KVM + +#define kvmppc_eieio() do { } while (0) + +static inline void kvmppc_dcbst_range(PowerPCCPU *cpu, uint8_t *addr, int len) +{ +} + +static inline void kvmppc_icbi_range(PowerPCCPU *cpu, uint8_t *addr, int len) +{ +} + +#else /* CONFIG_KVM */ + +#define kvmppc_eieio() \ + do { \ + if (kvm_enabled()) { \ + asm volatile("eieio" : : : "memory"); \ + } \ + } while (0) + +/* Store data cache blocks back to memory */ +static inline void kvmppc_dcbst_range(PowerPCCPU *cpu, uint8_t *addr, int len) +{ + uint8_t *p; + + for (p = addr; p < addr + len; p += cpu->env.dcache_line_size) { + asm volatile("dcbst 0,%0" : : "r"(p) : "memory"); + } +} + +/* Invalidate instruction cache blocks */ +static inline void kvmppc_icbi_range(PowerPCCPU *cpu, uint8_t *addr, int len) +{ + uint8_t *p; + + for (p = addr; p < addr + len; p += cpu->env.icache_line_size) { + asm volatile("icbi 0,%0" : : "r"(p)); + } +} + +#endif /* CONFIG_KVM */ + +#ifndef KVM_INTERRUPT_SET +#define KVM_INTERRUPT_SET -1 +#endif + +#ifndef KVM_INTERRUPT_UNSET +#define KVM_INTERRUPT_UNSET -2 +#endif + +#ifndef KVM_INTERRUPT_SET_LEVEL +#define KVM_INTERRUPT_SET_LEVEL -3 +#endif + +#endif /* KVM_PPC_H */ diff --git a/target/ppc/machine.c b/target/ppc/machine.c new file mode 100644 index 0000000000..18c16d2512 --- /dev/null +++ b/target/ppc/machine.c @@ -0,0 +1,615 @@ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "hw/hw.h" +#include "hw/boards.h" +#include "sysemu/kvm.h" +#include "helper_regs.h" +#include "mmu-hash64.h" +#include "migration/cpu.h" + +static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) +{ + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; + unsigned int i, j; + target_ulong sdr1; + uint32_t fpscr; + target_ulong xer; + + for (i = 0; i < 32; i++) + qemu_get_betls(f, &env->gpr[i]); +#if !defined(TARGET_PPC64) + for (i = 0; i < 32; i++) + qemu_get_betls(f, &env->gprh[i]); +#endif + qemu_get_betls(f, &env->lr); + qemu_get_betls(f, &env->ctr); + for (i = 0; i < 8; i++) + qemu_get_be32s(f, &env->crf[i]); + qemu_get_betls(f, &xer); + cpu_write_xer(env, xer); + qemu_get_betls(f, &env->reserve_addr); + qemu_get_betls(f, &env->msr); + for (i = 0; i < 4; i++) + qemu_get_betls(f, &env->tgpr[i]); + for (i = 0; i < 32; i++) { + union { + float64 d; + uint64_t l; + } u; + u.l = qemu_get_be64(f); + env->fpr[i] = u.d; + } + qemu_get_be32s(f, &fpscr); + env->fpscr = fpscr; + qemu_get_sbe32s(f, &env->access_type); +#if defined(TARGET_PPC64) + qemu_get_betls(f, &env->spr[SPR_ASR]); + qemu_get_sbe32s(f, &env->slb_nr); +#endif + qemu_get_betls(f, &sdr1); + for (i = 0; i < 32; i++) + qemu_get_betls(f, &env->sr[i]); + for (i = 0; i < 2; i++) + for (j = 0; j < 8; j++) + qemu_get_betls(f, &env->DBAT[i][j]); + for (i = 0; i < 2; i++) + for (j = 0; j < 8; j++) + qemu_get_betls(f, &env->IBAT[i][j]); + qemu_get_sbe32s(f, &env->nb_tlb); + qemu_get_sbe32s(f, &env->tlb_per_way); + qemu_get_sbe32s(f, &env->nb_ways); + qemu_get_sbe32s(f, &env->last_way); + qemu_get_sbe32s(f, &env->id_tlbs); + qemu_get_sbe32s(f, &env->nb_pids); + if (env->tlb.tlb6) { + // XXX assumes 6xx + for (i = 0; i < env->nb_tlb; i++) { + qemu_get_betls(f, &env->tlb.tlb6[i].pte0); + qemu_get_betls(f, &env->tlb.tlb6[i].pte1); + qemu_get_betls(f, &env->tlb.tlb6[i].EPN); + } + } + for (i = 0; i < 4; i++) + qemu_get_betls(f, &env->pb[i]); + for (i = 0; i < 1024; i++) + qemu_get_betls(f, &env->spr[i]); + if (!env->external_htab) { + ppc_store_sdr1(env, sdr1); + } + qemu_get_be32s(f, &env->vscr); + qemu_get_be64s(f, &env->spe_acc); + qemu_get_be32s(f, &env->spe_fscr); + qemu_get_betls(f, &env->msr_mask); + qemu_get_be32s(f, &env->flags); + qemu_get_sbe32s(f, &env->error_code); + qemu_get_be32s(f, &env->pending_interrupts); + qemu_get_be32s(f, &env->irq_input_state); + for (i = 0; i < POWERPC_EXCP_NB; i++) + qemu_get_betls(f, &env->excp_vectors[i]); + qemu_get_betls(f, &env->excp_prefix); + qemu_get_betls(f, &env->ivor_mask); + qemu_get_betls(f, &env->ivpr_mask); + qemu_get_betls(f, &env->hreset_vector); + qemu_get_betls(f, &env->nip); + qemu_get_betls(f, &env->hflags); + qemu_get_betls(f, &env->hflags_nmsr); + qemu_get_sbe32(f); /* Discard unused mmu_idx */ + qemu_get_sbe32(f); /* Discard unused power_mode */ + + /* Recompute mmu indices */ + hreg_compute_mem_idx(env); + + return 0; +} + +static int get_avr(QEMUFile *f, void *pv, size_t size) +{ + ppc_avr_t *v = pv; + + v->u64[0] = qemu_get_be64(f); + v->u64[1] = qemu_get_be64(f); + + return 0; +} + +static void put_avr(QEMUFile *f, void *pv, size_t size) +{ + ppc_avr_t *v = pv; + + qemu_put_be64(f, v->u64[0]); + qemu_put_be64(f, v->u64[1]); +} + +static const VMStateInfo vmstate_info_avr = { + .name = "avr", + .get = get_avr, + .put = put_avr, +}; + +#define VMSTATE_AVR_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_avr, ppc_avr_t) + +#define VMSTATE_AVR_ARRAY(_f, _s, _n) \ + VMSTATE_AVR_ARRAY_V(_f, _s, _n, 0) + +static bool cpu_pre_2_8_migration(void *opaque, int version_id) +{ + PowerPCCPU *cpu = opaque; + + return cpu->pre_2_8_migration; +} + +static void cpu_pre_save(void *opaque) +{ + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; + int i; + uint64_t insns_compat_mask = + PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB + | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES + | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | PPC_FLOAT_FRSQRTES + | PPC_FLOAT_STFIWX | PPC_FLOAT_EXT + | PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ + | PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | PPC_MEM_TLBSYNC + | PPC_64B | PPC_64BX | PPC_ALTIVEC + | PPC_SEGMENT_64B | PPC_SLBI | PPC_POPCNTB | PPC_POPCNTWD; + uint64_t insns_compat_mask2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX + | PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 + | PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 + | PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 + | PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 + | PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | PPC2_TM; + + env->spr[SPR_LR] = env->lr; + env->spr[SPR_CTR] = env->ctr; + env->spr[SPR_XER] = cpu_read_xer(env); +#if defined(TARGET_PPC64) + env->spr[SPR_CFAR] = env->cfar; +#endif + env->spr[SPR_BOOKE_SPEFSCR] = env->spe_fscr; + + for (i = 0; (i < 4) && (i < env->nb_BATs); i++) { + env->spr[SPR_DBAT0U + 2*i] = env->DBAT[0][i]; + env->spr[SPR_DBAT0U + 2*i + 1] = env->DBAT[1][i]; + env->spr[SPR_IBAT0U + 2*i] = env->IBAT[0][i]; + env->spr[SPR_IBAT0U + 2*i + 1] = env->IBAT[1][i]; + } + for (i = 0; (i < 4) && ((i+4) < env->nb_BATs); i++) { + env->spr[SPR_DBAT4U + 2*i] = env->DBAT[0][i+4]; + env->spr[SPR_DBAT4U + 2*i + 1] = env->DBAT[1][i+4]; + env->spr[SPR_IBAT4U + 2*i] = env->IBAT[0][i+4]; + env->spr[SPR_IBAT4U + 2*i + 1] = env->IBAT[1][i+4]; + } + + /* Hacks for migration compatibility between 2.6, 2.7 & 2.8 */ + if (cpu->pre_2_8_migration) { + cpu->mig_msr_mask = env->msr_mask; + cpu->mig_insns_flags = env->insns_flags & insns_compat_mask; + cpu->mig_insns_flags2 = env->insns_flags2 & insns_compat_mask2; + cpu->mig_nb_BATs = env->nb_BATs; + } +} + +static int cpu_post_load(void *opaque, int version_id) +{ + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; + int i; + target_ulong msr; + + /* + * We always ignore the source PVR. The user or management + * software has to take care of running QEMU in a compatible mode. + */ + env->spr[SPR_PVR] = env->spr_cb[SPR_PVR].default_value; + env->lr = env->spr[SPR_LR]; + env->ctr = env->spr[SPR_CTR]; + cpu_write_xer(env, env->spr[SPR_XER]); +#if defined(TARGET_PPC64) + env->cfar = env->spr[SPR_CFAR]; +#endif + env->spe_fscr = env->spr[SPR_BOOKE_SPEFSCR]; + + for (i = 0; (i < 4) && (i < env->nb_BATs); i++) { + env->DBAT[0][i] = env->spr[SPR_DBAT0U + 2*i]; + env->DBAT[1][i] = env->spr[SPR_DBAT0U + 2*i + 1]; + env->IBAT[0][i] = env->spr[SPR_IBAT0U + 2*i]; + env->IBAT[1][i] = env->spr[SPR_IBAT0U + 2*i + 1]; + } + for (i = 0; (i < 4) && ((i+4) < env->nb_BATs); i++) { + env->DBAT[0][i+4] = env->spr[SPR_DBAT4U + 2*i]; + env->DBAT[1][i+4] = env->spr[SPR_DBAT4U + 2*i + 1]; + env->IBAT[0][i+4] = env->spr[SPR_IBAT4U + 2*i]; + env->IBAT[1][i+4] = env->spr[SPR_IBAT4U + 2*i + 1]; + } + + if (!env->external_htab) { + /* Restore htab_base and htab_mask variables */ + ppc_store_sdr1(env, env->spr[SPR_SDR1]); + } + + /* Invalidate all msr bits except MSR_TGPR/MSR_HVB before restoring */ + msr = env->msr; + env->msr ^= ~((1ULL << MSR_TGPR) | MSR_HVB); + ppc_store_msr(env, msr); + + hreg_compute_mem_idx(env); + + return 0; +} + +static bool fpu_needed(void *opaque) +{ + PowerPCCPU *cpu = opaque; + + return (cpu->env.insns_flags & PPC_FLOAT); +} + +static const VMStateDescription vmstate_fpu = { + .name = "cpu/fpu", + .version_id = 1, + .minimum_version_id = 1, + .needed = fpu_needed, + .fields = (VMStateField[]) { + VMSTATE_FLOAT64_ARRAY(env.fpr, PowerPCCPU, 32), + VMSTATE_UINTTL(env.fpscr, PowerPCCPU), + VMSTATE_END_OF_LIST() + }, +}; + +static bool altivec_needed(void *opaque) +{ + PowerPCCPU *cpu = opaque; + + return (cpu->env.insns_flags & PPC_ALTIVEC); +} + +static const VMStateDescription vmstate_altivec = { + .name = "cpu/altivec", + .version_id = 1, + .minimum_version_id = 1, + .needed = altivec_needed, + .fields = (VMStateField[]) { + VMSTATE_AVR_ARRAY(env.avr, PowerPCCPU, 32), + VMSTATE_UINT32(env.vscr, PowerPCCPU), + VMSTATE_END_OF_LIST() + }, +}; + +static bool vsx_needed(void *opaque) +{ + PowerPCCPU *cpu = opaque; + + return (cpu->env.insns_flags2 & PPC2_VSX); +} + +static const VMStateDescription vmstate_vsx = { + .name = "cpu/vsx", + .version_id = 1, + .minimum_version_id = 1, + .needed = vsx_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64_ARRAY(env.vsr, PowerPCCPU, 32), + VMSTATE_END_OF_LIST() + }, +}; + +#ifdef TARGET_PPC64 +/* Transactional memory state */ +static bool tm_needed(void *opaque) +{ + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; + return msr_ts; +} + +static const VMStateDescription vmstate_tm = { + .name = "cpu/tm", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .needed = tm_needed, + .fields = (VMStateField []) { + VMSTATE_UINTTL_ARRAY(env.tm_gpr, PowerPCCPU, 32), + VMSTATE_AVR_ARRAY(env.tm_vsr, PowerPCCPU, 64), + VMSTATE_UINT64(env.tm_cr, PowerPCCPU), + VMSTATE_UINT64(env.tm_lr, PowerPCCPU), + VMSTATE_UINT64(env.tm_ctr, PowerPCCPU), + VMSTATE_UINT64(env.tm_fpscr, PowerPCCPU), + VMSTATE_UINT64(env.tm_amr, PowerPCCPU), + VMSTATE_UINT64(env.tm_ppr, PowerPCCPU), + VMSTATE_UINT64(env.tm_vrsave, PowerPCCPU), + VMSTATE_UINT32(env.tm_vscr, PowerPCCPU), + VMSTATE_UINT64(env.tm_dscr, PowerPCCPU), + VMSTATE_UINT64(env.tm_tar, PowerPCCPU), + VMSTATE_END_OF_LIST() + }, +}; +#endif + +static bool sr_needed(void *opaque) +{ +#ifdef TARGET_PPC64 + PowerPCCPU *cpu = opaque; + + return !(cpu->env.mmu_model & POWERPC_MMU_64); +#else + return true; +#endif +} + +static const VMStateDescription vmstate_sr = { + .name = "cpu/sr", + .version_id = 1, + .minimum_version_id = 1, + .needed = sr_needed, + .fields = (VMStateField[]) { + VMSTATE_UINTTL_ARRAY(env.sr, PowerPCCPU, 32), + VMSTATE_END_OF_LIST() + }, +}; + +#ifdef TARGET_PPC64 +static int get_slbe(QEMUFile *f, void *pv, size_t size) +{ + ppc_slb_t *v = pv; + + v->esid = qemu_get_be64(f); + v->vsid = qemu_get_be64(f); + + return 0; +} + +static void put_slbe(QEMUFile *f, void *pv, size_t size) +{ + ppc_slb_t *v = pv; + + qemu_put_be64(f, v->esid); + qemu_put_be64(f, v->vsid); +} + +static const VMStateInfo vmstate_info_slbe = { + .name = "slbe", + .get = get_slbe, + .put = put_slbe, +}; + +#define VMSTATE_SLB_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_slbe, ppc_slb_t) + +#define VMSTATE_SLB_ARRAY(_f, _s, _n) \ + VMSTATE_SLB_ARRAY_V(_f, _s, _n, 0) + +static bool slb_needed(void *opaque) +{ + PowerPCCPU *cpu = opaque; + + /* We don't support any of the old segment table based 64-bit CPUs */ + return (cpu->env.mmu_model & POWERPC_MMU_64); +} + +static int slb_post_load(void *opaque, int version_id) +{ + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; + int i; + + /* We've pulled in the raw esid and vsid values from the migration + * stream, but we need to recompute the page size pointers */ + for (i = 0; i < env->slb_nr; i++) { + if (ppc_store_slb(cpu, i, env->slb[i].esid, env->slb[i].vsid) < 0) { + /* Migration source had bad values in its SLB */ + return -1; + } + } + + return 0; +} + +static const VMStateDescription vmstate_slb = { + .name = "cpu/slb", + .version_id = 1, + .minimum_version_id = 1, + .needed = slb_needed, + .post_load = slb_post_load, + .fields = (VMStateField[]) { + VMSTATE_INT32_EQUAL(env.slb_nr, PowerPCCPU), + VMSTATE_SLB_ARRAY(env.slb, PowerPCCPU, MAX_SLB_ENTRIES), + VMSTATE_END_OF_LIST() + } +}; +#endif /* TARGET_PPC64 */ + +static const VMStateDescription vmstate_tlb6xx_entry = { + .name = "cpu/tlb6xx_entry", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINTTL(pte0, ppc6xx_tlb_t), + VMSTATE_UINTTL(pte1, ppc6xx_tlb_t), + VMSTATE_UINTTL(EPN, ppc6xx_tlb_t), + VMSTATE_END_OF_LIST() + }, +}; + +static bool tlb6xx_needed(void *opaque) +{ + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; + + return env->nb_tlb && (env->tlb_type == TLB_6XX); +} + +static const VMStateDescription vmstate_tlb6xx = { + .name = "cpu/tlb6xx", + .version_id = 1, + .minimum_version_id = 1, + .needed = tlb6xx_needed, + .fields = (VMStateField[]) { + VMSTATE_INT32_EQUAL(env.nb_tlb, PowerPCCPU), + VMSTATE_STRUCT_VARRAY_POINTER_INT32(env.tlb.tlb6, PowerPCCPU, + env.nb_tlb, + vmstate_tlb6xx_entry, + ppc6xx_tlb_t), + VMSTATE_UINTTL_ARRAY(env.tgpr, PowerPCCPU, 4), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_tlbemb_entry = { + .name = "cpu/tlbemb_entry", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(RPN, ppcemb_tlb_t), + VMSTATE_UINTTL(EPN, ppcemb_tlb_t), + VMSTATE_UINTTL(PID, ppcemb_tlb_t), + VMSTATE_UINTTL(size, ppcemb_tlb_t), + VMSTATE_UINT32(prot, ppcemb_tlb_t), + VMSTATE_UINT32(attr, ppcemb_tlb_t), + VMSTATE_END_OF_LIST() + }, +}; + +static bool tlbemb_needed(void *opaque) +{ + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; + + return env->nb_tlb && (env->tlb_type == TLB_EMB); +} + +static bool pbr403_needed(void *opaque) +{ + PowerPCCPU *cpu = opaque; + uint32_t pvr = cpu->env.spr[SPR_PVR]; + + return (pvr & 0xffff0000) == 0x00200000; +} + +static const VMStateDescription vmstate_pbr403 = { + .name = "cpu/pbr403", + .version_id = 1, + .minimum_version_id = 1, + .needed = pbr403_needed, + .fields = (VMStateField[]) { + VMSTATE_UINTTL_ARRAY(env.pb, PowerPCCPU, 4), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_tlbemb = { + .name = "cpu/tlb6xx", + .version_id = 1, + .minimum_version_id = 1, + .needed = tlbemb_needed, + .fields = (VMStateField[]) { + VMSTATE_INT32_EQUAL(env.nb_tlb, PowerPCCPU), + VMSTATE_STRUCT_VARRAY_POINTER_INT32(env.tlb.tlbe, PowerPCCPU, + env.nb_tlb, + vmstate_tlbemb_entry, + ppcemb_tlb_t), + /* 403 protection registers */ + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription*[]) { + &vmstate_pbr403, + NULL + } +}; + +static const VMStateDescription vmstate_tlbmas_entry = { + .name = "cpu/tlbmas_entry", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(mas8, ppcmas_tlb_t), + VMSTATE_UINT32(mas1, ppcmas_tlb_t), + VMSTATE_UINT64(mas2, ppcmas_tlb_t), + VMSTATE_UINT64(mas7_3, ppcmas_tlb_t), + VMSTATE_END_OF_LIST() + }, +}; + +static bool tlbmas_needed(void *opaque) +{ + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; + + return env->nb_tlb && (env->tlb_type == TLB_MAS); +} + +static const VMStateDescription vmstate_tlbmas = { + .name = "cpu/tlbmas", + .version_id = 1, + .minimum_version_id = 1, + .needed = tlbmas_needed, + .fields = (VMStateField[]) { + VMSTATE_INT32_EQUAL(env.nb_tlb, PowerPCCPU), + VMSTATE_STRUCT_VARRAY_POINTER_INT32(env.tlb.tlbm, PowerPCCPU, + env.nb_tlb, + vmstate_tlbmas_entry, + ppcmas_tlb_t), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_ppc_cpu = { + .name = "cpu", + .version_id = 5, + .minimum_version_id = 5, + .minimum_version_id_old = 4, + .load_state_old = cpu_load_old, + .pre_save = cpu_pre_save, + .post_load = cpu_post_load, + .fields = (VMStateField[]) { + VMSTATE_UNUSED(sizeof(target_ulong)), /* was _EQUAL(env.spr[SPR_PVR]) */ + + /* User mode architected state */ + VMSTATE_UINTTL_ARRAY(env.gpr, PowerPCCPU, 32), +#if !defined(TARGET_PPC64) + VMSTATE_UINTTL_ARRAY(env.gprh, PowerPCCPU, 32), +#endif + VMSTATE_UINT32_ARRAY(env.crf, PowerPCCPU, 8), + VMSTATE_UINTTL(env.nip, PowerPCCPU), + + /* SPRs */ + VMSTATE_UINTTL_ARRAY(env.spr, PowerPCCPU, 1024), + VMSTATE_UINT64(env.spe_acc, PowerPCCPU), + + /* Reservation */ + VMSTATE_UINTTL(env.reserve_addr, PowerPCCPU), + + /* Supervisor mode architected state */ + VMSTATE_UINTTL(env.msr, PowerPCCPU), + + /* Internal state */ + VMSTATE_UINTTL(env.hflags_nmsr, PowerPCCPU), + /* FIXME: access_type? */ + + /* Sanity checking */ + VMSTATE_UINTTL_TEST(mig_msr_mask, PowerPCCPU, cpu_pre_2_8_migration), + VMSTATE_UINT64_TEST(mig_insns_flags, PowerPCCPU, cpu_pre_2_8_migration), + VMSTATE_UINT64_TEST(mig_insns_flags2, PowerPCCPU, + cpu_pre_2_8_migration), + VMSTATE_UINT32_TEST(mig_nb_BATs, PowerPCCPU, cpu_pre_2_8_migration), + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription*[]) { + &vmstate_fpu, + &vmstate_altivec, + &vmstate_vsx, + &vmstate_sr, +#ifdef TARGET_PPC64 + &vmstate_tm, + &vmstate_slb, +#endif /* TARGET_PPC64 */ + &vmstate_tlb6xx, + &vmstate_tlbemb, + &vmstate_tlbmas, + NULL + } +}; diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c new file mode 100644 index 0000000000..1ab8a6eab4 --- /dev/null +++ b/target/ppc/mem_helper.c @@ -0,0 +1,310 @@ +/* + * PowerPC memory access emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * + * 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 "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "qemu/host-utils.h" +#include "exec/helper-proto.h" + +#include "helper_regs.h" +#include "exec/cpu_ldst.h" + +//#define DEBUG_OP + +static inline bool needs_byteswap(const CPUPPCState *env) +{ +#if defined(TARGET_WORDS_BIGENDIAN) + return msr_le; +#else + return !msr_le; +#endif +} + +/*****************************************************************************/ +/* Memory load and stores */ + +static inline target_ulong addr_add(CPUPPCState *env, target_ulong addr, + target_long arg) +{ +#if defined(TARGET_PPC64) + if (!msr_is_64bit(env, env->msr)) { + return (uint32_t)(addr + arg); + } else +#endif + { + return addr + arg; + } +} + +void helper_lmw(CPUPPCState *env, target_ulong addr, uint32_t reg) +{ + for (; reg < 32; reg++) { + if (needs_byteswap(env)) { + env->gpr[reg] = bswap32(cpu_ldl_data_ra(env, addr, GETPC())); + } else { + env->gpr[reg] = cpu_ldl_data_ra(env, addr, GETPC()); + } + addr = addr_add(env, addr, 4); + } +} + +void helper_stmw(CPUPPCState *env, target_ulong addr, uint32_t reg) +{ + for (; reg < 32; reg++) { + if (needs_byteswap(env)) { + cpu_stl_data_ra(env, addr, bswap32((uint32_t)env->gpr[reg]), + GETPC()); + } else { + cpu_stl_data_ra(env, addr, (uint32_t)env->gpr[reg], GETPC()); + } + addr = addr_add(env, addr, 4); + } +} + +static void do_lsw(CPUPPCState *env, target_ulong addr, uint32_t nb, + uint32_t reg, uintptr_t raddr) +{ + int sh; + + for (; nb > 3; nb -= 4) { + env->gpr[reg] = cpu_ldl_data_ra(env, addr, raddr); + reg = (reg + 1) % 32; + addr = addr_add(env, addr, 4); + } + if (unlikely(nb > 0)) { + env->gpr[reg] = 0; + for (sh = 24; nb > 0; nb--, sh -= 8) { + env->gpr[reg] |= cpu_ldub_data_ra(env, addr, raddr) << sh; + addr = addr_add(env, addr, 1); + } + } +} + +void helper_lsw(CPUPPCState *env, target_ulong addr, uint32_t nb, uint32_t reg) +{ + do_lsw(env, addr, nb, reg, GETPC()); +} + +/* PPC32 specification says we must generate an exception if + * rA is in the range of registers to be loaded. + * In an other hand, IBM says this is valid, but rA won't be loaded. + * For now, I'll follow the spec... + */ +void helper_lswx(CPUPPCState *env, target_ulong addr, uint32_t reg, + uint32_t ra, uint32_t rb) +{ + if (likely(xer_bc != 0)) { + int num_used_regs = (xer_bc + 3) / 4; + if (unlikely((ra != 0 && lsw_reg_in_range(reg, num_used_regs, ra)) || + lsw_reg_in_range(reg, num_used_regs, rb))) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | + POWERPC_EXCP_INVAL_LSWX, GETPC()); + } else { + do_lsw(env, addr, xer_bc, reg, GETPC()); + } + } +} + +void helper_stsw(CPUPPCState *env, target_ulong addr, uint32_t nb, + uint32_t reg) +{ + int sh; + + for (; nb > 3; nb -= 4) { + cpu_stl_data_ra(env, addr, env->gpr[reg], GETPC()); + reg = (reg + 1) % 32; + addr = addr_add(env, addr, 4); + } + if (unlikely(nb > 0)) { + for (sh = 24; nb > 0; nb--, sh -= 8) { + cpu_stb_data_ra(env, addr, (env->gpr[reg] >> sh) & 0xFF, GETPC()); + addr = addr_add(env, addr, 1); + } + } +} + +void helper_dcbz(CPUPPCState *env, target_ulong addr, uint32_t opcode) +{ + target_ulong mask, dcbz_size = env->dcache_line_size; + uint32_t i; + void *haddr; + +#if defined(TARGET_PPC64) + /* Check for dcbz vs dcbzl on 970 */ + if (env->excp_model == POWERPC_EXCP_970 && + !(opcode & 0x00200000) && ((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) { + dcbz_size = 32; + } +#endif + + /* Align address */ + mask = ~(dcbz_size - 1); + addr &= mask; + + /* Check reservation */ + if ((env->reserve_addr & mask) == (addr & mask)) { + env->reserve_addr = (target_ulong)-1ULL; + } + + /* Try fast path translate */ + haddr = tlb_vaddr_to_host(env, addr, MMU_DATA_STORE, env->dmmu_idx); + if (haddr) { + memset(haddr, 0, dcbz_size); + } else { + /* Slow path */ + for (i = 0; i < dcbz_size; i += 8) { + cpu_stq_data_ra(env, addr + i, 0, GETPC()); + } + } +} + +void helper_icbi(CPUPPCState *env, target_ulong addr) +{ + addr &= ~(env->dcache_line_size - 1); + /* Invalidate one cache line : + * PowerPC specification says this is to be treated like a load + * (not a fetch) by the MMU. To be sure it will be so, + * do the load "by hand". + */ + cpu_ldl_data_ra(env, addr, GETPC()); +} + +/* XXX: to be tested */ +target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg, + uint32_t ra, uint32_t rb) +{ + int i, c, d; + + d = 24; + for (i = 0; i < xer_bc; i++) { + c = cpu_ldub_data_ra(env, addr, GETPC()); + addr = addr_add(env, addr, 1); + /* ra (if not 0) and rb are never modified */ + if (likely(reg != rb && (ra == 0 || reg != ra))) { + env->gpr[reg] = (env->gpr[reg] & ~(0xFF << d)) | (c << d); + } + if (unlikely(c == xer_cmp)) { + break; + } + if (likely(d != 0)) { + d -= 8; + } else { + d = 24; + reg++; + reg = reg & 0x1F; + } + } + return i; +} + +/*****************************************************************************/ +/* Altivec extension helpers */ +#if defined(HOST_WORDS_BIGENDIAN) +#define HI_IDX 0 +#define LO_IDX 1 +#else +#define HI_IDX 1 +#define LO_IDX 0 +#endif + +/* We use msr_le to determine index ordering in a vector. However, + byteswapping is not simply controlled by msr_le. We also need to take + into account endianness of the target. This is done for the little-endian + PPC64 user-mode target. */ + +#define LVE(name, access, swap, element) \ + void helper_##name(CPUPPCState *env, ppc_avr_t *r, \ + target_ulong addr) \ + { \ + size_t n_elems = ARRAY_SIZE(r->element); \ + int adjust = HI_IDX*(n_elems - 1); \ + int sh = sizeof(r->element[0]) >> 1; \ + int index = (addr & 0xf) >> sh; \ + if (msr_le) { \ + index = n_elems - index - 1; \ + } \ + \ + if (needs_byteswap(env)) { \ + r->element[LO_IDX ? index : (adjust - index)] = \ + swap(access(env, addr, GETPC())); \ + } else { \ + r->element[LO_IDX ? index : (adjust - index)] = \ + access(env, addr, GETPC()); \ + } \ + } +#define I(x) (x) +LVE(lvebx, cpu_ldub_data_ra, I, u8) +LVE(lvehx, cpu_lduw_data_ra, bswap16, u16) +LVE(lvewx, cpu_ldl_data_ra, bswap32, u32) +#undef I +#undef LVE + +#define STVE(name, access, swap, element) \ + void helper_##name(CPUPPCState *env, ppc_avr_t *r, \ + target_ulong addr) \ + { \ + size_t n_elems = ARRAY_SIZE(r->element); \ + int adjust = HI_IDX * (n_elems - 1); \ + int sh = sizeof(r->element[0]) >> 1; \ + int index = (addr & 0xf) >> sh; \ + if (msr_le) { \ + index = n_elems - index - 1; \ + } \ + \ + if (needs_byteswap(env)) { \ + access(env, addr, swap(r->element[LO_IDX ? index : \ + (adjust - index)]), \ + GETPC()); \ + } else { \ + access(env, addr, r->element[LO_IDX ? index : \ + (adjust - index)], GETPC()); \ + } \ + } +#define I(x) (x) +STVE(stvebx, cpu_stb_data_ra, I, u8) +STVE(stvehx, cpu_stw_data_ra, bswap16, u16) +STVE(stvewx, cpu_stl_data_ra, bswap32, u32) +#undef I +#undef LVE + +#undef HI_IDX +#undef LO_IDX + +void helper_tbegin(CPUPPCState *env) +{ + /* As a degenerate implementation, always fail tbegin. The reason + * given is "Nesting overflow". The "persistent" bit is set, + * providing a hint to the error handler to not retry. The TFIAR + * captures the address of the failure, which is this tbegin + * instruction. Instruction execution will continue with the + * next instruction in memory, which is precisely what we want. + */ + + env->spr[SPR_TEXASR] = + (1ULL << TEXASR_FAILURE_PERSISTENT) | + (1ULL << TEXASR_NESTING_OVERFLOW) | + (msr_hv << TEXASR_PRIVILEGE_HV) | + (msr_pr << TEXASR_PRIVILEGE_PR) | + (1ULL << TEXASR_FAILURE_SUMMARY) | + (1ULL << TEXASR_TFIAR_EXACT); + env->spr[SPR_TFIAR] = env->nip | (msr_hv << 1) | msr_pr; + env->spr[SPR_TFHAR] = env->nip + 4; + env->crf[0] = 0xB; /* 0b1010 = transaction failure */ +} diff --git a/target/ppc/mfrom_table.c b/target/ppc/mfrom_table.c new file mode 100644 index 0000000000..6a1fa375c9 --- /dev/null +++ b/target/ppc/mfrom_table.c @@ -0,0 +1,79 @@ +static const uint8_t mfrom_ROM_table[602] = +{ + 77, 77, 76, 76, 75, 75, 74, 74, + 73, 73, 72, 72, 71, 71, 70, 70, + 69, 69, 68, 68, 68, 67, 67, 66, + 66, 65, 65, 64, 64, 64, 63, 63, + 62, 62, 61, 61, 61, 60, 60, 59, + 59, 58, 58, 58, 57, 57, 56, 56, + 56, 55, 55, 54, 54, 54, 53, 53, + 53, 52, 52, 51, 51, 51, 50, 50, + 50, 49, 49, 49, 48, 48, 47, 47, + 47, 46, 46, 46, 45, 45, 45, 44, + 44, 44, 43, 43, 43, 42, 42, 42, + 42, 41, 41, 41, 40, 40, 40, 39, + 39, 39, 39, 38, 38, 38, 37, 37, + 37, 37, 36, 36, 36, 35, 35, 35, + 35, 34, 34, 34, 34, 33, 33, 33, + 33, 32, 32, 32, 32, 31, 31, 31, + 31, 30, 30, 30, 30, 29, 29, 29, + 29, 28, 28, 28, 28, 28, 27, 27, + 27, 27, 26, 26, 26, 26, 26, 25, + 25, 25, 25, 25, 24, 24, 24, 24, + 24, 23, 23, 23, 23, 23, 23, 22, + 22, 22, 22, 22, 21, 21, 21, 21, + 21, 21, 20, 20, 20, 20, 20, 20, + 19, 19, 19, 19, 19, 19, 19, 18, + 18, 18, 18, 18, 18, 17, 17, 17, + 17, 17, 17, 17, 16, 16, 16, 16, + 16, 16, 16, 16, 15, 15, 15, 15, + 15, 15, 15, 15, 14, 14, 14, 14, + 14, 14, 14, 14, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 11, + 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, +}; diff --git a/target/ppc/mfrom_table_gen.c b/target/ppc/mfrom_table_gen.c new file mode 100644 index 0000000000..631791808e --- /dev/null +++ b/target/ppc/mfrom_table_gen.c @@ -0,0 +1,32 @@ +#define _GNU_SOURCE +#include "qemu/osdep.h" +#include <math.h> + +int main (void) +{ + double d; + uint8_t n; + int i; + + printf("static const uint8_t mfrom_ROM_table[602] =\n{\n "); + for (i = 0; i < 602; i++) { + /* Extremely decomposed: + * -T0 / 256 + * T0 = 256 * log10(10 + 1.0) + 0.5 + */ + d = -i; + d /= 256.0; + d = exp10(d); + d += 1.0; + d = log10(d); + d *= 256; + d += 0.5; + n = d; + printf("%3d, ", n); + if ((i & 7) == 7) + printf("\n "); + } + printf("\n};\n"); + + return 0; +} diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c new file mode 100644 index 0000000000..1e6e705a4e --- /dev/null +++ b/target/ppc/misc_helper.c @@ -0,0 +1,210 @@ +/* + * Miscellaneous PowerPC emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * + * 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 "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" + +#include "helper_regs.h" + +/*****************************************************************************/ +/* SPR accesses */ +void helper_load_dump_spr(CPUPPCState *env, uint32_t sprn) +{ + qemu_log("Read SPR %d %03x => " TARGET_FMT_lx "\n", sprn, sprn, + env->spr[sprn]); +} + +void helper_store_dump_spr(CPUPPCState *env, uint32_t sprn) +{ + qemu_log("Write SPR %d %03x <= " TARGET_FMT_lx "\n", sprn, sprn, + env->spr[sprn]); +} + +#ifdef TARGET_PPC64 +static void raise_fu_exception(CPUPPCState *env, uint32_t bit, + uint32_t sprn, uint32_t cause, + uintptr_t raddr) +{ + qemu_log("Facility SPR %d is unavailable (SPR FSCR:%d)\n", sprn, bit); + + env->spr[SPR_FSCR] &= ~((target_ulong)FSCR_IC_MASK << FSCR_IC_POS); + cause &= FSCR_IC_MASK; + env->spr[SPR_FSCR] |= (target_ulong)cause << FSCR_IC_POS; + + raise_exception_err_ra(env, POWERPC_EXCP_FU, 0, raddr); +} +#endif + +void helper_fscr_facility_check(CPUPPCState *env, uint32_t bit, + uint32_t sprn, uint32_t cause) +{ +#ifdef TARGET_PPC64 + if (env->spr[SPR_FSCR] & (1ULL << bit)) { + /* Facility is enabled, continue */ + return; + } + raise_fu_exception(env, bit, sprn, cause, GETPC()); +#endif +} + +void helper_msr_facility_check(CPUPPCState *env, uint32_t bit, + uint32_t sprn, uint32_t cause) +{ +#ifdef TARGET_PPC64 + if (env->msr & (1ULL << bit)) { + /* Facility is enabled, continue */ + return; + } + raise_fu_exception(env, bit, sprn, cause, GETPC()); +#endif +} + +#if !defined(CONFIG_USER_ONLY) + +void helper_store_sdr1(CPUPPCState *env, target_ulong val) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + if (!env->external_htab) { + if (env->spr[SPR_SDR1] != val) { + ppc_store_sdr1(env, val); + tlb_flush(CPU(cpu), 1); + } + } +} + +void helper_store_hid0_601(CPUPPCState *env, target_ulong val) +{ + target_ulong hid0; + + hid0 = env->spr[SPR_HID0]; + if ((val ^ hid0) & 0x00000008) { + /* Change current endianness */ + env->hflags &= ~(1 << MSR_LE); + env->hflags_nmsr &= ~(1 << MSR_LE); + env->hflags_nmsr |= (1 << MSR_LE) & (((val >> 3) & 1) << MSR_LE); + env->hflags |= env->hflags_nmsr; + qemu_log("%s: set endianness to %c => " TARGET_FMT_lx "\n", __func__, + val & 0x8 ? 'l' : 'b', env->hflags); + } + env->spr[SPR_HID0] = (uint32_t)val; +} + +void helper_store_403_pbr(CPUPPCState *env, uint32_t num, target_ulong value) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + if (likely(env->pb[num] != value)) { + env->pb[num] = value; + /* Should be optimized */ + tlb_flush(CPU(cpu), 1); + } +} + +void helper_store_40x_dbcr0(CPUPPCState *env, target_ulong val) +{ + store_40x_dbcr0(env, val); +} + +void helper_store_40x_sler(CPUPPCState *env, target_ulong val) +{ + store_40x_sler(env, val); +} +#endif +/*****************************************************************************/ +/* PowerPC 601 specific instructions (POWER bridge) */ + +target_ulong helper_clcs(CPUPPCState *env, uint32_t arg) +{ + switch (arg) { + case 0x0CUL: + /* Instruction cache line size */ + return env->icache_line_size; + break; + case 0x0DUL: + /* Data cache line size */ + return env->dcache_line_size; + break; + case 0x0EUL: + /* Minimum cache line size */ + return (env->icache_line_size < env->dcache_line_size) ? + env->icache_line_size : env->dcache_line_size; + break; + case 0x0FUL: + /* Maximum cache line size */ + return (env->icache_line_size > env->dcache_line_size) ? + env->icache_line_size : env->dcache_line_size; + break; + default: + /* Undefined */ + return 0; + break; + } +} + +/*****************************************************************************/ +/* Special registers manipulation */ + +/* GDBstub can read and write MSR... */ +void ppc_store_msr(CPUPPCState *env, target_ulong value) +{ + hreg_store_msr(env, value, 0); +} + +/* This code is lifted from MacOnLinux. It is called whenever + * THRM1,2 or 3 is read an fixes up the values in such a way + * that will make MacOS not hang. These registers exist on some + * 75x and 74xx processors. + */ +void helper_fixup_thrm(CPUPPCState *env) +{ + target_ulong v, t; + int i; + +#define THRM1_TIN (1 << 31) +#define THRM1_TIV (1 << 30) +#define THRM1_THRES(x) (((x) & 0x7f) << 23) +#define THRM1_TID (1 << 2) +#define THRM1_TIE (1 << 1) +#define THRM1_V (1 << 0) +#define THRM3_E (1 << 0) + + if (!(env->spr[SPR_THRM3] & THRM3_E)) { + return; + } + + /* Note: Thermal interrupts are unimplemented */ + for (i = SPR_THRM1; i <= SPR_THRM2; i++) { + v = env->spr[i]; + if (!(v & THRM1_V)) { + continue; + } + v |= THRM1_TIV; + v &= ~THRM1_TIN; + t = v & THRM1_THRES(127); + if ((v & THRM1_TID) && t < THRM1_THRES(24)) { + v |= THRM1_TIN; + } + if (!(v & THRM1_TID) && t > THRM1_THRES(24)) { + v |= THRM1_TIN; + } + env->spr[i] = v; + } +} diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c new file mode 100644 index 0000000000..29bace622a --- /dev/null +++ b/target/ppc/mmu-hash32.c @@ -0,0 +1,568 @@ +/* + * PowerPC MMU, TLB and BAT emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * Copyright (c) 2013 David Gibson, IBM Corporation + * + * 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 "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "sysemu/kvm.h" +#include "kvm_ppc.h" +#include "mmu-hash32.h" +#include "exec/log.h" + +//#define DEBUG_BAT + +#ifdef DEBUG_BATS +# define LOG_BATS(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__) +#else +# define LOG_BATS(...) do { } while (0) +#endif + +struct mmu_ctx_hash32 { + hwaddr raddr; /* Real address */ + int prot; /* Protection bits */ + int key; /* Access key */ +}; + +static int ppc_hash32_pp_prot(int key, int pp, int nx) +{ + int prot; + + if (key == 0) { + switch (pp) { + case 0x0: + case 0x1: + case 0x2: + prot = PAGE_READ | PAGE_WRITE; + break; + + case 0x3: + prot = PAGE_READ; + break; + + default: + abort(); + } + } else { + switch (pp) { + case 0x0: + prot = 0; + break; + + case 0x1: + case 0x3: + prot = PAGE_READ; + break; + + case 0x2: + prot = PAGE_READ | PAGE_WRITE; + break; + + default: + abort(); + } + } + if (nx == 0) { + prot |= PAGE_EXEC; + } + + return prot; +} + +static int ppc_hash32_pte_prot(PowerPCCPU *cpu, + target_ulong sr, ppc_hash_pte32_t pte) +{ + CPUPPCState *env = &cpu->env; + unsigned pp, key; + + key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS)); + pp = pte.pte1 & HPTE32_R_PP; + + return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX)); +} + +static target_ulong hash32_bat_size(PowerPCCPU *cpu, + target_ulong batu, target_ulong batl) +{ + CPUPPCState *env = &cpu->env; + + if ((msr_pr && !(batu & BATU32_VP)) + || (!msr_pr && !(batu & BATU32_VS))) { + return 0; + } + + return BATU32_BEPI & ~((batu & BATU32_BL) << 15); +} + +static int hash32_bat_prot(PowerPCCPU *cpu, + target_ulong batu, target_ulong batl) +{ + int pp, prot; + + prot = 0; + pp = batl & BATL32_PP; + if (pp != 0) { + prot = PAGE_READ | PAGE_EXEC; + if (pp == 0x2) { + prot |= PAGE_WRITE; + } + } + return prot; +} + +static target_ulong hash32_bat_601_size(PowerPCCPU *cpu, + target_ulong batu, target_ulong batl) +{ + if (!(batl & BATL32_601_V)) { + return 0; + } + + return BATU32_BEPI & ~((batl & BATL32_601_BL) << 17); +} + +static int hash32_bat_601_prot(PowerPCCPU *cpu, + target_ulong batu, target_ulong batl) +{ + CPUPPCState *env = &cpu->env; + int key, pp; + + pp = batu & BATU32_601_PP; + if (msr_pr == 0) { + key = !!(batu & BATU32_601_KS); + } else { + key = !!(batu & BATU32_601_KP); + } + return ppc_hash32_pp_prot(key, pp, 0); +} + +static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, int rwx, + int *prot) +{ + CPUPPCState *env = &cpu->env; + target_ulong *BATlt, *BATut; + int i; + + LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__, + rwx == 2 ? 'I' : 'D', ea); + if (rwx == 2) { + BATlt = env->IBAT[1]; + BATut = env->IBAT[0]; + } else { + BATlt = env->DBAT[1]; + BATut = env->DBAT[0]; + } + for (i = 0; i < env->nb_BATs; i++) { + target_ulong batu = BATut[i]; + target_ulong batl = BATlt[i]; + target_ulong mask; + + if (unlikely(env->mmu_model == POWERPC_MMU_601)) { + mask = hash32_bat_601_size(cpu, batu, batl); + } else { + mask = hash32_bat_size(cpu, batu, batl); + } + LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx + " BATl " TARGET_FMT_lx "\n", __func__, + type == ACCESS_CODE ? 'I' : 'D', i, ea, batu, batl); + + if (mask && ((ea & mask) == (batu & BATU32_BEPI))) { + hwaddr raddr = (batl & mask) | (ea & ~mask); + + if (unlikely(env->mmu_model == POWERPC_MMU_601)) { + *prot = hash32_bat_601_prot(cpu, batu, batl); + } else { + *prot = hash32_bat_prot(cpu, batu, batl); + } + + return raddr & TARGET_PAGE_MASK; + } + } + + /* No hit */ +#if defined(DEBUG_BATS) + if (qemu_log_enabled()) { + LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", ea); + for (i = 0; i < 4; i++) { + BATu = &BATut[i]; + BATl = &BATlt[i]; + BEPIu = *BATu & BATU32_BEPIU; + BEPIl = *BATu & BATU32_BEPIL; + bl = (*BATu & 0x00001FFC) << 15; + LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx + " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " + TARGET_FMT_lx " " TARGET_FMT_lx "\n", + __func__, type == ACCESS_CODE ? 'I' : 'D', i, ea, + *BATu, *BATl, BEPIu, BEPIl, bl); + } + } +#endif + + return -1; +} + +static int ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, + target_ulong eaddr, int rwx, + hwaddr *raddr, int *prot) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + int key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS)); + + qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); + + if ((sr & 0x1FF00000) >> 20 == 0x07f) { + /* Memory-forced I/O controller interface access */ + /* If T=1 and BUID=x'07F', the 601 performs a memory access + * to SR[28-31] LA[4-31], bypassing all protection mechanisms. + */ + *raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return 0; + } + + if (rwx == 2) { + /* No code fetch is allowed in direct-store areas */ + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + return 1; + } + + switch (env->access_type) { + case ACCESS_INT: + /* Integer load/store : only access allowed */ + break; + case ACCESS_FLOAT: + /* Floating point load/store */ + cs->exception_index = POWERPC_EXCP_ALIGN; + env->error_code = POWERPC_EXCP_ALIGN_FP; + env->spr[SPR_DAR] = eaddr; + return 1; + case ACCESS_RES: + /* lwarx, ldarx or srwcx. */ + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (rwx == 1) { + env->spr[SPR_DSISR] = 0x06000000; + } else { + env->spr[SPR_DSISR] = 0x04000000; + } + return 1; + case ACCESS_CACHE: + /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */ + /* Should make the instruction do no-op. + * As it already do no-op, it's quite easy :-) + */ + *raddr = eaddr; + return 0; + case ACCESS_EXT: + /* eciwx or ecowx */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (rwx == 1) { + env->spr[SPR_DSISR] = 0x06100000; + } else { + env->spr[SPR_DSISR] = 0x04100000; + } + return 1; + default: + cpu_abort(cs, "ERROR: instruction should not need " + "address translation\n"); + } + if ((rwx == 1 || key != 1) && (rwx == 0 || key != 0)) { + *raddr = eaddr; + return 0; + } else { + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (rwx == 1) { + env->spr[SPR_DSISR] = 0x0a000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } + return 1; + } +} + +hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash) +{ + CPUPPCState *env = &cpu->env; + + return (hash * HASH_PTEG_SIZE_32) & env->htab_mask; +} + +static hwaddr ppc_hash32_pteg_search(PowerPCCPU *cpu, hwaddr pteg_off, + bool secondary, target_ulong ptem, + ppc_hash_pte32_t *pte) +{ + hwaddr pte_offset = pteg_off; + target_ulong pte0, pte1; + int i; + + for (i = 0; i < HPTES_PER_GROUP; i++) { + pte0 = ppc_hash32_load_hpte0(cpu, pte_offset); + pte1 = ppc_hash32_load_hpte1(cpu, pte_offset); + + if ((pte0 & HPTE32_V_VALID) + && (secondary == !!(pte0 & HPTE32_V_SECONDARY)) + && HPTE32_V_COMPARE(pte0, ptem)) { + pte->pte0 = pte0; + pte->pte1 = pte1; + return pte_offset; + } + + pte_offset += HASH_PTE_SIZE_32; + } + + return -1; +} + +static hwaddr ppc_hash32_htab_lookup(PowerPCCPU *cpu, + target_ulong sr, target_ulong eaddr, + ppc_hash_pte32_t *pte) +{ + CPUPPCState *env = &cpu->env; + hwaddr pteg_off, pte_offset; + hwaddr hash; + uint32_t vsid, pgidx, ptem; + + vsid = sr & SR32_VSID; + pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS; + hash = vsid ^ pgidx; + ptem = (vsid << 7) | (pgidx >> 10); + + /* Page address translation */ + qemu_log_mask(CPU_LOG_MMU, "htab_base " TARGET_FMT_plx + " htab_mask " TARGET_FMT_plx + " hash " TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, hash); + + /* Primary PTEG lookup */ + qemu_log_mask(CPU_LOG_MMU, "0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=%" PRIx32 " ptem=%" PRIx32 + " hash=" TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, vsid, ptem, hash); + pteg_off = get_pteg_offset32(cpu, hash); + pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 0, ptem, pte); + if (pte_offset == -1) { + /* Secondary PTEG lookup */ + qemu_log_mask(CPU_LOG_MMU, "1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=%" PRIx32 " api=%" PRIx32 + " hash=" TARGET_FMT_plx "\n", env->htab_base, + env->htab_mask, vsid, ptem, ~hash); + pteg_off = get_pteg_offset32(cpu, ~hash); + pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 1, ptem, pte); + } + + return pte_offset; +} + +static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte, + target_ulong eaddr) +{ + hwaddr rpn = pte.pte1 & HPTE32_R_RPN; + hwaddr mask = ~TARGET_PAGE_MASK; + + return (rpn & ~mask) | (eaddr & mask); +} + +int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, + int mmu_idx) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + target_ulong sr; + hwaddr pte_offset; + ppc_hash_pte32_t pte; + int prot; + uint32_t new_pte1; + const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; + hwaddr raddr; + + assert((rwx == 0) || (rwx == 1) || (rwx == 2)); + + /* 1. Handle real mode accesses */ + if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { + /* Translation is off */ + raddr = eaddr; + tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, + PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, + TARGET_PAGE_SIZE); + return 0; + } + + /* 2. Check Block Address Translation entries (BATs) */ + if (env->nb_BATs != 0) { + raddr = ppc_hash32_bat_lookup(cpu, eaddr, rwx, &prot); + if (raddr != -1) { + if (need_prot[rwx] & ~prot) { + if (rwx == 2) { + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; + } else { + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (rwx == 1) { + env->spr[SPR_DSISR] = 0x0a000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } + } + return 1; + } + + tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, + raddr & TARGET_PAGE_MASK, prot, mmu_idx, + TARGET_PAGE_SIZE); + return 0; + } + } + + /* 3. Look up the Segment Register */ + sr = env->sr[eaddr >> 28]; + + /* 4. Handle direct store segments */ + if (sr & SR32_T) { + if (ppc_hash32_direct_store(cpu, sr, eaddr, rwx, + &raddr, &prot) == 0) { + tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, + raddr & TARGET_PAGE_MASK, prot, mmu_idx, + TARGET_PAGE_SIZE); + return 0; + } else { + return 1; + } + } + + /* 5. Check for segment level no-execute violation */ + if ((rwx == 2) && (sr & SR32_NX)) { + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + return 1; + } + + /* 6. Locate the PTE in the hash table */ + pte_offset = ppc_hash32_htab_lookup(cpu, sr, eaddr, &pte); + if (pte_offset == -1) { + if (rwx == 2) { + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x40000000; + } else { + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (rwx == 1) { + env->spr[SPR_DSISR] = 0x42000000; + } else { + env->spr[SPR_DSISR] = 0x40000000; + } + } + + return 1; + } + qemu_log_mask(CPU_LOG_MMU, + "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); + + /* 7. Check access permissions */ + + prot = ppc_hash32_pte_prot(cpu, sr, pte); + + if (need_prot[rwx] & ~prot) { + /* Access right violation */ + qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); + if (rwx == 2) { + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; + } else { + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (rwx == 1) { + env->spr[SPR_DSISR] = 0x0a000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } + } + return 1; + } + + qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); + + /* 8. Update PTE referenced and changed bits if necessary */ + + new_pte1 = pte.pte1 | HPTE32_R_R; /* set referenced bit */ + if (rwx == 1) { + new_pte1 |= HPTE32_R_C; /* set changed (dirty) bit */ + } else { + /* Treat the page as read-only for now, so that a later write + * will pass through this function again to set the C bit */ + prot &= ~PAGE_WRITE; + } + + if (new_pte1 != pte.pte1) { + ppc_hash32_store_hpte1(cpu, pte_offset, new_pte1); + } + + /* 9. Determine the real address from the PTE */ + + raddr = ppc_hash32_pte_raddr(sr, pte, eaddr); + + tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, + prot, mmu_idx, TARGET_PAGE_SIZE); + + return 0; +} + +hwaddr ppc_hash32_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr) +{ + CPUPPCState *env = &cpu->env; + target_ulong sr; + hwaddr pte_offset; + ppc_hash_pte32_t pte; + int prot; + + if (msr_dr == 0) { + /* Translation is off */ + return eaddr; + } + + if (env->nb_BATs != 0) { + hwaddr raddr = ppc_hash32_bat_lookup(cpu, eaddr, 0, &prot); + if (raddr != -1) { + return raddr; + } + } + + sr = env->sr[eaddr >> 28]; + + if (sr & SR32_T) { + /* FIXME: Add suitable debug support for Direct Store segments */ + return -1; + } + + pte_offset = ppc_hash32_htab_lookup(cpu, sr, eaddr, &pte); + if (pte_offset == -1) { + return -1; + } + + return ppc_hash32_pte_raddr(sr, pte, eaddr) & TARGET_PAGE_MASK; +} diff --git a/target/ppc/mmu-hash32.h b/target/ppc/mmu-hash32.h new file mode 100644 index 0000000000..5b9fb08d1a --- /dev/null +++ b/target/ppc/mmu-hash32.h @@ -0,0 +1,112 @@ +#ifndef MMU_HASH32_H +#define MMU_HASH32_H + +#ifndef CONFIG_USER_ONLY + +hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash); +hwaddr ppc_hash32_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr); +int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr address, int rw, + int mmu_idx); + +/* + * Segment register definitions + */ + +#define SR32_T 0x80000000 +#define SR32_KS 0x40000000 +#define SR32_KP 0x20000000 +#define SR32_NX 0x10000000 +#define SR32_VSID 0x00ffffff + +/* + * Block Address Translation (BAT) definitions + */ + +#define BATU32_BEPI 0xfffe0000 +#define BATU32_BL 0x00001ffc +#define BATU32_VS 0x00000002 +#define BATU32_VP 0x00000001 + + +#define BATL32_BRPN 0xfffe0000 +#define BATL32_WIMG 0x00000078 +#define BATL32_PP 0x00000003 + +/* PowerPC 601 has slightly different BAT registers */ + +#define BATU32_601_KS 0x00000008 +#define BATU32_601_KP 0x00000004 +#define BATU32_601_PP 0x00000003 + +#define BATL32_601_V 0x00000040 +#define BATL32_601_BL 0x0000003f + +/* + * Hash page table definitions + */ + +#define HPTES_PER_GROUP 8 +#define HASH_PTE_SIZE_32 8 +#define HASH_PTEG_SIZE_32 (HASH_PTE_SIZE_32 * HPTES_PER_GROUP) + +#define HPTE32_V_VALID 0x80000000 +#define HPTE32_V_VSID 0x7fffff80 +#define HPTE32_V_SECONDARY 0x00000040 +#define HPTE32_V_API 0x0000003f +#define HPTE32_V_COMPARE(x, y) (!(((x) ^ (y)) & 0x7fffffbf)) + +#define HPTE32_R_RPN 0xfffff000 +#define HPTE32_R_R 0x00000100 +#define HPTE32_R_C 0x00000080 +#define HPTE32_R_W 0x00000040 +#define HPTE32_R_I 0x00000020 +#define HPTE32_R_M 0x00000010 +#define HPTE32_R_G 0x00000008 +#define HPTE32_R_WIMG 0x00000078 +#define HPTE32_R_PP 0x00000003 + +static inline target_ulong ppc_hash32_load_hpte0(PowerPCCPU *cpu, + hwaddr pte_offset) +{ + CPUPPCState *env = &cpu->env; + + assert(!env->external_htab); /* Not supported on 32-bit for now */ + return ldl_phys(CPU(cpu)->as, env->htab_base + pte_offset); +} + +static inline target_ulong ppc_hash32_load_hpte1(PowerPCCPU *cpu, + hwaddr pte_offset) +{ + CPUPPCState *env = &cpu->env; + + assert(!env->external_htab); /* Not supported on 32-bit for now */ + return ldl_phys(CPU(cpu)->as, + env->htab_base + pte_offset + HASH_PTE_SIZE_32 / 2); +} + +static inline void ppc_hash32_store_hpte0(PowerPCCPU *cpu, + hwaddr pte_offset, target_ulong pte0) +{ + CPUPPCState *env = &cpu->env; + + assert(!env->external_htab); /* Not supported on 32-bit for now */ + stl_phys(CPU(cpu)->as, env->htab_base + pte_offset, pte0); +} + +static inline void ppc_hash32_store_hpte1(PowerPCCPU *cpu, + hwaddr pte_offset, target_ulong pte1) +{ + CPUPPCState *env = &cpu->env; + + assert(!env->external_htab); /* Not supported on 32-bit for now */ + stl_phys(CPU(cpu)->as, + env->htab_base + pte_offset + HASH_PTE_SIZE_32 / 2, pte1); +} + +typedef struct { + uint32_t pte0, pte1; +} ppc_hash_pte32_t; + +#endif /* CONFIG_USER_ONLY */ + +#endif /* MMU_HASH32_H */ diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c new file mode 100644 index 0000000000..fdb7a787bf --- /dev/null +++ b/target/ppc/mmu-hash64.c @@ -0,0 +1,1059 @@ +/* + * PowerPC MMU, TLB, SLB and BAT emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * Copyright (c) 2013 David Gibson, IBM Corporation + * + * 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 "qemu/osdep.h" +#include "qapi/error.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "qemu/error-report.h" +#include "sysemu/kvm.h" +#include "kvm_ppc.h" +#include "mmu-hash64.h" +#include "exec/log.h" + +//#define DEBUG_SLB + +#ifdef DEBUG_SLB +# define LOG_SLB(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__) +#else +# define LOG_SLB(...) do { } while (0) +#endif + +/* + * Used to indicate that a CPU has its hash page table (HPT) managed + * within the host kernel + */ +#define MMU_HASH64_KVM_MANAGED_HPT ((void *)-1) + +/* + * SLB handling + */ + +static ppc_slb_t *slb_lookup(PowerPCCPU *cpu, target_ulong eaddr) +{ + CPUPPCState *env = &cpu->env; + uint64_t esid_256M, esid_1T; + int n; + + LOG_SLB("%s: eaddr " TARGET_FMT_lx "\n", __func__, eaddr); + + esid_256M = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V; + esid_1T = (eaddr & SEGMENT_MASK_1T) | SLB_ESID_V; + + for (n = 0; n < env->slb_nr; n++) { + ppc_slb_t *slb = &env->slb[n]; + + LOG_SLB("%s: slot %d %016" PRIx64 " %016" + PRIx64 "\n", __func__, n, slb->esid, slb->vsid); + /* We check for 1T matches on all MMUs here - if the MMU + * doesn't have 1T segment support, we will have prevented 1T + * entries from being inserted in the slbmte code. */ + if (((slb->esid == esid_256M) && + ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_256M)) + || ((slb->esid == esid_1T) && + ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_1T))) { + return slb; + } + } + + return NULL; +} + +void dump_slb(FILE *f, fprintf_function cpu_fprintf, PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + int i; + uint64_t slbe, slbv; + + cpu_synchronize_state(CPU(cpu)); + + cpu_fprintf(f, "SLB\tESID\t\t\tVSID\n"); + for (i = 0; i < env->slb_nr; i++) { + slbe = env->slb[i].esid; + slbv = env->slb[i].vsid; + if (slbe == 0 && slbv == 0) { + continue; + } + cpu_fprintf(f, "%d\t0x%016" PRIx64 "\t0x%016" PRIx64 "\n", + i, slbe, slbv); + } +} + +void helper_slbia(CPUPPCState *env) +{ + int n; + + /* XXX: Warning: slbia never invalidates the first segment */ + for (n = 1; n < env->slb_nr; n++) { + ppc_slb_t *slb = &env->slb[n]; + + if (slb->esid & SLB_ESID_V) { + slb->esid &= ~SLB_ESID_V; + /* XXX: given the fact that segment size is 256 MB or 1TB, + * and we still don't have a tlb_flush_mask(env, n, mask) + * in QEMU, we just invalidate all TLBs + */ + env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH; + } + } +} + +void helper_slbie(CPUPPCState *env, target_ulong addr) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + ppc_slb_t *slb; + + slb = slb_lookup(cpu, addr); + if (!slb) { + return; + } + + if (slb->esid & SLB_ESID_V) { + slb->esid &= ~SLB_ESID_V; + + /* XXX: given the fact that segment size is 256 MB or 1TB, + * and we still don't have a tlb_flush_mask(env, n, mask) + * in QEMU, we just invalidate all TLBs + */ + env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH; + } +} + +int ppc_store_slb(PowerPCCPU *cpu, target_ulong slot, + target_ulong esid, target_ulong vsid) +{ + CPUPPCState *env = &cpu->env; + ppc_slb_t *slb = &env->slb[slot]; + const struct ppc_one_seg_page_size *sps = NULL; + int i; + + if (slot >= env->slb_nr) { + return -1; /* Bad slot number */ + } + if (esid & ~(SLB_ESID_ESID | SLB_ESID_V)) { + return -1; /* Reserved bits set */ + } + if (vsid & (SLB_VSID_B & ~SLB_VSID_B_1T)) { + return -1; /* Bad segment size */ + } + if ((vsid & SLB_VSID_B) && !(env->mmu_model & POWERPC_MMU_1TSEG)) { + return -1; /* 1T segment on MMU that doesn't support it */ + } + + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + const struct ppc_one_seg_page_size *sps1 = &env->sps.sps[i]; + + if (!sps1->page_shift) { + break; + } + + if ((vsid & SLB_VSID_LLP_MASK) == sps1->slb_enc) { + sps = sps1; + break; + } + } + + if (!sps) { + error_report("Bad page size encoding in SLB store: slot "TARGET_FMT_lu + " esid 0x"TARGET_FMT_lx" vsid 0x"TARGET_FMT_lx, + slot, esid, vsid); + return -1; + } + + slb->esid = esid; + slb->vsid = vsid; + slb->sps = sps; + + LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64 + " %016" PRIx64 "\n", __func__, slot, esid, vsid, + slb->esid, slb->vsid); + + return 0; +} + +static int ppc_load_slb_esid(PowerPCCPU *cpu, target_ulong rb, + target_ulong *rt) +{ + CPUPPCState *env = &cpu->env; + int slot = rb & 0xfff; + ppc_slb_t *slb = &env->slb[slot]; + + if (slot >= env->slb_nr) { + return -1; + } + + *rt = slb->esid; + return 0; +} + +static int ppc_load_slb_vsid(PowerPCCPU *cpu, target_ulong rb, + target_ulong *rt) +{ + CPUPPCState *env = &cpu->env; + int slot = rb & 0xfff; + ppc_slb_t *slb = &env->slb[slot]; + + if (slot >= env->slb_nr) { + return -1; + } + + *rt = slb->vsid; + return 0; +} + +static int ppc_find_slb_vsid(PowerPCCPU *cpu, target_ulong rb, + target_ulong *rt) +{ + CPUPPCState *env = &cpu->env; + ppc_slb_t *slb; + + if (!msr_is_64bit(env, env->msr)) { + rb &= 0xffffffff; + } + slb = slb_lookup(cpu, rb); + if (slb == NULL) { + *rt = (target_ulong)-1ul; + } else { + *rt = slb->vsid; + } + return 0; +} + +void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + if (ppc_store_slb(cpu, rb & 0xfff, rb & ~0xfffULL, rs) < 0) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL, GETPC()); + } +} + +target_ulong helper_load_slb_esid(CPUPPCState *env, target_ulong rb) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + target_ulong rt = 0; + + if (ppc_load_slb_esid(cpu, rb, &rt) < 0) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL, GETPC()); + } + return rt; +} + +target_ulong helper_find_slb_vsid(CPUPPCState *env, target_ulong rb) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + target_ulong rt = 0; + + if (ppc_find_slb_vsid(cpu, rb, &rt) < 0) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL, GETPC()); + } + return rt; +} + +target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + target_ulong rt = 0; + + if (ppc_load_slb_vsid(cpu, rb, &rt) < 0) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL, GETPC()); + } + return rt; +} + +/* + * 64-bit hash table MMU handling + */ +void ppc_hash64_set_sdr1(PowerPCCPU *cpu, target_ulong value, + Error **errp) +{ + CPUPPCState *env = &cpu->env; + target_ulong htabsize = value & SDR_64_HTABSIZE; + + env->spr[SPR_SDR1] = value; + if (htabsize > 28) { + error_setg(errp, + "Invalid HTABSIZE 0x" TARGET_FMT_lx" stored in SDR1", + htabsize); + htabsize = 28; + } + env->htab_mask = (1ULL << (htabsize + 18 - 7)) - 1; + env->htab_base = value & SDR_64_HTABORG; +} + +void ppc_hash64_set_external_hpt(PowerPCCPU *cpu, void *hpt, int shift, + Error **errp) +{ + CPUPPCState *env = &cpu->env; + Error *local_err = NULL; + + if (hpt) { + env->external_htab = hpt; + } else { + env->external_htab = MMU_HASH64_KVM_MANAGED_HPT; + } + ppc_hash64_set_sdr1(cpu, (target_ulong)(uintptr_t)hpt | (shift - 18), + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Not strictly necessary, but makes it clearer that an external + * htab is in use when debugging */ + env->htab_base = -1; + + if (kvm_enabled()) { + if (kvmppc_put_books_sregs(cpu) < 0) { + error_setg(errp, "Unable to update SDR1 in KVM"); + } + } +} + +static int ppc_hash64_pte_prot(PowerPCCPU *cpu, + ppc_slb_t *slb, ppc_hash_pte64_t pte) +{ + CPUPPCState *env = &cpu->env; + unsigned pp, key; + /* Some pp bit combinations have undefined behaviour, so default + * to no access in those cases */ + int prot = 0; + + key = !!(msr_pr ? (slb->vsid & SLB_VSID_KP) + : (slb->vsid & SLB_VSID_KS)); + pp = (pte.pte1 & HPTE64_R_PP) | ((pte.pte1 & HPTE64_R_PP0) >> 61); + + if (key == 0) { + switch (pp) { + case 0x0: + case 0x1: + case 0x2: + prot = PAGE_READ | PAGE_WRITE; + break; + + case 0x3: + case 0x6: + prot = PAGE_READ; + break; + } + } else { + switch (pp) { + case 0x0: + case 0x6: + prot = 0; + break; + + case 0x1: + case 0x3: + prot = PAGE_READ; + break; + + case 0x2: + prot = PAGE_READ | PAGE_WRITE; + break; + } + } + + /* No execute if either noexec or guarded bits set */ + if (!(pte.pte1 & HPTE64_R_N) || (pte.pte1 & HPTE64_R_G) + || (slb->vsid & SLB_VSID_N)) { + prot |= PAGE_EXEC; + } + + return prot; +} + +static int ppc_hash64_amr_prot(PowerPCCPU *cpu, ppc_hash_pte64_t pte) +{ + CPUPPCState *env = &cpu->env; + int key, amrbits; + int prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + + /* Only recent MMUs implement Virtual Page Class Key Protection */ + if (!(env->mmu_model & POWERPC_MMU_AMR)) { + return prot; + } + + key = HPTE64_R_KEY(pte.pte1); + amrbits = (env->spr[SPR_AMR] >> 2*(31 - key)) & 0x3; + + /* fprintf(stderr, "AMR protection: key=%d AMR=0x%" PRIx64 "\n", key, */ + /* env->spr[SPR_AMR]); */ + + /* + * A store is permitted if the AMR bit is 0. Remove write + * protection if it is set. + */ + if (amrbits & 0x2) { + prot &= ~PAGE_WRITE; + } + /* + * A load is permitted if the AMR bit is 0. Remove read + * protection if it is set. + */ + if (amrbits & 0x1) { + prot &= ~PAGE_READ; + } + + return prot; +} + +uint64_t ppc_hash64_start_access(PowerPCCPU *cpu, target_ulong pte_index) +{ + uint64_t token = 0; + hwaddr pte_offset; + + pte_offset = pte_index * HASH_PTE_SIZE_64; + if (cpu->env.external_htab == MMU_HASH64_KVM_MANAGED_HPT) { + /* + * HTAB is controlled by KVM. Fetch the PTEG into a new buffer. + */ + token = kvmppc_hash64_read_pteg(cpu, pte_index); + } else if (cpu->env.external_htab) { + /* + * HTAB is controlled by QEMU. Just point to the internally + * accessible PTEG. + */ + token = (uint64_t)(uintptr_t) cpu->env.external_htab + pte_offset; + } else if (cpu->env.htab_base) { + token = cpu->env.htab_base + pte_offset; + } + return token; +} + +void ppc_hash64_stop_access(PowerPCCPU *cpu, uint64_t token) +{ + if (cpu->env.external_htab == MMU_HASH64_KVM_MANAGED_HPT) { + kvmppc_hash64_free_pteg(token); + } +} + +static unsigned hpte_page_shift(const struct ppc_one_seg_page_size *sps, + uint64_t pte0, uint64_t pte1) +{ + int i; + + if (!(pte0 & HPTE64_V_LARGE)) { + if (sps->page_shift != 12) { + /* 4kiB page in a non 4kiB segment */ + return 0; + } + /* Normal 4kiB page */ + return 12; + } + + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + const struct ppc_one_page_size *ps = &sps->enc[i]; + uint64_t mask; + + if (!ps->page_shift) { + break; + } + + if (ps->page_shift == 12) { + /* L bit is set so this can't be a 4kiB page */ + continue; + } + + mask = ((1ULL << ps->page_shift) - 1) & HPTE64_R_RPN; + + if ((pte1 & mask) == ((uint64_t)ps->pte_enc << HPTE64_R_RPN_SHIFT)) { + return ps->page_shift; + } + } + + return 0; /* Bad page size encoding */ +} + +static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash, + const struct ppc_one_seg_page_size *sps, + target_ulong ptem, + ppc_hash_pte64_t *pte, unsigned *pshift) +{ + CPUPPCState *env = &cpu->env; + int i; + uint64_t token; + target_ulong pte0, pte1; + target_ulong pte_index; + + pte_index = (hash & env->htab_mask) * HPTES_PER_GROUP; + token = ppc_hash64_start_access(cpu, pte_index); + if (!token) { + return -1; + } + for (i = 0; i < HPTES_PER_GROUP; i++) { + pte0 = ppc_hash64_load_hpte0(cpu, token, i); + pte1 = ppc_hash64_load_hpte1(cpu, token, i); + + /* This compares V, B, H (secondary) and the AVPN */ + if (HPTE64_V_COMPARE(pte0, ptem)) { + *pshift = hpte_page_shift(sps, pte0, pte1); + /* + * If there is no match, ignore the PTE, it could simply + * be for a different segment size encoding and the + * architecture specifies we should not match. Linux will + * potentially leave behind PTEs for the wrong base page + * size when demoting segments. + */ + if (*pshift == 0) { + continue; + } + /* We don't do anything with pshift yet as qemu TLB only deals + * with 4K pages anyway + */ + pte->pte0 = pte0; + pte->pte1 = pte1; + ppc_hash64_stop_access(cpu, token); + return (pte_index + i) * HASH_PTE_SIZE_64; + } + } + ppc_hash64_stop_access(cpu, token); + /* + * We didn't find a valid entry. + */ + return -1; +} + +static hwaddr ppc_hash64_htab_lookup(PowerPCCPU *cpu, + ppc_slb_t *slb, target_ulong eaddr, + ppc_hash_pte64_t *pte, unsigned *pshift) +{ + CPUPPCState *env = &cpu->env; + hwaddr pte_offset; + hwaddr hash; + uint64_t vsid, epnmask, epn, ptem; + const struct ppc_one_seg_page_size *sps = slb->sps; + + /* The SLB store path should prevent any bad page size encodings + * getting in there, so: */ + assert(sps); + + /* If ISL is set in LPCR we need to clamp the page size to 4K */ + if (env->spr[SPR_LPCR] & LPCR_ISL) { + /* We assume that when using TCG, 4k is first entry of SPS */ + sps = &env->sps.sps[0]; + assert(sps->page_shift == 12); + } + + epnmask = ~((1ULL << sps->page_shift) - 1); + + if (slb->vsid & SLB_VSID_B) { + /* 1TB segment */ + vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T; + epn = (eaddr & ~SEGMENT_MASK_1T) & epnmask; + hash = vsid ^ (vsid << 25) ^ (epn >> sps->page_shift); + } else { + /* 256M segment */ + vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT; + epn = (eaddr & ~SEGMENT_MASK_256M) & epnmask; + hash = vsid ^ (epn >> sps->page_shift); + } + ptem = (slb->vsid & SLB_VSID_PTEM) | ((epn >> 16) & HPTE64_V_AVPN); + ptem |= HPTE64_V_VALID; + + /* Page address translation */ + qemu_log_mask(CPU_LOG_MMU, + "htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx + " hash " TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, hash); + + /* Primary PTEG lookup */ + qemu_log_mask(CPU_LOG_MMU, + "0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, vsid, ptem, hash); + pte_offset = ppc_hash64_pteg_search(cpu, hash, sps, ptem, pte, pshift); + + if (pte_offset == -1) { + /* Secondary PTEG lookup */ + ptem |= HPTE64_V_SECONDARY; + qemu_log_mask(CPU_LOG_MMU, + "1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", env->htab_base, + env->htab_mask, vsid, ptem, ~hash); + + pte_offset = ppc_hash64_pteg_search(cpu, ~hash, sps, ptem, pte, pshift); + } + + return pte_offset; +} + +unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, + uint64_t pte0, uint64_t pte1) +{ + CPUPPCState *env = &cpu->env; + int i; + + if (!(pte0 & HPTE64_V_LARGE)) { + return 12; + } + + /* + * The encodings in env->sps need to be carefully chosen so that + * this gives an unambiguous result. + */ + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + const struct ppc_one_seg_page_size *sps = &env->sps.sps[i]; + unsigned shift; + + if (!sps->page_shift) { + break; + } + + shift = hpte_page_shift(sps, pte0, pte1); + if (shift) { + return shift; + } + } + + return 0; +} + +static void ppc_hash64_set_isi(CPUState *cs, CPUPPCState *env, + uint64_t error_code) +{ + bool vpm; + + if (msr_ir) { + vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1); + } else { + vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0); + } + if (vpm && !msr_hv) { + cs->exception_index = POWERPC_EXCP_HISI; + } else { + cs->exception_index = POWERPC_EXCP_ISI; + } + env->error_code = error_code; +} + +static void ppc_hash64_set_dsi(CPUState *cs, CPUPPCState *env, uint64_t dar, + uint64_t dsisr) +{ + bool vpm; + + if (msr_dr) { + vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1); + } else { + vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0); + } + if (vpm && !msr_hv) { + cs->exception_index = POWERPC_EXCP_HDSI; + env->spr[SPR_HDAR] = dar; + env->spr[SPR_HDSISR] = dsisr; + } else { + cs->exception_index = POWERPC_EXCP_DSI; + env->spr[SPR_DAR] = dar; + env->spr[SPR_DSISR] = dsisr; + } + env->error_code = 0; +} + + +int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, + int rwx, int mmu_idx) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + ppc_slb_t *slb; + unsigned apshift; + hwaddr pte_offset; + ppc_hash_pte64_t pte; + int pp_prot, amr_prot, prot; + uint64_t new_pte1, dsisr; + const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; + hwaddr raddr; + + assert((rwx == 0) || (rwx == 1) || (rwx == 2)); + + /* Note on LPCR usage: 970 uses HID4, but our special variant + * of store_spr copies relevant fields into env->spr[SPR_LPCR]. + * Similarily we filter unimplemented bits when storing into + * LPCR depending on the MMU version. This code can thus just + * use the LPCR "as-is". + */ + + /* 1. Handle real mode accesses */ + if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { + /* Translation is supposedly "off" */ + /* In real mode the top 4 effective address bits are (mostly) ignored */ + raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; + + /* In HV mode, add HRMOR if top EA bit is clear */ + if (msr_hv || !env->has_hv_mode) { + if (!(eaddr >> 63)) { + raddr |= env->spr[SPR_HRMOR]; + } + } else { + /* Otherwise, check VPM for RMA vs VRMA */ + if (env->spr[SPR_LPCR] & LPCR_VPM0) { + slb = &env->vrma_slb; + if (slb->sps) { + goto skip_slb_search; + } + /* Not much else to do here */ + cs->exception_index = POWERPC_EXCP_MCHECK; + env->error_code = 0; + return 1; + } else if (raddr < env->rmls) { + /* RMA. Check bounds in RMLS */ + raddr |= env->spr[SPR_RMOR]; + } else { + /* The access failed, generate the approriate interrupt */ + if (rwx == 2) { + ppc_hash64_set_isi(cs, env, 0x08000000); + } else { + dsisr = 0x08000000; + if (rwx == 1) { + dsisr |= 0x02000000; + } + ppc_hash64_set_dsi(cs, env, eaddr, dsisr); + } + return 1; + } + } + tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, + PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, + TARGET_PAGE_SIZE); + return 0; + } + + /* 2. Translation is on, so look up the SLB */ + slb = slb_lookup(cpu, eaddr); + if (!slb) { + if (rwx == 2) { + cs->exception_index = POWERPC_EXCP_ISEG; + env->error_code = 0; + } else { + cs->exception_index = POWERPC_EXCP_DSEG; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + } + return 1; + } + +skip_slb_search: + + /* 3. Check for segment level no-execute violation */ + if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) { + ppc_hash64_set_isi(cs, env, 0x10000000); + return 1; + } + + /* 4. Locate the PTE in the hash table */ + pte_offset = ppc_hash64_htab_lookup(cpu, slb, eaddr, &pte, &apshift); + if (pte_offset == -1) { + dsisr = 0x40000000; + if (rwx == 2) { + ppc_hash64_set_isi(cs, env, dsisr); + } else { + if (rwx == 1) { + dsisr |= 0x02000000; + } + ppc_hash64_set_dsi(cs, env, eaddr, dsisr); + } + return 1; + } + qemu_log_mask(CPU_LOG_MMU, + "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); + + /* 5. Check access permissions */ + + pp_prot = ppc_hash64_pte_prot(cpu, slb, pte); + amr_prot = ppc_hash64_amr_prot(cpu, pte); + prot = pp_prot & amr_prot; + + if ((need_prot[rwx] & ~prot) != 0) { + /* Access right violation */ + qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); + if (rwx == 2) { + ppc_hash64_set_isi(cs, env, 0x08000000); + } else { + dsisr = 0; + if (need_prot[rwx] & ~pp_prot) { + dsisr |= 0x08000000; + } + if (rwx == 1) { + dsisr |= 0x02000000; + } + if (need_prot[rwx] & ~amr_prot) { + dsisr |= 0x00200000; + } + ppc_hash64_set_dsi(cs, env, eaddr, dsisr); + } + return 1; + } + + qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); + + /* 6. Update PTE referenced and changed bits if necessary */ + + new_pte1 = pte.pte1 | HPTE64_R_R; /* set referenced bit */ + if (rwx == 1) { + new_pte1 |= HPTE64_R_C; /* set changed (dirty) bit */ + } else { + /* Treat the page as read-only for now, so that a later write + * will pass through this function again to set the C bit */ + prot &= ~PAGE_WRITE; + } + + if (new_pte1 != pte.pte1) { + ppc_hash64_store_hpte(cpu, pte_offset / HASH_PTE_SIZE_64, + pte.pte0, new_pte1); + } + + /* 7. Determine the real address from the PTE */ + + raddr = deposit64(pte.pte1 & HPTE64_R_RPN, 0, apshift, eaddr); + + tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, + prot, mmu_idx, 1ULL << apshift); + + return 0; +} + +hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr) +{ + CPUPPCState *env = &cpu->env; + ppc_slb_t *slb; + hwaddr pte_offset, raddr; + ppc_hash_pte64_t pte; + unsigned apshift; + + /* Handle real mode */ + if (msr_dr == 0) { + /* In real mode the top 4 effective address bits are ignored */ + raddr = addr & 0x0FFFFFFFFFFFFFFFULL; + + /* In HV mode, add HRMOR if top EA bit is clear */ + if ((msr_hv || !env->has_hv_mode) && !(addr >> 63)) { + return raddr | env->spr[SPR_HRMOR]; + } + + /* Otherwise, check VPM for RMA vs VRMA */ + if (env->spr[SPR_LPCR] & LPCR_VPM0) { + slb = &env->vrma_slb; + if (!slb->sps) { + return -1; + } + } else if (raddr < env->rmls) { + /* RMA. Check bounds in RMLS */ + return raddr | env->spr[SPR_RMOR]; + } else { + return -1; + } + } else { + slb = slb_lookup(cpu, addr); + if (!slb) { + return -1; + } + } + + pte_offset = ppc_hash64_htab_lookup(cpu, slb, addr, &pte, &apshift); + if (pte_offset == -1) { + return -1; + } + + return deposit64(pte.pte1 & HPTE64_R_RPN, 0, apshift, addr) + & TARGET_PAGE_MASK; +} + +void ppc_hash64_store_hpte(PowerPCCPU *cpu, + target_ulong pte_index, + target_ulong pte0, target_ulong pte1) +{ + CPUPPCState *env = &cpu->env; + + if (env->external_htab == MMU_HASH64_KVM_MANAGED_HPT) { + kvmppc_hash64_write_pte(env, pte_index, pte0, pte1); + return; + } + + pte_index *= HASH_PTE_SIZE_64; + if (env->external_htab) { + stq_p(env->external_htab + pte_index, pte0); + stq_p(env->external_htab + pte_index + HASH_PTE_SIZE_64 / 2, pte1); + } else { + stq_phys(CPU(cpu)->as, env->htab_base + pte_index, pte0); + stq_phys(CPU(cpu)->as, + env->htab_base + pte_index + HASH_PTE_SIZE_64 / 2, pte1); + } +} + +void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, + target_ulong pte_index, + target_ulong pte0, target_ulong pte1) +{ + /* + * XXX: given the fact that there are too many segments to + * invalidate, and we still don't have a tlb_flush_mask(env, n, + * mask) in QEMU, we just invalidate all TLBs + */ + cpu->env.tlb_need_flush = TLB_NEED_GLOBAL_FLUSH | TLB_NEED_LOCAL_FLUSH; +} + +void ppc_hash64_update_rmls(CPUPPCState *env) +{ + uint64_t lpcr = env->spr[SPR_LPCR]; + + /* + * This is the full 4 bits encoding of POWER8. Previous + * CPUs only support a subset of these but the filtering + * is done when writing LPCR + */ + switch ((lpcr & LPCR_RMLS) >> LPCR_RMLS_SHIFT) { + case 0x8: /* 32MB */ + env->rmls = 0x2000000ull; + break; + case 0x3: /* 64MB */ + env->rmls = 0x4000000ull; + break; + case 0x7: /* 128MB */ + env->rmls = 0x8000000ull; + break; + case 0x4: /* 256MB */ + env->rmls = 0x10000000ull; + break; + case 0x2: /* 1GB */ + env->rmls = 0x40000000ull; + break; + case 0x1: /* 16GB */ + env->rmls = 0x400000000ull; + break; + default: + /* What to do here ??? */ + env->rmls = 0; + } +} + +void ppc_hash64_update_vrma(CPUPPCState *env) +{ + const struct ppc_one_seg_page_size *sps = NULL; + target_ulong esid, vsid, lpcr; + ppc_slb_t *slb = &env->vrma_slb; + uint32_t vrmasd; + int i; + + /* First clear it */ + slb->esid = slb->vsid = 0; + slb->sps = NULL; + + /* Is VRMA enabled ? */ + lpcr = env->spr[SPR_LPCR]; + if (!(lpcr & LPCR_VPM0)) { + return; + } + + /* Make one up. Mostly ignore the ESID which will not be + * needed for translation + */ + vsid = SLB_VSID_VRMA; + vrmasd = (lpcr & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT; + vsid |= (vrmasd << 4) & (SLB_VSID_L | SLB_VSID_LP); + esid = SLB_ESID_V; + + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + const struct ppc_one_seg_page_size *sps1 = &env->sps.sps[i]; + + if (!sps1->page_shift) { + break; + } + + if ((vsid & SLB_VSID_LLP_MASK) == sps1->slb_enc) { + sps = sps1; + break; + } + } + + if (!sps) { + error_report("Bad page size encoding esid 0x"TARGET_FMT_lx + " vsid 0x"TARGET_FMT_lx, esid, vsid); + return; + } + + slb->vsid = vsid; + slb->esid = esid; + slb->sps = sps; +} + +void helper_store_lpcr(CPUPPCState *env, target_ulong val) +{ + uint64_t lpcr = 0; + + /* Filter out bits */ + switch (env->mmu_model) { + case POWERPC_MMU_64B: /* 970 */ + if (val & 0x40) { + lpcr |= LPCR_LPES0; + } + if (val & 0x8000000000000000ull) { + lpcr |= LPCR_LPES1; + } + if (val & 0x20) { + lpcr |= (0x4ull << LPCR_RMLS_SHIFT); + } + if (val & 0x4000000000000000ull) { + lpcr |= (0x2ull << LPCR_RMLS_SHIFT); + } + if (val & 0x2000000000000000ull) { + lpcr |= (0x1ull << LPCR_RMLS_SHIFT); + } + env->spr[SPR_RMOR] = ((lpcr >> 41) & 0xffffull) << 26; + + /* XXX We could also write LPID from HID4 here + * but since we don't tag any translation on it + * it doesn't actually matter + */ + /* XXX For proper emulation of 970 we also need + * to dig HRMOR out of HID5 + */ + break; + case POWERPC_MMU_2_03: /* P5p */ + lpcr = val & (LPCR_RMLS | LPCR_ILE | + LPCR_LPES0 | LPCR_LPES1 | + LPCR_RMI | LPCR_HDICE); + break; + case POWERPC_MMU_2_06: /* P7 */ + lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_DPFD | + LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | + LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2 | + LPCR_MER | LPCR_TC | + LPCR_LPES0 | LPCR_LPES1 | LPCR_HDICE); + break; + case POWERPC_MMU_2_07: /* P8 */ + lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | + LPCR_DPFD | LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | + LPCR_AIL | LPCR_ONL | LPCR_P8_PECE0 | LPCR_P8_PECE1 | + LPCR_P8_PECE2 | LPCR_P8_PECE3 | LPCR_P8_PECE4 | + LPCR_MER | LPCR_TC | LPCR_LPES0 | LPCR_HDICE); + break; + default: + ; + } + env->spr[SPR_LPCR] = lpcr; + ppc_hash64_update_rmls(env); + ppc_hash64_update_vrma(env); +} diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h new file mode 100644 index 0000000000..ab5d347a53 --- /dev/null +++ b/target/ppc/mmu-hash64.h @@ -0,0 +1,136 @@ +#ifndef MMU_HASH64_H +#define MMU_HASH64_H + +#ifndef CONFIG_USER_ONLY + +#ifdef TARGET_PPC64 +void dump_slb(FILE *f, fprintf_function cpu_fprintf, PowerPCCPU *cpu); +int ppc_store_slb(PowerPCCPU *cpu, target_ulong slot, + target_ulong esid, target_ulong vsid); +hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr); +int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr address, int rw, + int mmu_idx); +void ppc_hash64_store_hpte(PowerPCCPU *cpu, target_ulong index, + target_ulong pte0, target_ulong pte1); +void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, + target_ulong pte_index, + target_ulong pte0, target_ulong pte1); +unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, + uint64_t pte0, uint64_t pte1); +void ppc_hash64_update_vrma(CPUPPCState *env); +void ppc_hash64_update_rmls(CPUPPCState *env); +#endif + +/* + * SLB definitions + */ + +/* Bits in the SLB ESID word */ +#define SLB_ESID_ESID 0xFFFFFFFFF0000000ULL +#define SLB_ESID_V 0x0000000008000000ULL /* valid */ + +/* Bits in the SLB VSID word */ +#define SLB_VSID_SHIFT 12 +#define SLB_VSID_SHIFT_1T 24 +#define SLB_VSID_SSIZE_SHIFT 62 +#define SLB_VSID_B 0xc000000000000000ULL +#define SLB_VSID_B_256M 0x0000000000000000ULL +#define SLB_VSID_B_1T 0x4000000000000000ULL +#define SLB_VSID_VSID 0x3FFFFFFFFFFFF000ULL +#define SLB_VSID_VRMA (0x0001FFFFFF000000ULL | SLB_VSID_B_1T) +#define SLB_VSID_PTEM (SLB_VSID_B | SLB_VSID_VSID) +#define SLB_VSID_KS 0x0000000000000800ULL +#define SLB_VSID_KP 0x0000000000000400ULL +#define SLB_VSID_N 0x0000000000000200ULL /* no-execute */ +#define SLB_VSID_L 0x0000000000000100ULL +#define SLB_VSID_C 0x0000000000000080ULL /* class */ +#define SLB_VSID_LP 0x0000000000000030ULL +#define SLB_VSID_ATTR 0x0000000000000FFFULL +#define SLB_VSID_LLP_MASK (SLB_VSID_L | SLB_VSID_LP) +#define SLB_VSID_4K 0x0000000000000000ULL +#define SLB_VSID_64K 0x0000000000000110ULL +#define SLB_VSID_16M 0x0000000000000100ULL +#define SLB_VSID_16G 0x0000000000000120ULL + +/* + * Hash page table definitions + */ + +#define HPTES_PER_GROUP 8 +#define HASH_PTE_SIZE_64 16 +#define HASH_PTEG_SIZE_64 (HASH_PTE_SIZE_64 * HPTES_PER_GROUP) + +#define HPTE64_V_SSIZE_SHIFT 62 +#define HPTE64_V_AVPN_SHIFT 7 +#define HPTE64_V_AVPN 0x3fffffffffffff80ULL +#define HPTE64_V_AVPN_VAL(x) (((x) & HPTE64_V_AVPN) >> HPTE64_V_AVPN_SHIFT) +#define HPTE64_V_COMPARE(x, y) (!(((x) ^ (y)) & 0xffffffffffffff83ULL)) +#define HPTE64_V_LARGE 0x0000000000000004ULL +#define HPTE64_V_SECONDARY 0x0000000000000002ULL +#define HPTE64_V_VALID 0x0000000000000001ULL + +#define HPTE64_R_PP0 0x8000000000000000ULL +#define HPTE64_R_TS 0x4000000000000000ULL +#define HPTE64_R_KEY_HI 0x3000000000000000ULL +#define HPTE64_R_RPN_SHIFT 12 +#define HPTE64_R_RPN 0x0ffffffffffff000ULL +#define HPTE64_R_FLAGS 0x00000000000003ffULL +#define HPTE64_R_PP 0x0000000000000003ULL +#define HPTE64_R_N 0x0000000000000004ULL +#define HPTE64_R_G 0x0000000000000008ULL +#define HPTE64_R_M 0x0000000000000010ULL +#define HPTE64_R_I 0x0000000000000020ULL +#define HPTE64_R_W 0x0000000000000040ULL +#define HPTE64_R_WIMG 0x0000000000000078ULL +#define HPTE64_R_C 0x0000000000000080ULL +#define HPTE64_R_R 0x0000000000000100ULL +#define HPTE64_R_KEY_LO 0x0000000000000e00ULL +#define HPTE64_R_KEY(x) ((((x) & HPTE64_R_KEY_HI) >> 60) | \ + (((x) & HPTE64_R_KEY_LO) >> 9)) + +#define HPTE64_V_1TB_SEG 0x4000000000000000ULL +#define HPTE64_V_VRMA_MASK 0x4001ffffff000000ULL + +void ppc_hash64_set_sdr1(PowerPCCPU *cpu, target_ulong value, + Error **errp); +void ppc_hash64_set_external_hpt(PowerPCCPU *cpu, void *hpt, int shift, + Error **errp); + +uint64_t ppc_hash64_start_access(PowerPCCPU *cpu, target_ulong pte_index); +void ppc_hash64_stop_access(PowerPCCPU *cpu, uint64_t token); + +static inline target_ulong ppc_hash64_load_hpte0(PowerPCCPU *cpu, + uint64_t token, int index) +{ + CPUPPCState *env = &cpu->env; + uint64_t addr; + + addr = token + (index * HASH_PTE_SIZE_64); + if (env->external_htab) { + return ldq_p((const void *)(uintptr_t)addr); + } else { + return ldq_phys(CPU(cpu)->as, addr); + } +} + +static inline target_ulong ppc_hash64_load_hpte1(PowerPCCPU *cpu, + uint64_t token, int index) +{ + CPUPPCState *env = &cpu->env; + uint64_t addr; + + addr = token + (index * HASH_PTE_SIZE_64) + HASH_PTE_SIZE_64/2; + if (env->external_htab) { + return ldq_p((const void *)(uintptr_t)addr); + } else { + return ldq_phys(CPU(cpu)->as, addr); + } +} + +typedef struct { + uint64_t pte0, pte1; +} ppc_hash_pte64_t; + +#endif /* CONFIG_USER_ONLY */ + +#endif /* MMU_HASH64_H */ diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c new file mode 100644 index 0000000000..d09fc0a85f --- /dev/null +++ b/target/ppc/mmu_helper.c @@ -0,0 +1,2907 @@ +/* + * PowerPC MMU, TLB, SLB and BAT emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * + * 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 "qemu/osdep.h" +#include "qapi/error.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "sysemu/kvm.h" +#include "kvm_ppc.h" +#include "mmu-hash64.h" +#include "mmu-hash32.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "exec/log.h" +#include "helper_regs.h" + +//#define DEBUG_MMU +//#define DEBUG_BATS +//#define DEBUG_SOFTWARE_TLB +//#define DUMP_PAGE_TABLES +//#define FLUSH_ALL_TLBS + +#ifdef DEBUG_MMU +# define LOG_MMU_STATE(cpu) log_cpu_state_mask(CPU_LOG_MMU, (cpu), 0) +#else +# define LOG_MMU_STATE(cpu) do { } while (0) +#endif + +#ifdef DEBUG_SOFTWARE_TLB +# define LOG_SWTLB(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__) +#else +# define LOG_SWTLB(...) do { } while (0) +#endif + +#ifdef DEBUG_BATS +# define LOG_BATS(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__) +#else +# define LOG_BATS(...) do { } while (0) +#endif + +/*****************************************************************************/ +/* PowerPC MMU emulation */ + +/* Context used internally during MMU translations */ +typedef struct mmu_ctx_t mmu_ctx_t; +struct mmu_ctx_t { + hwaddr raddr; /* Real address */ + hwaddr eaddr; /* Effective address */ + int prot; /* Protection bits */ + hwaddr hash[2]; /* Pagetable hash values */ + target_ulong ptem; /* Virtual segment ID | API */ + int key; /* Access key */ + int nx; /* Non-execute area */ +}; + +/* Common routines used by software and hardware TLBs emulation */ +static inline int pte_is_valid(target_ulong pte0) +{ + return pte0 & 0x80000000 ? 1 : 0; +} + +static inline void pte_invalidate(target_ulong *pte0) +{ + *pte0 &= ~0x80000000; +} + +#define PTE_PTEM_MASK 0x7FFFFFBF +#define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) + +static int pp_check(int key, int pp, int nx) +{ + int access; + + /* Compute access rights */ + access = 0; + if (key == 0) { + switch (pp) { + case 0x0: + case 0x1: + case 0x2: + access |= PAGE_WRITE; + /* No break here */ + case 0x3: + access |= PAGE_READ; + break; + } + } else { + switch (pp) { + case 0x0: + access = 0; + break; + case 0x1: + case 0x3: + access = PAGE_READ; + break; + case 0x2: + access = PAGE_READ | PAGE_WRITE; + break; + } + } + if (nx == 0) { + access |= PAGE_EXEC; + } + + return access; +} + +static int check_prot(int prot, int rw, int access_type) +{ + int ret; + + if (access_type == ACCESS_CODE) { + if (prot & PAGE_EXEC) { + ret = 0; + } else { + ret = -2; + } + } else if (rw) { + if (prot & PAGE_WRITE) { + ret = 0; + } else { + ret = -2; + } + } else { + if (prot & PAGE_READ) { + ret = 0; + } else { + ret = -2; + } + } + + return ret; +} + +static inline int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, + target_ulong pte1, int h, int rw, int type) +{ + target_ulong ptem, mmask; + int access, ret, pteh, ptev, pp; + + ret = -1; + /* Check validity and table match */ + ptev = pte_is_valid(pte0); + pteh = (pte0 >> 6) & 1; + if (ptev && h == pteh) { + /* Check vsid & api */ + ptem = pte0 & PTE_PTEM_MASK; + mmask = PTE_CHECK_MASK; + pp = pte1 & 0x00000003; + if (ptem == ctx->ptem) { + if (ctx->raddr != (hwaddr)-1ULL) { + /* all matches should have equal RPN, WIMG & PP */ + if ((ctx->raddr & mmask) != (pte1 & mmask)) { + qemu_log_mask(CPU_LOG_MMU, "Bad RPN/WIMG/PP\n"); + return -3; + } + } + /* Compute access rights */ + access = pp_check(ctx->key, pp, ctx->nx); + /* Keep the matching PTE informations */ + ctx->raddr = pte1; + ctx->prot = access; + ret = check_prot(ctx->prot, rw, type); + if (ret == 0) { + /* Access granted */ + qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); + } else { + /* Access right violation */ + qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); + } + } + } + + return ret; +} + +static int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, + int ret, int rw) +{ + int store = 0; + + /* Update page flags */ + if (!(*pte1p & 0x00000100)) { + /* Update accessed flag */ + *pte1p |= 0x00000100; + store = 1; + } + if (!(*pte1p & 0x00000080)) { + if (rw == 1 && ret == 0) { + /* Update changed flag */ + *pte1p |= 0x00000080; + store = 1; + } else { + /* Force page fault for first write access */ + ctx->prot &= ~PAGE_WRITE; + } + } + + return store; +} + +/* Software driven TLB helpers */ +static inline int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, + int way, int is_code) +{ + int nr; + + /* Select TLB num in a way from address */ + nr = (eaddr >> TARGET_PAGE_BITS) & (env->tlb_per_way - 1); + /* Select TLB way */ + nr += env->tlb_per_way * way; + /* 6xx have separate TLBs for instructions and data */ + if (is_code && env->id_tlbs == 1) { + nr += env->nb_tlb; + } + + return nr; +} + +static inline void ppc6xx_tlb_invalidate_all(CPUPPCState *env) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + ppc6xx_tlb_t *tlb; + int nr, max; + + /* LOG_SWTLB("Invalidate all TLBs\n"); */ + /* Invalidate all defined software TLB */ + max = env->nb_tlb; + if (env->id_tlbs == 1) { + max *= 2; + } + for (nr = 0; nr < max; nr++) { + tlb = &env->tlb.tlb6[nr]; + pte_invalidate(&tlb->pte0); + } + tlb_flush(CPU(cpu), 1); +} + +static inline void ppc6xx_tlb_invalidate_virt2(CPUPPCState *env, + target_ulong eaddr, + int is_code, int match_epn) +{ +#if !defined(FLUSH_ALL_TLBS) + CPUState *cs = CPU(ppc_env_get_cpu(env)); + ppc6xx_tlb_t *tlb; + int way, nr; + + /* Invalidate ITLB + DTLB, all ways */ + for (way = 0; way < env->nb_ways; way++) { + nr = ppc6xx_tlb_getnum(env, eaddr, way, is_code); + tlb = &env->tlb.tlb6[nr]; + if (pte_is_valid(tlb->pte0) && (match_epn == 0 || eaddr == tlb->EPN)) { + LOG_SWTLB("TLB invalidate %d/%d " TARGET_FMT_lx "\n", nr, + env->nb_tlb, eaddr); + pte_invalidate(&tlb->pte0); + tlb_flush_page(cs, tlb->EPN); + } + } +#else + /* XXX: PowerPC specification say this is valid as well */ + ppc6xx_tlb_invalidate_all(env); +#endif +} + +static inline void ppc6xx_tlb_invalidate_virt(CPUPPCState *env, + target_ulong eaddr, int is_code) +{ + ppc6xx_tlb_invalidate_virt2(env, eaddr, is_code, 0); +} + +static void ppc6xx_tlb_store(CPUPPCState *env, target_ulong EPN, int way, + int is_code, target_ulong pte0, target_ulong pte1) +{ + ppc6xx_tlb_t *tlb; + int nr; + + nr = ppc6xx_tlb_getnum(env, EPN, way, is_code); + tlb = &env->tlb.tlb6[nr]; + LOG_SWTLB("Set TLB %d/%d EPN " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx + " PTE1 " TARGET_FMT_lx "\n", nr, env->nb_tlb, EPN, pte0, pte1); + /* Invalidate any pending reference in QEMU for this virtual address */ + ppc6xx_tlb_invalidate_virt2(env, EPN, is_code, 1); + tlb->pte0 = pte0; + tlb->pte1 = pte1; + tlb->EPN = EPN; + /* Store last way for LRU mechanism */ + env->last_way = way; +} + +static inline int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int access_type) +{ + ppc6xx_tlb_t *tlb; + int nr, best, way; + int ret; + + best = -1; + ret = -1; /* No TLB found */ + for (way = 0; way < env->nb_ways; way++) { + nr = ppc6xx_tlb_getnum(env, eaddr, way, + access_type == ACCESS_CODE ? 1 : 0); + tlb = &env->tlb.tlb6[nr]; + /* This test "emulates" the PTE index match for hardware TLBs */ + if ((eaddr & TARGET_PAGE_MASK) != tlb->EPN) { + LOG_SWTLB("TLB %d/%d %s [" TARGET_FMT_lx " " TARGET_FMT_lx + "] <> " TARGET_FMT_lx "\n", nr, env->nb_tlb, + pte_is_valid(tlb->pte0) ? "valid" : "inval", + tlb->EPN, tlb->EPN + TARGET_PAGE_SIZE, eaddr); + continue; + } + LOG_SWTLB("TLB %d/%d %s " TARGET_FMT_lx " <> " TARGET_FMT_lx " " + TARGET_FMT_lx " %c %c\n", nr, env->nb_tlb, + pte_is_valid(tlb->pte0) ? "valid" : "inval", + tlb->EPN, eaddr, tlb->pte1, + rw ? 'S' : 'L', access_type == ACCESS_CODE ? 'I' : 'D'); + switch (ppc6xx_tlb_pte_check(ctx, tlb->pte0, tlb->pte1, 0, rw, access_type)) { + case -3: + /* TLB inconsistency */ + return -1; + case -2: + /* Access violation */ + ret = -2; + best = nr; + break; + case -1: + default: + /* No match */ + break; + case 0: + /* access granted */ + /* XXX: we should go on looping to check all TLBs consistency + * but we can speed-up the whole thing as the + * result would be undefined if TLBs are not consistent. + */ + ret = 0; + best = nr; + goto done; + } + } + if (best != -1) { + done: + LOG_SWTLB("found TLB at addr " TARGET_FMT_plx " prot=%01x ret=%d\n", + ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); + /* Update page flags */ + pte_update_flags(ctx, &env->tlb.tlb6[best].pte1, ret, rw); + } + + return ret; +} + +/* Perform BAT hit & translation */ +static inline void bat_size_prot(CPUPPCState *env, target_ulong *blp, + int *validp, int *protp, target_ulong *BATu, + target_ulong *BATl) +{ + target_ulong bl; + int pp, valid, prot; + + bl = (*BATu & 0x00001FFC) << 15; + valid = 0; + prot = 0; + if (((msr_pr == 0) && (*BATu & 0x00000002)) || + ((msr_pr != 0) && (*BATu & 0x00000001))) { + valid = 1; + pp = *BATl & 0x00000003; + if (pp != 0) { + prot = PAGE_READ | PAGE_EXEC; + if (pp == 0x2) { + prot |= PAGE_WRITE; + } + } + } + *blp = bl; + *validp = valid; + *protp = prot; +} + +static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong virtual, int rw, int type) +{ + target_ulong *BATlt, *BATut, *BATu, *BATl; + target_ulong BEPIl, BEPIu, bl; + int i, valid, prot; + int ret = -1; + + LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__, + type == ACCESS_CODE ? 'I' : 'D', virtual); + switch (type) { + case ACCESS_CODE: + BATlt = env->IBAT[1]; + BATut = env->IBAT[0]; + break; + default: + BATlt = env->DBAT[1]; + BATut = env->DBAT[0]; + break; + } + for (i = 0; i < env->nb_BATs; i++) { + BATu = &BATut[i]; + BATl = &BATlt[i]; + BEPIu = *BATu & 0xF0000000; + BEPIl = *BATu & 0x0FFE0000; + bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); + LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx + " BATl " TARGET_FMT_lx "\n", __func__, + type == ACCESS_CODE ? 'I' : 'D', i, virtual, *BATu, *BATl); + if ((virtual & 0xF0000000) == BEPIu && + ((virtual & 0x0FFE0000) & ~bl) == BEPIl) { + /* BAT matches */ + if (valid != 0) { + /* Get physical address */ + ctx->raddr = (*BATl & 0xF0000000) | + ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) | + (virtual & 0x0001F000); + /* Compute access rights */ + ctx->prot = prot; + ret = check_prot(ctx->prot, rw, type); + if (ret == 0) { + LOG_BATS("BAT %d match: r " TARGET_FMT_plx " prot=%c%c\n", + i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', + ctx->prot & PAGE_WRITE ? 'W' : '-'); + } + break; + } + } + } + if (ret < 0) { +#if defined(DEBUG_BATS) + if (qemu_log_enabled()) { + LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", virtual); + for (i = 0; i < 4; i++) { + BATu = &BATut[i]; + BATl = &BATlt[i]; + BEPIu = *BATu & 0xF0000000; + BEPIl = *BATu & 0x0FFE0000; + bl = (*BATu & 0x00001FFC) << 15; + LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx + " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " + TARGET_FMT_lx " " TARGET_FMT_lx "\n", + __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual, + *BATu, *BATl, BEPIu, BEPIl, bl); + } + } +#endif + } + /* No hit */ + return ret; +} + +/* Perform segment based translation */ +static inline int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int type) +{ + hwaddr hash; + target_ulong vsid; + int ds, pr, target_page_bits; + int ret; + target_ulong sr, pgidx; + + pr = msr_pr; + ctx->eaddr = eaddr; + + sr = env->sr[eaddr >> 28]; + ctx->key = (((sr & 0x20000000) && (pr != 0)) || + ((sr & 0x40000000) && (pr == 0))) ? 1 : 0; + ds = sr & 0x80000000 ? 1 : 0; + ctx->nx = sr & 0x10000000 ? 1 : 0; + vsid = sr & 0x00FFFFFF; + target_page_bits = TARGET_PAGE_BITS; + qemu_log_mask(CPU_LOG_MMU, + "Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx + " nip=" TARGET_FMT_lx " lr=" TARGET_FMT_lx + " ir=%d dr=%d pr=%d %d t=%d\n", + eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir, + (int)msr_dr, pr != 0 ? 1 : 0, rw, type); + pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; + hash = vsid ^ pgidx; + ctx->ptem = (vsid << 7) | (pgidx >> 10); + + qemu_log_mask(CPU_LOG_MMU, + "pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", + ctx->key, ds, ctx->nx, vsid); + ret = -1; + if (!ds) { + /* Check if instruction fetch is allowed, if needed */ + if (type != ACCESS_CODE || ctx->nx == 0) { + /* Page address translation */ + qemu_log_mask(CPU_LOG_MMU, "htab_base " TARGET_FMT_plx + " htab_mask " TARGET_FMT_plx + " hash " TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, hash); + ctx->hash[0] = hash; + ctx->hash[1] = ~hash; + + /* Initialize real address with an invalid value */ + ctx->raddr = (hwaddr)-1ULL; + /* Software TLB search */ + ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type); +#if defined(DUMP_PAGE_TABLES) + if (qemu_loglevel_mask(CPU_LOG_MMU)) { + CPUState *cs = ENV_GET_CPU(env); + hwaddr curaddr; + uint32_t a0, a1, a2, a3; + + qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx + "\n", env->htab_base, env->htab_mask + 0x80); + for (curaddr = env->htab_base; + curaddr < (env->htab_base + env->htab_mask + 0x80); + curaddr += 16) { + a0 = ldl_phys(cs->as, curaddr); + a1 = ldl_phys(cs->as, curaddr + 4); + a2 = ldl_phys(cs->as, curaddr + 8); + a3 = ldl_phys(cs->as, curaddr + 12); + if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { + qemu_log(TARGET_FMT_plx ": %08x %08x %08x %08x\n", + curaddr, a0, a1, a2, a3); + } + } + } +#endif + } else { + qemu_log_mask(CPU_LOG_MMU, "No access allowed\n"); + ret = -3; + } + } else { + target_ulong sr; + + qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); + /* Direct-store segment : absolutely *BUGGY* for now */ + + /* Direct-store implies a 32-bit MMU. + * Check the Segment Register's bus unit ID (BUID). + */ + sr = env->sr[eaddr >> 28]; + if ((sr & 0x1FF00000) >> 20 == 0x07f) { + /* Memory-forced I/O controller interface access */ + /* If T=1 and BUID=x'07F', the 601 performs a memory access + * to SR[28-31] LA[4-31], bypassing all protection mechanisms. + */ + ctx->raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); + ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return 0; + } + + switch (type) { + case ACCESS_INT: + /* Integer load/store : only access allowed */ + break; + case ACCESS_CODE: + /* No code fetch is allowed in direct-store areas */ + return -4; + case ACCESS_FLOAT: + /* Floating point load/store */ + return -4; + case ACCESS_RES: + /* lwarx, ldarx or srwcx. */ + return -4; + case ACCESS_CACHE: + /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */ + /* Should make the instruction do no-op. + * As it already do no-op, it's quite easy :-) + */ + ctx->raddr = eaddr; + return 0; + case ACCESS_EXT: + /* eciwx or ecowx */ + return -4; + default: + qemu_log_mask(CPU_LOG_MMU, "ERROR: instruction should not need " + "address translation\n"); + return -4; + } + if ((rw == 1 || ctx->key != 1) && (rw == 0 || ctx->key != 0)) { + ctx->raddr = eaddr; + ret = 2; + } else { + ret = -2; + } + } + + return ret; +} + +/* Generic TLB check function for embedded PowerPC implementations */ +static int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, + hwaddr *raddrp, + target_ulong address, uint32_t pid, int ext, + int i) +{ + target_ulong mask; + + /* Check valid flag */ + if (!(tlb->prot & PAGE_VALID)) { + return -1; + } + mask = ~(tlb->size - 1); + LOG_SWTLB("%s: TLB %d address " TARGET_FMT_lx " PID %u <=> " TARGET_FMT_lx + " " TARGET_FMT_lx " %u %x\n", __func__, i, address, pid, tlb->EPN, + mask, (uint32_t)tlb->PID, tlb->prot); + /* Check PID */ + if (tlb->PID != 0 && tlb->PID != pid) { + return -1; + } + /* Check effective address */ + if ((address & mask) != tlb->EPN) { + return -1; + } + *raddrp = (tlb->RPN & mask) | (address & ~mask); + if (ext) { + /* Extend the physical address to 36 bits */ + *raddrp |= (uint64_t)(tlb->RPN & 0xF) << 32; + } + + return 0; +} + +/* Generic TLB search function for PowerPC embedded implementations */ +static int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, + uint32_t pid) +{ + ppcemb_tlb_t *tlb; + hwaddr raddr; + int i, ret; + + /* Default return value is no match */ + ret = -1; + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, 0, i) == 0) { + ret = i; + break; + } + } + + return ret; +} + +/* Helpers specific to PowerPC 40x implementations */ +static inline void ppc4xx_tlb_invalidate_all(CPUPPCState *env) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + ppcemb_tlb_t *tlb; + int i; + + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + tlb->prot &= ~PAGE_VALID; + } + tlb_flush(CPU(cpu), 1); +} + +static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong address, int rw, + int access_type) +{ + ppcemb_tlb_t *tlb; + hwaddr raddr; + int i, ret, zsel, zpr, pr; + + ret = -1; + raddr = (hwaddr)-1ULL; + pr = msr_pr; + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + if (ppcemb_tlb_check(env, tlb, &raddr, address, + env->spr[SPR_40x_PID], 0, i) < 0) { + continue; + } + zsel = (tlb->attr >> 4) & 0xF; + zpr = (env->spr[SPR_40x_ZPR] >> (30 - (2 * zsel))) & 0x3; + LOG_SWTLB("%s: TLB %d zsel %d zpr %d rw %d attr %08x\n", + __func__, i, zsel, zpr, rw, tlb->attr); + /* Check execute enable bit */ + switch (zpr) { + case 0x2: + if (pr != 0) { + goto check_perms; + } + /* No break here */ + case 0x3: + /* All accesses granted */ + ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + ret = 0; + break; + case 0x0: + if (pr != 0) { + /* Raise Zone protection fault. */ + env->spr[SPR_40x_ESR] = 1 << 22; + ctx->prot = 0; + ret = -2; + break; + } + /* No break here */ + case 0x1: + check_perms: + /* Check from TLB entry */ + ctx->prot = tlb->prot; + ret = check_prot(ctx->prot, rw, access_type); + if (ret == -2) { + env->spr[SPR_40x_ESR] = 0; + } + break; + } + if (ret >= 0) { + ctx->raddr = raddr; + LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, ctx->raddr, ctx->prot, + ret); + return 0; + } + } + LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, raddr, ctx->prot, ret); + + return ret; +} + +void store_40x_sler(CPUPPCState *env, uint32_t val) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + /* XXX: TO BE FIXED */ + if (val != 0x00000000) { + cpu_abort(CPU(cpu), "Little-endian regions are not supported by now\n"); + } + env->spr[SPR_405_SLER] = val; +} + +static inline int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, + hwaddr *raddr, int *prot, + target_ulong address, int rw, + int access_type, int i) +{ + int ret, prot2; + + if (ppcemb_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID], + !env->nb_pids, i) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID1] && + ppcemb_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID1], 0, i) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID2] && + ppcemb_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID2], 0, i) >= 0) { + goto found_tlb; + } + + LOG_SWTLB("%s: TLB entry not found\n", __func__); + return -1; + +found_tlb: + + if (msr_pr != 0) { + prot2 = tlb->prot & 0xF; + } else { + prot2 = (tlb->prot >> 4) & 0xF; + } + + /* Check the address space */ + if (access_type == ACCESS_CODE) { + if (msr_ir != (tlb->attr & 1)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = prot2; + if (prot2 & PAGE_EXEC) { + LOG_SWTLB("%s: good TLB!\n", __func__); + return 0; + } + + LOG_SWTLB("%s: no PAGE_EXEC: %x\n", __func__, prot2); + ret = -3; + } else { + if (msr_dr != (tlb->attr & 1)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = prot2; + if ((!rw && prot2 & PAGE_READ) || (rw && (prot2 & PAGE_WRITE))) { + LOG_SWTLB("%s: found TLB!\n", __func__); + return 0; + } + + LOG_SWTLB("%s: PAGE_READ/WRITE doesn't match: %x\n", __func__, prot2); + ret = -2; + } + + return ret; +} + +static int mmubooke_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong address, int rw, + int access_type) +{ + ppcemb_tlb_t *tlb; + hwaddr raddr; + int i, ret; + + ret = -1; + raddr = (hwaddr)-1ULL; + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw, + access_type, i); + if (!ret) { + break; + } + } + + if (ret >= 0) { + ctx->raddr = raddr; + LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, ctx->raddr, ctx->prot, + ret); + } else { + LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, raddr, ctx->prot, ret); + } + + return ret; +} + +static void booke206_flush_tlb(CPUPPCState *env, int flags, + const int check_iprot) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + int tlb_size; + int i, j; + ppcmas_tlb_t *tlb = env->tlb.tlbm; + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + if (flags & (1 << i)) { + tlb_size = booke206_tlb_size(env, i); + for (j = 0; j < tlb_size; j++) { + if (!check_iprot || !(tlb[j].mas1 & MAS1_IPROT)) { + tlb[j].mas1 &= ~MAS1_VALID; + } + } + } + tlb += booke206_tlb_size(env, i); + } + + tlb_flush(CPU(cpu), 1); +} + +static hwaddr booke206_tlb_to_page_size(CPUPPCState *env, + ppcmas_tlb_t *tlb) +{ + int tlbm_size; + + tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; + + return 1024ULL << tlbm_size; +} + +/* TLB check function for MAS based SoftTLBs */ +static int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, + hwaddr *raddrp, target_ulong address, + uint32_t pid) +{ + hwaddr mask; + uint32_t tlb_pid; + + if (!msr_cm) { + /* In 32bit mode we can only address 32bit EAs */ + address = (uint32_t)address; + } + + /* Check valid flag */ + if (!(tlb->mas1 & MAS1_VALID)) { + return -1; + } + + mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); + LOG_SWTLB("%s: TLB ADDR=0x" TARGET_FMT_lx " PID=0x%x MAS1=0x%x MAS2=0x%" + PRIx64 " mask=0x%" HWADDR_PRIx " MAS7_3=0x%" PRIx64 " MAS8=0x%" + PRIx32 "\n", __func__, address, pid, tlb->mas1, tlb->mas2, mask, + tlb->mas7_3, tlb->mas8); + + /* Check PID */ + tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT; + if (tlb_pid != 0 && tlb_pid != pid) { + return -1; + } + + /* Check effective address */ + if ((address & mask) != (tlb->mas2 & MAS2_EPN_MASK)) { + return -1; + } + + if (raddrp) { + *raddrp = (tlb->mas7_3 & mask) | (address & ~mask); + } + + return 0; +} + +static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, + hwaddr *raddr, int *prot, + target_ulong address, int rw, + int access_type) +{ + int ret; + int prot2 = 0; + + if (ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID]) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID1] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID1]) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID2] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID2]) >= 0) { + goto found_tlb; + } + + LOG_SWTLB("%s: TLB entry not found\n", __func__); + return -1; + +found_tlb: + + if (msr_pr != 0) { + if (tlb->mas7_3 & MAS3_UR) { + prot2 |= PAGE_READ; + } + if (tlb->mas7_3 & MAS3_UW) { + prot2 |= PAGE_WRITE; + } + if (tlb->mas7_3 & MAS3_UX) { + prot2 |= PAGE_EXEC; + } + } else { + if (tlb->mas7_3 & MAS3_SR) { + prot2 |= PAGE_READ; + } + if (tlb->mas7_3 & MAS3_SW) { + prot2 |= PAGE_WRITE; + } + if (tlb->mas7_3 & MAS3_SX) { + prot2 |= PAGE_EXEC; + } + } + + /* Check the address space and permissions */ + if (access_type == ACCESS_CODE) { + if (msr_ir != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = prot2; + if (prot2 & PAGE_EXEC) { + LOG_SWTLB("%s: good TLB!\n", __func__); + return 0; + } + + LOG_SWTLB("%s: no PAGE_EXEC: %x\n", __func__, prot2); + ret = -3; + } else { + if (msr_dr != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = prot2; + if ((!rw && prot2 & PAGE_READ) || (rw && (prot2 & PAGE_WRITE))) { + LOG_SWTLB("%s: found TLB!\n", __func__); + return 0; + } + + LOG_SWTLB("%s: PAGE_READ/WRITE doesn't match: %x\n", __func__, prot2); + ret = -2; + } + + return ret; +} + +static int mmubooke206_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong address, int rw, + int access_type) +{ + ppcmas_tlb_t *tlb; + hwaddr raddr; + int i, j, ret; + + ret = -1; + raddr = (hwaddr)-1ULL; + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + int ways = booke206_tlb_ways(env, i); + + for (j = 0; j < ways; j++) { + tlb = booke206_get_tlbm(env, i, address, j); + if (!tlb) { + continue; + } + ret = mmubooke206_check_tlb(env, tlb, &raddr, &ctx->prot, address, + rw, access_type); + if (ret != -1) { + goto found_tlb; + } + } + } + +found_tlb: + + if (ret >= 0) { + ctx->raddr = raddr; + LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, ctx->raddr, ctx->prot, + ret); + } else { + LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, raddr, ctx->prot, ret); + } + + return ret; +} + +static const char *book3e_tsize_to_str[32] = { + "1K", "2K", "4K", "8K", "16K", "32K", "64K", "128K", "256K", "512K", + "1M", "2M", "4M", "8M", "16M", "32M", "64M", "128M", "256M", "512M", + "1G", "2G", "4G", "8G", "16G", "32G", "64G", "128G", "256G", "512G", + "1T", "2T" +}; + +static void mmubooke_dump_mmu(FILE *f, fprintf_function cpu_fprintf, + CPUPPCState *env) +{ + ppcemb_tlb_t *entry; + int i; + + if (kvm_enabled() && !env->kvm_sw_tlb) { + cpu_fprintf(f, "Cannot access KVM TLB\n"); + return; + } + + cpu_fprintf(f, "\nTLB:\n"); + cpu_fprintf(f, "Effective Physical Size PID Prot " + "Attr\n"); + + entry = &env->tlb.tlbe[0]; + for (i = 0; i < env->nb_tlb; i++, entry++) { + hwaddr ea, pa; + target_ulong mask; + uint64_t size = (uint64_t)entry->size; + char size_buf[20]; + + /* Check valid flag */ + if (!(entry->prot & PAGE_VALID)) { + continue; + } + + mask = ~(entry->size - 1); + ea = entry->EPN & mask; + pa = entry->RPN & mask; + /* Extend the physical address to 36 bits */ + pa |= (hwaddr)(entry->RPN & 0xF) << 32; + size /= 1024; + if (size >= 1024) { + snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "M", size / 1024); + } else { + snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "k", size); + } + cpu_fprintf(f, "0x%016" PRIx64 " 0x%016" PRIx64 " %s %-5u %08x %08x\n", + (uint64_t)ea, (uint64_t)pa, size_buf, (uint32_t)entry->PID, + entry->prot, entry->attr); + } + +} + +static void mmubooke206_dump_one_tlb(FILE *f, fprintf_function cpu_fprintf, + CPUPPCState *env, int tlbn, int offset, + int tlbsize) +{ + ppcmas_tlb_t *entry; + int i; + + cpu_fprintf(f, "\nTLB%d:\n", tlbn); + cpu_fprintf(f, "Effective Physical Size TID TS SRWX" + " URWX WIMGE U0123\n"); + + entry = &env->tlb.tlbm[offset]; + for (i = 0; i < tlbsize; i++, entry++) { + hwaddr ea, pa, size; + int tsize; + + if (!(entry->mas1 & MAS1_VALID)) { + continue; + } + + tsize = (entry->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; + size = 1024ULL << tsize; + ea = entry->mas2 & ~(size - 1); + pa = entry->mas7_3 & ~(size - 1); + + cpu_fprintf(f, "0x%016" PRIx64 " 0x%016" PRIx64 " %4s %-5u %1u S%c%c%c" + "U%c%c%c %c%c%c%c%c U%c%c%c%c\n", + (uint64_t)ea, (uint64_t)pa, + book3e_tsize_to_str[tsize], + (entry->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT, + (entry->mas1 & MAS1_TS) >> MAS1_TS_SHIFT, + entry->mas7_3 & MAS3_SR ? 'R' : '-', + entry->mas7_3 & MAS3_SW ? 'W' : '-', + entry->mas7_3 & MAS3_SX ? 'X' : '-', + entry->mas7_3 & MAS3_UR ? 'R' : '-', + entry->mas7_3 & MAS3_UW ? 'W' : '-', + entry->mas7_3 & MAS3_UX ? 'X' : '-', + entry->mas2 & MAS2_W ? 'W' : '-', + entry->mas2 & MAS2_I ? 'I' : '-', + entry->mas2 & MAS2_M ? 'M' : '-', + entry->mas2 & MAS2_G ? 'G' : '-', + entry->mas2 & MAS2_E ? 'E' : '-', + entry->mas7_3 & MAS3_U0 ? '0' : '-', + entry->mas7_3 & MAS3_U1 ? '1' : '-', + entry->mas7_3 & MAS3_U2 ? '2' : '-', + entry->mas7_3 & MAS3_U3 ? '3' : '-'); + } +} + +static void mmubooke206_dump_mmu(FILE *f, fprintf_function cpu_fprintf, + CPUPPCState *env) +{ + int offset = 0; + int i; + + if (kvm_enabled() && !env->kvm_sw_tlb) { + cpu_fprintf(f, "Cannot access KVM TLB\n"); + return; + } + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + int size = booke206_tlb_size(env, i); + + if (size == 0) { + continue; + } + + mmubooke206_dump_one_tlb(f, cpu_fprintf, env, i, offset, size); + offset += size; + } +} + +static void mmu6xx_dump_BATs(FILE *f, fprintf_function cpu_fprintf, + CPUPPCState *env, int type) +{ + target_ulong *BATlt, *BATut, *BATu, *BATl; + target_ulong BEPIl, BEPIu, bl; + int i; + + switch (type) { + case ACCESS_CODE: + BATlt = env->IBAT[1]; + BATut = env->IBAT[0]; + break; + default: + BATlt = env->DBAT[1]; + BATut = env->DBAT[0]; + break; + } + + for (i = 0; i < env->nb_BATs; i++) { + BATu = &BATut[i]; + BATl = &BATlt[i]; + BEPIu = *BATu & 0xF0000000; + BEPIl = *BATu & 0x0FFE0000; + bl = (*BATu & 0x00001FFC) << 15; + cpu_fprintf(f, "%s BAT%d BATu " TARGET_FMT_lx + " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " + TARGET_FMT_lx " " TARGET_FMT_lx "\n", + type == ACCESS_CODE ? "code" : "data", i, + *BATu, *BATl, BEPIu, BEPIl, bl); + } +} + +static void mmu6xx_dump_mmu(FILE *f, fprintf_function cpu_fprintf, + CPUPPCState *env) +{ + ppc6xx_tlb_t *tlb; + target_ulong sr; + int type, way, entry, i; + + cpu_fprintf(f, "HTAB base = 0x%"HWADDR_PRIx"\n", env->htab_base); + cpu_fprintf(f, "HTAB mask = 0x%"HWADDR_PRIx"\n", env->htab_mask); + + cpu_fprintf(f, "\nSegment registers:\n"); + for (i = 0; i < 32; i++) { + sr = env->sr[i]; + if (sr & 0x80000000) { + cpu_fprintf(f, "%02d T=%d Ks=%d Kp=%d BUID=0x%03x " + "CNTLR_SPEC=0x%05x\n", i, + sr & 0x80000000 ? 1 : 0, sr & 0x40000000 ? 1 : 0, + sr & 0x20000000 ? 1 : 0, (uint32_t)((sr >> 20) & 0x1FF), + (uint32_t)(sr & 0xFFFFF)); + } else { + cpu_fprintf(f, "%02d T=%d Ks=%d Kp=%d N=%d VSID=0x%06x\n", i, + sr & 0x80000000 ? 1 : 0, sr & 0x40000000 ? 1 : 0, + sr & 0x20000000 ? 1 : 0, sr & 0x10000000 ? 1 : 0, + (uint32_t)(sr & 0x00FFFFFF)); + } + } + + cpu_fprintf(f, "\nBATs:\n"); + mmu6xx_dump_BATs(f, cpu_fprintf, env, ACCESS_INT); + mmu6xx_dump_BATs(f, cpu_fprintf, env, ACCESS_CODE); + + if (env->id_tlbs != 1) { + cpu_fprintf(f, "ERROR: 6xx MMU should have separated TLB" + " for code and data\n"); + } + + cpu_fprintf(f, "\nTLBs [EPN EPN + SIZE]\n"); + + for (type = 0; type < 2; type++) { + for (way = 0; way < env->nb_ways; way++) { + for (entry = env->nb_tlb * type + env->tlb_per_way * way; + entry < (env->nb_tlb * type + env->tlb_per_way * (way + 1)); + entry++) { + + tlb = &env->tlb.tlb6[entry]; + cpu_fprintf(f, "%s TLB %02d/%02d way:%d %s [" + TARGET_FMT_lx " " TARGET_FMT_lx "]\n", + type ? "code" : "data", entry % env->nb_tlb, + env->nb_tlb, way, + pte_is_valid(tlb->pte0) ? "valid" : "inval", + tlb->EPN, tlb->EPN + TARGET_PAGE_SIZE); + } + } + } +} + +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env) +{ + switch (env->mmu_model) { + case POWERPC_MMU_BOOKE: + mmubooke_dump_mmu(f, cpu_fprintf, env); + break; + case POWERPC_MMU_BOOKE206: + mmubooke206_dump_mmu(f, cpu_fprintf, env); + break; + case POWERPC_MMU_SOFT_6xx: + case POWERPC_MMU_SOFT_74xx: + mmu6xx_dump_mmu(f, cpu_fprintf, env); + break; +#if defined(TARGET_PPC64) + case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06a: + case POWERPC_MMU_2_07: + case POWERPC_MMU_2_07a: + dump_slb(f, cpu_fprintf, ppc_env_get_cpu(env)); + break; +#endif + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented\n", __func__); + } +} + +static inline int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw) +{ + int in_plb, ret; + + ctx->raddr = eaddr; + ctx->prot = PAGE_READ | PAGE_EXEC; + ret = 0; + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + case POWERPC_MMU_SOFT_74xx: + case POWERPC_MMU_SOFT_4xx: + case POWERPC_MMU_REAL: + case POWERPC_MMU_BOOKE: + ctx->prot |= PAGE_WRITE; + break; + + case POWERPC_MMU_SOFT_4xx_Z: + if (unlikely(msr_pe != 0)) { + /* 403 family add some particular protections, + * using PBL/PBU registers for accesses with no translation. + */ + in_plb = + /* Check PLB validity */ + (env->pb[0] < env->pb[1] && + /* and address in plb area */ + eaddr >= env->pb[0] && eaddr < env->pb[1]) || + (env->pb[2] < env->pb[3] && + eaddr >= env->pb[2] && eaddr < env->pb[3]) ? 1 : 0; + if (in_plb ^ msr_px) { + /* Access in protected area */ + if (rw == 1) { + /* Access is not allowed */ + ret = -2; + } + } else { + /* Read-write access is allowed */ + ctx->prot |= PAGE_WRITE; + } + } + break; + + default: + /* Caller's checks mean we should never get here for other models */ + abort(); + return -1; + } + + return ret; +} + +static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int access_type) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + int ret = -1; + bool real_mode = (access_type == ACCESS_CODE && msr_ir == 0) + || (access_type != ACCESS_CODE && msr_dr == 0); + +#if 0 + qemu_log("%s\n", __func__); +#endif + + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + case POWERPC_MMU_SOFT_74xx: + if (real_mode) { + ret = check_physical(env, ctx, eaddr, rw); + } else { + /* Try to find a BAT */ + if (env->nb_BATs != 0) { + ret = get_bat_6xx_tlb(env, ctx, eaddr, rw, access_type); + } + if (ret < 0) { + /* We didn't match any BAT entry or don't have BATs */ + ret = get_segment_6xx_tlb(env, ctx, eaddr, rw, access_type); + } + } + break; + + case POWERPC_MMU_SOFT_4xx: + case POWERPC_MMU_SOFT_4xx_Z: + if (real_mode) { + ret = check_physical(env, ctx, eaddr, rw); + } else { + ret = mmu40x_get_physical_address(env, ctx, eaddr, + rw, access_type); + } + break; + case POWERPC_MMU_BOOKE: + ret = mmubooke_get_physical_address(env, ctx, eaddr, + rw, access_type); + break; + case POWERPC_MMU_BOOKE206: + ret = mmubooke206_get_physical_address(env, ctx, eaddr, rw, + access_type); + break; + case POWERPC_MMU_MPC8xx: + /* XXX: TODO */ + cpu_abort(CPU(cpu), "MPC8xx MMU model is not implemented\n"); + break; + case POWERPC_MMU_REAL: + if (real_mode) { + ret = check_physical(env, ctx, eaddr, rw); + } else { + cpu_abort(CPU(cpu), "PowerPC in real mode do not do any translation\n"); + } + return -1; + default: + cpu_abort(CPU(cpu), "Unknown or invalid MMU model\n"); + return -1; + } +#if 0 + qemu_log("%s address " TARGET_FMT_lx " => %d " TARGET_FMT_plx "\n", + __func__, eaddr, ret, ctx->raddr); +#endif + + return ret; +} + +hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + mmu_ctx_t ctx; + + switch (env->mmu_model) { +#if defined(TARGET_PPC64) + case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06a: + case POWERPC_MMU_2_07: + case POWERPC_MMU_2_07a: + return ppc_hash64_get_phys_page_debug(cpu, addr); +#endif + + case POWERPC_MMU_32B: + case POWERPC_MMU_601: + return ppc_hash32_get_phys_page_debug(cpu, addr); + + default: + ; + } + + if (unlikely(get_physical_address(env, &ctx, addr, 0, ACCESS_INT) != 0)) { + + /* Some MMUs have separate TLBs for code and data. If we only try an + * ACCESS_INT, we may not be able to read instructions mapped by code + * TLBs, so we also try a ACCESS_CODE. + */ + if (unlikely(get_physical_address(env, &ctx, addr, 0, + ACCESS_CODE) != 0)) { + return -1; + } + } + + return ctx.raddr & TARGET_PAGE_MASK; +} + +static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, + int rw) +{ + env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; + env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK; + env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK; + env->spr[SPR_BOOKE_MAS3] = 0; + env->spr[SPR_BOOKE_MAS6] = 0; + env->spr[SPR_BOOKE_MAS7] = 0; + + /* AS */ + if (((rw == 2) && msr_ir) || ((rw != 2) && msr_dr)) { + env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; + env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS; + } + + env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID; + env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK; + + switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) { + case MAS4_TIDSELD_PID0: + env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID] << MAS1_TID_SHIFT; + break; + case MAS4_TIDSELD_PID1: + env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID1] << MAS1_TID_SHIFT; + break; + case MAS4_TIDSELD_PID2: + env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID2] << MAS1_TID_SHIFT; + break; + } + + env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16; + + /* next victim logic */ + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT; + env->last_way++; + env->last_way &= booke206_tlb_ways(env, 0) - 1; + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; +} + +/* Perform address translation */ +static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, + int rw, int mmu_idx) +{ + CPUState *cs = CPU(ppc_env_get_cpu(env)); + PowerPCCPU *cpu = POWERPC_CPU(cs); + mmu_ctx_t ctx; + int access_type; + int ret = 0; + + if (rw == 2) { + /* code access */ + rw = 0; + access_type = ACCESS_CODE; + } else { + /* data access */ + access_type = env->access_type; + } + ret = get_physical_address(env, &ctx, address, rw, access_type); + if (ret == 0) { + tlb_set_page(cs, address & TARGET_PAGE_MASK, + ctx.raddr & TARGET_PAGE_MASK, ctx.prot, + mmu_idx, TARGET_PAGE_SIZE); + ret = 0; + } else if (ret < 0) { + LOG_MMU_STATE(cs); + if (access_type == ACCESS_CODE) { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + cs->exception_index = POWERPC_EXCP_IFTLB; + env->error_code = 1 << 18; + env->spr[SPR_IMISS] = address; + env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; + goto tlb_miss; + case POWERPC_MMU_SOFT_74xx: + cs->exception_index = POWERPC_EXCP_IFTLB; + goto tlb_miss_74xx; + case POWERPC_MMU_SOFT_4xx: + case POWERPC_MMU_SOFT_4xx_Z: + cs->exception_index = POWERPC_EXCP_ITLB; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = address; + env->spr[SPR_40x_ESR] = 0x00000000; + break; + case POWERPC_MMU_BOOKE206: + booke206_update_mas_tlb_miss(env, address, rw); + /* fall through */ + case POWERPC_MMU_BOOKE: + cs->exception_index = POWERPC_EXCP_ITLB; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = address; + return -1; + case POWERPC_MMU_MPC8xx: + /* XXX: TODO */ + cpu_abort(cs, "MPC8xx MMU model is not implemented\n"); + break; + case POWERPC_MMU_REAL: + cpu_abort(cs, "PowerPC in real mode should never raise " + "any MMU exceptions\n"); + return -1; + default: + cpu_abort(cs, "Unknown or invalid MMU model\n"); + return -1; + } + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; + break; + case -3: + /* No execute protection violation */ + if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { + env->spr[SPR_BOOKE_ESR] = 0x00000000; + } + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + break; + case -4: + /* Direct store exception */ + /* No code fetch is allowed in direct-store areas */ + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + break; + } + } else { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + if (rw == 1) { + cs->exception_index = POWERPC_EXCP_DSTLB; + env->error_code = 1 << 16; + } else { + cs->exception_index = POWERPC_EXCP_DLTLB; + env->error_code = 0; + } + env->spr[SPR_DMISS] = address; + env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; + tlb_miss: + env->error_code |= ctx.key << 19; + env->spr[SPR_HASH1] = env->htab_base + + get_pteg_offset32(cpu, ctx.hash[0]); + env->spr[SPR_HASH2] = env->htab_base + + get_pteg_offset32(cpu, ctx.hash[1]); + break; + case POWERPC_MMU_SOFT_74xx: + if (rw == 1) { + cs->exception_index = POWERPC_EXCP_DSTLB; + } else { + cs->exception_index = POWERPC_EXCP_DLTLB; + } + tlb_miss_74xx: + /* Implement LRU algorithm */ + env->error_code = ctx.key << 19; + env->spr[SPR_TLBMISS] = (address & ~((target_ulong)0x3)) | + ((env->last_way + 1) & (env->nb_ways - 1)); + env->spr[SPR_PTEHI] = 0x80000000 | ctx.ptem; + break; + case POWERPC_MMU_SOFT_4xx: + case POWERPC_MMU_SOFT_4xx_Z: + cs->exception_index = POWERPC_EXCP_DTLB; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = address; + if (rw) { + env->spr[SPR_40x_ESR] = 0x00800000; + } else { + env->spr[SPR_40x_ESR] = 0x00000000; + } + break; + case POWERPC_MMU_MPC8xx: + /* XXX: TODO */ + cpu_abort(cs, "MPC8xx MMU model is not implemented\n"); + break; + case POWERPC_MMU_BOOKE206: + booke206_update_mas_tlb_miss(env, address, rw); + /* fall through */ + case POWERPC_MMU_BOOKE: + cs->exception_index = POWERPC_EXCP_DTLB; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = address; + env->spr[SPR_BOOKE_ESR] = rw ? ESR_ST : 0; + return -1; + case POWERPC_MMU_REAL: + cpu_abort(cs, "PowerPC in real mode should never raise " + "any MMU exceptions\n"); + return -1; + default: + cpu_abort(cs, "Unknown or invalid MMU model\n"); + return -1; + } + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + if (env->mmu_model == POWERPC_MMU_SOFT_4xx + || env->mmu_model == POWERPC_MMU_SOFT_4xx_Z) { + env->spr[SPR_40x_DEAR] = address; + if (rw) { + env->spr[SPR_40x_ESR] |= 0x00800000; + } + } else if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { + env->spr[SPR_BOOKE_DEAR] = address; + env->spr[SPR_BOOKE_ESR] = rw ? ESR_ST : 0; + } else { + env->spr[SPR_DAR] = address; + if (rw == 1) { + env->spr[SPR_DSISR] = 0x0A000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } + } + break; + case -4: + /* Direct store exception */ + switch (access_type) { + case ACCESS_FLOAT: + /* Floating point load/store */ + cs->exception_index = POWERPC_EXCP_ALIGN; + env->error_code = POWERPC_EXCP_ALIGN_FP; + env->spr[SPR_DAR] = address; + break; + case ACCESS_RES: + /* lwarx, ldarx or stwcx. */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = address; + if (rw == 1) { + env->spr[SPR_DSISR] = 0x06000000; + } else { + env->spr[SPR_DSISR] = 0x04000000; + } + break; + case ACCESS_EXT: + /* eciwx or ecowx */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = address; + if (rw == 1) { + env->spr[SPR_DSISR] = 0x06100000; + } else { + env->spr[SPR_DSISR] = 0x04100000; + } + break; + default: + printf("DSI: invalid exception (%d)\n", ret); + cs->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = + POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; + env->spr[SPR_DAR] = address; + break; + } + break; + } + } +#if 0 + printf("%s: set exception to %d %02x\n", __func__, + cs->exception, env->error_code); +#endif + ret = 1; + } + + return ret; +} + +/*****************************************************************************/ +/* BATs management */ +#if !defined(FLUSH_ALL_TLBS) +static inline void do_invalidate_BAT(CPUPPCState *env, target_ulong BATu, + target_ulong mask) +{ + CPUState *cs = CPU(ppc_env_get_cpu(env)); + target_ulong base, end, page; + + base = BATu & ~0x0001FFFF; + end = base + mask + 0x00020000; + LOG_BATS("Flush BAT from " TARGET_FMT_lx " to " TARGET_FMT_lx " (" + TARGET_FMT_lx ")\n", base, end, mask); + for (page = base; page != end; page += TARGET_PAGE_SIZE) { + tlb_flush_page(cs, page); + } + LOG_BATS("Flush done\n"); +} +#endif + +static inline void dump_store_bat(CPUPPCState *env, char ID, int ul, int nr, + target_ulong value) +{ + LOG_BATS("Set %cBAT%d%c to " TARGET_FMT_lx " (" TARGET_FMT_lx ")\n", ID, + nr, ul == 0 ? 'u' : 'l', value, env->nip); +} + +void helper_store_ibatu(CPUPPCState *env, uint32_t nr, target_ulong value) +{ + target_ulong mask; +#if defined(FLUSH_ALL_TLBS) + PowerPCCPU *cpu = ppc_env_get_cpu(env); +#endif + + dump_store_bat(env, 'I', 0, nr, value); + if (env->IBAT[0][nr] != value) { + mask = (value << 15) & 0x0FFE0000UL; +#if !defined(FLUSH_ALL_TLBS) + do_invalidate_BAT(env, env->IBAT[0][nr], mask); +#endif + /* When storing valid upper BAT, mask BEPI and BRPN + * and invalidate all TLBs covered by this BAT + */ + mask = (value << 15) & 0x0FFE0000UL; + env->IBAT[0][nr] = (value & 0x00001FFFUL) | + (value & ~0x0001FFFFUL & ~mask); + env->IBAT[1][nr] = (env->IBAT[1][nr] & 0x0000007B) | + (env->IBAT[1][nr] & ~0x0001FFFF & ~mask); +#if !defined(FLUSH_ALL_TLBS) + do_invalidate_BAT(env, env->IBAT[0][nr], mask); +#else + tlb_flush(CPU(cpu), 1); +#endif + } +} + +void helper_store_ibatl(CPUPPCState *env, uint32_t nr, target_ulong value) +{ + dump_store_bat(env, 'I', 1, nr, value); + env->IBAT[1][nr] = value; +} + +void helper_store_dbatu(CPUPPCState *env, uint32_t nr, target_ulong value) +{ + target_ulong mask; +#if defined(FLUSH_ALL_TLBS) + PowerPCCPU *cpu = ppc_env_get_cpu(env); +#endif + + dump_store_bat(env, 'D', 0, nr, value); + if (env->DBAT[0][nr] != value) { + /* When storing valid upper BAT, mask BEPI and BRPN + * and invalidate all TLBs covered by this BAT + */ + mask = (value << 15) & 0x0FFE0000UL; +#if !defined(FLUSH_ALL_TLBS) + do_invalidate_BAT(env, env->DBAT[0][nr], mask); +#endif + mask = (value << 15) & 0x0FFE0000UL; + env->DBAT[0][nr] = (value & 0x00001FFFUL) | + (value & ~0x0001FFFFUL & ~mask); + env->DBAT[1][nr] = (env->DBAT[1][nr] & 0x0000007B) | + (env->DBAT[1][nr] & ~0x0001FFFF & ~mask); +#if !defined(FLUSH_ALL_TLBS) + do_invalidate_BAT(env, env->DBAT[0][nr], mask); +#else + tlb_flush(CPU(cpu), 1); +#endif + } +} + +void helper_store_dbatl(CPUPPCState *env, uint32_t nr, target_ulong value) +{ + dump_store_bat(env, 'D', 1, nr, value); + env->DBAT[1][nr] = value; +} + +void helper_store_601_batu(CPUPPCState *env, uint32_t nr, target_ulong value) +{ + target_ulong mask; +#if defined(FLUSH_ALL_TLBS) + PowerPCCPU *cpu = ppc_env_get_cpu(env); + int do_inval; +#endif + + dump_store_bat(env, 'I', 0, nr, value); + if (env->IBAT[0][nr] != value) { +#if defined(FLUSH_ALL_TLBS) + do_inval = 0; +#endif + mask = (env->IBAT[1][nr] << 17) & 0x0FFE0000UL; + if (env->IBAT[1][nr] & 0x40) { + /* Invalidate BAT only if it is valid */ +#if !defined(FLUSH_ALL_TLBS) + do_invalidate_BAT(env, env->IBAT[0][nr], mask); +#else + do_inval = 1; +#endif + } + /* When storing valid upper BAT, mask BEPI and BRPN + * and invalidate all TLBs covered by this BAT + */ + env->IBAT[0][nr] = (value & 0x00001FFFUL) | + (value & ~0x0001FFFFUL & ~mask); + env->DBAT[0][nr] = env->IBAT[0][nr]; + if (env->IBAT[1][nr] & 0x40) { +#if !defined(FLUSH_ALL_TLBS) + do_invalidate_BAT(env, env->IBAT[0][nr], mask); +#else + do_inval = 1; +#endif + } +#if defined(FLUSH_ALL_TLBS) + if (do_inval) { + tlb_flush(CPU(cpu), 1); + } +#endif + } +} + +void helper_store_601_batl(CPUPPCState *env, uint32_t nr, target_ulong value) +{ +#if !defined(FLUSH_ALL_TLBS) + target_ulong mask; +#else + PowerPCCPU *cpu = ppc_env_get_cpu(env); + int do_inval; +#endif + + dump_store_bat(env, 'I', 1, nr, value); + if (env->IBAT[1][nr] != value) { +#if defined(FLUSH_ALL_TLBS) + do_inval = 0; +#endif + if (env->IBAT[1][nr] & 0x40) { +#if !defined(FLUSH_ALL_TLBS) + mask = (env->IBAT[1][nr] << 17) & 0x0FFE0000UL; + do_invalidate_BAT(env, env->IBAT[0][nr], mask); +#else + do_inval = 1; +#endif + } + if (value & 0x40) { +#if !defined(FLUSH_ALL_TLBS) + mask = (value << 17) & 0x0FFE0000UL; + do_invalidate_BAT(env, env->IBAT[0][nr], mask); +#else + do_inval = 1; +#endif + } + env->IBAT[1][nr] = value; + env->DBAT[1][nr] = value; +#if defined(FLUSH_ALL_TLBS) + if (do_inval) { + tlb_flush(CPU(cpu), 1); + } +#endif + } +} + +/*****************************************************************************/ +/* TLB management */ +void ppc_tlb_invalidate_all(CPUPPCState *env) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + case POWERPC_MMU_SOFT_74xx: + ppc6xx_tlb_invalidate_all(env); + break; + case POWERPC_MMU_SOFT_4xx: + case POWERPC_MMU_SOFT_4xx_Z: + ppc4xx_tlb_invalidate_all(env); + break; + case POWERPC_MMU_REAL: + cpu_abort(CPU(cpu), "No TLB for PowerPC 4xx in real mode\n"); + break; + case POWERPC_MMU_MPC8xx: + /* XXX: TODO */ + cpu_abort(CPU(cpu), "MPC8xx MMU model is not implemented\n"); + break; + case POWERPC_MMU_BOOKE: + tlb_flush(CPU(cpu), 1); + break; + case POWERPC_MMU_BOOKE206: + booke206_flush_tlb(env, -1, 0); + break; + case POWERPC_MMU_32B: + case POWERPC_MMU_601: +#if defined(TARGET_PPC64) + case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06a: + case POWERPC_MMU_2_07: + case POWERPC_MMU_2_07a: +#endif /* defined(TARGET_PPC64) */ + env->tlb_need_flush = 0; + tlb_flush(CPU(cpu), 1); + break; + default: + /* XXX: TODO */ + cpu_abort(CPU(cpu), "Unknown MMU model %d\n", env->mmu_model); + break; + } +} + +void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) +{ +#if !defined(FLUSH_ALL_TLBS) + addr &= TARGET_PAGE_MASK; + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + case POWERPC_MMU_SOFT_74xx: + ppc6xx_tlb_invalidate_virt(env, addr, 0); + if (env->id_tlbs == 1) { + ppc6xx_tlb_invalidate_virt(env, addr, 1); + } + break; + case POWERPC_MMU_32B: + case POWERPC_MMU_601: + /* Actual CPUs invalidate entire congruence classes based on the + * geometry of their TLBs and some OSes take that into account, + * we just mark the TLB to be flushed later (context synchronizing + * event or sync instruction on 32-bit). + */ + env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH; + break; +#if defined(TARGET_PPC64) + case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06a: + case POWERPC_MMU_2_07: + case POWERPC_MMU_2_07a: + /* tlbie invalidate TLBs for all segments */ + /* XXX: given the fact that there are too many segments to invalidate, + * and we still don't have a tlb_flush_mask(env, n, mask) in QEMU, + * we just invalidate all TLBs + */ + env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH; + break; +#endif /* defined(TARGET_PPC64) */ + default: + /* Should never reach here with other MMU models */ + assert(0); + } +#else + ppc_tlb_invalidate_all(env); +#endif +} + +/*****************************************************************************/ +/* Special registers manipulation */ +void ppc_store_sdr1(CPUPPCState *env, target_ulong value) +{ + qemu_log_mask(CPU_LOG_MMU, "%s: " TARGET_FMT_lx "\n", __func__, value); + assert(!env->external_htab); + env->spr[SPR_SDR1] = value; +#if defined(TARGET_PPC64) + if (env->mmu_model & POWERPC_MMU_64) { + PowerPCCPU *cpu = ppc_env_get_cpu(env); + Error *local_err = NULL; + + ppc_hash64_set_sdr1(cpu, value, &local_err); + if (local_err) { + error_report_err(local_err); + error_free(local_err); + } + } else +#endif /* defined(TARGET_PPC64) */ + { + /* FIXME: Should check for valid HTABMASK values */ + env->htab_mask = ((value & SDR_32_HTABMASK) << 16) | 0xFFFF; + env->htab_base = value & SDR_32_HTABORG; + } +} + +/* Segment registers load and store */ +target_ulong helper_load_sr(CPUPPCState *env, target_ulong sr_num) +{ +#if defined(TARGET_PPC64) + if (env->mmu_model & POWERPC_MMU_64) { + /* XXX */ + return 0; + } +#endif + return env->sr[sr_num]; +} + +void helper_store_sr(CPUPPCState *env, target_ulong srnum, target_ulong value) +{ + qemu_log_mask(CPU_LOG_MMU, + "%s: reg=%d " TARGET_FMT_lx " " TARGET_FMT_lx "\n", __func__, + (int)srnum, value, env->sr[srnum]); +#if defined(TARGET_PPC64) + if (env->mmu_model & POWERPC_MMU_64) { + PowerPCCPU *cpu = ppc_env_get_cpu(env); + uint64_t esid, vsid; + + /* ESID = srnum */ + esid = ((uint64_t)(srnum & 0xf) << 28) | SLB_ESID_V; + + /* VSID = VSID */ + vsid = (value & 0xfffffff) << 12; + /* flags = flags */ + vsid |= ((value >> 27) & 0xf) << 8; + + ppc_store_slb(cpu, srnum, esid, vsid); + } else +#endif + if (env->sr[srnum] != value) { + env->sr[srnum] = value; +/* Invalidating 256MB of virtual memory in 4kB pages is way longer than + flusing the whole TLB. */ +#if !defined(FLUSH_ALL_TLBS) && 0 + { + target_ulong page, end; + /* Invalidate 256 MB of virtual memory */ + page = (16 << 20) * srnum; + end = page + (16 << 20); + for (; page != end; page += TARGET_PAGE_SIZE) { + tlb_flush_page(CPU(cpu), page); + } + } +#else + env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH; +#endif + } +} + +/* TLB management */ +void helper_tlbia(CPUPPCState *env) +{ + ppc_tlb_invalidate_all(env); +} + +void helper_tlbie(CPUPPCState *env, target_ulong addr) +{ + ppc_tlb_invalidate_one(env, addr); +} + +void helper_tlbiva(CPUPPCState *env, target_ulong addr) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + /* tlbiva instruction only exists on BookE */ + assert(env->mmu_model == POWERPC_MMU_BOOKE); + /* XXX: TODO */ + cpu_abort(CPU(cpu), "BookE MMU model is not implemented\n"); +} + +/* Software driven TLBs management */ +/* PowerPC 602/603 software TLB load instructions helpers */ +static void do_6xx_tlb(CPUPPCState *env, target_ulong new_EPN, int is_code) +{ + target_ulong RPN, CMP, EPN; + int way; + + RPN = env->spr[SPR_RPA]; + if (is_code) { + CMP = env->spr[SPR_ICMP]; + EPN = env->spr[SPR_IMISS]; + } else { + CMP = env->spr[SPR_DCMP]; + EPN = env->spr[SPR_DMISS]; + } + way = (env->spr[SPR_SRR1] >> 17) & 1; + (void)EPN; /* avoid a compiler warning */ + LOG_SWTLB("%s: EPN " TARGET_FMT_lx " " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx + " PTE1 " TARGET_FMT_lx " way %d\n", __func__, new_EPN, EPN, CMP, + RPN, way); + /* Store this TLB */ + ppc6xx_tlb_store(env, (uint32_t)(new_EPN & TARGET_PAGE_MASK), + way, is_code, CMP, RPN); +} + +void helper_6xx_tlbd(CPUPPCState *env, target_ulong EPN) +{ + do_6xx_tlb(env, EPN, 0); +} + +void helper_6xx_tlbi(CPUPPCState *env, target_ulong EPN) +{ + do_6xx_tlb(env, EPN, 1); +} + +/* PowerPC 74xx software TLB load instructions helpers */ +static void do_74xx_tlb(CPUPPCState *env, target_ulong new_EPN, int is_code) +{ + target_ulong RPN, CMP, EPN; + int way; + + RPN = env->spr[SPR_PTELO]; + CMP = env->spr[SPR_PTEHI]; + EPN = env->spr[SPR_TLBMISS] & ~0x3; + way = env->spr[SPR_TLBMISS] & 0x3; + (void)EPN; /* avoid a compiler warning */ + LOG_SWTLB("%s: EPN " TARGET_FMT_lx " " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx + " PTE1 " TARGET_FMT_lx " way %d\n", __func__, new_EPN, EPN, CMP, + RPN, way); + /* Store this TLB */ + ppc6xx_tlb_store(env, (uint32_t)(new_EPN & TARGET_PAGE_MASK), + way, is_code, CMP, RPN); +} + +void helper_74xx_tlbd(CPUPPCState *env, target_ulong EPN) +{ + do_74xx_tlb(env, EPN, 0); +} + +void helper_74xx_tlbi(CPUPPCState *env, target_ulong EPN) +{ + do_74xx_tlb(env, EPN, 1); +} + +/*****************************************************************************/ +/* PowerPC 601 specific instructions (POWER bridge) */ + +target_ulong helper_rac(CPUPPCState *env, target_ulong addr) +{ + mmu_ctx_t ctx; + int nb_BATs; + target_ulong ret = 0; + + /* We don't have to generate many instances of this instruction, + * as rac is supervisor only. + */ + /* XXX: FIX THIS: Pretend we have no BAT */ + nb_BATs = env->nb_BATs; + env->nb_BATs = 0; + if (get_physical_address(env, &ctx, addr, 0, ACCESS_INT) == 0) { + ret = ctx.raddr; + } + env->nb_BATs = nb_BATs; + return ret; +} + +static inline target_ulong booke_tlb_to_page_size(int size) +{ + return 1024 << (2 * size); +} + +static inline int booke_page_size_to_tlb(target_ulong page_size) +{ + int size; + + switch (page_size) { + case 0x00000400UL: + size = 0x0; + break; + case 0x00001000UL: + size = 0x1; + break; + case 0x00004000UL: + size = 0x2; + break; + case 0x00010000UL: + size = 0x3; + break; + case 0x00040000UL: + size = 0x4; + break; + case 0x00100000UL: + size = 0x5; + break; + case 0x00400000UL: + size = 0x6; + break; + case 0x01000000UL: + size = 0x7; + break; + case 0x04000000UL: + size = 0x8; + break; + case 0x10000000UL: + size = 0x9; + break; + case 0x40000000UL: + size = 0xA; + break; +#if defined(TARGET_PPC64) + case 0x000100000000ULL: + size = 0xB; + break; + case 0x000400000000ULL: + size = 0xC; + break; + case 0x001000000000ULL: + size = 0xD; + break; + case 0x004000000000ULL: + size = 0xE; + break; + case 0x010000000000ULL: + size = 0xF; + break; +#endif + default: + size = -1; + break; + } + + return size; +} + +/* Helpers for 4xx TLB management */ +#define PPC4XX_TLB_ENTRY_MASK 0x0000003f /* Mask for 64 TLB entries */ + +#define PPC4XX_TLBHI_V 0x00000040 +#define PPC4XX_TLBHI_E 0x00000020 +#define PPC4XX_TLBHI_SIZE_MIN 0 +#define PPC4XX_TLBHI_SIZE_MAX 7 +#define PPC4XX_TLBHI_SIZE_DEFAULT 1 +#define PPC4XX_TLBHI_SIZE_SHIFT 7 +#define PPC4XX_TLBHI_SIZE_MASK 0x00000007 + +#define PPC4XX_TLBLO_EX 0x00000200 +#define PPC4XX_TLBLO_WR 0x00000100 +#define PPC4XX_TLBLO_ATTR_MASK 0x000000FF +#define PPC4XX_TLBLO_RPN_MASK 0xFFFFFC00 + +target_ulong helper_4xx_tlbre_hi(CPUPPCState *env, target_ulong entry) +{ + ppcemb_tlb_t *tlb; + target_ulong ret; + int size; + + entry &= PPC4XX_TLB_ENTRY_MASK; + tlb = &env->tlb.tlbe[entry]; + ret = tlb->EPN; + if (tlb->prot & PAGE_VALID) { + ret |= PPC4XX_TLBHI_V; + } + size = booke_page_size_to_tlb(tlb->size); + if (size < PPC4XX_TLBHI_SIZE_MIN || size > PPC4XX_TLBHI_SIZE_MAX) { + size = PPC4XX_TLBHI_SIZE_DEFAULT; + } + ret |= size << PPC4XX_TLBHI_SIZE_SHIFT; + env->spr[SPR_40x_PID] = tlb->PID; + return ret; +} + +target_ulong helper_4xx_tlbre_lo(CPUPPCState *env, target_ulong entry) +{ + ppcemb_tlb_t *tlb; + target_ulong ret; + + entry &= PPC4XX_TLB_ENTRY_MASK; + tlb = &env->tlb.tlbe[entry]; + ret = tlb->RPN; + if (tlb->prot & PAGE_EXEC) { + ret |= PPC4XX_TLBLO_EX; + } + if (tlb->prot & PAGE_WRITE) { + ret |= PPC4XX_TLBLO_WR; + } + return ret; +} + +void helper_4xx_tlbwe_hi(CPUPPCState *env, target_ulong entry, + target_ulong val) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + CPUState *cs = CPU(cpu); + ppcemb_tlb_t *tlb; + target_ulong page, end; + + LOG_SWTLB("%s entry %d val " TARGET_FMT_lx "\n", __func__, (int)entry, + val); + entry &= PPC4XX_TLB_ENTRY_MASK; + tlb = &env->tlb.tlbe[entry]; + /* Invalidate previous TLB (if it's valid) */ + if (tlb->prot & PAGE_VALID) { + end = tlb->EPN + tlb->size; + LOG_SWTLB("%s: invalidate old TLB %d start " TARGET_FMT_lx " end " + TARGET_FMT_lx "\n", __func__, (int)entry, tlb->EPN, end); + for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) { + tlb_flush_page(cs, page); + } + } + tlb->size = booke_tlb_to_page_size((val >> PPC4XX_TLBHI_SIZE_SHIFT) + & PPC4XX_TLBHI_SIZE_MASK); + /* We cannot handle TLB size < TARGET_PAGE_SIZE. + * If this ever occurs, one should use the ppcemb target instead + * of the ppc or ppc64 one + */ + if ((val & PPC4XX_TLBHI_V) && tlb->size < TARGET_PAGE_SIZE) { + cpu_abort(cs, "TLB size " TARGET_FMT_lu " < %u " + "are not supported (%d)\n", + tlb->size, TARGET_PAGE_SIZE, (int)((val >> 7) & 0x7)); + } + tlb->EPN = val & ~(tlb->size - 1); + if (val & PPC4XX_TLBHI_V) { + tlb->prot |= PAGE_VALID; + if (val & PPC4XX_TLBHI_E) { + /* XXX: TO BE FIXED */ + cpu_abort(cs, + "Little-endian TLB entries are not supported by now\n"); + } + } else { + tlb->prot &= ~PAGE_VALID; + } + tlb->PID = env->spr[SPR_40x_PID]; /* PID */ + LOG_SWTLB("%s: set up TLB %d RPN " TARGET_FMT_plx " EPN " TARGET_FMT_lx + " size " TARGET_FMT_lx " prot %c%c%c%c PID %d\n", __func__, + (int)entry, tlb->RPN, tlb->EPN, tlb->size, + tlb->prot & PAGE_READ ? 'r' : '-', + tlb->prot & PAGE_WRITE ? 'w' : '-', + tlb->prot & PAGE_EXEC ? 'x' : '-', + tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID); + /* Invalidate new TLB (if valid) */ + if (tlb->prot & PAGE_VALID) { + end = tlb->EPN + tlb->size; + LOG_SWTLB("%s: invalidate TLB %d start " TARGET_FMT_lx " end " + TARGET_FMT_lx "\n", __func__, (int)entry, tlb->EPN, end); + for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) { + tlb_flush_page(cs, page); + } + } +} + +void helper_4xx_tlbwe_lo(CPUPPCState *env, target_ulong entry, + target_ulong val) +{ + ppcemb_tlb_t *tlb; + + LOG_SWTLB("%s entry %i val " TARGET_FMT_lx "\n", __func__, (int)entry, + val); + entry &= PPC4XX_TLB_ENTRY_MASK; + tlb = &env->tlb.tlbe[entry]; + tlb->attr = val & PPC4XX_TLBLO_ATTR_MASK; + tlb->RPN = val & PPC4XX_TLBLO_RPN_MASK; + tlb->prot = PAGE_READ; + if (val & PPC4XX_TLBLO_EX) { + tlb->prot |= PAGE_EXEC; + } + if (val & PPC4XX_TLBLO_WR) { + tlb->prot |= PAGE_WRITE; + } + LOG_SWTLB("%s: set up TLB %d RPN " TARGET_FMT_plx " EPN " TARGET_FMT_lx + " size " TARGET_FMT_lx " prot %c%c%c%c PID %d\n", __func__, + (int)entry, tlb->RPN, tlb->EPN, tlb->size, + tlb->prot & PAGE_READ ? 'r' : '-', + tlb->prot & PAGE_WRITE ? 'w' : '-', + tlb->prot & PAGE_EXEC ? 'x' : '-', + tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID); +} + +target_ulong helper_4xx_tlbsx(CPUPPCState *env, target_ulong address) +{ + return ppcemb_tlb_search(env, address, env->spr[SPR_40x_PID]); +} + +/* PowerPC 440 TLB management */ +void helper_440_tlbwe(CPUPPCState *env, uint32_t word, target_ulong entry, + target_ulong value) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + ppcemb_tlb_t *tlb; + target_ulong EPN, RPN, size; + int do_flush_tlbs; + + LOG_SWTLB("%s word %d entry %d value " TARGET_FMT_lx "\n", + __func__, word, (int)entry, value); + do_flush_tlbs = 0; + entry &= 0x3F; + tlb = &env->tlb.tlbe[entry]; + switch (word) { + default: + /* Just here to please gcc */ + case 0: + EPN = value & 0xFFFFFC00; + if ((tlb->prot & PAGE_VALID) && EPN != tlb->EPN) { + do_flush_tlbs = 1; + } + tlb->EPN = EPN; + size = booke_tlb_to_page_size((value >> 4) & 0xF); + if ((tlb->prot & PAGE_VALID) && tlb->size < size) { + do_flush_tlbs = 1; + } + tlb->size = size; + tlb->attr &= ~0x1; + tlb->attr |= (value >> 8) & 1; + if (value & 0x200) { + tlb->prot |= PAGE_VALID; + } else { + if (tlb->prot & PAGE_VALID) { + tlb->prot &= ~PAGE_VALID; + do_flush_tlbs = 1; + } + } + tlb->PID = env->spr[SPR_440_MMUCR] & 0x000000FF; + if (do_flush_tlbs) { + tlb_flush(CPU(cpu), 1); + } + break; + case 1: + RPN = value & 0xFFFFFC0F; + if ((tlb->prot & PAGE_VALID) && tlb->RPN != RPN) { + tlb_flush(CPU(cpu), 1); + } + tlb->RPN = RPN; + break; + case 2: + tlb->attr = (tlb->attr & 0x1) | (value & 0x0000FF00); + tlb->prot = tlb->prot & PAGE_VALID; + if (value & 0x1) { + tlb->prot |= PAGE_READ << 4; + } + if (value & 0x2) { + tlb->prot |= PAGE_WRITE << 4; + } + if (value & 0x4) { + tlb->prot |= PAGE_EXEC << 4; + } + if (value & 0x8) { + tlb->prot |= PAGE_READ; + } + if (value & 0x10) { + tlb->prot |= PAGE_WRITE; + } + if (value & 0x20) { + tlb->prot |= PAGE_EXEC; + } + break; + } +} + +target_ulong helper_440_tlbre(CPUPPCState *env, uint32_t word, + target_ulong entry) +{ + ppcemb_tlb_t *tlb; + target_ulong ret; + int size; + + entry &= 0x3F; + tlb = &env->tlb.tlbe[entry]; + switch (word) { + default: + /* Just here to please gcc */ + case 0: + ret = tlb->EPN; + size = booke_page_size_to_tlb(tlb->size); + if (size < 0 || size > 0xF) { + size = 1; + } + ret |= size << 4; + if (tlb->attr & 0x1) { + ret |= 0x100; + } + if (tlb->prot & PAGE_VALID) { + ret |= 0x200; + } + env->spr[SPR_440_MMUCR] &= ~0x000000FF; + env->spr[SPR_440_MMUCR] |= tlb->PID; + break; + case 1: + ret = tlb->RPN; + break; + case 2: + ret = tlb->attr & ~0x1; + if (tlb->prot & (PAGE_READ << 4)) { + ret |= 0x1; + } + if (tlb->prot & (PAGE_WRITE << 4)) { + ret |= 0x2; + } + if (tlb->prot & (PAGE_EXEC << 4)) { + ret |= 0x4; + } + if (tlb->prot & PAGE_READ) { + ret |= 0x8; + } + if (tlb->prot & PAGE_WRITE) { + ret |= 0x10; + } + if (tlb->prot & PAGE_EXEC) { + ret |= 0x20; + } + break; + } + return ret; +} + +target_ulong helper_440_tlbsx(CPUPPCState *env, target_ulong address) +{ + return ppcemb_tlb_search(env, address, env->spr[SPR_440_MMUCR] & 0xFF); +} + +/* PowerPC BookE 2.06 TLB management */ + +static ppcmas_tlb_t *booke206_cur_tlb(CPUPPCState *env) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + uint32_t tlbncfg = 0; + int esel = (env->spr[SPR_BOOKE_MAS0] & MAS0_ESEL_MASK) >> MAS0_ESEL_SHIFT; + int ea = (env->spr[SPR_BOOKE_MAS2] & MAS2_EPN_MASK); + int tlb; + + tlb = (env->spr[SPR_BOOKE_MAS0] & MAS0_TLBSEL_MASK) >> MAS0_TLBSEL_SHIFT; + tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlb]; + + if ((tlbncfg & TLBnCFG_HES) && (env->spr[SPR_BOOKE_MAS0] & MAS0_HES)) { + cpu_abort(CPU(cpu), "we don't support HES yet\n"); + } + + return booke206_get_tlbm(env, tlb, ea, esel); +} + +void helper_booke_setpid(CPUPPCState *env, uint32_t pidn, target_ulong pid) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + env->spr[pidn] = pid; + /* changing PIDs mean we're in a different address space now */ + tlb_flush(CPU(cpu), 1); +} + +void helper_booke206_tlbwe(CPUPPCState *env) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + uint32_t tlbncfg, tlbn; + ppcmas_tlb_t *tlb; + uint32_t size_tlb, size_ps; + target_ulong mask; + + + switch (env->spr[SPR_BOOKE_MAS0] & MAS0_WQ_MASK) { + case MAS0_WQ_ALWAYS: + /* good to go, write that entry */ + break; + case MAS0_WQ_COND: + /* XXX check if reserved */ + if (0) { + return; + } + break; + case MAS0_WQ_CLR_RSRV: + /* XXX clear entry */ + return; + default: + /* no idea what to do */ + return; + } + + if (((env->spr[SPR_BOOKE_MAS0] & MAS0_ATSEL) == MAS0_ATSEL_LRAT) && + !msr_gs) { + /* XXX we don't support direct LRAT setting yet */ + fprintf(stderr, "cpu: don't support LRAT setting yet\n"); + return; + } + + tlbn = (env->spr[SPR_BOOKE_MAS0] & MAS0_TLBSEL_MASK) >> MAS0_TLBSEL_SHIFT; + tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlbn]; + + tlb = booke206_cur_tlb(env); + + if (!tlb) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | + POWERPC_EXCP_INVAL_INVAL, GETPC()); + } + + /* check that we support the targeted size */ + size_tlb = (env->spr[SPR_BOOKE_MAS1] & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; + size_ps = booke206_tlbnps(env, tlbn); + if ((env->spr[SPR_BOOKE_MAS1] & MAS1_VALID) && (tlbncfg & TLBnCFG_AVAIL) && + !(size_ps & (1 << size_tlb))) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | + POWERPC_EXCP_INVAL_INVAL, GETPC()); + } + + if (msr_gs) { + cpu_abort(CPU(cpu), "missing HV implementation\n"); + } + tlb->mas7_3 = ((uint64_t)env->spr[SPR_BOOKE_MAS7] << 32) | + env->spr[SPR_BOOKE_MAS3]; + tlb->mas1 = env->spr[SPR_BOOKE_MAS1]; + + /* MAV 1.0 only */ + if (!(tlbncfg & TLBnCFG_AVAIL)) { + /* force !AVAIL TLB entries to correct page size */ + tlb->mas1 &= ~MAS1_TSIZE_MASK; + /* XXX can be configured in MMUCSR0 */ + tlb->mas1 |= (tlbncfg & TLBnCFG_MINSIZE) >> 12; + } + + /* Make a mask from TLB size to discard invalid bits in EPN field */ + mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); + /* Add a mask for page attributes */ + mask |= MAS2_ACM | MAS2_VLE | MAS2_W | MAS2_I | MAS2_M | MAS2_G | MAS2_E; + + if (!msr_cm) { + /* Executing a tlbwe instruction in 32-bit mode will set + * bits 0:31 of the TLB EPN field to zero. + */ + mask &= 0xffffffff; + } + + tlb->mas2 = env->spr[SPR_BOOKE_MAS2] & mask; + + if (!(tlbncfg & TLBnCFG_IPROT)) { + /* no IPROT supported by TLB */ + tlb->mas1 &= ~MAS1_IPROT; + } + + if (booke206_tlb_to_page_size(env, tlb) == TARGET_PAGE_SIZE) { + tlb_flush_page(CPU(cpu), tlb->mas2 & MAS2_EPN_MASK); + } else { + tlb_flush(CPU(cpu), 1); + } +} + +static inline void booke206_tlb_to_mas(CPUPPCState *env, ppcmas_tlb_t *tlb) +{ + int tlbn = booke206_tlbm_to_tlbn(env, tlb); + int way = booke206_tlbm_to_way(env, tlb); + + env->spr[SPR_BOOKE_MAS0] = tlbn << MAS0_TLBSEL_SHIFT; + env->spr[SPR_BOOKE_MAS0] |= way << MAS0_ESEL_SHIFT; + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; + + env->spr[SPR_BOOKE_MAS1] = tlb->mas1; + env->spr[SPR_BOOKE_MAS2] = tlb->mas2; + env->spr[SPR_BOOKE_MAS3] = tlb->mas7_3; + env->spr[SPR_BOOKE_MAS7] = tlb->mas7_3 >> 32; +} + +void helper_booke206_tlbre(CPUPPCState *env) +{ + ppcmas_tlb_t *tlb = NULL; + + tlb = booke206_cur_tlb(env); + if (!tlb) { + env->spr[SPR_BOOKE_MAS1] = 0; + } else { + booke206_tlb_to_mas(env, tlb); + } +} + +void helper_booke206_tlbsx(CPUPPCState *env, target_ulong address) +{ + ppcmas_tlb_t *tlb = NULL; + int i, j; + hwaddr raddr; + uint32_t spid, sas; + + spid = (env->spr[SPR_BOOKE_MAS6] & MAS6_SPID_MASK) >> MAS6_SPID_SHIFT; + sas = env->spr[SPR_BOOKE_MAS6] & MAS6_SAS; + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + int ways = booke206_tlb_ways(env, i); + + for (j = 0; j < ways; j++) { + tlb = booke206_get_tlbm(env, i, address, j); + + if (!tlb) { + continue; + } + + if (ppcmas_tlb_check(env, tlb, &raddr, address, spid)) { + continue; + } + + if (sas != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + continue; + } + + booke206_tlb_to_mas(env, tlb); + return; + } + } + + /* no entry found, fill with defaults */ + env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; + env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK; + env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK; + env->spr[SPR_BOOKE_MAS3] = 0; + env->spr[SPR_BOOKE_MAS7] = 0; + + if (env->spr[SPR_BOOKE_MAS6] & MAS6_SAS) { + env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; + } + + env->spr[SPR_BOOKE_MAS1] |= (env->spr[SPR_BOOKE_MAS6] >> 16) + << MAS1_TID_SHIFT; + + /* next victim logic */ + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT; + env->last_way++; + env->last_way &= booke206_tlb_ways(env, 0) - 1; + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; +} + +static inline void booke206_invalidate_ea_tlb(CPUPPCState *env, int tlbn, + uint32_t ea) +{ + int i; + int ways = booke206_tlb_ways(env, tlbn); + target_ulong mask; + + for (i = 0; i < ways; i++) { + ppcmas_tlb_t *tlb = booke206_get_tlbm(env, tlbn, ea, i); + if (!tlb) { + continue; + } + mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); + if (((tlb->mas2 & MAS2_EPN_MASK) == (ea & mask)) && + !(tlb->mas1 & MAS1_IPROT)) { + tlb->mas1 &= ~MAS1_VALID; + } + } +} + +void helper_booke206_tlbivax(CPUPPCState *env, target_ulong address) +{ + CPUState *cs; + + if (address & 0x4) { + /* flush all entries */ + if (address & 0x8) { + /* flush all of TLB1 */ + booke206_flush_tlb(env, BOOKE206_FLUSH_TLB1, 1); + } else { + /* flush all of TLB0 */ + booke206_flush_tlb(env, BOOKE206_FLUSH_TLB0, 0); + } + return; + } + + if (address & 0x8) { + /* flush TLB1 entries */ + booke206_invalidate_ea_tlb(env, 1, address); + CPU_FOREACH(cs) { + tlb_flush(cs, 1); + } + } else { + /* flush TLB0 entries */ + booke206_invalidate_ea_tlb(env, 0, address); + CPU_FOREACH(cs) { + tlb_flush_page(cs, address & MAS2_EPN_MASK); + } + } +} + +void helper_booke206_tlbilx0(CPUPPCState *env, target_ulong address) +{ + /* XXX missing LPID handling */ + booke206_flush_tlb(env, -1, 1); +} + +void helper_booke206_tlbilx1(CPUPPCState *env, target_ulong address) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + int i, j; + int tid = (env->spr[SPR_BOOKE_MAS6] & MAS6_SPID); + ppcmas_tlb_t *tlb = env->tlb.tlbm; + int tlb_size; + + /* XXX missing LPID handling */ + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + tlb_size = booke206_tlb_size(env, i); + for (j = 0; j < tlb_size; j++) { + if (!(tlb[j].mas1 & MAS1_IPROT) && + ((tlb[j].mas1 & MAS1_TID_MASK) == tid)) { + tlb[j].mas1 &= ~MAS1_VALID; + } + } + tlb += booke206_tlb_size(env, i); + } + tlb_flush(CPU(cpu), 1); +} + +void helper_booke206_tlbilx3(CPUPPCState *env, target_ulong address) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + int i, j; + ppcmas_tlb_t *tlb; + int tid = (env->spr[SPR_BOOKE_MAS6] & MAS6_SPID); + int pid = tid >> MAS6_SPID_SHIFT; + int sgs = env->spr[SPR_BOOKE_MAS5] & MAS5_SGS; + int ind = (env->spr[SPR_BOOKE_MAS6] & MAS6_SIND) ? MAS1_IND : 0; + /* XXX check for unsupported isize and raise an invalid opcode then */ + int size = env->spr[SPR_BOOKE_MAS6] & MAS6_ISIZE_MASK; + /* XXX implement MAV2 handling */ + bool mav2 = false; + + /* XXX missing LPID handling */ + /* flush by pid and ea */ + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + int ways = booke206_tlb_ways(env, i); + + for (j = 0; j < ways; j++) { + tlb = booke206_get_tlbm(env, i, address, j); + if (!tlb) { + continue; + } + if ((ppcmas_tlb_check(env, tlb, NULL, address, pid) != 0) || + (tlb->mas1 & MAS1_IPROT) || + ((tlb->mas1 & MAS1_IND) != ind) || + ((tlb->mas8 & MAS8_TGS) != sgs)) { + continue; + } + if (mav2 && ((tlb->mas1 & MAS1_TSIZE_MASK) != size)) { + /* XXX only check when MMUCFG[TWC] || TLBnCFG[HES] */ + continue; + } + /* XXX e500mc doesn't match SAS, but other cores might */ + tlb->mas1 &= ~MAS1_VALID; + } + } + tlb_flush(CPU(cpu), 1); +} + +void helper_booke206_tlbflush(CPUPPCState *env, target_ulong type) +{ + int flags = 0; + + if (type & 2) { + flags |= BOOKE206_FLUSH_TLB1; + } + + if (type & 4) { + flags |= BOOKE206_FLUSH_TLB0; + } + + booke206_flush_tlb(env, flags, 1); +} + + +void helper_check_tlb_flush_local(CPUPPCState *env) +{ + check_tlb_flush(env, false); +} + +void helper_check_tlb_flush_global(CPUPPCState *env) +{ + check_tlb_flush(env, true); +} + +/*****************************************************************************/ + +/* 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, MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); + CPUPPCState *env = &cpu->env; + int ret; + + if (pcc->handle_mmu_fault) { + ret = pcc->handle_mmu_fault(cpu, addr, access_type, mmu_idx); + } else { + ret = cpu_ppc_handle_mmu_fault(env, addr, access_type, mmu_idx); + } + if (unlikely(ret != 0)) { + raise_exception_err_ra(env, cs->exception_index, env->error_code, + retaddr); + } +} diff --git a/target/ppc/monitor.c b/target/ppc/monitor.c new file mode 100644 index 0000000000..c2d0806dd1 --- /dev/null +++ b/target/ppc/monitor.c @@ -0,0 +1,147 @@ +/* + * QEMU monitor + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "cpu.h" +#include "monitor/monitor.h" +#include "monitor/hmp-target.h" +#include "hmp.h" + +static target_long monitor_get_ccr (const struct MonitorDef *md, int val) +{ + CPUArchState *env = mon_get_cpu_env(); + unsigned int u; + int i; + + u = 0; + for (i = 0; i < 8; i++) + u |= env->crf[i] << (32 - (4 * (i + 1))); + + return u; +} + +static target_long monitor_get_decr (const struct MonitorDef *md, int val) +{ + CPUArchState *env = mon_get_cpu_env(); + return cpu_ppc_load_decr(env); +} + +static target_long monitor_get_tbu (const struct MonitorDef *md, int val) +{ + CPUArchState *env = mon_get_cpu_env(); + return cpu_ppc_load_tbu(env); +} + +static target_long monitor_get_tbl (const struct MonitorDef *md, int val) +{ + CPUArchState *env = mon_get_cpu_env(); + return cpu_ppc_load_tbl(env); +} + +void hmp_info_tlb(Monitor *mon, const QDict *qdict) +{ + CPUArchState *env1 = mon_get_cpu_env(); + + dump_mmu((FILE*)mon, (fprintf_function)monitor_printf, env1); +} + +const MonitorDef monitor_defs[] = { + { "fpscr", offsetof(CPUPPCState, fpscr) }, + /* Next instruction pointer */ + { "nip|pc", offsetof(CPUPPCState, nip) }, + { "lr", offsetof(CPUPPCState, lr) }, + { "ctr", offsetof(CPUPPCState, ctr) }, + { "decr", 0, &monitor_get_decr, }, + { "ccr|cr", 0, &monitor_get_ccr, }, + /* Machine state register */ + { "xer", offsetof(CPUPPCState, xer) }, + { "msr", offsetof(CPUPPCState, msr) }, + { "tbu", 0, &monitor_get_tbu, }, + { "tbl", 0, &monitor_get_tbl, }, + { NULL }, +}; + +const MonitorDef *target_monitor_defs(void) +{ + return monitor_defs; +} + +static int ppc_cpu_get_reg_num(const char *numstr, int maxnum, int *pregnum) +{ + int regnum; + char *endptr = NULL; + + if (!*numstr) { + return false; + } + + regnum = strtoul(numstr, &endptr, 10); + if (*endptr || (regnum >= maxnum)) { + return false; + } + *pregnum = regnum; + + return true; +} + +int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval) +{ + int i, regnum; + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + /* General purpose registers */ + if ((tolower(name[0]) == 'r') && + ppc_cpu_get_reg_num(name + 1, ARRAY_SIZE(env->gpr), ®num)) { + *pval = env->gpr[regnum]; + return 0; + } + + /* Floating point registers */ + if ((tolower(name[0]) == 'f') && + ppc_cpu_get_reg_num(name + 1, ARRAY_SIZE(env->fpr), ®num)) { + *pval = env->fpr[regnum]; + return 0; + } + + /* Special purpose registers */ + for (i = 0; i < ARRAY_SIZE(env->spr_cb); ++i) { + ppc_spr_t *spr = &env->spr_cb[i]; + + if (spr->name && (strcasecmp(name, spr->name) == 0)) { + *pval = env->spr[i]; + return 0; + } + } + + /* Segment registers */ +#if !defined(CONFIG_USER_ONLY) + if ((strncasecmp(name, "sr", 2) == 0) && + ppc_cpu_get_reg_num(name + 2, ARRAY_SIZE(env->sr), ®num)) { + *pval = env->sr[regnum]; + return 0; + } +#endif + + return -EINVAL; +} diff --git a/target/ppc/timebase_helper.c b/target/ppc/timebase_helper.c new file mode 100644 index 0000000000..73363e08ae --- /dev/null +++ b/target/ppc/timebase_helper.c @@ -0,0 +1,176 @@ +/* + * PowerPC emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * + * 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 "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "exec/exec-all.h" +#include "qemu/log.h" + +/*****************************************************************************/ +/* SPR accesses */ + +target_ulong helper_load_tbl(CPUPPCState *env) +{ + return (target_ulong)cpu_ppc_load_tbl(env); +} + +target_ulong helper_load_tbu(CPUPPCState *env) +{ + return cpu_ppc_load_tbu(env); +} + +target_ulong helper_load_atbl(CPUPPCState *env) +{ + return (target_ulong)cpu_ppc_load_atbl(env); +} + +target_ulong helper_load_atbu(CPUPPCState *env) +{ + return cpu_ppc_load_atbu(env); +} + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +target_ulong helper_load_purr(CPUPPCState *env) +{ + return (target_ulong)cpu_ppc_load_purr(env); +} +#endif + +target_ulong helper_load_601_rtcl(CPUPPCState *env) +{ + return cpu_ppc601_load_rtcl(env); +} + +target_ulong helper_load_601_rtcu(CPUPPCState *env) +{ + return cpu_ppc601_load_rtcu(env); +} + +#if !defined(CONFIG_USER_ONLY) +void helper_store_tbl(CPUPPCState *env, target_ulong val) +{ + cpu_ppc_store_tbl(env, val); +} + +void helper_store_tbu(CPUPPCState *env, target_ulong val) +{ + cpu_ppc_store_tbu(env, val); +} + +void helper_store_atbl(CPUPPCState *env, target_ulong val) +{ + cpu_ppc_store_atbl(env, val); +} + +void helper_store_atbu(CPUPPCState *env, target_ulong val) +{ + cpu_ppc_store_atbu(env, val); +} + +void helper_store_601_rtcl(CPUPPCState *env, target_ulong val) +{ + cpu_ppc601_store_rtcl(env, val); +} + +void helper_store_601_rtcu(CPUPPCState *env, target_ulong val) +{ + cpu_ppc601_store_rtcu(env, val); +} + +target_ulong helper_load_decr(CPUPPCState *env) +{ + return cpu_ppc_load_decr(env); +} + +void helper_store_decr(CPUPPCState *env, target_ulong val) +{ + cpu_ppc_store_decr(env, val); +} + +target_ulong helper_load_hdecr(CPUPPCState *env) +{ + return cpu_ppc_load_hdecr(env); +} + +void helper_store_hdecr(CPUPPCState *env, target_ulong val) +{ + cpu_ppc_store_hdecr(env, val); +} + +target_ulong helper_load_40x_pit(CPUPPCState *env) +{ + return load_40x_pit(env); +} + +void helper_store_40x_pit(CPUPPCState *env, target_ulong val) +{ + store_40x_pit(env, val); +} + +void helper_store_booke_tcr(CPUPPCState *env, target_ulong val) +{ + store_booke_tcr(env, val); +} + +void helper_store_booke_tsr(CPUPPCState *env, target_ulong val) +{ + store_booke_tsr(env, val); +} +#endif + +/*****************************************************************************/ +/* Embedded PowerPC specific helpers */ + +/* XXX: to be improved to check access rights when in user-mode */ +target_ulong helper_load_dcr(CPUPPCState *env, target_ulong dcrn) +{ + uint32_t val = 0; + + if (unlikely(env->dcr_env == NULL)) { + qemu_log_mask(LOG_GUEST_ERROR, "No DCR environment\n"); + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | + POWERPC_EXCP_INVAL_INVAL, GETPC()); + } else if (unlikely(ppc_dcr_read(env->dcr_env, + (uint32_t)dcrn, &val) != 0)) { + qemu_log_mask(LOG_GUEST_ERROR, "DCR read error %d %03x\n", + (uint32_t)dcrn, (uint32_t)dcrn); + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | + POWERPC_EXCP_PRIV_REG, GETPC()); + } + return val; +} + +void helper_store_dcr(CPUPPCState *env, target_ulong dcrn, target_ulong val) +{ + if (unlikely(env->dcr_env == NULL)) { + qemu_log_mask(LOG_GUEST_ERROR, "No DCR environment\n"); + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | + POWERPC_EXCP_INVAL_INVAL, GETPC()); + } else if (unlikely(ppc_dcr_write(env->dcr_env, (uint32_t)dcrn, + (uint32_t)val) != 0)) { + qemu_log_mask(LOG_GUEST_ERROR, "DCR write error %d %03x\n", + (uint32_t)dcrn, (uint32_t)dcrn); + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | + POWERPC_EXCP_PRIV_REG, GETPC()); + } +} diff --git a/target/ppc/trace-events b/target/ppc/trace-events new file mode 100644 index 0000000000..b666156114 --- /dev/null +++ b/target/ppc/trace-events @@ -0,0 +1,5 @@ +# See docs/tracing.txt for syntax documentation. + +# target/ppc/kvm.c +kvm_failed_spr_set(int str, const char *msg) "Warning: Unable to set SPR %d to KVM: %s" +kvm_failed_spr_get(int str, const char *msg) "Warning: Unable to retrieve SPR %d from KVM: %s" diff --git a/target/ppc/translate.c b/target/ppc/translate.c new file mode 100644 index 0000000000..59e9552d2b --- /dev/null +++ b/target/ppc/translate.c @@ -0,0 +1,7200 @@ +/* + * PowerPC emulation for qemu: main translation routines. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * + * 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 "qemu/osdep.h" +#include "cpu.h" +#include "internal.h" +#include "disas/disas.h" +#include "exec/exec-all.h" +#include "tcg-op.h" +#include "qemu/host-utils.h" +#include "exec/cpu_ldst.h" + +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" + +#include "trace-tcg.h" +#include "exec/log.h" + + +#define CPU_SINGLE_STEP 0x1 +#define CPU_BRANCH_STEP 0x2 +#define GDBSTUB_SINGLE_STEP 0x4 + +/* Include definitions for instructions classes and implementations flags */ +//#define PPC_DEBUG_DISAS +//#define DO_PPC_STATISTICS + +#ifdef PPC_DEBUG_DISAS +# define LOG_DISAS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__) +#else +# define LOG_DISAS(...) do { } while (0) +#endif +/*****************************************************************************/ +/* Code translation helpers */ + +/* global register indexes */ +static TCGv_env cpu_env; +static char cpu_reg_names[10*3 + 22*4 /* GPR */ + + 10*4 + 22*5 /* SPE GPRh */ + + 10*4 + 22*5 /* FPR */ + + 2*(10*6 + 22*7) /* AVRh, AVRl */ + + 10*5 + 22*6 /* VSR */ + + 8*5 /* CRF */]; +static TCGv cpu_gpr[32]; +static TCGv cpu_gprh[32]; +static TCGv_i64 cpu_fpr[32]; +static TCGv_i64 cpu_avrh[32], cpu_avrl[32]; +static TCGv_i64 cpu_vsr[32]; +static TCGv_i32 cpu_crf[8]; +static TCGv cpu_nip; +static TCGv cpu_msr; +static TCGv cpu_ctr; +static TCGv cpu_lr; +#if defined(TARGET_PPC64) +static TCGv cpu_cfar; +#endif +static TCGv cpu_xer, cpu_so, cpu_ov, cpu_ca; +static TCGv cpu_reserve; +static TCGv cpu_fpscr; +static TCGv_i32 cpu_access_type; + +#include "exec/gen-icount.h" + +void ppc_translate_init(void) +{ + int i; + char* p; + size_t cpu_reg_names_size; + static int done_init = 0; + + if (done_init) + return; + + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + tcg_ctx.tcg_env = cpu_env; + + p = cpu_reg_names; + cpu_reg_names_size = sizeof(cpu_reg_names); + + for (i = 0; i < 8; i++) { + snprintf(p, cpu_reg_names_size, "crf%d", i); + cpu_crf[i] = tcg_global_mem_new_i32(cpu_env, + offsetof(CPUPPCState, crf[i]), p); + p += 5; + cpu_reg_names_size -= 5; + } + + for (i = 0; i < 32; i++) { + snprintf(p, cpu_reg_names_size, "r%d", i); + cpu_gpr[i] = tcg_global_mem_new(cpu_env, + offsetof(CPUPPCState, gpr[i]), p); + p += (i < 10) ? 3 : 4; + cpu_reg_names_size -= (i < 10) ? 3 : 4; + snprintf(p, cpu_reg_names_size, "r%dH", i); + cpu_gprh[i] = tcg_global_mem_new(cpu_env, + offsetof(CPUPPCState, gprh[i]), p); + p += (i < 10) ? 4 : 5; + cpu_reg_names_size -= (i < 10) ? 4 : 5; + + snprintf(p, cpu_reg_names_size, "fp%d", i); + cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUPPCState, fpr[i]), p); + p += (i < 10) ? 4 : 5; + cpu_reg_names_size -= (i < 10) ? 4 : 5; + + snprintf(p, cpu_reg_names_size, "avr%dH", i); +#ifdef HOST_WORDS_BIGENDIAN + cpu_avrh[i] = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUPPCState, avr[i].u64[0]), p); +#else + cpu_avrh[i] = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUPPCState, avr[i].u64[1]), p); +#endif + p += (i < 10) ? 6 : 7; + cpu_reg_names_size -= (i < 10) ? 6 : 7; + + snprintf(p, cpu_reg_names_size, "avr%dL", i); +#ifdef HOST_WORDS_BIGENDIAN + cpu_avrl[i] = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUPPCState, avr[i].u64[1]), p); +#else + cpu_avrl[i] = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUPPCState, avr[i].u64[0]), p); +#endif + p += (i < 10) ? 6 : 7; + cpu_reg_names_size -= (i < 10) ? 6 : 7; + snprintf(p, cpu_reg_names_size, "vsr%d", i); + cpu_vsr[i] = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUPPCState, vsr[i]), p); + p += (i < 10) ? 5 : 6; + cpu_reg_names_size -= (i < 10) ? 5 : 6; + } + + cpu_nip = tcg_global_mem_new(cpu_env, + offsetof(CPUPPCState, nip), "nip"); + + cpu_msr = tcg_global_mem_new(cpu_env, + offsetof(CPUPPCState, msr), "msr"); + + cpu_ctr = tcg_global_mem_new(cpu_env, + offsetof(CPUPPCState, ctr), "ctr"); + + cpu_lr = tcg_global_mem_new(cpu_env, + offsetof(CPUPPCState, lr), "lr"); + +#if defined(TARGET_PPC64) + cpu_cfar = tcg_global_mem_new(cpu_env, + offsetof(CPUPPCState, cfar), "cfar"); +#endif + + cpu_xer = tcg_global_mem_new(cpu_env, + offsetof(CPUPPCState, xer), "xer"); + cpu_so = tcg_global_mem_new(cpu_env, + offsetof(CPUPPCState, so), "SO"); + cpu_ov = tcg_global_mem_new(cpu_env, + offsetof(CPUPPCState, ov), "OV"); + cpu_ca = tcg_global_mem_new(cpu_env, + offsetof(CPUPPCState, ca), "CA"); + + cpu_reserve = tcg_global_mem_new(cpu_env, + offsetof(CPUPPCState, reserve_addr), + "reserve_addr"); + + cpu_fpscr = tcg_global_mem_new(cpu_env, + offsetof(CPUPPCState, fpscr), "fpscr"); + + cpu_access_type = tcg_global_mem_new_i32(cpu_env, + offsetof(CPUPPCState, access_type), "access_type"); + + done_init = 1; +} + +/* internal defines */ +struct DisasContext { + struct TranslationBlock *tb; + target_ulong nip; + uint32_t opcode; + uint32_t exception; + /* Routine used to access memory */ + bool pr, hv, dr, le_mode; + bool lazy_tlb_flush; + bool need_access_type; + int mem_idx; + int access_type; + /* Translation flags */ + TCGMemOp default_tcg_memop_mask; +#if defined(TARGET_PPC64) + bool sf_mode; + bool has_cfar; +#endif + bool fpu_enabled; + bool altivec_enabled; + bool vsx_enabled; + bool spe_enabled; + bool tm_enabled; + ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */ + int singlestep_enabled; + uint64_t insns_flags; + uint64_t insns_flags2; +}; + +/* Return true iff byteswap is needed in a scalar memop */ +static inline bool need_byteswap(const DisasContext *ctx) +{ +#if defined(TARGET_WORDS_BIGENDIAN) + return ctx->le_mode; +#else + return !ctx->le_mode; +#endif +} + +/* True when active word size < size of target_long. */ +#ifdef TARGET_PPC64 +# define NARROW_MODE(C) (!(C)->sf_mode) +#else +# define NARROW_MODE(C) 0 +#endif + +struct opc_handler_t { + /* invalid bits for instruction 1 (Rc(opcode) == 0) */ + uint32_t inval1; + /* invalid bits for instruction 2 (Rc(opcode) == 1) */ + uint32_t inval2; + /* instruction type */ + uint64_t type; + /* extended instruction type */ + uint64_t type2; + /* handler */ + void (*handler)(DisasContext *ctx); +#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU) + const char *oname; +#endif +#if defined(DO_PPC_STATISTICS) + uint64_t count; +#endif +}; + +static inline void gen_set_access_type(DisasContext *ctx, int access_type) +{ + if (ctx->need_access_type && ctx->access_type != access_type) { + tcg_gen_movi_i32(cpu_access_type, access_type); + ctx->access_type = access_type; + } +} + +static inline void gen_update_nip(DisasContext *ctx, target_ulong nip) +{ + if (NARROW_MODE(ctx)) { + nip = (uint32_t)nip; + } + tcg_gen_movi_tl(cpu_nip, nip); +} + +static void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error) +{ + TCGv_i32 t0, t1; + + /* These are all synchronous exceptions, we set the PC back to + * the faulting instruction + */ + if (ctx->exception == POWERPC_EXCP_NONE) { + gen_update_nip(ctx, ctx->nip - 4); + } + t0 = tcg_const_i32(excp); + t1 = tcg_const_i32(error); + gen_helper_raise_exception_err(cpu_env, t0, t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + ctx->exception = (excp); +} + +static void gen_exception(DisasContext *ctx, uint32_t excp) +{ + TCGv_i32 t0; + + /* These are all synchronous exceptions, we set the PC back to + * the faulting instruction + */ + if (ctx->exception == POWERPC_EXCP_NONE) { + gen_update_nip(ctx, ctx->nip - 4); + } + t0 = tcg_const_i32(excp); + gen_helper_raise_exception(cpu_env, t0); + tcg_temp_free_i32(t0); + ctx->exception = (excp); +} + +static void gen_exception_nip(DisasContext *ctx, uint32_t excp, + target_ulong nip) +{ + TCGv_i32 t0; + + gen_update_nip(ctx, nip); + t0 = tcg_const_i32(excp); + gen_helper_raise_exception(cpu_env, t0); + tcg_temp_free_i32(t0); + ctx->exception = (excp); +} + +static void gen_debug_exception(DisasContext *ctx) +{ + TCGv_i32 t0; + + /* These are all synchronous exceptions, we set the PC back to + * the faulting instruction + */ + if ((ctx->exception != POWERPC_EXCP_BRANCH) && + (ctx->exception != POWERPC_EXCP_SYNC)) { + gen_update_nip(ctx, ctx->nip); + } + t0 = tcg_const_i32(EXCP_DEBUG); + gen_helper_raise_exception(cpu_env, t0); + tcg_temp_free_i32(t0); +} + +static inline void gen_inval_exception(DisasContext *ctx, uint32_t error) +{ + /* Will be converted to program check if needed */ + gen_exception_err(ctx, POWERPC_EXCP_HV_EMU, POWERPC_EXCP_INVAL | error); +} + +static inline void gen_priv_exception(DisasContext *ctx, uint32_t error) +{ + gen_exception_err(ctx, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_PRIV | error); +} + +static inline void gen_hvpriv_exception(DisasContext *ctx, uint32_t error) +{ + /* Will be converted to program check if needed */ + gen_exception_err(ctx, POWERPC_EXCP_HV_EMU, POWERPC_EXCP_PRIV | error); +} + +/* Stop translation */ +static inline void gen_stop_exception(DisasContext *ctx) +{ + gen_update_nip(ctx, ctx->nip); + ctx->exception = POWERPC_EXCP_STOP; +} + +#ifndef CONFIG_USER_ONLY +/* No need to update nip here, as execution flow will change */ +static inline void gen_sync_exception(DisasContext *ctx) +{ + ctx->exception = POWERPC_EXCP_SYNC; +} +#endif + +#define GEN_HANDLER(name, opc1, opc2, opc3, inval, type) \ +GEN_OPCODE(name, opc1, opc2, opc3, inval, type, PPC_NONE) + +#define GEN_HANDLER_E(name, opc1, opc2, opc3, inval, type, type2) \ +GEN_OPCODE(name, opc1, opc2, opc3, inval, type, type2) + +#define GEN_HANDLER2(name, onam, opc1, opc2, opc3, inval, type) \ +GEN_OPCODE2(name, onam, opc1, opc2, opc3, inval, type, PPC_NONE) + +#define GEN_HANDLER2_E(name, onam, opc1, opc2, opc3, inval, type, type2) \ +GEN_OPCODE2(name, onam, opc1, opc2, opc3, inval, type, type2) + +#define GEN_HANDLER_E_2(name, opc1, opc2, opc3, opc4, inval, type, type2) \ +GEN_OPCODE3(name, opc1, opc2, opc3, opc4, inval, type, type2) + +#define GEN_HANDLER2_E_2(name, onam, opc1, opc2, opc3, opc4, inval, typ, typ2) \ +GEN_OPCODE4(name, onam, opc1, opc2, opc3, opc4, inval, typ, typ2) + +typedef struct opcode_t { + unsigned char opc1, opc2, opc3, opc4; +#if HOST_LONG_BITS == 64 /* Explicitly align to 64 bits */ + unsigned char pad[4]; +#endif + opc_handler_t handler; + const char *oname; +} opcode_t; + +/* Helpers for priv. check */ +#define GEN_PRIV \ + do { \ + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; \ + } while (0) + +#if defined(CONFIG_USER_ONLY) +#define CHK_HV GEN_PRIV +#define CHK_SV GEN_PRIV +#define CHK_HVRM GEN_PRIV +#else +#define CHK_HV \ + do { \ + if (unlikely(ctx->pr || !ctx->hv)) { \ + GEN_PRIV; \ + } \ + } while (0) +#define CHK_SV \ + do { \ + if (unlikely(ctx->pr)) { \ + GEN_PRIV; \ + } \ + } while (0) +#define CHK_HVRM \ + do { \ + if (unlikely(ctx->pr || !ctx->hv || ctx->dr)) { \ + GEN_PRIV; \ + } \ + } while (0) +#endif + +#define CHK_NONE + + +/*****************************************************************************/ +/*** Instruction decoding ***/ +#define EXTRACT_HELPER(name, shift, nb) \ +static inline uint32_t name(uint32_t opcode) \ +{ \ + return (opcode >> (shift)) & ((1 << (nb)) - 1); \ +} + +#define EXTRACT_SHELPER(name, shift, nb) \ +static inline int32_t name(uint32_t opcode) \ +{ \ + return (int16_t)((opcode >> (shift)) & ((1 << (nb)) - 1)); \ +} + +#define EXTRACT_HELPER_SPLIT(name, shift1, nb1, shift2, nb2) \ +static inline uint32_t name(uint32_t opcode) \ +{ \ + return (((opcode >> (shift1)) & ((1 << (nb1)) - 1)) << nb2) | \ + ((opcode >> (shift2)) & ((1 << (nb2)) - 1)); \ +} + +#define EXTRACT_HELPER_DXFORM(name, \ + d0_bits, shift_op_d0, shift_d0, \ + d1_bits, shift_op_d1, shift_d1, \ + d2_bits, shift_op_d2, shift_d2) \ +static inline int16_t name(uint32_t opcode) \ +{ \ + return \ + (((opcode >> (shift_op_d0)) & ((1 << (d0_bits)) - 1)) << (shift_d0)) | \ + (((opcode >> (shift_op_d1)) & ((1 << (d1_bits)) - 1)) << (shift_d1)) | \ + (((opcode >> (shift_op_d2)) & ((1 << (d2_bits)) - 1)) << (shift_d2)); \ +} + + +/* Opcode part 1 */ +EXTRACT_HELPER(opc1, 26, 6); +/* Opcode part 2 */ +EXTRACT_HELPER(opc2, 1, 5); +/* Opcode part 3 */ +EXTRACT_HELPER(opc3, 6, 5); +/* Opcode part 4 */ +EXTRACT_HELPER(opc4, 16, 5); +/* Update Cr0 flags */ +EXTRACT_HELPER(Rc, 0, 1); +/* Update Cr6 flags (Altivec) */ +EXTRACT_HELPER(Rc21, 10, 1); +/* Destination */ +EXTRACT_HELPER(rD, 21, 5); +/* Source */ +EXTRACT_HELPER(rS, 21, 5); +/* First operand */ +EXTRACT_HELPER(rA, 16, 5); +/* Second operand */ +EXTRACT_HELPER(rB, 11, 5); +/* Third operand */ +EXTRACT_HELPER(rC, 6, 5); +/*** Get CRn ***/ +EXTRACT_HELPER(crfD, 23, 3); +EXTRACT_HELPER(crfS, 18, 3); +EXTRACT_HELPER(crbD, 21, 5); +EXTRACT_HELPER(crbA, 16, 5); +EXTRACT_HELPER(crbB, 11, 5); +/* SPR / TBL */ +EXTRACT_HELPER(_SPR, 11, 10); +static inline uint32_t SPR(uint32_t opcode) +{ + uint32_t sprn = _SPR(opcode); + + return ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5); +} +/*** Get constants ***/ +/* 16 bits signed immediate value */ +EXTRACT_SHELPER(SIMM, 0, 16); +/* 16 bits unsigned immediate value */ +EXTRACT_HELPER(UIMM, 0, 16); +/* 5 bits signed immediate value */ +EXTRACT_HELPER(SIMM5, 16, 5); +/* 5 bits signed immediate value */ +EXTRACT_HELPER(UIMM5, 16, 5); +/* 4 bits unsigned immediate value */ +EXTRACT_HELPER(UIMM4, 16, 4); +/* Bit count */ +EXTRACT_HELPER(NB, 11, 5); +/* Shift count */ +EXTRACT_HELPER(SH, 11, 5); +/* Vector shift count */ +EXTRACT_HELPER(VSH, 6, 4); +/* Mask start */ +EXTRACT_HELPER(MB, 6, 5); +/* Mask end */ +EXTRACT_HELPER(ME, 1, 5); +/* Trap operand */ +EXTRACT_HELPER(TO, 21, 5); + +EXTRACT_HELPER(CRM, 12, 8); + +#ifndef CONFIG_USER_ONLY +EXTRACT_HELPER(SR, 16, 4); +#endif + +/* mtfsf/mtfsfi */ +EXTRACT_HELPER(FPBF, 23, 3); +EXTRACT_HELPER(FPIMM, 12, 4); +EXTRACT_HELPER(FPL, 25, 1); +EXTRACT_HELPER(FPFLM, 17, 8); +EXTRACT_HELPER(FPW, 16, 1); + +/* addpcis */ +EXTRACT_HELPER_DXFORM(DX, 10, 6, 6, 5, 16, 1, 1, 0, 0) +#if defined(TARGET_PPC64) +/* darn */ +EXTRACT_HELPER(L, 16, 2); +#endif + +/*** Jump target decoding ***/ +/* Immediate address */ +static inline target_ulong LI(uint32_t opcode) +{ + return (opcode >> 0) & 0x03FFFFFC; +} + +static inline uint32_t BD(uint32_t opcode) +{ + return (opcode >> 0) & 0xFFFC; +} + +EXTRACT_HELPER(BO, 21, 5); +EXTRACT_HELPER(BI, 16, 5); +/* Absolute/relative address */ +EXTRACT_HELPER(AA, 1, 1); +/* Link */ +EXTRACT_HELPER(LK, 0, 1); + +/* DFP Z22-form */ +EXTRACT_HELPER(DCM, 10, 6) + +/* DFP Z23-form */ +EXTRACT_HELPER(RMC, 9, 2) + +EXTRACT_HELPER_SPLIT(xT, 0, 1, 21, 5); +EXTRACT_HELPER_SPLIT(xS, 0, 1, 21, 5); +EXTRACT_HELPER_SPLIT(xA, 2, 1, 16, 5); +EXTRACT_HELPER_SPLIT(xB, 1, 1, 11, 5); +EXTRACT_HELPER_SPLIT(xC, 3, 1, 6, 5); +EXTRACT_HELPER(DM, 8, 2); +EXTRACT_HELPER(UIM, 16, 2); +EXTRACT_HELPER(SHW, 8, 2); +EXTRACT_HELPER(SP, 19, 2); +EXTRACT_HELPER(IMM8, 11, 8); + +/*****************************************************************************/ +/* PowerPC instructions table */ + +#if defined(DO_PPC_STATISTICS) +#define GEN_OPCODE(name, op1, op2, op3, invl, _typ, _typ2) \ +{ \ + .opc1 = op1, \ + .opc2 = op2, \ + .opc3 = op3, \ + .opc4 = 0xff, \ + .handler = { \ + .inval1 = invl, \ + .type = _typ, \ + .type2 = _typ2, \ + .handler = &gen_##name, \ + .oname = stringify(name), \ + }, \ + .oname = stringify(name), \ +} +#define GEN_OPCODE_DUAL(name, op1, op2, op3, invl1, invl2, _typ, _typ2) \ +{ \ + .opc1 = op1, \ + .opc2 = op2, \ + .opc3 = op3, \ + .opc4 = 0xff, \ + .handler = { \ + .inval1 = invl1, \ + .inval2 = invl2, \ + .type = _typ, \ + .type2 = _typ2, \ + .handler = &gen_##name, \ + .oname = stringify(name), \ + }, \ + .oname = stringify(name), \ +} +#define GEN_OPCODE2(name, onam, op1, op2, op3, invl, _typ, _typ2) \ +{ \ + .opc1 = op1, \ + .opc2 = op2, \ + .opc3 = op3, \ + .opc4 = 0xff, \ + .handler = { \ + .inval1 = invl, \ + .type = _typ, \ + .type2 = _typ2, \ + .handler = &gen_##name, \ + .oname = onam, \ + }, \ + .oname = onam, \ +} +#define GEN_OPCODE3(name, op1, op2, op3, op4, invl, _typ, _typ2) \ +{ \ + .opc1 = op1, \ + .opc2 = op2, \ + .opc3 = op3, \ + .opc4 = op4, \ + .handler = { \ + .inval1 = invl, \ + .type = _typ, \ + .type2 = _typ2, \ + .handler = &gen_##name, \ + .oname = stringify(name), \ + }, \ + .oname = stringify(name), \ +} +#define GEN_OPCODE4(name, onam, op1, op2, op3, op4, invl, _typ, _typ2) \ +{ \ + .opc1 = op1, \ + .opc2 = op2, \ + .opc3 = op3, \ + .opc4 = op4, \ + .handler = { \ + .inval1 = invl, \ + .type = _typ, \ + .type2 = _typ2, \ + .handler = &gen_##name, \ + .oname = onam, \ + }, \ + .oname = onam, \ +} +#else +#define GEN_OPCODE(name, op1, op2, op3, invl, _typ, _typ2) \ +{ \ + .opc1 = op1, \ + .opc2 = op2, \ + .opc3 = op3, \ + .opc4 = 0xff, \ + .handler = { \ + .inval1 = invl, \ + .type = _typ, \ + .type2 = _typ2, \ + .handler = &gen_##name, \ + }, \ + .oname = stringify(name), \ +} +#define GEN_OPCODE_DUAL(name, op1, op2, op3, invl1, invl2, _typ, _typ2) \ +{ \ + .opc1 = op1, \ + .opc2 = op2, \ + .opc3 = op3, \ + .opc4 = 0xff, \ + .handler = { \ + .inval1 = invl1, \ + .inval2 = invl2, \ + .type = _typ, \ + .type2 = _typ2, \ + .handler = &gen_##name, \ + }, \ + .oname = stringify(name), \ +} +#define GEN_OPCODE2(name, onam, op1, op2, op3, invl, _typ, _typ2) \ +{ \ + .opc1 = op1, \ + .opc2 = op2, \ + .opc3 = op3, \ + .opc4 = 0xff, \ + .handler = { \ + .inval1 = invl, \ + .type = _typ, \ + .type2 = _typ2, \ + .handler = &gen_##name, \ + }, \ + .oname = onam, \ +} +#define GEN_OPCODE3(name, op1, op2, op3, op4, invl, _typ, _typ2) \ +{ \ + .opc1 = op1, \ + .opc2 = op2, \ + .opc3 = op3, \ + .opc4 = op4, \ + .handler = { \ + .inval1 = invl, \ + .type = _typ, \ + .type2 = _typ2, \ + .handler = &gen_##name, \ + }, \ + .oname = stringify(name), \ +} +#define GEN_OPCODE4(name, onam, op1, op2, op3, op4, invl, _typ, _typ2) \ +{ \ + .opc1 = op1, \ + .opc2 = op2, \ + .opc3 = op3, \ + .opc4 = op4, \ + .handler = { \ + .inval1 = invl, \ + .type = _typ, \ + .type2 = _typ2, \ + .handler = &gen_##name, \ + }, \ + .oname = onam, \ +} +#endif + +/* SPR load/store helpers */ +static inline void gen_load_spr(TCGv t, int reg) +{ + tcg_gen_ld_tl(t, cpu_env, offsetof(CPUPPCState, spr[reg])); +} + +static inline void gen_store_spr(int reg, TCGv t) +{ + tcg_gen_st_tl(t, cpu_env, offsetof(CPUPPCState, spr[reg])); +} + +/* Invalid instruction */ +static void gen_invalid(DisasContext *ctx) +{ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); +} + +static opc_handler_t invalid_handler = { + .inval1 = 0xFFFFFFFF, + .inval2 = 0xFFFFFFFF, + .type = PPC_NONE, + .type2 = PPC_NONE, + .handler = gen_invalid, +}; + +/*** Integer comparison ***/ + +static inline void gen_op_cmp(TCGv arg0, TCGv arg1, int s, int crf) +{ + TCGv t0 = tcg_temp_new(); + TCGv_i32 t1 = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(cpu_crf[crf], cpu_so); + + tcg_gen_setcond_tl((s ? TCG_COND_LT: TCG_COND_LTU), t0, arg0, arg1); + tcg_gen_trunc_tl_i32(t1, t0); + tcg_gen_shli_i32(t1, t1, CRF_LT); + tcg_gen_or_i32(cpu_crf[crf], cpu_crf[crf], t1); + + tcg_gen_setcond_tl((s ? TCG_COND_GT: TCG_COND_GTU), t0, arg0, arg1); + tcg_gen_trunc_tl_i32(t1, t0); + tcg_gen_shli_i32(t1, t1, CRF_GT); + tcg_gen_or_i32(cpu_crf[crf], cpu_crf[crf], t1); + + tcg_gen_setcond_tl(TCG_COND_EQ, t0, arg0, arg1); + tcg_gen_trunc_tl_i32(t1, t0); + tcg_gen_shli_i32(t1, t1, CRF_EQ); + tcg_gen_or_i32(cpu_crf[crf], cpu_crf[crf], t1); + + tcg_temp_free(t0); + tcg_temp_free_i32(t1); +} + +static inline void gen_op_cmpi(TCGv arg0, target_ulong arg1, int s, int crf) +{ + TCGv t0 = tcg_const_tl(arg1); + gen_op_cmp(arg0, t0, s, crf); + tcg_temp_free(t0); +} + +static inline void gen_op_cmp32(TCGv arg0, TCGv arg1, int s, int crf) +{ + TCGv t0, t1; + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + if (s) { + tcg_gen_ext32s_tl(t0, arg0); + tcg_gen_ext32s_tl(t1, arg1); + } else { + tcg_gen_ext32u_tl(t0, arg0); + tcg_gen_ext32u_tl(t1, arg1); + } + gen_op_cmp(t0, t1, s, crf); + tcg_temp_free(t1); + tcg_temp_free(t0); +} + +static inline void gen_op_cmpi32(TCGv arg0, target_ulong arg1, int s, int crf) +{ + TCGv t0 = tcg_const_tl(arg1); + gen_op_cmp32(arg0, t0, s, crf); + tcg_temp_free(t0); +} + +static inline void gen_set_Rc0(DisasContext *ctx, TCGv reg) +{ + if (NARROW_MODE(ctx)) { + gen_op_cmpi32(reg, 0, 1, 0); + } else { + gen_op_cmpi(reg, 0, 1, 0); + } +} + +/* cmp */ +static void gen_cmp(DisasContext *ctx) +{ + if ((ctx->opcode & 0x00200000) && (ctx->insns_flags & PPC_64B)) { + gen_op_cmp(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], + 1, crfD(ctx->opcode)); + } else { + gen_op_cmp32(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], + 1, crfD(ctx->opcode)); + } +} + +/* cmpi */ +static void gen_cmpi(DisasContext *ctx) +{ + if ((ctx->opcode & 0x00200000) && (ctx->insns_flags & PPC_64B)) { + gen_op_cmpi(cpu_gpr[rA(ctx->opcode)], SIMM(ctx->opcode), + 1, crfD(ctx->opcode)); + } else { + gen_op_cmpi32(cpu_gpr[rA(ctx->opcode)], SIMM(ctx->opcode), + 1, crfD(ctx->opcode)); + } +} + +/* cmpl */ +static void gen_cmpl(DisasContext *ctx) +{ + if ((ctx->opcode & 0x00200000) && (ctx->insns_flags & PPC_64B)) { + gen_op_cmp(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], + 0, crfD(ctx->opcode)); + } else { + gen_op_cmp32(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], + 0, crfD(ctx->opcode)); + } +} + +/* cmpli */ +static void gen_cmpli(DisasContext *ctx) +{ + if ((ctx->opcode & 0x00200000) && (ctx->insns_flags & PPC_64B)) { + gen_op_cmpi(cpu_gpr[rA(ctx->opcode)], UIMM(ctx->opcode), + 0, crfD(ctx->opcode)); + } else { + gen_op_cmpi32(cpu_gpr[rA(ctx->opcode)], UIMM(ctx->opcode), + 0, crfD(ctx->opcode)); + } +} + +/* cmprb - range comparison: isupper, isaplha, islower*/ +static void gen_cmprb(DisasContext *ctx) +{ + TCGv_i32 src1 = tcg_temp_new_i32(); + TCGv_i32 src2 = tcg_temp_new_i32(); + TCGv_i32 src2lo = tcg_temp_new_i32(); + TCGv_i32 src2hi = tcg_temp_new_i32(); + TCGv_i32 crf = cpu_crf[crfD(ctx->opcode)]; + + tcg_gen_trunc_tl_i32(src1, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_trunc_tl_i32(src2, cpu_gpr[rB(ctx->opcode)]); + + tcg_gen_andi_i32(src1, src1, 0xFF); + tcg_gen_ext8u_i32(src2lo, src2); + tcg_gen_shri_i32(src2, src2, 8); + tcg_gen_ext8u_i32(src2hi, src2); + + tcg_gen_setcond_i32(TCG_COND_LEU, src2lo, src2lo, src1); + tcg_gen_setcond_i32(TCG_COND_LEU, src2hi, src1, src2hi); + tcg_gen_and_i32(crf, src2lo, src2hi); + + if (ctx->opcode & 0x00200000) { + tcg_gen_shri_i32(src2, src2, 8); + tcg_gen_ext8u_i32(src2lo, src2); + tcg_gen_shri_i32(src2, src2, 8); + tcg_gen_ext8u_i32(src2hi, src2); + tcg_gen_setcond_i32(TCG_COND_LEU, src2lo, src2lo, src1); + tcg_gen_setcond_i32(TCG_COND_LEU, src2hi, src1, src2hi); + tcg_gen_and_i32(src2lo, src2lo, src2hi); + tcg_gen_or_i32(crf, crf, src2lo); + } + tcg_gen_shli_i32(crf, crf, CRF_GT); + tcg_temp_free_i32(src1); + tcg_temp_free_i32(src2); + tcg_temp_free_i32(src2lo); + tcg_temp_free_i32(src2hi); +} + +#if defined(TARGET_PPC64) +/* cmpeqb */ +static void gen_cmpeqb(DisasContext *ctx) +{ + gen_helper_cmpeqb(cpu_crf[crfD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rB(ctx->opcode)]); +} +#endif + +/* isel (PowerPC 2.03 specification) */ +static void gen_isel(DisasContext *ctx) +{ + uint32_t bi = rC(ctx->opcode); + uint32_t mask = 0x08 >> (bi & 0x03); + TCGv t0 = tcg_temp_new(); + TCGv zr; + + tcg_gen_extu_i32_tl(t0, cpu_crf[bi >> 2]); + tcg_gen_andi_tl(t0, t0, mask); + + zr = tcg_const_tl(0); + tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr[rD(ctx->opcode)], t0, zr, + rA(ctx->opcode) ? cpu_gpr[rA(ctx->opcode)] : zr, + cpu_gpr[rB(ctx->opcode)]); + tcg_temp_free(zr); + tcg_temp_free(t0); +} + +/* cmpb: PowerPC 2.05 specification */ +static void gen_cmpb(DisasContext *ctx) +{ + gen_helper_cmpb(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], + cpu_gpr[rB(ctx->opcode)]); +} + +/*** Integer arithmetic ***/ + +static inline void gen_op_arith_compute_ov(DisasContext *ctx, TCGv arg0, + TCGv arg1, TCGv arg2, int sub) +{ + TCGv t0 = tcg_temp_new(); + + tcg_gen_xor_tl(cpu_ov, arg0, arg2); + tcg_gen_xor_tl(t0, arg1, arg2); + if (sub) { + tcg_gen_and_tl(cpu_ov, cpu_ov, t0); + } else { + tcg_gen_andc_tl(cpu_ov, cpu_ov, t0); + } + tcg_temp_free(t0); + if (NARROW_MODE(ctx)) { + tcg_gen_ext32s_tl(cpu_ov, cpu_ov); + } + tcg_gen_shri_tl(cpu_ov, cpu_ov, TARGET_LONG_BITS - 1); + tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); +} + +/* Common add function */ +static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1, + TCGv arg2, bool add_ca, bool compute_ca, + bool compute_ov, bool compute_rc0) +{ + TCGv t0 = ret; + + if (compute_ca || compute_ov) { + t0 = tcg_temp_new(); + } + + if (compute_ca) { + if (NARROW_MODE(ctx)) { + /* Caution: a non-obvious corner case of the spec is that we + must produce the *entire* 64-bit addition, but produce the + carry into bit 32. */ + TCGv t1 = tcg_temp_new(); + tcg_gen_xor_tl(t1, arg1, arg2); /* add without carry */ + tcg_gen_add_tl(t0, arg1, arg2); + if (add_ca) { + tcg_gen_add_tl(t0, t0, cpu_ca); + } + tcg_gen_xor_tl(cpu_ca, t0, t1); /* bits changed w/ carry */ + tcg_temp_free(t1); + tcg_gen_shri_tl(cpu_ca, cpu_ca, 32); /* extract bit 32 */ + tcg_gen_andi_tl(cpu_ca, cpu_ca, 1); + } else { + TCGv zero = tcg_const_tl(0); + if (add_ca) { + tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, cpu_ca, zero); + tcg_gen_add2_tl(t0, cpu_ca, t0, cpu_ca, arg2, zero); + } else { + tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, arg2, zero); + } + tcg_temp_free(zero); + } + } else { + tcg_gen_add_tl(t0, arg1, arg2); + if (add_ca) { + tcg_gen_add_tl(t0, t0, cpu_ca); + } + } + + if (compute_ov) { + gen_op_arith_compute_ov(ctx, t0, arg1, arg2, 0); + } + if (unlikely(compute_rc0)) { + gen_set_Rc0(ctx, t0); + } + + if (!TCGV_EQUAL(t0, ret)) { + tcg_gen_mov_tl(ret, t0); + tcg_temp_free(t0); + } +} +/* Add functions with two operands */ +#define GEN_INT_ARITH_ADD(name, opc3, add_ca, compute_ca, compute_ov) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + gen_op_arith_add(ctx, cpu_gpr[rD(ctx->opcode)], \ + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ + add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \ +} +/* Add functions with one operand and one immediate */ +#define GEN_INT_ARITH_ADD_CONST(name, opc3, const_val, \ + add_ca, compute_ca, compute_ov) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv t0 = tcg_const_tl(const_val); \ + gen_op_arith_add(ctx, cpu_gpr[rD(ctx->opcode)], \ + cpu_gpr[rA(ctx->opcode)], t0, \ + add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \ + tcg_temp_free(t0); \ +} + +/* add add. addo addo. */ +GEN_INT_ARITH_ADD(add, 0x08, 0, 0, 0) +GEN_INT_ARITH_ADD(addo, 0x18, 0, 0, 1) +/* addc addc. addco addco. */ +GEN_INT_ARITH_ADD(addc, 0x00, 0, 1, 0) +GEN_INT_ARITH_ADD(addco, 0x10, 0, 1, 1) +/* adde adde. addeo addeo. */ +GEN_INT_ARITH_ADD(adde, 0x04, 1, 1, 0) +GEN_INT_ARITH_ADD(addeo, 0x14, 1, 1, 1) +/* addme addme. addmeo addmeo. */ +GEN_INT_ARITH_ADD_CONST(addme, 0x07, -1LL, 1, 1, 0) +GEN_INT_ARITH_ADD_CONST(addmeo, 0x17, -1LL, 1, 1, 1) +/* addze addze. addzeo addzeo.*/ +GEN_INT_ARITH_ADD_CONST(addze, 0x06, 0, 1, 1, 0) +GEN_INT_ARITH_ADD_CONST(addzeo, 0x16, 0, 1, 1, 1) +/* addi */ +static void gen_addi(DisasContext *ctx) +{ + target_long simm = SIMM(ctx->opcode); + + if (rA(ctx->opcode) == 0) { + /* li case */ + tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], simm); + } else { + tcg_gen_addi_tl(cpu_gpr[rD(ctx->opcode)], + cpu_gpr[rA(ctx->opcode)], simm); + } +} +/* addic addic.*/ +static inline void gen_op_addic(DisasContext *ctx, bool compute_rc0) +{ + TCGv c = tcg_const_tl(SIMM(ctx->opcode)); + gen_op_arith_add(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], + c, 0, 1, 0, compute_rc0); + tcg_temp_free(c); +} + +static void gen_addic(DisasContext *ctx) +{ + gen_op_addic(ctx, 0); +} + +static void gen_addic_(DisasContext *ctx) +{ + gen_op_addic(ctx, 1); +} + +/* addis */ +static void gen_addis(DisasContext *ctx) +{ + target_long simm = SIMM(ctx->opcode); + + if (rA(ctx->opcode) == 0) { + /* lis case */ + tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], simm << 16); + } else { + tcg_gen_addi_tl(cpu_gpr[rD(ctx->opcode)], + cpu_gpr[rA(ctx->opcode)], simm << 16); + } +} + +/* addpcis */ +static void gen_addpcis(DisasContext *ctx) +{ + target_long d = DX(ctx->opcode); + + tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], ctx->nip + (d << 16)); +} + +static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, TCGv arg1, + TCGv arg2, int sign, int compute_ov) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(t0, arg1); + tcg_gen_trunc_tl_i32(t1, arg2); + if (sign) { + tcg_gen_setcondi_i32(TCG_COND_EQ, t2, t0, INT_MIN); + tcg_gen_setcondi_i32(TCG_COND_EQ, t3, t1, -1); + tcg_gen_and_i32(t2, t2, t3); + tcg_gen_setcondi_i32(TCG_COND_EQ, t3, t1, 0); + tcg_gen_or_i32(t2, t2, t3); + tcg_gen_movi_i32(t3, 0); + tcg_gen_movcond_i32(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_div_i32(t3, t0, t1); + tcg_gen_extu_i32_tl(ret, t3); + } else { + tcg_gen_setcondi_i32(TCG_COND_EQ, t2, t1, 0); + tcg_gen_movi_i32(t3, 0); + tcg_gen_movcond_i32(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_divu_i32(t3, t0, t1); + tcg_gen_extu_i32_tl(ret, t3); + } + if (compute_ov) { + tcg_gen_extu_i32_tl(cpu_ov, t2); + tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); + } + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, ret); +} +/* Div functions */ +#define GEN_INT_ARITH_DIVW(name, opc3, sign, compute_ov) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + gen_op_arith_divw(ctx, cpu_gpr[rD(ctx->opcode)], \ + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ + sign, compute_ov); \ +} +/* divwu divwu. divwuo divwuo. */ +GEN_INT_ARITH_DIVW(divwu, 0x0E, 0, 0); +GEN_INT_ARITH_DIVW(divwuo, 0x1E, 0, 1); +/* divw divw. divwo divwo. */ +GEN_INT_ARITH_DIVW(divw, 0x0F, 1, 0); +GEN_INT_ARITH_DIVW(divwo, 0x1F, 1, 1); + +/* div[wd]eu[o][.] */ +#define GEN_DIVE(name, hlpr, compute_ov) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_i32 t0 = tcg_const_i32(compute_ov); \ + gen_helper_##hlpr(cpu_gpr[rD(ctx->opcode)], cpu_env, \ + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); \ + tcg_temp_free_i32(t0); \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); \ + } \ +} + +GEN_DIVE(divweu, divweu, 0); +GEN_DIVE(divweuo, divweu, 1); +GEN_DIVE(divwe, divwe, 0); +GEN_DIVE(divweo, divwe, 1); + +#if defined(TARGET_PPC64) +static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, TCGv arg1, + TCGv arg2, int sign, int compute_ov) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + tcg_gen_mov_i64(t0, arg1); + tcg_gen_mov_i64(t1, arg2); + if (sign) { + tcg_gen_setcondi_i64(TCG_COND_EQ, t2, t0, INT64_MIN); + tcg_gen_setcondi_i64(TCG_COND_EQ, t3, t1, -1); + tcg_gen_and_i64(t2, t2, t3); + tcg_gen_setcondi_i64(TCG_COND_EQ, t3, t1, 0); + tcg_gen_or_i64(t2, t2, t3); + tcg_gen_movi_i64(t3, 0); + tcg_gen_movcond_i64(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_div_i64(ret, t0, t1); + } else { + tcg_gen_setcondi_i64(TCG_COND_EQ, t2, t1, 0); + tcg_gen_movi_i64(t3, 0); + tcg_gen_movcond_i64(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_divu_i64(ret, t0, t1); + } + if (compute_ov) { + tcg_gen_mov_tl(cpu_ov, t2); + tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); + } + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t3); + + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, ret); +} + +#define GEN_INT_ARITH_DIVD(name, opc3, sign, compute_ov) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + gen_op_arith_divd(ctx, cpu_gpr[rD(ctx->opcode)], \ + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ + sign, compute_ov); \ +} +/* divwu divwu. divwuo divwuo. */ +GEN_INT_ARITH_DIVD(divdu, 0x0E, 0, 0); +GEN_INT_ARITH_DIVD(divduo, 0x1E, 0, 1); +/* divw divw. divwo divwo. */ +GEN_INT_ARITH_DIVD(divd, 0x0F, 1, 0); +GEN_INT_ARITH_DIVD(divdo, 0x1F, 1, 1); + +GEN_DIVE(divdeu, divdeu, 0); +GEN_DIVE(divdeuo, divdeu, 1); +GEN_DIVE(divde, divde, 0); +GEN_DIVE(divdeo, divde, 1); +#endif + +static inline void gen_op_arith_modw(DisasContext *ctx, TCGv ret, TCGv arg1, + TCGv arg2, int sign) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(t0, arg1); + tcg_gen_trunc_tl_i32(t1, arg2); + if (sign) { + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + tcg_gen_setcondi_i32(TCG_COND_EQ, t2, t0, INT_MIN); + tcg_gen_setcondi_i32(TCG_COND_EQ, t3, t1, -1); + tcg_gen_and_i32(t2, t2, t3); + tcg_gen_setcondi_i32(TCG_COND_EQ, t3, t1, 0); + tcg_gen_or_i32(t2, t2, t3); + tcg_gen_movi_i32(t3, 0); + tcg_gen_movcond_i32(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_rem_i32(t3, t0, t1); + tcg_gen_ext_i32_tl(ret, t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + } else { + TCGv_i32 t2 = tcg_const_i32(1); + TCGv_i32 t3 = tcg_const_i32(0); + tcg_gen_movcond_i32(TCG_COND_EQ, t1, t1, t3, t2, t1); + tcg_gen_remu_i32(t3, t0, t1); + tcg_gen_extu_i32_tl(ret, t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + } + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); +} + +#define GEN_INT_ARITH_MODW(name, opc3, sign) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + gen_op_arith_modw(ctx, cpu_gpr[rD(ctx->opcode)], \ + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ + sign); \ +} + +GEN_INT_ARITH_MODW(moduw, 0x08, 0); +GEN_INT_ARITH_MODW(modsw, 0x18, 1); + +#if defined(TARGET_PPC64) +static inline void gen_op_arith_modd(DisasContext *ctx, TCGv ret, TCGv arg1, + TCGv arg2, int sign) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_mov_i64(t0, arg1); + tcg_gen_mov_i64(t1, arg2); + if (sign) { + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + tcg_gen_setcondi_i64(TCG_COND_EQ, t2, t0, INT64_MIN); + tcg_gen_setcondi_i64(TCG_COND_EQ, t3, t1, -1); + tcg_gen_and_i64(t2, t2, t3); + tcg_gen_setcondi_i64(TCG_COND_EQ, t3, t1, 0); + tcg_gen_or_i64(t2, t2, t3); + tcg_gen_movi_i64(t3, 0); + tcg_gen_movcond_i64(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_rem_i64(ret, t0, t1); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t3); + } else { + TCGv_i64 t2 = tcg_const_i64(1); + TCGv_i64 t3 = tcg_const_i64(0); + tcg_gen_movcond_i64(TCG_COND_EQ, t1, t1, t3, t2, t1); + tcg_gen_remu_i64(ret, t0, t1); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t3); + } + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +} + +#define GEN_INT_ARITH_MODD(name, opc3, sign) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + gen_op_arith_modd(ctx, cpu_gpr[rD(ctx->opcode)], \ + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ + sign); \ +} + +GEN_INT_ARITH_MODD(modud, 0x08, 0); +GEN_INT_ARITH_MODD(modsd, 0x18, 1); +#endif + +/* mulhw mulhw. */ +static void gen_mulhw(DisasContext *ctx) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); + tcg_gen_muls2_i32(t0, t1, t0, t1); + tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* mulhwu mulhwu. */ +static void gen_mulhwu(DisasContext *ctx) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); + tcg_gen_mulu2_i32(t0, t1, t0, t1); + tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* mullw mullw. */ +static void gen_mullw(DisasContext *ctx) +{ +#if defined(TARGET_PPC64) + TCGv_i64 t0, t1; + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + tcg_gen_ext32s_tl(t0, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_ext32s_tl(t1, cpu_gpr[rB(ctx->opcode)]); + tcg_gen_mul_i64(cpu_gpr[rD(ctx->opcode)], t0, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); +#else + tcg_gen_mul_i32(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rB(ctx->opcode)]); +#endif + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* mullwo mullwo. */ +static void gen_mullwo(DisasContext *ctx) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); + tcg_gen_muls2_i32(t0, t1, t0, t1); +#if defined(TARGET_PPC64) + tcg_gen_concat_i32_i64(cpu_gpr[rD(ctx->opcode)], t0, t1); +#else + tcg_gen_mov_i32(cpu_gpr[rD(ctx->opcode)], t0); +#endif + + tcg_gen_sari_i32(t0, t0, 31); + tcg_gen_setcond_i32(TCG_COND_NE, t0, t0, t1); + tcg_gen_extu_i32_tl(cpu_ov, t0); + tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* mulli */ +static void gen_mulli(DisasContext *ctx) +{ + tcg_gen_muli_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], + SIMM(ctx->opcode)); +} + +#if defined(TARGET_PPC64) +/* mulhd mulhd. */ +static void gen_mulhd(DisasContext *ctx) +{ + TCGv lo = tcg_temp_new(); + tcg_gen_muls2_tl(lo, cpu_gpr[rD(ctx->opcode)], + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + tcg_temp_free(lo); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); + } +} + +/* mulhdu mulhdu. */ +static void gen_mulhdu(DisasContext *ctx) +{ + TCGv lo = tcg_temp_new(); + tcg_gen_mulu2_tl(lo, cpu_gpr[rD(ctx->opcode)], + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + tcg_temp_free(lo); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); + } +} + +/* mulld mulld. */ +static void gen_mulld(DisasContext *ctx) +{ + tcg_gen_mul_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rB(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* mulldo mulldo. */ +static void gen_mulldo(DisasContext *ctx) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_muls2_i64(t0, t1, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rB(ctx->opcode)]); + tcg_gen_mov_i64(cpu_gpr[rD(ctx->opcode)], t0); + + tcg_gen_sari_i64(t0, t0, 63); + tcg_gen_setcond_i64(TCG_COND_NE, cpu_ov, t0, t1); + tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); + } +} +#endif + +/* Common subf function */ +static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, + TCGv arg2, bool add_ca, bool compute_ca, + bool compute_ov, bool compute_rc0) +{ + TCGv t0 = ret; + + if (compute_ca || compute_ov) { + t0 = tcg_temp_new(); + } + + if (compute_ca) { + /* dest = ~arg1 + arg2 [+ ca]. */ + if (NARROW_MODE(ctx)) { + /* Caution: a non-obvious corner case of the spec is that we + must produce the *entire* 64-bit addition, but produce the + carry into bit 32. */ + TCGv inv1 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + tcg_gen_not_tl(inv1, arg1); + if (add_ca) { + tcg_gen_add_tl(t0, arg2, cpu_ca); + } else { + tcg_gen_addi_tl(t0, arg2, 1); + } + tcg_gen_xor_tl(t1, arg2, inv1); /* add without carry */ + tcg_gen_add_tl(t0, t0, inv1); + tcg_temp_free(inv1); + tcg_gen_xor_tl(cpu_ca, t0, t1); /* bits changes w/ carry */ + tcg_temp_free(t1); + tcg_gen_shri_tl(cpu_ca, cpu_ca, 32); /* extract bit 32 */ + tcg_gen_andi_tl(cpu_ca, cpu_ca, 1); + } else if (add_ca) { + TCGv zero, inv1 = tcg_temp_new(); + tcg_gen_not_tl(inv1, arg1); + zero = tcg_const_tl(0); + tcg_gen_add2_tl(t0, cpu_ca, arg2, zero, cpu_ca, zero); + tcg_gen_add2_tl(t0, cpu_ca, t0, cpu_ca, inv1, zero); + tcg_temp_free(zero); + tcg_temp_free(inv1); + } else { + tcg_gen_setcond_tl(TCG_COND_GEU, cpu_ca, arg2, arg1); + tcg_gen_sub_tl(t0, arg2, arg1); + } + } else if (add_ca) { + /* Since we're ignoring carry-out, we can simplify the + standard ~arg1 + arg2 + ca to arg2 - arg1 + ca - 1. */ + tcg_gen_sub_tl(t0, arg2, arg1); + tcg_gen_add_tl(t0, t0, cpu_ca); + tcg_gen_subi_tl(t0, t0, 1); + } else { + tcg_gen_sub_tl(t0, arg2, arg1); + } + + if (compute_ov) { + gen_op_arith_compute_ov(ctx, t0, arg1, arg2, 1); + } + if (unlikely(compute_rc0)) { + gen_set_Rc0(ctx, t0); + } + + if (!TCGV_EQUAL(t0, ret)) { + tcg_gen_mov_tl(ret, t0); + tcg_temp_free(t0); + } +} +/* Sub functions with Two operands functions */ +#define GEN_INT_ARITH_SUBF(name, opc3, add_ca, compute_ca, compute_ov) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], \ + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ + add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \ +} +/* Sub functions with one operand and one immediate */ +#define GEN_INT_ARITH_SUBF_CONST(name, opc3, const_val, \ + add_ca, compute_ca, compute_ov) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv t0 = tcg_const_tl(const_val); \ + gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], \ + cpu_gpr[rA(ctx->opcode)], t0, \ + add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \ + tcg_temp_free(t0); \ +} +/* subf subf. subfo subfo. */ +GEN_INT_ARITH_SUBF(subf, 0x01, 0, 0, 0) +GEN_INT_ARITH_SUBF(subfo, 0x11, 0, 0, 1) +/* subfc subfc. subfco subfco. */ +GEN_INT_ARITH_SUBF(subfc, 0x00, 0, 1, 0) +GEN_INT_ARITH_SUBF(subfco, 0x10, 0, 1, 1) +/* subfe subfe. subfeo subfo. */ +GEN_INT_ARITH_SUBF(subfe, 0x04, 1, 1, 0) +GEN_INT_ARITH_SUBF(subfeo, 0x14, 1, 1, 1) +/* subfme subfme. subfmeo subfmeo. */ +GEN_INT_ARITH_SUBF_CONST(subfme, 0x07, -1LL, 1, 1, 0) +GEN_INT_ARITH_SUBF_CONST(subfmeo, 0x17, -1LL, 1, 1, 1) +/* subfze subfze. subfzeo subfzeo.*/ +GEN_INT_ARITH_SUBF_CONST(subfze, 0x06, 0, 1, 1, 0) +GEN_INT_ARITH_SUBF_CONST(subfzeo, 0x16, 0, 1, 1, 1) + +/* subfic */ +static void gen_subfic(DisasContext *ctx) +{ + TCGv c = tcg_const_tl(SIMM(ctx->opcode)); + gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], + c, 0, 1, 0, 0); + tcg_temp_free(c); +} + +/* neg neg. nego nego. */ +static inline void gen_op_arith_neg(DisasContext *ctx, bool compute_ov) +{ + TCGv zero = tcg_const_tl(0); + gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], + zero, 0, 0, compute_ov, Rc(ctx->opcode)); + tcg_temp_free(zero); +} + +static void gen_neg(DisasContext *ctx) +{ + gen_op_arith_neg(ctx, 0); +} + +static void gen_nego(DisasContext *ctx) +{ + gen_op_arith_neg(ctx, 1); +} + +/*** Integer logical ***/ +#define GEN_LOGICAL2(name, tcg_op, opc, type) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + tcg_op(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], \ + cpu_gpr[rB(ctx->opcode)]); \ + if (unlikely(Rc(ctx->opcode) != 0)) \ + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); \ +} + +#define GEN_LOGICAL1(name, tcg_op, opc, type) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + tcg_op(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); \ + if (unlikely(Rc(ctx->opcode) != 0)) \ + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); \ +} + +/* and & and. */ +GEN_LOGICAL2(and, tcg_gen_and_tl, 0x00, PPC_INTEGER); +/* andc & andc. */ +GEN_LOGICAL2(andc, tcg_gen_andc_tl, 0x01, PPC_INTEGER); + +/* andi. */ +static void gen_andi_(DisasContext *ctx) +{ + tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], UIMM(ctx->opcode)); + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* andis. */ +static void gen_andis_(DisasContext *ctx) +{ + tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], UIMM(ctx->opcode) << 16); + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* cntlzw */ +static void gen_cntlzw(DisasContext *ctx) +{ + gen_helper_cntlzw(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* cnttzw */ +static void gen_cnttzw(DisasContext *ctx) +{ + gen_helper_cnttzw(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); + } +} + +/* eqv & eqv. */ +GEN_LOGICAL2(eqv, tcg_gen_eqv_tl, 0x08, PPC_INTEGER); +/* extsb & extsb. */ +GEN_LOGICAL1(extsb, tcg_gen_ext8s_tl, 0x1D, PPC_INTEGER); +/* extsh & extsh. */ +GEN_LOGICAL1(extsh, tcg_gen_ext16s_tl, 0x1C, PPC_INTEGER); +/* nand & nand. */ +GEN_LOGICAL2(nand, tcg_gen_nand_tl, 0x0E, PPC_INTEGER); +/* nor & nor. */ +GEN_LOGICAL2(nor, tcg_gen_nor_tl, 0x03, PPC_INTEGER); + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +static void gen_pause(DisasContext *ctx) +{ + TCGv_i32 t0 = tcg_const_i32(0); + tcg_gen_st_i32(t0, cpu_env, + -offsetof(PowerPCCPU, env) + offsetof(CPUState, halted)); + tcg_temp_free_i32(t0); + + /* Stop translation, this gives other CPUs a chance to run */ + gen_exception_nip(ctx, EXCP_HLT, ctx->nip); +} +#endif /* defined(TARGET_PPC64) */ + +/* or & or. */ +static void gen_or(DisasContext *ctx) +{ + int rs, ra, rb; + + rs = rS(ctx->opcode); + ra = rA(ctx->opcode); + rb = rB(ctx->opcode); + /* Optimisation for mr. ri case */ + if (rs != ra || rs != rb) { + if (rs != rb) + tcg_gen_or_tl(cpu_gpr[ra], cpu_gpr[rs], cpu_gpr[rb]); + else + tcg_gen_mov_tl(cpu_gpr[ra], cpu_gpr[rs]); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[ra]); + } else if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, cpu_gpr[rs]); +#if defined(TARGET_PPC64) + } else if (rs != 0) { /* 0 is nop */ + int prio = 0; + + switch (rs) { + case 1: + /* Set process priority to low */ + prio = 2; + break; + case 6: + /* Set process priority to medium-low */ + prio = 3; + break; + case 2: + /* Set process priority to normal */ + prio = 4; + break; +#if !defined(CONFIG_USER_ONLY) + case 31: + if (!ctx->pr) { + /* Set process priority to very low */ + prio = 1; + } + break; + case 5: + if (!ctx->pr) { + /* Set process priority to medium-hight */ + prio = 5; + } + break; + case 3: + if (!ctx->pr) { + /* Set process priority to high */ + prio = 6; + } + break; + case 7: + if (ctx->hv && !ctx->pr) { + /* Set process priority to very high */ + prio = 7; + } + break; +#endif + default: + break; + } + if (prio) { + TCGv t0 = tcg_temp_new(); + gen_load_spr(t0, SPR_PPR); + tcg_gen_andi_tl(t0, t0, ~0x001C000000000000ULL); + tcg_gen_ori_tl(t0, t0, ((uint64_t)prio) << 50); + gen_store_spr(SPR_PPR, t0); + tcg_temp_free(t0); + } +#if !defined(CONFIG_USER_ONLY) + /* Pause out of TCG otherwise spin loops with smt_low eat too much + * CPU and the kernel hangs. This applies to all encodings other + * than no-op, e.g., miso(rs=26), yield(27), mdoio(29), mdoom(30), + * and all currently undefined. + */ + gen_pause(ctx); +#endif +#endif + } +} +/* orc & orc. */ +GEN_LOGICAL2(orc, tcg_gen_orc_tl, 0x0C, PPC_INTEGER); + +/* xor & xor. */ +static void gen_xor(DisasContext *ctx) +{ + /* Optimisation for "set to zero" case */ + if (rS(ctx->opcode) != rB(ctx->opcode)) + tcg_gen_xor_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + else + tcg_gen_movi_tl(cpu_gpr[rA(ctx->opcode)], 0); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* ori */ +static void gen_ori(DisasContext *ctx) +{ + target_ulong uimm = UIMM(ctx->opcode); + + if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { + return; + } + tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm); +} + +/* oris */ +static void gen_oris(DisasContext *ctx) +{ + target_ulong uimm = UIMM(ctx->opcode); + + if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { + /* NOP */ + return; + } + tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm << 16); +} + +/* xori */ +static void gen_xori(DisasContext *ctx) +{ + target_ulong uimm = UIMM(ctx->opcode); + + if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { + /* NOP */ + return; + } + tcg_gen_xori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm); +} + +/* xoris */ +static void gen_xoris(DisasContext *ctx) +{ + target_ulong uimm = UIMM(ctx->opcode); + + if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { + /* NOP */ + return; + } + tcg_gen_xori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm << 16); +} + +/* popcntb : PowerPC 2.03 specification */ +static void gen_popcntb(DisasContext *ctx) +{ + gen_helper_popcntb(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); +} + +static void gen_popcntw(DisasContext *ctx) +{ + gen_helper_popcntw(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); +} + +#if defined(TARGET_PPC64) +/* popcntd: PowerPC 2.06 specification */ +static void gen_popcntd(DisasContext *ctx) +{ + gen_helper_popcntd(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); +} +#endif + +/* prtyw: PowerPC 2.05 specification */ +static void gen_prtyw(DisasContext *ctx) +{ + TCGv ra = cpu_gpr[rA(ctx->opcode)]; + TCGv rs = cpu_gpr[rS(ctx->opcode)]; + TCGv t0 = tcg_temp_new(); + tcg_gen_shri_tl(t0, rs, 16); + tcg_gen_xor_tl(ra, rs, t0); + tcg_gen_shri_tl(t0, ra, 8); + tcg_gen_xor_tl(ra, ra, t0); + tcg_gen_andi_tl(ra, ra, (target_ulong)0x100000001ULL); + tcg_temp_free(t0); +} + +#if defined(TARGET_PPC64) +/* prtyd: PowerPC 2.05 specification */ +static void gen_prtyd(DisasContext *ctx) +{ + TCGv ra = cpu_gpr[rA(ctx->opcode)]; + TCGv rs = cpu_gpr[rS(ctx->opcode)]; + TCGv t0 = tcg_temp_new(); + tcg_gen_shri_tl(t0, rs, 32); + tcg_gen_xor_tl(ra, rs, t0); + tcg_gen_shri_tl(t0, ra, 16); + tcg_gen_xor_tl(ra, ra, t0); + tcg_gen_shri_tl(t0, ra, 8); + tcg_gen_xor_tl(ra, ra, t0); + tcg_gen_andi_tl(ra, ra, 1); + tcg_temp_free(t0); +} +#endif + +#if defined(TARGET_PPC64) +/* bpermd */ +static void gen_bpermd(DisasContext *ctx) +{ + gen_helper_bpermd(cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); +} +#endif + +#if defined(TARGET_PPC64) +/* extsw & extsw. */ +GEN_LOGICAL1(extsw, tcg_gen_ext32s_tl, 0x1E, PPC_64B); + +/* cntlzd */ +static void gen_cntlzd(DisasContext *ctx) +{ + gen_helper_cntlzd(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* cnttzd */ +static void gen_cnttzd(DisasContext *ctx) +{ + gen_helper_cnttzd(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); + } +} + +/* darn */ +static void gen_darn(DisasContext *ctx) +{ + int l = L(ctx->opcode); + + if (l == 0) { + gen_helper_darn32(cpu_gpr[rD(ctx->opcode)]); + } else if (l <= 2) { + /* Return 64-bit random for both CRN and RRN */ + gen_helper_darn64(cpu_gpr[rD(ctx->opcode)]); + } else { + tcg_gen_movi_i64(cpu_gpr[rD(ctx->opcode)], -1); + } +} +#endif + +/*** Integer rotate ***/ + +/* rlwimi & rlwimi. */ +static void gen_rlwimi(DisasContext *ctx) +{ + TCGv t_ra = cpu_gpr[rA(ctx->opcode)]; + TCGv t_rs = cpu_gpr[rS(ctx->opcode)]; + uint32_t sh = SH(ctx->opcode); + uint32_t mb = MB(ctx->opcode); + uint32_t me = ME(ctx->opcode); + + if (sh == (31-me) && mb <= me) { + tcg_gen_deposit_tl(t_ra, t_ra, t_rs, sh, me - mb + 1); + } else { + target_ulong mask; + TCGv t1; + +#if defined(TARGET_PPC64) + mb += 32; + me += 32; +#endif + mask = MASK(mb, me); + + t1 = tcg_temp_new(); + if (mask <= 0xffffffffu) { + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t0, t_rs); + tcg_gen_rotli_i32(t0, t0, sh); + tcg_gen_extu_i32_tl(t1, t0); + tcg_temp_free_i32(t0); + } else { +#if defined(TARGET_PPC64) + tcg_gen_deposit_i64(t1, t_rs, t_rs, 32, 32); + tcg_gen_rotli_i64(t1, t1, sh); +#else + g_assert_not_reached(); +#endif + } + + tcg_gen_andi_tl(t1, t1, mask); + tcg_gen_andi_tl(t_ra, t_ra, ~mask); + tcg_gen_or_tl(t_ra, t_ra, t1); + tcg_temp_free(t1); + } + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, t_ra); + } +} + +/* rlwinm & rlwinm. */ +static void gen_rlwinm(DisasContext *ctx) +{ + TCGv t_ra = cpu_gpr[rA(ctx->opcode)]; + TCGv t_rs = cpu_gpr[rS(ctx->opcode)]; + uint32_t sh = SH(ctx->opcode); + uint32_t mb = MB(ctx->opcode); + uint32_t me = ME(ctx->opcode); + + if (mb == 0 && me == (31 - sh)) { + tcg_gen_shli_tl(t_ra, t_rs, sh); + tcg_gen_ext32u_tl(t_ra, t_ra); + } else if (sh != 0 && me == 31 && sh == (32 - mb)) { + tcg_gen_ext32u_tl(t_ra, t_rs); + tcg_gen_shri_tl(t_ra, t_ra, mb); + } else { + target_ulong mask; +#if defined(TARGET_PPC64) + mb += 32; + me += 32; +#endif + mask = MASK(mb, me); + + if (mask <= 0xffffffffu) { + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t0, t_rs); + tcg_gen_rotli_i32(t0, t0, sh); + tcg_gen_andi_i32(t0, t0, mask); + tcg_gen_extu_i32_tl(t_ra, t0); + tcg_temp_free_i32(t0); + } else { +#if defined(TARGET_PPC64) + tcg_gen_deposit_i64(t_ra, t_rs, t_rs, 32, 32); + tcg_gen_rotli_i64(t_ra, t_ra, sh); + tcg_gen_andi_i64(t_ra, t_ra, mask); +#else + g_assert_not_reached(); +#endif + } + } + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, t_ra); + } +} + +/* rlwnm & rlwnm. */ +static void gen_rlwnm(DisasContext *ctx) +{ + TCGv t_ra = cpu_gpr[rA(ctx->opcode)]; + TCGv t_rs = cpu_gpr[rS(ctx->opcode)]; + TCGv t_rb = cpu_gpr[rB(ctx->opcode)]; + uint32_t mb = MB(ctx->opcode); + uint32_t me = ME(ctx->opcode); + target_ulong mask; + +#if defined(TARGET_PPC64) + mb += 32; + me += 32; +#endif + mask = MASK(mb, me); + + if (mask <= 0xffffffffu) { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t0, t_rb); + tcg_gen_trunc_tl_i32(t1, t_rs); + tcg_gen_andi_i32(t0, t0, 0x1f); + tcg_gen_rotl_i32(t1, t1, t0); + tcg_gen_extu_i32_tl(t_ra, t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + } else { +#if defined(TARGET_PPC64) + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_andi_i64(t0, t_rb, 0x1f); + tcg_gen_deposit_i64(t_ra, t_rs, t_rs, 32, 32); + tcg_gen_rotl_i64(t_ra, t_ra, t0); + tcg_temp_free_i64(t0); +#else + g_assert_not_reached(); +#endif + } + + tcg_gen_andi_tl(t_ra, t_ra, mask); + + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, t_ra); + } +} + +#if defined(TARGET_PPC64) +#define GEN_PPC64_R2(name, opc1, opc2) \ +static void glue(gen_, name##0)(DisasContext *ctx) \ +{ \ + gen_##name(ctx, 0); \ +} \ + \ +static void glue(gen_, name##1)(DisasContext *ctx) \ +{ \ + gen_##name(ctx, 1); \ +} +#define GEN_PPC64_R4(name, opc1, opc2) \ +static void glue(gen_, name##0)(DisasContext *ctx) \ +{ \ + gen_##name(ctx, 0, 0); \ +} \ + \ +static void glue(gen_, name##1)(DisasContext *ctx) \ +{ \ + gen_##name(ctx, 0, 1); \ +} \ + \ +static void glue(gen_, name##2)(DisasContext *ctx) \ +{ \ + gen_##name(ctx, 1, 0); \ +} \ + \ +static void glue(gen_, name##3)(DisasContext *ctx) \ +{ \ + gen_##name(ctx, 1, 1); \ +} + +static void gen_rldinm(DisasContext *ctx, int mb, int me, int sh) +{ + TCGv t_ra = cpu_gpr[rA(ctx->opcode)]; + TCGv t_rs = cpu_gpr[rS(ctx->opcode)]; + + if (sh != 0 && mb == 0 && me == (63 - sh)) { + tcg_gen_shli_tl(t_ra, t_rs, sh); + } else if (sh != 0 && me == 63 && sh == (64 - mb)) { + tcg_gen_shri_tl(t_ra, t_rs, mb); + } else { + tcg_gen_rotli_tl(t_ra, t_rs, sh); + tcg_gen_andi_tl(t_ra, t_ra, MASK(mb, me)); + } + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, t_ra); + } +} + +/* rldicl - rldicl. */ +static inline void gen_rldicl(DisasContext *ctx, int mbn, int shn) +{ + uint32_t sh, mb; + + sh = SH(ctx->opcode) | (shn << 5); + mb = MB(ctx->opcode) | (mbn << 5); + gen_rldinm(ctx, mb, 63, sh); +} +GEN_PPC64_R4(rldicl, 0x1E, 0x00); + +/* rldicr - rldicr. */ +static inline void gen_rldicr(DisasContext *ctx, int men, int shn) +{ + uint32_t sh, me; + + sh = SH(ctx->opcode) | (shn << 5); + me = MB(ctx->opcode) | (men << 5); + gen_rldinm(ctx, 0, me, sh); +} +GEN_PPC64_R4(rldicr, 0x1E, 0x02); + +/* rldic - rldic. */ +static inline void gen_rldic(DisasContext *ctx, int mbn, int shn) +{ + uint32_t sh, mb; + + sh = SH(ctx->opcode) | (shn << 5); + mb = MB(ctx->opcode) | (mbn << 5); + gen_rldinm(ctx, mb, 63 - sh, sh); +} +GEN_PPC64_R4(rldic, 0x1E, 0x04); + +static void gen_rldnm(DisasContext *ctx, int mb, int me) +{ + TCGv t_ra = cpu_gpr[rA(ctx->opcode)]; + TCGv t_rs = cpu_gpr[rS(ctx->opcode)]; + TCGv t_rb = cpu_gpr[rB(ctx->opcode)]; + TCGv t0; + + t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, t_rb, 0x3f); + tcg_gen_rotl_tl(t_ra, t_rs, t0); + tcg_temp_free(t0); + + tcg_gen_andi_tl(t_ra, t_ra, MASK(mb, me)); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, t_ra); + } +} + +/* rldcl - rldcl. */ +static inline void gen_rldcl(DisasContext *ctx, int mbn) +{ + uint32_t mb; + + mb = MB(ctx->opcode) | (mbn << 5); + gen_rldnm(ctx, mb, 63); +} +GEN_PPC64_R2(rldcl, 0x1E, 0x08); + +/* rldcr - rldcr. */ +static inline void gen_rldcr(DisasContext *ctx, int men) +{ + uint32_t me; + + me = MB(ctx->opcode) | (men << 5); + gen_rldnm(ctx, 0, me); +} +GEN_PPC64_R2(rldcr, 0x1E, 0x09); + +/* rldimi - rldimi. */ +static void gen_rldimi(DisasContext *ctx, int mbn, int shn) +{ + TCGv t_ra = cpu_gpr[rA(ctx->opcode)]; + TCGv t_rs = cpu_gpr[rS(ctx->opcode)]; + uint32_t sh = SH(ctx->opcode) | (shn << 5); + uint32_t mb = MB(ctx->opcode) | (mbn << 5); + uint32_t me = 63 - sh; + + if (mb <= me) { + tcg_gen_deposit_tl(t_ra, t_ra, t_rs, sh, me - mb + 1); + } else { + target_ulong mask = MASK(mb, me); + TCGv t1 = tcg_temp_new(); + + tcg_gen_rotli_tl(t1, t_rs, sh); + tcg_gen_andi_tl(t1, t1, mask); + tcg_gen_andi_tl(t_ra, t_ra, ~mask); + tcg_gen_or_tl(t_ra, t_ra, t1); + tcg_temp_free(t1); + } + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, t_ra); + } +} +GEN_PPC64_R4(rldimi, 0x1E, 0x06); +#endif + +/*** Integer shift ***/ + +/* slw & slw. */ +static void gen_slw(DisasContext *ctx) +{ + TCGv t0, t1; + + t0 = tcg_temp_new(); + /* AND rS with a mask that is 0 when rB >= 0x20 */ +#if defined(TARGET_PPC64) + tcg_gen_shli_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x3a); + tcg_gen_sari_tl(t0, t0, 0x3f); +#else + tcg_gen_shli_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1a); + tcg_gen_sari_tl(t0, t0, 0x1f); +#endif + tcg_gen_andc_tl(t0, cpu_gpr[rS(ctx->opcode)], t0); + t1 = tcg_temp_new(); + tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1f); + tcg_gen_shl_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); + tcg_temp_free(t1); + tcg_temp_free(t0); + tcg_gen_ext32u_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* sraw & sraw. */ +static void gen_sraw(DisasContext *ctx) +{ + gen_helper_sraw(cpu_gpr[rA(ctx->opcode)], cpu_env, + cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* srawi & srawi. */ +static void gen_srawi(DisasContext *ctx) +{ + int sh = SH(ctx->opcode); + TCGv dst = cpu_gpr[rA(ctx->opcode)]; + TCGv src = cpu_gpr[rS(ctx->opcode)]; + if (sh == 0) { + tcg_gen_ext32s_tl(dst, src); + tcg_gen_movi_tl(cpu_ca, 0); + } else { + TCGv t0; + tcg_gen_ext32s_tl(dst, src); + tcg_gen_andi_tl(cpu_ca, dst, (1ULL << sh) - 1); + t0 = tcg_temp_new(); + tcg_gen_sari_tl(t0, dst, TARGET_LONG_BITS - 1); + tcg_gen_and_tl(cpu_ca, cpu_ca, t0); + tcg_temp_free(t0); + tcg_gen_setcondi_tl(TCG_COND_NE, cpu_ca, cpu_ca, 0); + tcg_gen_sari_tl(dst, dst, sh); + } + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, dst); + } +} + +/* srw & srw. */ +static void gen_srw(DisasContext *ctx) +{ + TCGv t0, t1; + + t0 = tcg_temp_new(); + /* AND rS with a mask that is 0 when rB >= 0x20 */ +#if defined(TARGET_PPC64) + tcg_gen_shli_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x3a); + tcg_gen_sari_tl(t0, t0, 0x3f); +#else + tcg_gen_shli_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1a); + tcg_gen_sari_tl(t0, t0, 0x1f); +#endif + tcg_gen_andc_tl(t0, cpu_gpr[rS(ctx->opcode)], t0); + tcg_gen_ext32u_tl(t0, t0); + t1 = tcg_temp_new(); + tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1f); + tcg_gen_shr_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); + tcg_temp_free(t1); + tcg_temp_free(t0); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +#if defined(TARGET_PPC64) +/* sld & sld. */ +static void gen_sld(DisasContext *ctx) +{ + TCGv t0, t1; + + t0 = tcg_temp_new(); + /* AND rS with a mask that is 0 when rB >= 0x40 */ + tcg_gen_shli_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x39); + tcg_gen_sari_tl(t0, t0, 0x3f); + tcg_gen_andc_tl(t0, cpu_gpr[rS(ctx->opcode)], t0); + t1 = tcg_temp_new(); + tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x3f); + tcg_gen_shl_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); + tcg_temp_free(t1); + tcg_temp_free(t0); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* srad & srad. */ +static void gen_srad(DisasContext *ctx) +{ + gen_helper_srad(cpu_gpr[rA(ctx->opcode)], cpu_env, + cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} +/* sradi & sradi. */ +static inline void gen_sradi(DisasContext *ctx, int n) +{ + int sh = SH(ctx->opcode) + (n << 5); + TCGv dst = cpu_gpr[rA(ctx->opcode)]; + TCGv src = cpu_gpr[rS(ctx->opcode)]; + if (sh == 0) { + tcg_gen_mov_tl(dst, src); + tcg_gen_movi_tl(cpu_ca, 0); + } else { + TCGv t0; + tcg_gen_andi_tl(cpu_ca, src, (1ULL << sh) - 1); + t0 = tcg_temp_new(); + tcg_gen_sari_tl(t0, src, TARGET_LONG_BITS - 1); + tcg_gen_and_tl(cpu_ca, cpu_ca, t0); + tcg_temp_free(t0); + tcg_gen_setcondi_tl(TCG_COND_NE, cpu_ca, cpu_ca, 0); + tcg_gen_sari_tl(dst, src, sh); + } + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, dst); + } +} + +static void gen_sradi0(DisasContext *ctx) +{ + gen_sradi(ctx, 0); +} + +static void gen_sradi1(DisasContext *ctx) +{ + gen_sradi(ctx, 1); +} + +/* extswsli & extswsli. */ +static inline void gen_extswsli(DisasContext *ctx, int n) +{ + int sh = SH(ctx->opcode) + (n << 5); + TCGv dst = cpu_gpr[rA(ctx->opcode)]; + TCGv src = cpu_gpr[rS(ctx->opcode)]; + + tcg_gen_ext32s_tl(dst, src); + tcg_gen_shli_tl(dst, dst, sh); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, dst); + } +} + +static void gen_extswsli0(DisasContext *ctx) +{ + gen_extswsli(ctx, 0); +} + +static void gen_extswsli1(DisasContext *ctx) +{ + gen_extswsli(ctx, 1); +} + +/* srd & srd. */ +static void gen_srd(DisasContext *ctx) +{ + TCGv t0, t1; + + t0 = tcg_temp_new(); + /* AND rS with a mask that is 0 when rB >= 0x40 */ + tcg_gen_shli_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x39); + tcg_gen_sari_tl(t0, t0, 0x3f); + tcg_gen_andc_tl(t0, cpu_gpr[rS(ctx->opcode)], t0); + t1 = tcg_temp_new(); + tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x3f); + tcg_gen_shr_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); + tcg_temp_free(t1); + tcg_temp_free(t0); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} +#endif + +/*** Addressing modes ***/ +/* Register indirect with immediate index : EA = (rA|0) + SIMM */ +static inline void gen_addr_imm_index(DisasContext *ctx, TCGv EA, + target_long maskl) +{ + target_long simm = SIMM(ctx->opcode); + + simm &= ~maskl; + if (rA(ctx->opcode) == 0) { + if (NARROW_MODE(ctx)) { + simm = (uint32_t)simm; + } + tcg_gen_movi_tl(EA, simm); + } else if (likely(simm != 0)) { + tcg_gen_addi_tl(EA, cpu_gpr[rA(ctx->opcode)], simm); + if (NARROW_MODE(ctx)) { + tcg_gen_ext32u_tl(EA, EA); + } + } else { + if (NARROW_MODE(ctx)) { + tcg_gen_ext32u_tl(EA, cpu_gpr[rA(ctx->opcode)]); + } else { + tcg_gen_mov_tl(EA, cpu_gpr[rA(ctx->opcode)]); + } + } +} + +static inline void gen_addr_reg_index(DisasContext *ctx, TCGv EA) +{ + if (rA(ctx->opcode) == 0) { + if (NARROW_MODE(ctx)) { + tcg_gen_ext32u_tl(EA, cpu_gpr[rB(ctx->opcode)]); + } else { + tcg_gen_mov_tl(EA, cpu_gpr[rB(ctx->opcode)]); + } + } else { + tcg_gen_add_tl(EA, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + if (NARROW_MODE(ctx)) { + tcg_gen_ext32u_tl(EA, EA); + } + } +} + +static inline void gen_addr_register(DisasContext *ctx, TCGv EA) +{ + if (rA(ctx->opcode) == 0) { + tcg_gen_movi_tl(EA, 0); + } else if (NARROW_MODE(ctx)) { + tcg_gen_ext32u_tl(EA, cpu_gpr[rA(ctx->opcode)]); + } else { + tcg_gen_mov_tl(EA, cpu_gpr[rA(ctx->opcode)]); + } +} + +static inline void gen_addr_add(DisasContext *ctx, TCGv ret, TCGv arg1, + target_long val) +{ + tcg_gen_addi_tl(ret, arg1, val); + if (NARROW_MODE(ctx)) { + tcg_gen_ext32u_tl(ret, ret); + } +} + +static inline void gen_check_align(DisasContext *ctx, TCGv EA, int mask) +{ + TCGLabel *l1 = gen_new_label(); + TCGv t0 = tcg_temp_new(); + TCGv_i32 t1, t2; + tcg_gen_andi_tl(t0, EA, mask); + tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); + t1 = tcg_const_i32(POWERPC_EXCP_ALIGN); + t2 = tcg_const_i32(ctx->opcode & 0x03FF0000); + gen_update_nip(ctx, ctx->nip - 4); + gen_helper_raise_exception_err(cpu_env, t1, t2); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t2); + gen_set_label(l1); + tcg_temp_free(t0); +} + +static inline void gen_align_no_le(DisasContext *ctx) +{ + gen_exception_err(ctx, POWERPC_EXCP_ALIGN, + (ctx->opcode & 0x03FF0000) | POWERPC_EXCP_ALIGN_LE); +} + +/*** Integer load ***/ +#define DEF_MEMOP(op) ((op) | ctx->default_tcg_memop_mask) +#define BSWAP_MEMOP(op) ((op) | (ctx->default_tcg_memop_mask ^ MO_BSWAP)) + +#define GEN_QEMU_LOAD_TL(ldop, op) \ +static void glue(gen_qemu_, ldop)(DisasContext *ctx, \ + TCGv val, \ + TCGv addr) \ +{ \ + tcg_gen_qemu_ld_tl(val, addr, ctx->mem_idx, op); \ +} + +GEN_QEMU_LOAD_TL(ld8u, DEF_MEMOP(MO_UB)) +GEN_QEMU_LOAD_TL(ld16u, DEF_MEMOP(MO_UW)) +GEN_QEMU_LOAD_TL(ld16s, DEF_MEMOP(MO_SW)) +GEN_QEMU_LOAD_TL(ld32u, DEF_MEMOP(MO_UL)) +GEN_QEMU_LOAD_TL(ld32s, DEF_MEMOP(MO_SL)) + +GEN_QEMU_LOAD_TL(ld16ur, BSWAP_MEMOP(MO_UW)) +GEN_QEMU_LOAD_TL(ld32ur, BSWAP_MEMOP(MO_UL)) + +#define GEN_QEMU_LOAD_64(ldop, op) \ +static void glue(gen_qemu_, glue(ldop, _i64))(DisasContext *ctx, \ + TCGv_i64 val, \ + TCGv addr) \ +{ \ + tcg_gen_qemu_ld_i64(val, addr, ctx->mem_idx, op); \ +} + +GEN_QEMU_LOAD_64(ld8u, DEF_MEMOP(MO_UB)) +GEN_QEMU_LOAD_64(ld16u, DEF_MEMOP(MO_UW)) +GEN_QEMU_LOAD_64(ld32u, DEF_MEMOP(MO_UL)) +GEN_QEMU_LOAD_64(ld32s, DEF_MEMOP(MO_SL)) +GEN_QEMU_LOAD_64(ld64, DEF_MEMOP(MO_Q)) + +#if defined(TARGET_PPC64) +GEN_QEMU_LOAD_64(ld64ur, BSWAP_MEMOP(MO_Q)) +#endif + +#define GEN_QEMU_STORE_TL(stop, op) \ +static void glue(gen_qemu_, stop)(DisasContext *ctx, \ + TCGv val, \ + TCGv addr) \ +{ \ + tcg_gen_qemu_st_tl(val, addr, ctx->mem_idx, op); \ +} + +GEN_QEMU_STORE_TL(st8, DEF_MEMOP(MO_UB)) +GEN_QEMU_STORE_TL(st16, DEF_MEMOP(MO_UW)) +GEN_QEMU_STORE_TL(st32, DEF_MEMOP(MO_UL)) + +GEN_QEMU_STORE_TL(st16r, BSWAP_MEMOP(MO_UW)) +GEN_QEMU_STORE_TL(st32r, BSWAP_MEMOP(MO_UL)) + +#define GEN_QEMU_STORE_64(stop, op) \ +static void glue(gen_qemu_, glue(stop, _i64))(DisasContext *ctx, \ + TCGv_i64 val, \ + TCGv addr) \ +{ \ + tcg_gen_qemu_st_i64(val, addr, ctx->mem_idx, op); \ +} + +GEN_QEMU_STORE_64(st8, DEF_MEMOP(MO_UB)) +GEN_QEMU_STORE_64(st16, DEF_MEMOP(MO_UW)) +GEN_QEMU_STORE_64(st32, DEF_MEMOP(MO_UL)) +GEN_QEMU_STORE_64(st64, DEF_MEMOP(MO_Q)) + +#if defined(TARGET_PPC64) +GEN_QEMU_STORE_64(st64r, BSWAP_MEMOP(MO_Q)) +#endif + +#define GEN_LD(name, ldop, opc, type) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_imm_index(ctx, EA, 0); \ + gen_qemu_##ldop(ctx, cpu_gpr[rD(ctx->opcode)], EA); \ + tcg_temp_free(EA); \ +} + +#define GEN_LDU(name, ldop, opc, type) \ +static void glue(gen_, name##u)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(rA(ctx->opcode) == 0 || \ + rA(ctx->opcode) == rD(ctx->opcode))) { \ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + if (type == PPC_64B) \ + gen_addr_imm_index(ctx, EA, 0x03); \ + else \ + gen_addr_imm_index(ctx, EA, 0); \ + gen_qemu_##ldop(ctx, cpu_gpr[rD(ctx->opcode)], EA); \ + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \ + tcg_temp_free(EA); \ +} + +#define GEN_LDUX(name, ldop, opc2, opc3, type) \ +static void glue(gen_, name##ux)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(rA(ctx->opcode) == 0 || \ + rA(ctx->opcode) == rD(ctx->opcode))) { \ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + gen_qemu_##ldop(ctx, cpu_gpr[rD(ctx->opcode)], EA); \ + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \ + tcg_temp_free(EA); \ +} + +#define GEN_LDX_E(name, ldop, opc2, opc3, type, type2, chk) \ +static void glue(gen_, name##x)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + chk; \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + gen_qemu_##ldop(ctx, cpu_gpr[rD(ctx->opcode)], EA); \ + tcg_temp_free(EA); \ +} + +#define GEN_LDX(name, ldop, opc2, opc3, type) \ + GEN_LDX_E(name, ldop, opc2, opc3, type, PPC_NONE, CHK_NONE) + +#define GEN_LDX_HVRM(name, ldop, opc2, opc3, type) \ + GEN_LDX_E(name, ldop, opc2, opc3, type, PPC_NONE, CHK_HVRM) + +#define GEN_LDS(name, ldop, op, type) \ +GEN_LD(name, ldop, op | 0x20, type); \ +GEN_LDU(name, ldop, op | 0x21, type); \ +GEN_LDUX(name, ldop, 0x17, op | 0x01, type); \ +GEN_LDX(name, ldop, 0x17, op | 0x00, type) + +/* lbz lbzu lbzux lbzx */ +GEN_LDS(lbz, ld8u, 0x02, PPC_INTEGER); +/* lha lhau lhaux lhax */ +GEN_LDS(lha, ld16s, 0x0A, PPC_INTEGER); +/* lhz lhzu lhzux lhzx */ +GEN_LDS(lhz, ld16u, 0x08, PPC_INTEGER); +/* lwz lwzu lwzux lwzx */ +GEN_LDS(lwz, ld32u, 0x00, PPC_INTEGER); +#if defined(TARGET_PPC64) +/* lwaux */ +GEN_LDUX(lwa, ld32s, 0x15, 0x0B, PPC_64B); +/* lwax */ +GEN_LDX(lwa, ld32s, 0x15, 0x0A, PPC_64B); +/* ldux */ +GEN_LDUX(ld, ld64_i64, 0x15, 0x01, PPC_64B); +/* ldx */ +GEN_LDX(ld, ld64_i64, 0x15, 0x00, PPC_64B); + +/* CI load/store variants */ +GEN_LDX_HVRM(ldcix, ld64_i64, 0x15, 0x1b, PPC_CILDST) +GEN_LDX_HVRM(lwzcix, ld32u, 0x15, 0x15, PPC_CILDST) +GEN_LDX_HVRM(lhzcix, ld16u, 0x15, 0x19, PPC_CILDST) +GEN_LDX_HVRM(lbzcix, ld8u, 0x15, 0x1a, PPC_CILDST) + +static void gen_ld(DisasContext *ctx) +{ + TCGv EA; + if (Rc(ctx->opcode)) { + if (unlikely(rA(ctx->opcode) == 0 || + rA(ctx->opcode) == rD(ctx->opcode))) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + return; + } + } + gen_set_access_type(ctx, ACCESS_INT); + EA = tcg_temp_new(); + gen_addr_imm_index(ctx, EA, 0x03); + if (ctx->opcode & 0x02) { + /* lwa (lwau is undefined) */ + gen_qemu_ld32s(ctx, cpu_gpr[rD(ctx->opcode)], EA); + } else { + /* ld - ldu */ + gen_qemu_ld64_i64(ctx, cpu_gpr[rD(ctx->opcode)], EA); + } + if (Rc(ctx->opcode)) + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); + tcg_temp_free(EA); +} + +/* lq */ +static void gen_lq(DisasContext *ctx) +{ + int ra, rd; + TCGv EA; + + /* lq is a legal user mode instruction starting in ISA 2.07 */ + bool legal_in_user_mode = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; + bool le_is_supported = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; + + if (!legal_in_user_mode && ctx->pr) { + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); + return; + } + + if (!le_is_supported && ctx->le_mode) { + gen_align_no_le(ctx); + return; + } + ra = rA(ctx->opcode); + rd = rD(ctx->opcode); + if (unlikely((rd & 1) || rd == ra)) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + return; + } + + gen_set_access_type(ctx, ACCESS_INT); + EA = tcg_temp_new(); + gen_addr_imm_index(ctx, EA, 0x0F); + + /* We only need to swap high and low halves. gen_qemu_ld64_i64 does + necessary 64-bit byteswap already. */ + if (unlikely(ctx->le_mode)) { + gen_qemu_ld64_i64(ctx, cpu_gpr[rd + 1], EA); + gen_addr_add(ctx, EA, EA, 8); + gen_qemu_ld64_i64(ctx, cpu_gpr[rd], EA); + } else { + gen_qemu_ld64_i64(ctx, cpu_gpr[rd], EA); + gen_addr_add(ctx, EA, EA, 8); + gen_qemu_ld64_i64(ctx, cpu_gpr[rd + 1], EA); + } + tcg_temp_free(EA); +} +#endif + +/*** Integer store ***/ +#define GEN_ST(name, stop, opc, type) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_imm_index(ctx, EA, 0); \ + gen_qemu_##stop(ctx, cpu_gpr[rS(ctx->opcode)], EA); \ + tcg_temp_free(EA); \ +} + +#define GEN_STU(name, stop, opc, type) \ +static void glue(gen_, stop##u)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(rA(ctx->opcode) == 0)) { \ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + if (type == PPC_64B) \ + gen_addr_imm_index(ctx, EA, 0x03); \ + else \ + gen_addr_imm_index(ctx, EA, 0); \ + gen_qemu_##stop(ctx, cpu_gpr[rS(ctx->opcode)], EA); \ + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \ + tcg_temp_free(EA); \ +} + +#define GEN_STUX(name, stop, opc2, opc3, type) \ +static void glue(gen_, name##ux)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(rA(ctx->opcode) == 0)) { \ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + gen_qemu_##stop(ctx, cpu_gpr[rS(ctx->opcode)], EA); \ + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \ + tcg_temp_free(EA); \ +} + +#define GEN_STX_E(name, stop, opc2, opc3, type, type2, chk) \ +static void glue(gen_, name##x)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + chk; \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + gen_qemu_##stop(ctx, cpu_gpr[rS(ctx->opcode)], EA); \ + tcg_temp_free(EA); \ +} +#define GEN_STX(name, stop, opc2, opc3, type) \ + GEN_STX_E(name, stop, opc2, opc3, type, PPC_NONE, CHK_NONE) + +#define GEN_STX_HVRM(name, stop, opc2, opc3, type) \ + GEN_STX_E(name, stop, opc2, opc3, type, PPC_NONE, CHK_HVRM) + +#define GEN_STS(name, stop, op, type) \ +GEN_ST(name, stop, op | 0x20, type); \ +GEN_STU(name, stop, op | 0x21, type); \ +GEN_STUX(name, stop, 0x17, op | 0x01, type); \ +GEN_STX(name, stop, 0x17, op | 0x00, type) + +/* stb stbu stbux stbx */ +GEN_STS(stb, st8, 0x06, PPC_INTEGER); +/* sth sthu sthux sthx */ +GEN_STS(sth, st16, 0x0C, PPC_INTEGER); +/* stw stwu stwux stwx */ +GEN_STS(stw, st32, 0x04, PPC_INTEGER); +#if defined(TARGET_PPC64) +GEN_STUX(std, st64_i64, 0x15, 0x05, PPC_64B); +GEN_STX(std, st64_i64, 0x15, 0x04, PPC_64B); +GEN_STX_HVRM(stdcix, st64_i64, 0x15, 0x1f, PPC_CILDST) +GEN_STX_HVRM(stwcix, st32, 0x15, 0x1c, PPC_CILDST) +GEN_STX_HVRM(sthcix, st16, 0x15, 0x1d, PPC_CILDST) +GEN_STX_HVRM(stbcix, st8, 0x15, 0x1e, PPC_CILDST) + +static void gen_std(DisasContext *ctx) +{ + int rs; + TCGv EA; + + rs = rS(ctx->opcode); + if ((ctx->opcode & 0x3) == 0x2) { /* stq */ + bool legal_in_user_mode = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; + bool le_is_supported = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; + + if (!(ctx->insns_flags & PPC_64BX)) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + } + + if (!legal_in_user_mode && ctx->pr) { + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); + return; + } + + if (!le_is_supported && ctx->le_mode) { + gen_align_no_le(ctx); + return; + } + + if (unlikely(rs & 1)) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + return; + } + gen_set_access_type(ctx, ACCESS_INT); + EA = tcg_temp_new(); + gen_addr_imm_index(ctx, EA, 0x03); + + /* We only need to swap high and low halves. gen_qemu_st64_i64 does + necessary 64-bit byteswap already. */ + if (unlikely(ctx->le_mode)) { + gen_qemu_st64_i64(ctx, cpu_gpr[rs + 1], EA); + gen_addr_add(ctx, EA, EA, 8); + gen_qemu_st64_i64(ctx, cpu_gpr[rs], EA); + } else { + gen_qemu_st64_i64(ctx, cpu_gpr[rs], EA); + gen_addr_add(ctx, EA, EA, 8); + gen_qemu_st64_i64(ctx, cpu_gpr[rs + 1], EA); + } + tcg_temp_free(EA); + } else { + /* std / stdu*/ + if (Rc(ctx->opcode)) { + if (unlikely(rA(ctx->opcode) == 0)) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + return; + } + } + gen_set_access_type(ctx, ACCESS_INT); + EA = tcg_temp_new(); + gen_addr_imm_index(ctx, EA, 0x03); + gen_qemu_st64_i64(ctx, cpu_gpr[rs], EA); + if (Rc(ctx->opcode)) + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); + tcg_temp_free(EA); + } +} +#endif +/*** Integer load and store with byte reverse ***/ + +/* lhbrx */ +GEN_LDX(lhbr, ld16ur, 0x16, 0x18, PPC_INTEGER); + +/* lwbrx */ +GEN_LDX(lwbr, ld32ur, 0x16, 0x10, PPC_INTEGER); + +#if defined(TARGET_PPC64) +/* ldbrx */ +GEN_LDX_E(ldbr, ld64ur_i64, 0x14, 0x10, PPC_NONE, PPC2_DBRX, CHK_NONE); +/* stdbrx */ +GEN_STX_E(stdbr, st64r_i64, 0x14, 0x14, PPC_NONE, PPC2_DBRX, CHK_NONE); +#endif /* TARGET_PPC64 */ + +/* sthbrx */ +GEN_STX(sthbr, st16r, 0x16, 0x1C, PPC_INTEGER); +/* stwbrx */ +GEN_STX(stwbr, st32r, 0x16, 0x14, PPC_INTEGER); + +/*** Integer load and store multiple ***/ + +/* lmw */ +static void gen_lmw(DisasContext *ctx) +{ + TCGv t0; + TCGv_i32 t1; + + if (ctx->le_mode) { + gen_align_no_le(ctx); + return; + } + gen_set_access_type(ctx, ACCESS_INT); + t0 = tcg_temp_new(); + t1 = tcg_const_i32(rD(ctx->opcode)); + gen_addr_imm_index(ctx, t0, 0); + gen_helper_lmw(cpu_env, t0, t1); + tcg_temp_free(t0); + tcg_temp_free_i32(t1); +} + +/* stmw */ +static void gen_stmw(DisasContext *ctx) +{ + TCGv t0; + TCGv_i32 t1; + + if (ctx->le_mode) { + gen_align_no_le(ctx); + return; + } + gen_set_access_type(ctx, ACCESS_INT); + t0 = tcg_temp_new(); + t1 = tcg_const_i32(rS(ctx->opcode)); + gen_addr_imm_index(ctx, t0, 0); + gen_helper_stmw(cpu_env, t0, t1); + tcg_temp_free(t0); + tcg_temp_free_i32(t1); +} + +/*** Integer load and store strings ***/ + +/* lswi */ +/* PowerPC32 specification says we must generate an exception if + * rA is in the range of registers to be loaded. + * In an other hand, IBM says this is valid, but rA won't be loaded. + * For now, I'll follow the spec... + */ +static void gen_lswi(DisasContext *ctx) +{ + TCGv t0; + TCGv_i32 t1, t2; + int nb = NB(ctx->opcode); + int start = rD(ctx->opcode); + int ra = rA(ctx->opcode); + int nr; + + if (ctx->le_mode) { + gen_align_no_le(ctx); + return; + } + if (nb == 0) + nb = 32; + nr = (nb + 3) / 4; + if (unlikely(lsw_reg_in_range(start, nr, ra))) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_LSWX); + return; + } + gen_set_access_type(ctx, ACCESS_INT); + t0 = tcg_temp_new(); + gen_addr_register(ctx, t0); + t1 = tcg_const_i32(nb); + t2 = tcg_const_i32(start); + gen_helper_lsw(cpu_env, t0, t1, t2); + tcg_temp_free(t0); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t2); +} + +/* lswx */ +static void gen_lswx(DisasContext *ctx) +{ + TCGv t0; + TCGv_i32 t1, t2, t3; + + if (ctx->le_mode) { + gen_align_no_le(ctx); + return; + } + gen_set_access_type(ctx, ACCESS_INT); + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + t1 = tcg_const_i32(rD(ctx->opcode)); + t2 = tcg_const_i32(rA(ctx->opcode)); + t3 = tcg_const_i32(rB(ctx->opcode)); + gen_helper_lswx(cpu_env, t0, t1, t2, t3); + tcg_temp_free(t0); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); +} + +/* stswi */ +static void gen_stswi(DisasContext *ctx) +{ + TCGv t0; + TCGv_i32 t1, t2; + int nb = NB(ctx->opcode); + + if (ctx->le_mode) { + gen_align_no_le(ctx); + return; + } + gen_set_access_type(ctx, ACCESS_INT); + t0 = tcg_temp_new(); + gen_addr_register(ctx, t0); + if (nb == 0) + nb = 32; + t1 = tcg_const_i32(nb); + t2 = tcg_const_i32(rS(ctx->opcode)); + gen_helper_stsw(cpu_env, t0, t1, t2); + tcg_temp_free(t0); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t2); +} + +/* stswx */ +static void gen_stswx(DisasContext *ctx) +{ + TCGv t0; + TCGv_i32 t1, t2; + + if (ctx->le_mode) { + gen_align_no_le(ctx); + return; + } + gen_set_access_type(ctx, ACCESS_INT); + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + t1 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t1, cpu_xer); + tcg_gen_andi_i32(t1, t1, 0x7F); + t2 = tcg_const_i32(rS(ctx->opcode)); + gen_helper_stsw(cpu_env, t0, t1, t2); + tcg_temp_free(t0); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t2); +} + +/*** Memory synchronisation ***/ +/* eieio */ +static void gen_eieio(DisasContext *ctx) +{ +} + +#if !defined(CONFIG_USER_ONLY) +static inline void gen_check_tlb_flush(DisasContext *ctx, bool global) +{ + TCGv_i32 t; + TCGLabel *l; + + if (!ctx->lazy_tlb_flush) { + return; + } + l = gen_new_label(); + t = tcg_temp_new_i32(); + tcg_gen_ld_i32(t, cpu_env, offsetof(CPUPPCState, tlb_need_flush)); + tcg_gen_brcondi_i32(TCG_COND_EQ, t, 0, l); + if (global) { + gen_helper_check_tlb_flush_global(cpu_env); + } else { + gen_helper_check_tlb_flush_local(cpu_env); + } + gen_set_label(l); + tcg_temp_free_i32(t); +} +#else +static inline void gen_check_tlb_flush(DisasContext *ctx, bool global) { } +#endif + +/* isync */ +static void gen_isync(DisasContext *ctx) +{ + /* + * We need to check for a pending TLB flush. This can only happen in + * kernel mode however so check MSR_PR + */ + if (!ctx->pr) { + gen_check_tlb_flush(ctx, false); + } + gen_stop_exception(ctx); +} + +#define MEMOP_GET_SIZE(x) (1 << ((x) & MO_SIZE)) + +#define LARX(name, memop) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv t0; \ + TCGv gpr = cpu_gpr[rD(ctx->opcode)]; \ + int len = MEMOP_GET_SIZE(memop); \ + gen_set_access_type(ctx, ACCESS_RES); \ + t0 = tcg_temp_local_new(); \ + gen_addr_reg_index(ctx, t0); \ + if ((len) > 1) { \ + gen_check_align(ctx, t0, (len)-1); \ + } \ + tcg_gen_qemu_ld_tl(gpr, t0, ctx->mem_idx, memop); \ + tcg_gen_mov_tl(cpu_reserve, t0); \ + tcg_gen_st_tl(gpr, cpu_env, offsetof(CPUPPCState, reserve_val)); \ + tcg_temp_free(t0); \ +} + +/* lwarx */ +LARX(lbarx, DEF_MEMOP(MO_UB)) +LARX(lharx, DEF_MEMOP(MO_UW)) +LARX(lwarx, DEF_MEMOP(MO_UL)) + +#if defined(CONFIG_USER_ONLY) +static void gen_conditional_store(DisasContext *ctx, TCGv EA, + int reg, int memop) +{ + TCGv t0 = tcg_temp_new(); + + tcg_gen_st_tl(EA, cpu_env, offsetof(CPUPPCState, reserve_ea)); + tcg_gen_movi_tl(t0, (MEMOP_GET_SIZE(memop) << 5) | reg); + tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, reserve_info)); + tcg_temp_free(t0); + gen_exception_err(ctx, POWERPC_EXCP_STCX, 0); +} +#else +static void gen_conditional_store(DisasContext *ctx, TCGv EA, + int reg, int memop) +{ + TCGLabel *l1; + + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); + l1 = gen_new_label(); + tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, l1); + tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ); + tcg_gen_qemu_st_tl(cpu_gpr[reg], EA, ctx->mem_idx, memop); + gen_set_label(l1); + tcg_gen_movi_tl(cpu_reserve, -1); +} +#endif + +#define STCX(name, memop) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv t0; \ + int len = MEMOP_GET_SIZE(memop); \ + gen_set_access_type(ctx, ACCESS_RES); \ + t0 = tcg_temp_local_new(); \ + gen_addr_reg_index(ctx, t0); \ + if (len > 1) { \ + gen_check_align(ctx, t0, (len) - 1); \ + } \ + gen_conditional_store(ctx, t0, rS(ctx->opcode), memop); \ + tcg_temp_free(t0); \ +} + +STCX(stbcx_, DEF_MEMOP(MO_UB)) +STCX(sthcx_, DEF_MEMOP(MO_UW)) +STCX(stwcx_, DEF_MEMOP(MO_UL)) + +#if defined(TARGET_PPC64) +/* ldarx */ +LARX(ldarx, DEF_MEMOP(MO_Q)) +/* stdcx. */ +STCX(stdcx_, DEF_MEMOP(MO_Q)) + +/* lqarx */ +static void gen_lqarx(DisasContext *ctx) +{ + TCGv EA; + int rd = rD(ctx->opcode); + TCGv gpr1, gpr2; + + if (unlikely((rd & 1) || (rd == rA(ctx->opcode)) || + (rd == rB(ctx->opcode)))) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + return; + } + + gen_set_access_type(ctx, ACCESS_RES); + EA = tcg_temp_local_new(); + gen_addr_reg_index(ctx, EA); + gen_check_align(ctx, EA, 15); + if (unlikely(ctx->le_mode)) { + gpr1 = cpu_gpr[rd+1]; + gpr2 = cpu_gpr[rd]; + } else { + gpr1 = cpu_gpr[rd]; + gpr2 = cpu_gpr[rd+1]; + } + tcg_gen_qemu_ld_i64(gpr1, EA, ctx->mem_idx, DEF_MEMOP(MO_Q)); + tcg_gen_mov_tl(cpu_reserve, EA); + gen_addr_add(ctx, EA, EA, 8); + tcg_gen_qemu_ld_i64(gpr2, EA, ctx->mem_idx, DEF_MEMOP(MO_Q)); + + tcg_gen_st_tl(gpr1, cpu_env, offsetof(CPUPPCState, reserve_val)); + tcg_gen_st_tl(gpr2, cpu_env, offsetof(CPUPPCState, reserve_val2)); + tcg_temp_free(EA); +} + +/* stqcx. */ +static void gen_stqcx_(DisasContext *ctx) +{ + TCGv EA; + int reg = rS(ctx->opcode); + int len = 16; +#if !defined(CONFIG_USER_ONLY) + TCGLabel *l1; + TCGv gpr1, gpr2; +#endif + + if (unlikely((rD(ctx->opcode) & 1))) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + return; + } + gen_set_access_type(ctx, ACCESS_RES); + EA = tcg_temp_local_new(); + gen_addr_reg_index(ctx, EA); + if (len > 1) { + gen_check_align(ctx, EA, (len) - 1); + } + +#if defined(CONFIG_USER_ONLY) + gen_conditional_store(ctx, EA, reg, 16); +#else + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); + l1 = gen_new_label(); + tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, l1); + tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ); + + if (unlikely(ctx->le_mode)) { + gpr1 = cpu_gpr[reg + 1]; + gpr2 = cpu_gpr[reg]; + } else { + gpr1 = cpu_gpr[reg]; + gpr2 = cpu_gpr[reg + 1]; + } + tcg_gen_qemu_st_tl(gpr1, EA, ctx->mem_idx, DEF_MEMOP(MO_Q)); + gen_addr_add(ctx, EA, EA, 8); + tcg_gen_qemu_st_tl(gpr2, EA, ctx->mem_idx, DEF_MEMOP(MO_Q)); + + gen_set_label(l1); + tcg_gen_movi_tl(cpu_reserve, -1); +#endif + tcg_temp_free(EA); +} + +#endif /* defined(TARGET_PPC64) */ + +/* sync */ +static void gen_sync(DisasContext *ctx) +{ + uint32_t l = (ctx->opcode >> 21) & 3; + + /* + * We may need to check for a pending TLB flush. + * + * We do this on ptesync (l == 2) on ppc64 and any sync pn ppc32. + * + * Additionally, this can only happen in kernel mode however so + * check MSR_PR as well. + */ + if (((l == 2) || !(ctx->insns_flags & PPC_64B)) && !ctx->pr) { + gen_check_tlb_flush(ctx, true); + } +} + +/* wait */ +static void gen_wait(DisasContext *ctx) +{ + TCGv_i32 t0 = tcg_const_i32(1); + tcg_gen_st_i32(t0, cpu_env, + -offsetof(PowerPCCPU, env) + offsetof(CPUState, halted)); + tcg_temp_free_i32(t0); + /* Stop translation, as the CPU is supposed to sleep from now */ + gen_exception_nip(ctx, EXCP_HLT, ctx->nip); +} + +#if defined(TARGET_PPC64) +static void gen_doze(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv_i32 t; + + CHK_HV; + t = tcg_const_i32(PPC_PM_DOZE); + gen_helper_pminsn(cpu_env, t); + tcg_temp_free_i32(t); + gen_stop_exception(ctx); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_nap(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv_i32 t; + + CHK_HV; + t = tcg_const_i32(PPC_PM_NAP); + gen_helper_pminsn(cpu_env, t); + tcg_temp_free_i32(t); + gen_stop_exception(ctx); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_sleep(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv_i32 t; + + CHK_HV; + t = tcg_const_i32(PPC_PM_SLEEP); + gen_helper_pminsn(cpu_env, t); + tcg_temp_free_i32(t); + gen_stop_exception(ctx); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_rvwinkle(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv_i32 t; + + CHK_HV; + t = tcg_const_i32(PPC_PM_RVWINKLE); + gen_helper_pminsn(cpu_env, t); + tcg_temp_free_i32(t); + gen_stop_exception(ctx); +#endif /* defined(CONFIG_USER_ONLY) */ +} +#endif /* #if defined(TARGET_PPC64) */ + +static inline void gen_update_cfar(DisasContext *ctx, target_ulong nip) +{ +#if defined(TARGET_PPC64) + if (ctx->has_cfar) + tcg_gen_movi_tl(cpu_cfar, nip); +#endif +} + +static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest) +{ + if (unlikely(ctx->singlestep_enabled)) { + return false; + } + +#ifndef CONFIG_USER_ONLY + return (ctx->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); +#else + return true; +#endif +} + +/*** Branch ***/ +static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) +{ + if (NARROW_MODE(ctx)) { + dest = (uint32_t) dest; + } + if (use_goto_tb(ctx, dest)) { + tcg_gen_goto_tb(n); + tcg_gen_movi_tl(cpu_nip, dest & ~3); + tcg_gen_exit_tb((uintptr_t)ctx->tb + n); + } else { + tcg_gen_movi_tl(cpu_nip, dest & ~3); + if (unlikely(ctx->singlestep_enabled)) { + if ((ctx->singlestep_enabled & + (CPU_BRANCH_STEP | CPU_SINGLE_STEP)) && + (ctx->exception == POWERPC_EXCP_BRANCH || + ctx->exception == POWERPC_EXCP_TRACE)) { + gen_exception_nip(ctx, POWERPC_EXCP_TRACE, dest); + } + if (ctx->singlestep_enabled & GDBSTUB_SINGLE_STEP) { + gen_debug_exception(ctx); + } + } + tcg_gen_exit_tb(0); + } +} + +static inline void gen_setlr(DisasContext *ctx, target_ulong nip) +{ + if (NARROW_MODE(ctx)) { + nip = (uint32_t)nip; + } + tcg_gen_movi_tl(cpu_lr, nip); +} + +/* b ba bl bla */ +static void gen_b(DisasContext *ctx) +{ + target_ulong li, target; + + ctx->exception = POWERPC_EXCP_BRANCH; + /* sign extend LI */ + li = LI(ctx->opcode); + li = (li ^ 0x02000000) - 0x02000000; + if (likely(AA(ctx->opcode) == 0)) { + target = ctx->nip + li - 4; + } else { + target = li; + } + if (LK(ctx->opcode)) { + gen_setlr(ctx, ctx->nip); + } + gen_update_cfar(ctx, ctx->nip - 4); + gen_goto_tb(ctx, 0, target); +} + +#define BCOND_IM 0 +#define BCOND_LR 1 +#define BCOND_CTR 2 +#define BCOND_TAR 3 + +static inline void gen_bcond(DisasContext *ctx, int type) +{ + uint32_t bo = BO(ctx->opcode); + TCGLabel *l1; + TCGv target; + + ctx->exception = POWERPC_EXCP_BRANCH; + if (type == BCOND_LR || type == BCOND_CTR || type == BCOND_TAR) { + target = tcg_temp_local_new(); + if (type == BCOND_CTR) + tcg_gen_mov_tl(target, cpu_ctr); + else if (type == BCOND_TAR) + gen_load_spr(target, SPR_TAR); + else + tcg_gen_mov_tl(target, cpu_lr); + } else { + TCGV_UNUSED(target); + } + if (LK(ctx->opcode)) + gen_setlr(ctx, ctx->nip); + l1 = gen_new_label(); + if ((bo & 0x4) == 0) { + /* Decrement and test CTR */ + TCGv temp = tcg_temp_new(); + if (unlikely(type == BCOND_CTR)) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + return; + } + tcg_gen_subi_tl(cpu_ctr, cpu_ctr, 1); + if (NARROW_MODE(ctx)) { + tcg_gen_ext32u_tl(temp, cpu_ctr); + } else { + tcg_gen_mov_tl(temp, cpu_ctr); + } + if (bo & 0x2) { + tcg_gen_brcondi_tl(TCG_COND_NE, temp, 0, l1); + } else { + tcg_gen_brcondi_tl(TCG_COND_EQ, temp, 0, l1); + } + tcg_temp_free(temp); + } + if ((bo & 0x10) == 0) { + /* Test CR */ + uint32_t bi = BI(ctx->opcode); + uint32_t mask = 0x08 >> (bi & 0x03); + TCGv_i32 temp = tcg_temp_new_i32(); + + if (bo & 0x8) { + tcg_gen_andi_i32(temp, cpu_crf[bi >> 2], mask); + tcg_gen_brcondi_i32(TCG_COND_EQ, temp, 0, l1); + } else { + tcg_gen_andi_i32(temp, cpu_crf[bi >> 2], mask); + tcg_gen_brcondi_i32(TCG_COND_NE, temp, 0, l1); + } + tcg_temp_free_i32(temp); + } + gen_update_cfar(ctx, ctx->nip - 4); + if (type == BCOND_IM) { + target_ulong li = (target_long)((int16_t)(BD(ctx->opcode))); + if (likely(AA(ctx->opcode) == 0)) { + gen_goto_tb(ctx, 0, ctx->nip + li - 4); + } else { + gen_goto_tb(ctx, 0, li); + } + if ((bo & 0x14) != 0x14) { + gen_set_label(l1); + gen_goto_tb(ctx, 1, ctx->nip); + } + } else { + if (NARROW_MODE(ctx)) { + tcg_gen_andi_tl(cpu_nip, target, (uint32_t)~3); + } else { + tcg_gen_andi_tl(cpu_nip, target, ~3); + } + tcg_gen_exit_tb(0); + if ((bo & 0x14) != 0x14) { + gen_set_label(l1); + gen_update_nip(ctx, ctx->nip); + tcg_gen_exit_tb(0); + } + } + if (type == BCOND_LR || type == BCOND_CTR || type == BCOND_TAR) { + tcg_temp_free(target); + } +} + +static void gen_bc(DisasContext *ctx) +{ + gen_bcond(ctx, BCOND_IM); +} + +static void gen_bcctr(DisasContext *ctx) +{ + gen_bcond(ctx, BCOND_CTR); +} + +static void gen_bclr(DisasContext *ctx) +{ + gen_bcond(ctx, BCOND_LR); +} + +static void gen_bctar(DisasContext *ctx) +{ + gen_bcond(ctx, BCOND_TAR); +} + +/*** Condition register logical ***/ +#define GEN_CRLOGIC(name, tcg_op, opc) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + uint8_t bitmask; \ + int sh; \ + TCGv_i32 t0, t1; \ + sh = (crbD(ctx->opcode) & 0x03) - (crbA(ctx->opcode) & 0x03); \ + t0 = tcg_temp_new_i32(); \ + if (sh > 0) \ + tcg_gen_shri_i32(t0, cpu_crf[crbA(ctx->opcode) >> 2], sh); \ + else if (sh < 0) \ + tcg_gen_shli_i32(t0, cpu_crf[crbA(ctx->opcode) >> 2], -sh); \ + else \ + tcg_gen_mov_i32(t0, cpu_crf[crbA(ctx->opcode) >> 2]); \ + t1 = tcg_temp_new_i32(); \ + sh = (crbD(ctx->opcode) & 0x03) - (crbB(ctx->opcode) & 0x03); \ + if (sh > 0) \ + tcg_gen_shri_i32(t1, cpu_crf[crbB(ctx->opcode) >> 2], sh); \ + else if (sh < 0) \ + tcg_gen_shli_i32(t1, cpu_crf[crbB(ctx->opcode) >> 2], -sh); \ + else \ + tcg_gen_mov_i32(t1, cpu_crf[crbB(ctx->opcode) >> 2]); \ + tcg_op(t0, t0, t1); \ + bitmask = 0x08 >> (crbD(ctx->opcode) & 0x03); \ + tcg_gen_andi_i32(t0, t0, bitmask); \ + tcg_gen_andi_i32(t1, cpu_crf[crbD(ctx->opcode) >> 2], ~bitmask); \ + tcg_gen_or_i32(cpu_crf[crbD(ctx->opcode) >> 2], t0, t1); \ + tcg_temp_free_i32(t0); \ + tcg_temp_free_i32(t1); \ +} + +/* crand */ +GEN_CRLOGIC(crand, tcg_gen_and_i32, 0x08); +/* crandc */ +GEN_CRLOGIC(crandc, tcg_gen_andc_i32, 0x04); +/* creqv */ +GEN_CRLOGIC(creqv, tcg_gen_eqv_i32, 0x09); +/* crnand */ +GEN_CRLOGIC(crnand, tcg_gen_nand_i32, 0x07); +/* crnor */ +GEN_CRLOGIC(crnor, tcg_gen_nor_i32, 0x01); +/* cror */ +GEN_CRLOGIC(cror, tcg_gen_or_i32, 0x0E); +/* crorc */ +GEN_CRLOGIC(crorc, tcg_gen_orc_i32, 0x0D); +/* crxor */ +GEN_CRLOGIC(crxor, tcg_gen_xor_i32, 0x06); + +/* mcrf */ +static void gen_mcrf(DisasContext *ctx) +{ + tcg_gen_mov_i32(cpu_crf[crfD(ctx->opcode)], cpu_crf[crfS(ctx->opcode)]); +} + +/*** System linkage ***/ + +/* rfi (supervisor only) */ +static void gen_rfi(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + /* This instruction doesn't exist anymore on 64-bit server + * processors compliant with arch 2.x + */ + if (ctx->insns_flags & PPC_SEGMENT_64B) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + return; + } + /* Restore CPU state */ + CHK_SV; + gen_update_cfar(ctx, ctx->nip - 4); + gen_helper_rfi(cpu_env); + gen_sync_exception(ctx); +#endif +} + +#if defined(TARGET_PPC64) +static void gen_rfid(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + /* Restore CPU state */ + CHK_SV; + gen_update_cfar(ctx, ctx->nip - 4); + gen_helper_rfid(cpu_env); + gen_sync_exception(ctx); +#endif +} + +static void gen_hrfid(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + /* Restore CPU state */ + CHK_HV; + gen_helper_hrfid(cpu_env); + gen_sync_exception(ctx); +#endif +} +#endif + +/* sc */ +#if defined(CONFIG_USER_ONLY) +#define POWERPC_SYSCALL POWERPC_EXCP_SYSCALL_USER +#else +#define POWERPC_SYSCALL POWERPC_EXCP_SYSCALL +#endif +static void gen_sc(DisasContext *ctx) +{ + uint32_t lev; + + lev = (ctx->opcode >> 5) & 0x7F; + gen_exception_err(ctx, POWERPC_SYSCALL, lev); +} + +/*** Trap ***/ + +/* Check for unconditional traps (always or never) */ +static bool check_unconditional_trap(DisasContext *ctx) +{ + /* Trap never */ + if (TO(ctx->opcode) == 0) { + return true; + } + /* Trap always */ + if (TO(ctx->opcode) == 31) { + gen_exception_err(ctx, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_TRAP); + return true; + } + return false; +} + +/* tw */ +static void gen_tw(DisasContext *ctx) +{ + TCGv_i32 t0; + + if (check_unconditional_trap(ctx)) { + return; + } + t0 = tcg_const_i32(TO(ctx->opcode)); + gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], + t0); + tcg_temp_free_i32(t0); +} + +/* twi */ +static void gen_twi(DisasContext *ctx) +{ + TCGv t0; + TCGv_i32 t1; + + if (check_unconditional_trap(ctx)) { + return; + } + t0 = tcg_const_tl(SIMM(ctx->opcode)); + t1 = tcg_const_i32(TO(ctx->opcode)); + gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, t1); + tcg_temp_free(t0); + tcg_temp_free_i32(t1); +} + +#if defined(TARGET_PPC64) +/* td */ +static void gen_td(DisasContext *ctx) +{ + TCGv_i32 t0; + + if (check_unconditional_trap(ctx)) { + return; + } + t0 = tcg_const_i32(TO(ctx->opcode)); + gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], + t0); + tcg_temp_free_i32(t0); +} + +/* tdi */ +static void gen_tdi(DisasContext *ctx) +{ + TCGv t0; + TCGv_i32 t1; + + if (check_unconditional_trap(ctx)) { + return; + } + t0 = tcg_const_tl(SIMM(ctx->opcode)); + t1 = tcg_const_i32(TO(ctx->opcode)); + gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, t1); + tcg_temp_free(t0); + tcg_temp_free_i32(t1); +} +#endif + +/*** Processor control ***/ + +static void gen_read_xer(TCGv dst) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + tcg_gen_mov_tl(dst, cpu_xer); + tcg_gen_shli_tl(t0, cpu_so, XER_SO); + tcg_gen_shli_tl(t1, cpu_ov, XER_OV); + tcg_gen_shli_tl(t2, cpu_ca, XER_CA); + tcg_gen_or_tl(t0, t0, t1); + tcg_gen_or_tl(dst, dst, t2); + tcg_gen_or_tl(dst, dst, t0); + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); +} + +static void gen_write_xer(TCGv src) +{ + tcg_gen_andi_tl(cpu_xer, src, + ~((1u << XER_SO) | (1u << XER_OV) | (1u << XER_CA))); + tcg_gen_shri_tl(cpu_so, src, XER_SO); + tcg_gen_shri_tl(cpu_ov, src, XER_OV); + tcg_gen_shri_tl(cpu_ca, src, XER_CA); + tcg_gen_andi_tl(cpu_so, cpu_so, 1); + tcg_gen_andi_tl(cpu_ov, cpu_ov, 1); + tcg_gen_andi_tl(cpu_ca, cpu_ca, 1); +} + +/* mcrxr */ +static void gen_mcrxr(DisasContext *ctx) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 dst = cpu_crf[crfD(ctx->opcode)]; + + tcg_gen_trunc_tl_i32(t0, cpu_so); + tcg_gen_trunc_tl_i32(t1, cpu_ov); + tcg_gen_trunc_tl_i32(dst, cpu_ca); + tcg_gen_shli_i32(t0, t0, 3); + tcg_gen_shli_i32(t1, t1, 2); + tcg_gen_shli_i32(dst, dst, 1); + tcg_gen_or_i32(dst, dst, t0); + tcg_gen_or_i32(dst, dst, t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + + tcg_gen_movi_tl(cpu_so, 0); + tcg_gen_movi_tl(cpu_ov, 0); + tcg_gen_movi_tl(cpu_ca, 0); +} + +/* mfcr mfocrf */ +static void gen_mfcr(DisasContext *ctx) +{ + uint32_t crm, crn; + + if (likely(ctx->opcode & 0x00100000)) { + crm = CRM(ctx->opcode); + if (likely(crm && ((crm & (crm - 1)) == 0))) { + crn = ctz32 (crm); + tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], cpu_crf[7 - crn]); + tcg_gen_shli_tl(cpu_gpr[rD(ctx->opcode)], + cpu_gpr[rD(ctx->opcode)], crn * 4); + } + } else { + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_mov_i32(t0, cpu_crf[0]); + tcg_gen_shli_i32(t0, t0, 4); + tcg_gen_or_i32(t0, t0, cpu_crf[1]); + tcg_gen_shli_i32(t0, t0, 4); + tcg_gen_or_i32(t0, t0, cpu_crf[2]); + tcg_gen_shli_i32(t0, t0, 4); + tcg_gen_or_i32(t0, t0, cpu_crf[3]); + tcg_gen_shli_i32(t0, t0, 4); + tcg_gen_or_i32(t0, t0, cpu_crf[4]); + tcg_gen_shli_i32(t0, t0, 4); + tcg_gen_or_i32(t0, t0, cpu_crf[5]); + tcg_gen_shli_i32(t0, t0, 4); + tcg_gen_or_i32(t0, t0, cpu_crf[6]); + tcg_gen_shli_i32(t0, t0, 4); + tcg_gen_or_i32(t0, t0, cpu_crf[7]); + tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t0); + tcg_temp_free_i32(t0); + } +} + +/* mfmsr */ +static void gen_mfmsr(DisasContext *ctx) +{ + CHK_SV; + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_msr); +} + +static void spr_noaccess(DisasContext *ctx, int gprn, int sprn) +{ +#if 0 + sprn = ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5); + printf("ERROR: try to access SPR %d !\n", sprn); +#endif +} +#define SPR_NOACCESS (&spr_noaccess) + +/* mfspr */ +static inline void gen_op_mfspr(DisasContext *ctx) +{ + void (*read_cb)(DisasContext *ctx, int gprn, int sprn); + uint32_t sprn = SPR(ctx->opcode); + +#if defined(CONFIG_USER_ONLY) + read_cb = ctx->spr_cb[sprn].uea_read; +#else + if (ctx->pr) { + read_cb = ctx->spr_cb[sprn].uea_read; + } else if (ctx->hv) { + read_cb = ctx->spr_cb[sprn].hea_read; + } else { + read_cb = ctx->spr_cb[sprn].oea_read; + } +#endif + if (likely(read_cb != NULL)) { + if (likely(read_cb != SPR_NOACCESS)) { + (*read_cb)(ctx, rD(ctx->opcode), sprn); + } else { + /* Privilege exception */ + /* This is a hack to avoid warnings when running Linux: + * this OS breaks the PowerPC virtualisation model, + * allowing userland application to read the PVR + */ + if (sprn != SPR_PVR) { + fprintf(stderr, "Trying to read privileged spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + if (qemu_log_separate()) { + qemu_log("Trying to read privileged spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + } + } + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG); + } + } else { + /* ISA 2.07 defines these as no-ops */ + if ((ctx->insns_flags2 & PPC2_ISA207S) && + (sprn >= 808 && sprn <= 811)) { + /* This is a nop */ + return; + } + /* Not defined */ + fprintf(stderr, "Trying to read invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + if (qemu_log_separate()) { + qemu_log("Trying to read invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + } + + /* The behaviour depends on MSR:PR and SPR# bit 0x10, + * it can generate a priv, a hv emu or a no-op + */ + if (sprn & 0x10) { + if (ctx->pr) { + gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + } + } else { + if (ctx->pr || sprn == 0 || sprn == 4 || sprn == 5 || sprn == 6) { + gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + } + } + } +} + +static void gen_mfspr(DisasContext *ctx) +{ + gen_op_mfspr(ctx); +} + +/* mftb */ +static void gen_mftb(DisasContext *ctx) +{ + gen_op_mfspr(ctx); +} + +/* mtcrf mtocrf*/ +static void gen_mtcrf(DisasContext *ctx) +{ + uint32_t crm, crn; + + crm = CRM(ctx->opcode); + if (likely((ctx->opcode & 0x00100000))) { + if (crm && ((crm & (crm - 1)) == 0)) { + TCGv_i32 temp = tcg_temp_new_i32(); + crn = ctz32 (crm); + tcg_gen_trunc_tl_i32(temp, cpu_gpr[rS(ctx->opcode)]); + tcg_gen_shri_i32(temp, temp, crn * 4); + tcg_gen_andi_i32(cpu_crf[7 - crn], temp, 0xf); + tcg_temp_free_i32(temp); + } + } else { + TCGv_i32 temp = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(temp, cpu_gpr[rS(ctx->opcode)]); + for (crn = 0 ; crn < 8 ; crn++) { + if (crm & (1 << crn)) { + tcg_gen_shri_i32(cpu_crf[7 - crn], temp, crn * 4); + tcg_gen_andi_i32(cpu_crf[7 - crn], cpu_crf[7 - crn], 0xf); + } + } + tcg_temp_free_i32(temp); + } +} + +/* mtmsr */ +#if defined(TARGET_PPC64) +static void gen_mtmsrd(DisasContext *ctx) +{ + CHK_SV; + +#if !defined(CONFIG_USER_ONLY) + if (ctx->opcode & 0x00010000) { + /* Special form that does not need any synchronisation */ + TCGv t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE)); + tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE))); + tcg_gen_or_tl(cpu_msr, cpu_msr, t0); + tcg_temp_free(t0); + } else { + /* XXX: we need to update nip before the store + * if we enter power saving mode, we will exit the loop + * directly from ppc_store_msr + */ + gen_update_nip(ctx, ctx->nip); + gen_helper_store_msr(cpu_env, cpu_gpr[rS(ctx->opcode)]); + /* Must stop the translation as machine state (may have) changed */ + /* Note that mtmsr is not always defined as context-synchronizing */ + gen_stop_exception(ctx); + } +#endif /* !defined(CONFIG_USER_ONLY) */ +} +#endif /* defined(TARGET_PPC64) */ + +static void gen_mtmsr(DisasContext *ctx) +{ + CHK_SV; + +#if !defined(CONFIG_USER_ONLY) + if (ctx->opcode & 0x00010000) { + /* Special form that does not need any synchronisation */ + TCGv t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE)); + tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE))); + tcg_gen_or_tl(cpu_msr, cpu_msr, t0); + tcg_temp_free(t0); + } else { + TCGv msr = tcg_temp_new(); + + /* XXX: we need to update nip before the store + * if we enter power saving mode, we will exit the loop + * directly from ppc_store_msr + */ + gen_update_nip(ctx, ctx->nip); +#if defined(TARGET_PPC64) + tcg_gen_deposit_tl(msr, cpu_msr, cpu_gpr[rS(ctx->opcode)], 0, 32); +#else + tcg_gen_mov_tl(msr, cpu_gpr[rS(ctx->opcode)]); +#endif + gen_helper_store_msr(cpu_env, msr); + tcg_temp_free(msr); + /* Must stop the translation as machine state (may have) changed */ + /* Note that mtmsr is not always defined as context-synchronizing */ + gen_stop_exception(ctx); + } +#endif +} + +/* mtspr */ +static void gen_mtspr(DisasContext *ctx) +{ + void (*write_cb)(DisasContext *ctx, int sprn, int gprn); + uint32_t sprn = SPR(ctx->opcode); + +#if defined(CONFIG_USER_ONLY) + write_cb = ctx->spr_cb[sprn].uea_write; +#else + if (ctx->pr) { + write_cb = ctx->spr_cb[sprn].uea_write; + } else if (ctx->hv) { + write_cb = ctx->spr_cb[sprn].hea_write; + } else { + write_cb = ctx->spr_cb[sprn].oea_write; + } +#endif + if (likely(write_cb != NULL)) { + if (likely(write_cb != SPR_NOACCESS)) { + (*write_cb)(ctx, sprn, rS(ctx->opcode)); + } else { + /* Privilege exception */ + fprintf(stderr, "Trying to write privileged spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + if (qemu_log_separate()) { + qemu_log("Trying to write privileged spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + } + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG); + } + } else { + /* ISA 2.07 defines these as no-ops */ + if ((ctx->insns_flags2 & PPC2_ISA207S) && + (sprn >= 808 && sprn <= 811)) { + /* This is a nop */ + return; + } + + /* Not defined */ + if (qemu_log_separate()) { + qemu_log("Trying to write invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + } + fprintf(stderr, "Trying to write invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + + + /* The behaviour depends on MSR:PR and SPR# bit 0x10, + * it can generate a priv, a hv emu or a no-op + */ + if (sprn & 0x10) { + if (ctx->pr) { + gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + } + } else { + if (ctx->pr || sprn == 0) { + gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + } + } + } +} + +#if defined(TARGET_PPC64) +/* setb */ +static void gen_setb(DisasContext *ctx) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t8 = tcg_temp_new_i32(); + TCGv_i32 tm1 = tcg_temp_new_i32(); + int crf = crfS(ctx->opcode); + + tcg_gen_setcondi_i32(TCG_COND_GEU, t0, cpu_crf[crf], 4); + tcg_gen_movi_i32(t8, 8); + tcg_gen_movi_i32(tm1, -1); + tcg_gen_movcond_i32(TCG_COND_GEU, t0, cpu_crf[crf], t8, tm1, t0); + tcg_gen_ext_i32_tl(cpu_gpr[rD(ctx->opcode)], t0); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t8); + tcg_temp_free_i32(tm1); +} +#endif + +/*** Cache management ***/ + +/* dcbf */ +static void gen_dcbf(DisasContext *ctx) +{ + /* XXX: specification says this is treated as a load by the MMU */ + TCGv t0; + gen_set_access_type(ctx, ACCESS_CACHE); + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + gen_qemu_ld8u(ctx, t0, t0); + tcg_temp_free(t0); +} + +/* dcbi (Supervisor only) */ +static void gen_dcbi(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv EA, val; + + CHK_SV; + EA = tcg_temp_new(); + gen_set_access_type(ctx, ACCESS_CACHE); + gen_addr_reg_index(ctx, EA); + val = tcg_temp_new(); + /* XXX: specification says this should be treated as a store by the MMU */ + gen_qemu_ld8u(ctx, val, EA); + gen_qemu_st8(ctx, val, EA); + tcg_temp_free(val); + tcg_temp_free(EA); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* dcdst */ +static void gen_dcbst(DisasContext *ctx) +{ + /* XXX: specification say this is treated as a load by the MMU */ + TCGv t0; + gen_set_access_type(ctx, ACCESS_CACHE); + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + gen_qemu_ld8u(ctx, t0, t0); + tcg_temp_free(t0); +} + +/* dcbt */ +static void gen_dcbt(DisasContext *ctx) +{ + /* interpreted as no-op */ + /* XXX: specification say this is treated as a load by the MMU + * but does not generate any exception + */ +} + +/* dcbtst */ +static void gen_dcbtst(DisasContext *ctx) +{ + /* interpreted as no-op */ + /* XXX: specification say this is treated as a load by the MMU + * but does not generate any exception + */ +} + +/* dcbtls */ +static void gen_dcbtls(DisasContext *ctx) +{ + /* Always fails locking the cache */ + TCGv t0 = tcg_temp_new(); + gen_load_spr(t0, SPR_Exxx_L1CSR0); + tcg_gen_ori_tl(t0, t0, L1CSR0_CUL); + gen_store_spr(SPR_Exxx_L1CSR0, t0); + tcg_temp_free(t0); +} + +/* dcbz */ +static void gen_dcbz(DisasContext *ctx) +{ + TCGv tcgv_addr; + TCGv_i32 tcgv_op; + + gen_set_access_type(ctx, ACCESS_CACHE); + tcgv_addr = tcg_temp_new(); + tcgv_op = tcg_const_i32(ctx->opcode & 0x03FF000); + gen_addr_reg_index(ctx, tcgv_addr); + gen_helper_dcbz(cpu_env, tcgv_addr, tcgv_op); + tcg_temp_free(tcgv_addr); + tcg_temp_free_i32(tcgv_op); +} + +/* dst / dstt */ +static void gen_dst(DisasContext *ctx) +{ + if (rA(ctx->opcode) == 0) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + } else { + /* interpreted as no-op */ + } +} + +/* dstst /dststt */ +static void gen_dstst(DisasContext *ctx) +{ + if (rA(ctx->opcode) == 0) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + } else { + /* interpreted as no-op */ + } + +} + +/* dss / dssall */ +static void gen_dss(DisasContext *ctx) +{ + /* interpreted as no-op */ +} + +/* icbi */ +static void gen_icbi(DisasContext *ctx) +{ + TCGv t0; + gen_set_access_type(ctx, ACCESS_CACHE); + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + gen_helper_icbi(cpu_env, t0); + tcg_temp_free(t0); +} + +/* Optional: */ +/* dcba */ +static void gen_dcba(DisasContext *ctx) +{ + /* interpreted as no-op */ + /* XXX: specification say this is treated as a store by the MMU + * but does not generate any exception + */ +} + +/*** Segment register manipulation ***/ +/* Supervisor only: */ + +/* mfsr */ +static void gen_mfsr(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv t0; + + CHK_SV; + t0 = tcg_const_tl(SR(ctx->opcode)); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); + tcg_temp_free(t0); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* mfsrin */ +static void gen_mfsrin(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv t0; + + CHK_SV; + t0 = tcg_temp_new(); + tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28); + tcg_gen_andi_tl(t0, t0, 0xF); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); + tcg_temp_free(t0); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* mtsr */ +static void gen_mtsr(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv t0; + + CHK_SV; + t0 = tcg_const_tl(SR(ctx->opcode)); + gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); + tcg_temp_free(t0); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* mtsrin */ +static void gen_mtsrin(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv t0; + CHK_SV; + + t0 = tcg_temp_new(); + tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28); + tcg_gen_andi_tl(t0, t0, 0xF); + gen_helper_store_sr(cpu_env, t0, cpu_gpr[rD(ctx->opcode)]); + tcg_temp_free(t0); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +#if defined(TARGET_PPC64) +/* Specific implementation for PowerPC 64 "bridge" emulation using SLB */ + +/* mfsr */ +static void gen_mfsr_64b(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv t0; + + CHK_SV; + t0 = tcg_const_tl(SR(ctx->opcode)); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); + tcg_temp_free(t0); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* mfsrin */ +static void gen_mfsrin_64b(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv t0; + + CHK_SV; + t0 = tcg_temp_new(); + tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28); + tcg_gen_andi_tl(t0, t0, 0xF); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); + tcg_temp_free(t0); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* mtsr */ +static void gen_mtsr_64b(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv t0; + + CHK_SV; + t0 = tcg_const_tl(SR(ctx->opcode)); + gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); + tcg_temp_free(t0); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* mtsrin */ +static void gen_mtsrin_64b(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv t0; + + CHK_SV; + t0 = tcg_temp_new(); + tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28); + tcg_gen_andi_tl(t0, t0, 0xF); + gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); + tcg_temp_free(t0); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* slbmte */ +static void gen_slbmte(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + + gen_helper_store_slb(cpu_env, cpu_gpr[rB(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)]); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_slbmfee(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + + gen_helper_load_slb_esid(cpu_gpr[rS(ctx->opcode)], cpu_env, + cpu_gpr[rB(ctx->opcode)]); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_slbmfev(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + + gen_helper_load_slb_vsid(cpu_gpr[rS(ctx->opcode)], cpu_env, + cpu_gpr[rB(ctx->opcode)]); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_slbfee_(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); +#else + TCGLabel *l1, *l2; + + if (unlikely(ctx->pr)) { + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + return; + } + gen_helper_find_slb_vsid(cpu_gpr[rS(ctx->opcode)], cpu_env, + cpu_gpr[rB(ctx->opcode)]); + l1 = gen_new_label(); + l2 = gen_new_label(); + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[rS(ctx->opcode)], -1, l1); + tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_movi_tl(cpu_gpr[rS(ctx->opcode)], 0); + gen_set_label(l2); +#endif +} +#endif /* defined(TARGET_PPC64) */ + +/*** Lookaside buffer management ***/ +/* Optional & supervisor only: */ + +/* tlbia */ +static void gen_tlbia(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_HV; + + gen_helper_tlbia(cpu_env); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* tlbiel */ +static void gen_tlbiel(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + + gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* tlbie */ +static void gen_tlbie(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv_i32 t1; + CHK_HV; + + if (NARROW_MODE(ctx)) { + TCGv t0 = tcg_temp_new(); + tcg_gen_ext32u_tl(t0, cpu_gpr[rB(ctx->opcode)]); + gen_helper_tlbie(cpu_env, t0); + tcg_temp_free(t0); + } else { + gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); + } + t1 = tcg_temp_new_i32(); + tcg_gen_ld_i32(t1, cpu_env, offsetof(CPUPPCState, tlb_need_flush)); + tcg_gen_ori_i32(t1, t1, TLB_NEED_GLOBAL_FLUSH); + tcg_gen_st_i32(t1, cpu_env, offsetof(CPUPPCState, tlb_need_flush)); + tcg_temp_free_i32(t1); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* tlbsync */ +static void gen_tlbsync(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_HV; + + /* BookS does both ptesync and tlbsync make tlbsync a nop for server */ + if (ctx->insns_flags & PPC_BOOKE) { + gen_check_tlb_flush(ctx, true); + } +#endif /* defined(CONFIG_USER_ONLY) */ +} + +#if defined(TARGET_PPC64) +/* slbia */ +static void gen_slbia(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + + gen_helper_slbia(cpu_env); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* slbie */ +static void gen_slbie(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + + gen_helper_slbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); +#endif /* defined(CONFIG_USER_ONLY) */ +} +#endif /* defined(TARGET_PPC64) */ + +/*** External control ***/ +/* Optional: */ + +/* eciwx */ +static void gen_eciwx(DisasContext *ctx) +{ + TCGv t0; + /* Should check EAR[E] ! */ + gen_set_access_type(ctx, ACCESS_EXT); + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + gen_check_align(ctx, t0, 0x03); + gen_qemu_ld32u(ctx, cpu_gpr[rD(ctx->opcode)], t0); + tcg_temp_free(t0); +} + +/* ecowx */ +static void gen_ecowx(DisasContext *ctx) +{ + TCGv t0; + /* Should check EAR[E] ! */ + gen_set_access_type(ctx, ACCESS_EXT); + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + gen_check_align(ctx, t0, 0x03); + gen_qemu_st32(ctx, cpu_gpr[rD(ctx->opcode)], t0); + tcg_temp_free(t0); +} + +/* PowerPC 601 specific instructions */ + +/* abs - abs. */ +static void gen_abs(DisasContext *ctx) +{ + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_GE, cpu_gpr[rA(ctx->opcode)], 0, l1); + tcg_gen_neg_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + gen_set_label(l2); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* abso - abso. */ +static void gen_abso(DisasContext *ctx) +{ + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGLabel *l3 = gen_new_label(); + /* Start with XER OV disabled, the most likely case */ + tcg_gen_movi_tl(cpu_ov, 0); + tcg_gen_brcondi_tl(TCG_COND_GE, cpu_gpr[rA(ctx->opcode)], 0, l2); + tcg_gen_brcondi_tl(TCG_COND_NE, cpu_gpr[rA(ctx->opcode)], 0x80000000, l1); + tcg_gen_movi_tl(cpu_ov, 1); + tcg_gen_movi_tl(cpu_so, 1); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_neg_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + tcg_gen_br(l3); + gen_set_label(l2); + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + gen_set_label(l3); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* clcs */ +static void gen_clcs(DisasContext *ctx) +{ + TCGv_i32 t0 = tcg_const_i32(rA(ctx->opcode)); + gen_helper_clcs(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); + tcg_temp_free_i32(t0); + /* Rc=1 sets CR0 to an undefined state */ +} + +/* div - div. */ +static void gen_div(DisasContext *ctx) +{ + gen_helper_div(cpu_gpr[rD(ctx->opcode)], cpu_env, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rB(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* divo - divo. */ +static void gen_divo(DisasContext *ctx) +{ + gen_helper_divo(cpu_gpr[rD(ctx->opcode)], cpu_env, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rB(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* divs - divs. */ +static void gen_divs(DisasContext *ctx) +{ + gen_helper_divs(cpu_gpr[rD(ctx->opcode)], cpu_env, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rB(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* divso - divso. */ +static void gen_divso(DisasContext *ctx) +{ + gen_helper_divso(cpu_gpr[rD(ctx->opcode)], cpu_env, + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* doz - doz. */ +static void gen_doz(DisasContext *ctx) +{ + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + tcg_gen_brcond_tl(TCG_COND_GE, cpu_gpr[rB(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], l1); + tcg_gen_sub_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], 0); + gen_set_label(l2); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* dozo - dozo. */ +static void gen_dozo(DisasContext *ctx) +{ + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + /* Start with XER OV disabled, the most likely case */ + tcg_gen_movi_tl(cpu_ov, 0); + tcg_gen_brcond_tl(TCG_COND_GE, cpu_gpr[rB(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], l1); + tcg_gen_sub_tl(t0, cpu_gpr[rB(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + tcg_gen_xor_tl(t1, cpu_gpr[rB(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + tcg_gen_xor_tl(t2, cpu_gpr[rA(ctx->opcode)], t0); + tcg_gen_andc_tl(t1, t1, t2); + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0); + tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l2); + tcg_gen_movi_tl(cpu_ov, 1); + tcg_gen_movi_tl(cpu_so, 1); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], 0); + gen_set_label(l2); + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* dozi */ +static void gen_dozi(DisasContext *ctx) +{ + target_long simm = SIMM(ctx->opcode); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_LT, cpu_gpr[rA(ctx->opcode)], simm, l1); + tcg_gen_subfi_tl(cpu_gpr[rD(ctx->opcode)], simm, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], 0); + gen_set_label(l2); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* lscbx - lscbx. */ +static void gen_lscbx(DisasContext *ctx) +{ + TCGv t0 = tcg_temp_new(); + TCGv_i32 t1 = tcg_const_i32(rD(ctx->opcode)); + TCGv_i32 t2 = tcg_const_i32(rA(ctx->opcode)); + TCGv_i32 t3 = tcg_const_i32(rB(ctx->opcode)); + + gen_addr_reg_index(ctx, t0); + gen_helper_lscbx(t0, cpu_env, t0, t1, t2, t3); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + tcg_gen_andi_tl(cpu_xer, cpu_xer, ~0x7F); + tcg_gen_or_tl(cpu_xer, cpu_xer, t0); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, t0); + tcg_temp_free(t0); +} + +/* maskg - maskg. */ +static void gen_maskg(DisasContext *ctx) +{ + TCGLabel *l1 = gen_new_label(); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + tcg_gen_movi_tl(t3, 0xFFFFFFFF); + tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1F); + tcg_gen_andi_tl(t1, cpu_gpr[rS(ctx->opcode)], 0x1F); + tcg_gen_addi_tl(t2, t0, 1); + tcg_gen_shr_tl(t2, t3, t2); + tcg_gen_shr_tl(t3, t3, t1); + tcg_gen_xor_tl(cpu_gpr[rA(ctx->opcode)], t2, t3); + tcg_gen_brcond_tl(TCG_COND_GE, t0, t1, l1); + tcg_gen_neg_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + gen_set_label(l1); + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); + tcg_temp_free(t3); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* maskir - maskir. */ +static void gen_maskir(DisasContext *ctx) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + tcg_gen_and_tl(t0, cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + tcg_gen_andc_tl(t1, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* mul - mul. */ +static void gen_mul(DisasContext *ctx) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv t2 = tcg_temp_new(); + tcg_gen_extu_tl_i64(t0, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_extu_tl_i64(t1, cpu_gpr[rB(ctx->opcode)]); + tcg_gen_mul_i64(t0, t0, t1); + tcg_gen_trunc_i64_tl(t2, t0); + gen_store_spr(SPR_MQ, t2); + tcg_gen_shri_i64(t1, t0, 32); + tcg_gen_trunc_i64_tl(cpu_gpr[rD(ctx->opcode)], t1); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + tcg_temp_free(t2); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* mulo - mulo. */ +static void gen_mulo(DisasContext *ctx) +{ + TCGLabel *l1 = gen_new_label(); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv t2 = tcg_temp_new(); + /* Start with XER OV disabled, the most likely case */ + tcg_gen_movi_tl(cpu_ov, 0); + tcg_gen_extu_tl_i64(t0, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_extu_tl_i64(t1, cpu_gpr[rB(ctx->opcode)]); + tcg_gen_mul_i64(t0, t0, t1); + tcg_gen_trunc_i64_tl(t2, t0); + gen_store_spr(SPR_MQ, t2); + tcg_gen_shri_i64(t1, t0, 32); + tcg_gen_trunc_i64_tl(cpu_gpr[rD(ctx->opcode)], t1); + tcg_gen_ext32s_i64(t1, t0); + tcg_gen_brcond_i64(TCG_COND_EQ, t0, t1, l1); + tcg_gen_movi_tl(cpu_ov, 1); + tcg_gen_movi_tl(cpu_so, 1); + gen_set_label(l1); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + tcg_temp_free(t2); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* nabs - nabs. */ +static void gen_nabs(DisasContext *ctx) +{ + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_GT, cpu_gpr[rA(ctx->opcode)], 0, l1); + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_neg_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + gen_set_label(l2); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* nabso - nabso. */ +static void gen_nabso(DisasContext *ctx) +{ + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_GT, cpu_gpr[rA(ctx->opcode)], 0, l1); + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_neg_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + gen_set_label(l2); + /* nabs never overflows */ + tcg_gen_movi_tl(cpu_ov, 0); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); +} + +/* rlmi - rlmi. */ +static void gen_rlmi(DisasContext *ctx) +{ + uint32_t mb = MB(ctx->opcode); + uint32_t me = ME(ctx->opcode); + TCGv t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1F); + tcg_gen_rotl_tl(t0, cpu_gpr[rS(ctx->opcode)], t0); + tcg_gen_andi_tl(t0, t0, MASK(mb, me)); + tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], ~MASK(mb, me)); + tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], t0); + tcg_temp_free(t0); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* rrib - rrib. */ +static void gen_rrib(DisasContext *ctx) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1F); + tcg_gen_movi_tl(t1, 0x80000000); + tcg_gen_shr_tl(t1, t1, t0); + tcg_gen_shr_tl(t0, cpu_gpr[rS(ctx->opcode)], t0); + tcg_gen_and_tl(t0, t0, t1); + tcg_gen_andc_tl(t1, cpu_gpr[rA(ctx->opcode)], t1); + tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* sle - sle. */ +static void gen_sle(DisasContext *ctx) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1F); + tcg_gen_shl_tl(t0, cpu_gpr[rS(ctx->opcode)], t1); + tcg_gen_subfi_tl(t1, 32, t1); + tcg_gen_shr_tl(t1, cpu_gpr[rS(ctx->opcode)], t1); + tcg_gen_or_tl(t1, t0, t1); + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0); + gen_store_spr(SPR_MQ, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* sleq - sleq. */ +static void gen_sleq(DisasContext *ctx) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1F); + tcg_gen_movi_tl(t2, 0xFFFFFFFF); + tcg_gen_shl_tl(t2, t2, t0); + tcg_gen_rotl_tl(t0, cpu_gpr[rS(ctx->opcode)], t0); + gen_load_spr(t1, SPR_MQ); + gen_store_spr(SPR_MQ, t0); + tcg_gen_and_tl(t0, t0, t2); + tcg_gen_andc_tl(t1, t1, t2); + tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* sliq - sliq. */ +static void gen_sliq(DisasContext *ctx) +{ + int sh = SH(ctx->opcode); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + tcg_gen_shli_tl(t0, cpu_gpr[rS(ctx->opcode)], sh); + tcg_gen_shri_tl(t1, cpu_gpr[rS(ctx->opcode)], 32 - sh); + tcg_gen_or_tl(t1, t0, t1); + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0); + gen_store_spr(SPR_MQ, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* slliq - slliq. */ +static void gen_slliq(DisasContext *ctx) +{ + int sh = SH(ctx->opcode); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + tcg_gen_rotli_tl(t0, cpu_gpr[rS(ctx->opcode)], sh); + gen_load_spr(t1, SPR_MQ); + gen_store_spr(SPR_MQ, t0); + tcg_gen_andi_tl(t0, t0, (0xFFFFFFFFU << sh)); + tcg_gen_andi_tl(t1, t1, ~(0xFFFFFFFFU << sh)); + tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* sllq - sllq. */ +static void gen_sllq(DisasContext *ctx) +{ + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGv t0 = tcg_temp_local_new(); + TCGv t1 = tcg_temp_local_new(); + TCGv t2 = tcg_temp_local_new(); + tcg_gen_andi_tl(t2, cpu_gpr[rB(ctx->opcode)], 0x1F); + tcg_gen_movi_tl(t1, 0xFFFFFFFF); + tcg_gen_shl_tl(t1, t1, t2); + tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x20); + tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); + gen_load_spr(t0, SPR_MQ); + tcg_gen_and_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_shl_tl(t0, cpu_gpr[rS(ctx->opcode)], t2); + gen_load_spr(t2, SPR_MQ); + tcg_gen_andc_tl(t1, t2, t1); + tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); + gen_set_label(l2); + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* slq - slq. */ +static void gen_slq(DisasContext *ctx) +{ + TCGLabel *l1 = gen_new_label(); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1F); + tcg_gen_shl_tl(t0, cpu_gpr[rS(ctx->opcode)], t1); + tcg_gen_subfi_tl(t1, 32, t1); + tcg_gen_shr_tl(t1, cpu_gpr[rS(ctx->opcode)], t1); + tcg_gen_or_tl(t1, t0, t1); + gen_store_spr(SPR_MQ, t1); + tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x20); + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0); + tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); + tcg_gen_movi_tl(cpu_gpr[rA(ctx->opcode)], 0); + gen_set_label(l1); + tcg_temp_free(t0); + tcg_temp_free(t1); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* sraiq - sraiq. */ +static void gen_sraiq(DisasContext *ctx) +{ + int sh = SH(ctx->opcode); + TCGLabel *l1 = gen_new_label(); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], sh); + tcg_gen_shli_tl(t1, cpu_gpr[rS(ctx->opcode)], 32 - sh); + tcg_gen_or_tl(t0, t0, t1); + gen_store_spr(SPR_MQ, t0); + tcg_gen_movi_tl(cpu_ca, 0); + tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); + tcg_gen_brcondi_tl(TCG_COND_GE, cpu_gpr[rS(ctx->opcode)], 0, l1); + tcg_gen_movi_tl(cpu_ca, 1); + gen_set_label(l1); + tcg_gen_sari_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], sh); + tcg_temp_free(t0); + tcg_temp_free(t1); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* sraq - sraq. */ +static void gen_sraq(DisasContext *ctx) +{ + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_local_new(); + TCGv t2 = tcg_temp_local_new(); + tcg_gen_andi_tl(t2, cpu_gpr[rB(ctx->opcode)], 0x1F); + tcg_gen_shr_tl(t0, cpu_gpr[rS(ctx->opcode)], t2); + tcg_gen_sar_tl(t1, cpu_gpr[rS(ctx->opcode)], t2); + tcg_gen_subfi_tl(t2, 32, t2); + tcg_gen_shl_tl(t2, cpu_gpr[rS(ctx->opcode)], t2); + tcg_gen_or_tl(t0, t0, t2); + gen_store_spr(SPR_MQ, t0); + tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x20); + tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, l1); + tcg_gen_mov_tl(t2, cpu_gpr[rS(ctx->opcode)]); + tcg_gen_sari_tl(t1, cpu_gpr[rS(ctx->opcode)], 31); + gen_set_label(l1); + tcg_temp_free(t0); + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t1); + tcg_gen_movi_tl(cpu_ca, 0); + tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l2); + tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, l2); + tcg_gen_movi_tl(cpu_ca, 1); + gen_set_label(l2); + tcg_temp_free(t1); + tcg_temp_free(t2); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* sre - sre. */ +static void gen_sre(DisasContext *ctx) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1F); + tcg_gen_shr_tl(t0, cpu_gpr[rS(ctx->opcode)], t1); + tcg_gen_subfi_tl(t1, 32, t1); + tcg_gen_shl_tl(t1, cpu_gpr[rS(ctx->opcode)], t1); + tcg_gen_or_tl(t1, t0, t1); + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0); + gen_store_spr(SPR_MQ, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* srea - srea. */ +static void gen_srea(DisasContext *ctx) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1F); + tcg_gen_rotr_tl(t0, cpu_gpr[rS(ctx->opcode)], t1); + gen_store_spr(SPR_MQ, t0); + tcg_gen_sar_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* sreq */ +static void gen_sreq(DisasContext *ctx) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1F); + tcg_gen_movi_tl(t1, 0xFFFFFFFF); + tcg_gen_shr_tl(t1, t1, t0); + tcg_gen_rotr_tl(t0, cpu_gpr[rS(ctx->opcode)], t0); + gen_load_spr(t2, SPR_MQ); + gen_store_spr(SPR_MQ, t0); + tcg_gen_and_tl(t0, t0, t1); + tcg_gen_andc_tl(t2, t2, t1); + tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t2); + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* sriq */ +static void gen_sriq(DisasContext *ctx) +{ + int sh = SH(ctx->opcode); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], sh); + tcg_gen_shli_tl(t1, cpu_gpr[rS(ctx->opcode)], 32 - sh); + tcg_gen_or_tl(t1, t0, t1); + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0); + gen_store_spr(SPR_MQ, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* srliq */ +static void gen_srliq(DisasContext *ctx) +{ + int sh = SH(ctx->opcode); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + tcg_gen_rotri_tl(t0, cpu_gpr[rS(ctx->opcode)], sh); + gen_load_spr(t1, SPR_MQ); + gen_store_spr(SPR_MQ, t0); + tcg_gen_andi_tl(t0, t0, (0xFFFFFFFFU >> sh)); + tcg_gen_andi_tl(t1, t1, ~(0xFFFFFFFFU >> sh)); + tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* srlq */ +static void gen_srlq(DisasContext *ctx) +{ + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGv t0 = tcg_temp_local_new(); + TCGv t1 = tcg_temp_local_new(); + TCGv t2 = tcg_temp_local_new(); + tcg_gen_andi_tl(t2, cpu_gpr[rB(ctx->opcode)], 0x1F); + tcg_gen_movi_tl(t1, 0xFFFFFFFF); + tcg_gen_shr_tl(t2, t1, t2); + tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x20); + tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); + gen_load_spr(t0, SPR_MQ); + tcg_gen_and_tl(cpu_gpr[rA(ctx->opcode)], t0, t2); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_shr_tl(t0, cpu_gpr[rS(ctx->opcode)], t2); + tcg_gen_and_tl(t0, t0, t2); + gen_load_spr(t1, SPR_MQ); + tcg_gen_andc_tl(t1, t1, t2); + tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); + gen_set_label(l2); + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* srq */ +static void gen_srq(DisasContext *ctx) +{ + TCGLabel *l1 = gen_new_label(); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1F); + tcg_gen_shr_tl(t0, cpu_gpr[rS(ctx->opcode)], t1); + tcg_gen_subfi_tl(t1, 32, t1); + tcg_gen_shl_tl(t1, cpu_gpr[rS(ctx->opcode)], t1); + tcg_gen_or_tl(t1, t0, t1); + gen_store_spr(SPR_MQ, t1); + tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x20); + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0); + tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); + tcg_gen_movi_tl(cpu_gpr[rA(ctx->opcode)], 0); + gen_set_label(l1); + tcg_temp_free(t0); + tcg_temp_free(t1); + if (unlikely(Rc(ctx->opcode) != 0)) + gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); +} + +/* PowerPC 602 specific instructions */ + +/* dsa */ +static void gen_dsa(DisasContext *ctx) +{ + /* XXX: TODO */ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); +} + +/* esa */ +static void gen_esa(DisasContext *ctx) +{ + /* XXX: TODO */ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); +} + +/* mfrom */ +static void gen_mfrom(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + gen_helper_602_mfrom(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* 602 - 603 - G2 TLB management */ + +/* tlbld */ +static void gen_tlbld_6xx(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + gen_helper_6xx_tlbd(cpu_env, cpu_gpr[rB(ctx->opcode)]); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* tlbli */ +static void gen_tlbli_6xx(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + gen_helper_6xx_tlbi(cpu_env, cpu_gpr[rB(ctx->opcode)]); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* 74xx TLB management */ + +/* tlbld */ +static void gen_tlbld_74xx(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + gen_helper_74xx_tlbd(cpu_env, cpu_gpr[rB(ctx->opcode)]); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* tlbli */ +static void gen_tlbli_74xx(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + gen_helper_74xx_tlbi(cpu_env, cpu_gpr[rB(ctx->opcode)]); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* POWER instructions not in PowerPC 601 */ + +/* clf */ +static void gen_clf(DisasContext *ctx) +{ + /* Cache line flush: implemented as no-op */ +} + +/* cli */ +static void gen_cli(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + /* Cache line invalidate: privileged and treated as no-op */ + CHK_SV; +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* dclst */ +static void gen_dclst(DisasContext *ctx) +{ + /* Data cache line store: treated as no-op */ +} + +static void gen_mfsri(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + int ra = rA(ctx->opcode); + int rd = rD(ctx->opcode); + TCGv t0; + + CHK_SV; + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + tcg_gen_shri_tl(t0, t0, 28); + tcg_gen_andi_tl(t0, t0, 0xF); + gen_helper_load_sr(cpu_gpr[rd], cpu_env, t0); + tcg_temp_free(t0); + if (ra != 0 && ra != rd) + tcg_gen_mov_tl(cpu_gpr[ra], cpu_gpr[rd]); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_rac(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv t0; + + CHK_SV; + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + gen_helper_rac(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); + tcg_temp_free(t0); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_rfsvc(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + + gen_helper_rfsvc(cpu_env); + gen_sync_exception(ctx); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* svc is not implemented for now */ + +/* BookE specific instructions */ + +/* XXX: not implemented on 440 ? */ +static void gen_mfapidi(DisasContext *ctx) +{ + /* XXX: TODO */ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); +} + +/* XXX: not implemented on 440 ? */ +static void gen_tlbiva(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv t0; + + CHK_SV; + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + gen_helper_tlbiva(cpu_env, cpu_gpr[rB(ctx->opcode)]); + tcg_temp_free(t0); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* All 405 MAC instructions are translated here */ +static inline void gen_405_mulladd_insn(DisasContext *ctx, int opc2, int opc3, + int ra, int rb, int rt, int Rc) +{ + TCGv t0, t1; + + t0 = tcg_temp_local_new(); + t1 = tcg_temp_local_new(); + + switch (opc3 & 0x0D) { + case 0x05: + /* macchw - macchw. - macchwo - macchwo. */ + /* macchws - macchws. - macchwso - macchwso. */ + /* nmacchw - nmacchw. - nmacchwo - nmacchwo. */ + /* nmacchws - nmacchws. - nmacchwso - nmacchwso. */ + /* mulchw - mulchw. */ + tcg_gen_ext16s_tl(t0, cpu_gpr[ra]); + tcg_gen_sari_tl(t1, cpu_gpr[rb], 16); + tcg_gen_ext16s_tl(t1, t1); + break; + case 0x04: + /* macchwu - macchwu. - macchwuo - macchwuo. */ + /* macchwsu - macchwsu. - macchwsuo - macchwsuo. */ + /* mulchwu - mulchwu. */ + tcg_gen_ext16u_tl(t0, cpu_gpr[ra]); + tcg_gen_shri_tl(t1, cpu_gpr[rb], 16); + tcg_gen_ext16u_tl(t1, t1); + break; + case 0x01: + /* machhw - machhw. - machhwo - machhwo. */ + /* machhws - machhws. - machhwso - machhwso. */ + /* nmachhw - nmachhw. - nmachhwo - nmachhwo. */ + /* nmachhws - nmachhws. - nmachhwso - nmachhwso. */ + /* mulhhw - mulhhw. */ + tcg_gen_sari_tl(t0, cpu_gpr[ra], 16); + tcg_gen_ext16s_tl(t0, t0); + tcg_gen_sari_tl(t1, cpu_gpr[rb], 16); + tcg_gen_ext16s_tl(t1, t1); + break; + case 0x00: + /* machhwu - machhwu. - machhwuo - machhwuo. */ + /* machhwsu - machhwsu. - machhwsuo - machhwsuo. */ + /* mulhhwu - mulhhwu. */ + tcg_gen_shri_tl(t0, cpu_gpr[ra], 16); + tcg_gen_ext16u_tl(t0, t0); + tcg_gen_shri_tl(t1, cpu_gpr[rb], 16); + tcg_gen_ext16u_tl(t1, t1); + break; + case 0x0D: + /* maclhw - maclhw. - maclhwo - maclhwo. */ + /* maclhws - maclhws. - maclhwso - maclhwso. */ + /* nmaclhw - nmaclhw. - nmaclhwo - nmaclhwo. */ + /* nmaclhws - nmaclhws. - nmaclhwso - nmaclhwso. */ + /* mullhw - mullhw. */ + tcg_gen_ext16s_tl(t0, cpu_gpr[ra]); + tcg_gen_ext16s_tl(t1, cpu_gpr[rb]); + break; + case 0x0C: + /* maclhwu - maclhwu. - maclhwuo - maclhwuo. */ + /* maclhwsu - maclhwsu. - maclhwsuo - maclhwsuo. */ + /* mullhwu - mullhwu. */ + tcg_gen_ext16u_tl(t0, cpu_gpr[ra]); + tcg_gen_ext16u_tl(t1, cpu_gpr[rb]); + break; + } + if (opc2 & 0x04) { + /* (n)multiply-and-accumulate (0x0C / 0x0E) */ + tcg_gen_mul_tl(t1, t0, t1); + if (opc2 & 0x02) { + /* nmultiply-and-accumulate (0x0E) */ + tcg_gen_sub_tl(t0, cpu_gpr[rt], t1); + } else { + /* multiply-and-accumulate (0x0C) */ + tcg_gen_add_tl(t0, cpu_gpr[rt], t1); + } + + if (opc3 & 0x12) { + /* Check overflow and/or saturate */ + TCGLabel *l1 = gen_new_label(); + + if (opc3 & 0x10) { + /* Start with XER OV disabled, the most likely case */ + tcg_gen_movi_tl(cpu_ov, 0); + } + if (opc3 & 0x01) { + /* Signed */ + tcg_gen_xor_tl(t1, cpu_gpr[rt], t1); + tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); + tcg_gen_xor_tl(t1, cpu_gpr[rt], t0); + tcg_gen_brcondi_tl(TCG_COND_LT, t1, 0, l1); + if (opc3 & 0x02) { + /* Saturate */ + tcg_gen_sari_tl(t0, cpu_gpr[rt], 31); + tcg_gen_xori_tl(t0, t0, 0x7fffffff); + } + } else { + /* Unsigned */ + tcg_gen_brcond_tl(TCG_COND_GEU, t0, t1, l1); + if (opc3 & 0x02) { + /* Saturate */ + tcg_gen_movi_tl(t0, UINT32_MAX); + } + } + if (opc3 & 0x10) { + /* Check overflow */ + tcg_gen_movi_tl(cpu_ov, 1); + tcg_gen_movi_tl(cpu_so, 1); + } + gen_set_label(l1); + tcg_gen_mov_tl(cpu_gpr[rt], t0); + } + } else { + tcg_gen_mul_tl(cpu_gpr[rt], t0, t1); + } + tcg_temp_free(t0); + tcg_temp_free(t1); + if (unlikely(Rc) != 0) { + /* Update Rc0 */ + gen_set_Rc0(ctx, cpu_gpr[rt]); + } +} + +#define GEN_MAC_HANDLER(name, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + gen_405_mulladd_insn(ctx, opc2, opc3, rA(ctx->opcode), rB(ctx->opcode), \ + rD(ctx->opcode), Rc(ctx->opcode)); \ +} + +/* macchw - macchw. */ +GEN_MAC_HANDLER(macchw, 0x0C, 0x05); +/* macchwo - macchwo. */ +GEN_MAC_HANDLER(macchwo, 0x0C, 0x15); +/* macchws - macchws. */ +GEN_MAC_HANDLER(macchws, 0x0C, 0x07); +/* macchwso - macchwso. */ +GEN_MAC_HANDLER(macchwso, 0x0C, 0x17); +/* macchwsu - macchwsu. */ +GEN_MAC_HANDLER(macchwsu, 0x0C, 0x06); +/* macchwsuo - macchwsuo. */ +GEN_MAC_HANDLER(macchwsuo, 0x0C, 0x16); +/* macchwu - macchwu. */ +GEN_MAC_HANDLER(macchwu, 0x0C, 0x04); +/* macchwuo - macchwuo. */ +GEN_MAC_HANDLER(macchwuo, 0x0C, 0x14); +/* machhw - machhw. */ +GEN_MAC_HANDLER(machhw, 0x0C, 0x01); +/* machhwo - machhwo. */ +GEN_MAC_HANDLER(machhwo, 0x0C, 0x11); +/* machhws - machhws. */ +GEN_MAC_HANDLER(machhws, 0x0C, 0x03); +/* machhwso - machhwso. */ +GEN_MAC_HANDLER(machhwso, 0x0C, 0x13); +/* machhwsu - machhwsu. */ +GEN_MAC_HANDLER(machhwsu, 0x0C, 0x02); +/* machhwsuo - machhwsuo. */ +GEN_MAC_HANDLER(machhwsuo, 0x0C, 0x12); +/* machhwu - machhwu. */ +GEN_MAC_HANDLER(machhwu, 0x0C, 0x00); +/* machhwuo - machhwuo. */ +GEN_MAC_HANDLER(machhwuo, 0x0C, 0x10); +/* maclhw - maclhw. */ +GEN_MAC_HANDLER(maclhw, 0x0C, 0x0D); +/* maclhwo - maclhwo. */ +GEN_MAC_HANDLER(maclhwo, 0x0C, 0x1D); +/* maclhws - maclhws. */ +GEN_MAC_HANDLER(maclhws, 0x0C, 0x0F); +/* maclhwso - maclhwso. */ +GEN_MAC_HANDLER(maclhwso, 0x0C, 0x1F); +/* maclhwu - maclhwu. */ +GEN_MAC_HANDLER(maclhwu, 0x0C, 0x0C); +/* maclhwuo - maclhwuo. */ +GEN_MAC_HANDLER(maclhwuo, 0x0C, 0x1C); +/* maclhwsu - maclhwsu. */ +GEN_MAC_HANDLER(maclhwsu, 0x0C, 0x0E); +/* maclhwsuo - maclhwsuo. */ +GEN_MAC_HANDLER(maclhwsuo, 0x0C, 0x1E); +/* nmacchw - nmacchw. */ +GEN_MAC_HANDLER(nmacchw, 0x0E, 0x05); +/* nmacchwo - nmacchwo. */ +GEN_MAC_HANDLER(nmacchwo, 0x0E, 0x15); +/* nmacchws - nmacchws. */ +GEN_MAC_HANDLER(nmacchws, 0x0E, 0x07); +/* nmacchwso - nmacchwso. */ +GEN_MAC_HANDLER(nmacchwso, 0x0E, 0x17); +/* nmachhw - nmachhw. */ +GEN_MAC_HANDLER(nmachhw, 0x0E, 0x01); +/* nmachhwo - nmachhwo. */ +GEN_MAC_HANDLER(nmachhwo, 0x0E, 0x11); +/* nmachhws - nmachhws. */ +GEN_MAC_HANDLER(nmachhws, 0x0E, 0x03); +/* nmachhwso - nmachhwso. */ +GEN_MAC_HANDLER(nmachhwso, 0x0E, 0x13); +/* nmaclhw - nmaclhw. */ +GEN_MAC_HANDLER(nmaclhw, 0x0E, 0x0D); +/* nmaclhwo - nmaclhwo. */ +GEN_MAC_HANDLER(nmaclhwo, 0x0E, 0x1D); +/* nmaclhws - nmaclhws. */ +GEN_MAC_HANDLER(nmaclhws, 0x0E, 0x0F); +/* nmaclhwso - nmaclhwso. */ +GEN_MAC_HANDLER(nmaclhwso, 0x0E, 0x1F); + +/* mulchw - mulchw. */ +GEN_MAC_HANDLER(mulchw, 0x08, 0x05); +/* mulchwu - mulchwu. */ +GEN_MAC_HANDLER(mulchwu, 0x08, 0x04); +/* mulhhw - mulhhw. */ +GEN_MAC_HANDLER(mulhhw, 0x08, 0x01); +/* mulhhwu - mulhhwu. */ +GEN_MAC_HANDLER(mulhhwu, 0x08, 0x00); +/* mullhw - mullhw. */ +GEN_MAC_HANDLER(mullhw, 0x08, 0x0D); +/* mullhwu - mullhwu. */ +GEN_MAC_HANDLER(mullhwu, 0x08, 0x0C); + +/* mfdcr */ +static void gen_mfdcr(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv dcrn; + + CHK_SV; + dcrn = tcg_const_tl(SPR(ctx->opcode)); + gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, dcrn); + tcg_temp_free(dcrn); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* mtdcr */ +static void gen_mtdcr(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv dcrn; + + CHK_SV; + dcrn = tcg_const_tl(SPR(ctx->opcode)); + gen_helper_store_dcr(cpu_env, dcrn, cpu_gpr[rS(ctx->opcode)]); + tcg_temp_free(dcrn); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* mfdcrx */ +/* XXX: not implemented on 440 ? */ +static void gen_mfdcrx(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, + cpu_gpr[rA(ctx->opcode)]); + /* Note: Rc update flag set leads to undefined state of Rc0 */ +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* mtdcrx */ +/* XXX: not implemented on 440 ? */ +static void gen_mtdcrx(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + gen_helper_store_dcr(cpu_env, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)]); + /* Note: Rc update flag set leads to undefined state of Rc0 */ +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* mfdcrux (PPC 460) : user-mode access to DCR */ +static void gen_mfdcrux(DisasContext *ctx) +{ + gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, + cpu_gpr[rA(ctx->opcode)]); + /* Note: Rc update flag set leads to undefined state of Rc0 */ +} + +/* mtdcrux (PPC 460) : user-mode access to DCR */ +static void gen_mtdcrux(DisasContext *ctx) +{ + gen_helper_store_dcr(cpu_env, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)]); + /* Note: Rc update flag set leads to undefined state of Rc0 */ +} + +/* dccci */ +static void gen_dccci(DisasContext *ctx) +{ + CHK_SV; + /* interpreted as no-op */ +} + +/* dcread */ +static void gen_dcread(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv EA, val; + + CHK_SV; + gen_set_access_type(ctx, ACCESS_CACHE); + EA = tcg_temp_new(); + gen_addr_reg_index(ctx, EA); + val = tcg_temp_new(); + gen_qemu_ld32u(ctx, val, EA); + tcg_temp_free(val); + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], EA); + tcg_temp_free(EA); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* icbt */ +static void gen_icbt_40x(DisasContext *ctx) +{ + /* interpreted as no-op */ + /* XXX: specification say this is treated as a load by the MMU + * but does not generate any exception + */ +} + +/* iccci */ +static void gen_iccci(DisasContext *ctx) +{ + CHK_SV; + /* interpreted as no-op */ +} + +/* icread */ +static void gen_icread(DisasContext *ctx) +{ + CHK_SV; + /* interpreted as no-op */ +} + +/* rfci (supervisor only) */ +static void gen_rfci_40x(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + /* Restore CPU state */ + gen_helper_40x_rfci(cpu_env); + gen_sync_exception(ctx); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_rfci(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + /* Restore CPU state */ + gen_helper_rfci(cpu_env); + gen_sync_exception(ctx); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* BookE specific */ + +/* XXX: not implemented on 440 ? */ +static void gen_rfdi(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + /* Restore CPU state */ + gen_helper_rfdi(cpu_env); + gen_sync_exception(ctx); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* XXX: not implemented on 440 ? */ +static void gen_rfmci(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + /* Restore CPU state */ + gen_helper_rfmci(cpu_env); + gen_sync_exception(ctx); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* TLB management - PowerPC 405 implementation */ + +/* tlbre */ +static void gen_tlbre_40x(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + switch (rB(ctx->opcode)) { + case 0: + gen_helper_4xx_tlbre_hi(cpu_gpr[rD(ctx->opcode)], cpu_env, + cpu_gpr[rA(ctx->opcode)]); + break; + case 1: + gen_helper_4xx_tlbre_lo(cpu_gpr[rD(ctx->opcode)], cpu_env, + cpu_gpr[rA(ctx->opcode)]); + break; + default: + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + break; + } +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* tlbsx - tlbsx. */ +static void gen_tlbsx_40x(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv t0; + + CHK_SV; + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + gen_helper_4xx_tlbsx(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); + tcg_temp_free(t0); + if (Rc(ctx->opcode)) { + TCGLabel *l1 = gen_new_label(); + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[rD(ctx->opcode)], -1, l1); + tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 0x02); + gen_set_label(l1); + } +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* tlbwe */ +static void gen_tlbwe_40x(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + + switch (rB(ctx->opcode)) { + case 0: + gen_helper_4xx_tlbwe_hi(cpu_env, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)]); + break; + case 1: + gen_helper_4xx_tlbwe_lo(cpu_env, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)]); + break; + default: + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + break; + } +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* TLB management - PowerPC 440 implementation */ + +/* tlbre */ +static void gen_tlbre_440(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + + switch (rB(ctx->opcode)) { + case 0: + case 1: + case 2: + { + TCGv_i32 t0 = tcg_const_i32(rB(ctx->opcode)); + gen_helper_440_tlbre(cpu_gpr[rD(ctx->opcode)], cpu_env, + t0, cpu_gpr[rA(ctx->opcode)]); + tcg_temp_free_i32(t0); + } + break; + default: + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + break; + } +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* tlbsx - tlbsx. */ +static void gen_tlbsx_440(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv t0; + + CHK_SV; + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + gen_helper_440_tlbsx(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); + tcg_temp_free(t0); + if (Rc(ctx->opcode)) { + TCGLabel *l1 = gen_new_label(); + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[rD(ctx->opcode)], -1, l1); + tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 0x02); + gen_set_label(l1); + } +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* tlbwe */ +static void gen_tlbwe_440(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + switch (rB(ctx->opcode)) { + case 0: + case 1: + case 2: + { + TCGv_i32 t0 = tcg_const_i32(rB(ctx->opcode)); + gen_helper_440_tlbwe(cpu_env, t0, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)]); + tcg_temp_free_i32(t0); + } + break; + default: + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + break; + } +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* TLB management - PowerPC BookE 2.06 implementation */ + +/* tlbre */ +static void gen_tlbre_booke206(DisasContext *ctx) +{ + #if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + gen_helper_booke206_tlbre(cpu_env); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* tlbsx - tlbsx. */ +static void gen_tlbsx_booke206(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv t0; + + CHK_SV; + if (rA(ctx->opcode)) { + t0 = tcg_temp_new(); + tcg_gen_mov_tl(t0, cpu_gpr[rD(ctx->opcode)]); + } else { + t0 = tcg_const_tl(0); + } + + tcg_gen_add_tl(t0, t0, cpu_gpr[rB(ctx->opcode)]); + gen_helper_booke206_tlbsx(cpu_env, t0); + tcg_temp_free(t0); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* tlbwe */ +static void gen_tlbwe_booke206(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + gen_helper_booke206_tlbwe(cpu_env); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_tlbivax_booke206(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv t0; + + CHK_SV; + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + gen_helper_booke206_tlbivax(cpu_env, t0); + tcg_temp_free(t0); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_tlbilx_booke206(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv t0; + + CHK_SV; + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + + switch((ctx->opcode >> 21) & 0x3) { + case 0: + gen_helper_booke206_tlbilx0(cpu_env, t0); + break; + case 1: + gen_helper_booke206_tlbilx1(cpu_env, t0); + break; + case 3: + gen_helper_booke206_tlbilx3(cpu_env, t0); + break; + default: + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + break; + } + + tcg_temp_free(t0); +#endif /* defined(CONFIG_USER_ONLY) */ +} + + +/* wrtee */ +static void gen_wrtee(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv t0; + + CHK_SV; + t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_gpr[rD(ctx->opcode)], (1 << MSR_EE)); + tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(1 << MSR_EE)); + tcg_gen_or_tl(cpu_msr, cpu_msr, t0); + tcg_temp_free(t0); + /* Stop translation to have a chance to raise an exception + * if we just set msr_ee to 1 + */ + gen_stop_exception(ctx); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* wrteei */ +static void gen_wrteei(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + if (ctx->opcode & 0x00008000) { + tcg_gen_ori_tl(cpu_msr, cpu_msr, (1 << MSR_EE)); + /* Stop translation to have a chance to raise an exception */ + gen_stop_exception(ctx); + } else { + tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(1 << MSR_EE)); + } +#endif /* defined(CONFIG_USER_ONLY) */ +} + +/* PowerPC 440 specific instructions */ + +/* dlmzb */ +static void gen_dlmzb(DisasContext *ctx) +{ + TCGv_i32 t0 = tcg_const_i32(Rc(ctx->opcode)); + gen_helper_dlmzb(cpu_gpr[rA(ctx->opcode)], cpu_env, + cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); + tcg_temp_free_i32(t0); +} + +/* mbar replaces eieio on 440 */ +static void gen_mbar(DisasContext *ctx) +{ + /* interpreted as no-op */ +} + +/* msync replaces sync on 440 */ +static void gen_msync_4xx(DisasContext *ctx) +{ + /* interpreted as no-op */ +} + +/* icbt */ +static void gen_icbt_440(DisasContext *ctx) +{ + /* interpreted as no-op */ + /* XXX: specification say this is treated as a load by the MMU + * but does not generate any exception + */ +} + +/* Embedded.Processor Control */ + +static void gen_msgclr(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + gen_helper_msgclr(cpu_env, cpu_gpr[rB(ctx->opcode)]); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_msgsnd(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + gen_helper_msgsnd(cpu_gpr[rB(ctx->opcode)]); +#endif /* defined(CONFIG_USER_ONLY) */ +} + + +#if defined(TARGET_PPC64) +static void gen_maddld(DisasContext *ctx) +{ + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_mul_i64(t1, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + tcg_gen_add_i64(cpu_gpr[rD(ctx->opcode)], t1, cpu_gpr[rC(ctx->opcode)]); + tcg_temp_free_i64(t1); +} + +/* maddhd maddhdu */ +static void gen_maddhd_maddhdu(DisasContext *ctx) +{ + TCGv_i64 lo = tcg_temp_new_i64(); + TCGv_i64 hi = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + if (Rc(ctx->opcode)) { + tcg_gen_mulu2_i64(lo, hi, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rB(ctx->opcode)]); + tcg_gen_movi_i64(t1, 0); + } else { + tcg_gen_muls2_i64(lo, hi, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rB(ctx->opcode)]); + tcg_gen_sari_i64(t1, cpu_gpr[rC(ctx->opcode)], 63); + } + tcg_gen_add2_i64(t1, cpu_gpr[rD(ctx->opcode)], lo, hi, + cpu_gpr[rC(ctx->opcode)], t1); + tcg_temp_free_i64(lo); + tcg_temp_free_i64(hi); + tcg_temp_free_i64(t1); +} +#endif /* defined(TARGET_PPC64) */ + +static void gen_tbegin(DisasContext *ctx) +{ + if (unlikely(!ctx->tm_enabled)) { + gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM); + return; + } + gen_helper_tbegin(cpu_env); +} + +#define GEN_TM_NOOP(name) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + if (unlikely(!ctx->tm_enabled)) { \ + gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM); \ + return; \ + } \ + /* Because tbegin always fails in QEMU, these user \ + * space instructions all have a simple implementation: \ + * \ + * CR[0] = 0b0 || MSR[TS] || 0b0 \ + * = 0b0 || 0b00 || 0b0 \ + */ \ + tcg_gen_movi_i32(cpu_crf[0], 0); \ +} + +GEN_TM_NOOP(tend); +GEN_TM_NOOP(tabort); +GEN_TM_NOOP(tabortwc); +GEN_TM_NOOP(tabortwci); +GEN_TM_NOOP(tabortdc); +GEN_TM_NOOP(tabortdci); +GEN_TM_NOOP(tsr); + +static void gen_tcheck(DisasContext *ctx) +{ + if (unlikely(!ctx->tm_enabled)) { + gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM); + return; + } + /* Because tbegin always fails, the tcheck implementation + * is simple: + * + * CR[CRF] = TDOOMED || MSR[TS] || 0b0 + * = 0b1 || 0b00 || 0b0 + */ + tcg_gen_movi_i32(cpu_crf[crfD(ctx->opcode)], 0x8); +} + +#if defined(CONFIG_USER_ONLY) +#define GEN_TM_PRIV_NOOP(name) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); \ +} + +#else + +#define GEN_TM_PRIV_NOOP(name) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + CHK_SV; \ + if (unlikely(!ctx->tm_enabled)) { \ + gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM); \ + return; \ + } \ + /* Because tbegin always fails, the implementation is \ + * simple: \ + * \ + * CR[0] = 0b0 || MSR[TS] || 0b0 \ + * = 0b0 || 0b00 | 0b0 \ + */ \ + tcg_gen_movi_i32(cpu_crf[0], 0); \ +} + +#endif + +GEN_TM_PRIV_NOOP(treclaim); +GEN_TM_PRIV_NOOP(trechkpt); + +#include "translate/fp-impl.inc.c" + +#include "translate/vmx-impl.inc.c" + +#include "translate/vsx-impl.inc.c" + +#include "translate/dfp-impl.inc.c" + +#include "translate/spe-impl.inc.c" + +static opcode_t opcodes[] = { +GEN_HANDLER(invalid, 0x00, 0x00, 0x00, 0xFFFFFFFF, PPC_NONE), +GEN_HANDLER(cmp, 0x1F, 0x00, 0x00, 0x00400000, PPC_INTEGER), +GEN_HANDLER(cmpi, 0x0B, 0xFF, 0xFF, 0x00400000, PPC_INTEGER), +GEN_HANDLER(cmpl, 0x1F, 0x00, 0x01, 0x00400001, PPC_INTEGER), +GEN_HANDLER(cmpli, 0x0A, 0xFF, 0xFF, 0x00400000, PPC_INTEGER), +#if defined(TARGET_PPC64) +GEN_HANDLER_E(cmpeqb, 0x1F, 0x00, 0x07, 0x00600000, PPC_NONE, PPC2_ISA300), +#endif +GEN_HANDLER_E(cmpb, 0x1F, 0x1C, 0x0F, 0x00000001, PPC_NONE, PPC2_ISA205), +GEN_HANDLER_E(cmprb, 0x1F, 0x00, 0x06, 0x00400001, PPC_NONE, PPC2_ISA300), +GEN_HANDLER(isel, 0x1F, 0x0F, 0xFF, 0x00000001, PPC_ISEL), +GEN_HANDLER(addi, 0x0E, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), +GEN_HANDLER(addic, 0x0C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), +GEN_HANDLER2(addic_, "addic.", 0x0D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), +GEN_HANDLER(addis, 0x0F, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), +GEN_HANDLER_E(addpcis, 0x13, 0x2, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA300), +GEN_HANDLER(mulhw, 0x1F, 0x0B, 0x02, 0x00000400, PPC_INTEGER), +GEN_HANDLER(mulhwu, 0x1F, 0x0B, 0x00, 0x00000400, PPC_INTEGER), +GEN_HANDLER(mullw, 0x1F, 0x0B, 0x07, 0x00000000, PPC_INTEGER), +GEN_HANDLER(mullwo, 0x1F, 0x0B, 0x17, 0x00000000, PPC_INTEGER), +GEN_HANDLER(mulli, 0x07, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), +#if defined(TARGET_PPC64) +GEN_HANDLER(mulld, 0x1F, 0x09, 0x07, 0x00000000, PPC_64B), +#endif +GEN_HANDLER(neg, 0x1F, 0x08, 0x03, 0x0000F800, PPC_INTEGER), +GEN_HANDLER(nego, 0x1F, 0x08, 0x13, 0x0000F800, PPC_INTEGER), +GEN_HANDLER(subfic, 0x08, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), +GEN_HANDLER2(andi_, "andi.", 0x1C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), +GEN_HANDLER2(andis_, "andis.", 0x1D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), +GEN_HANDLER(cntlzw, 0x1F, 0x1A, 0x00, 0x00000000, PPC_INTEGER), +GEN_HANDLER_E(cnttzw, 0x1F, 0x1A, 0x10, 0x00000000, PPC_NONE, PPC2_ISA300), +GEN_HANDLER(or, 0x1F, 0x1C, 0x0D, 0x00000000, PPC_INTEGER), +GEN_HANDLER(xor, 0x1F, 0x1C, 0x09, 0x00000000, PPC_INTEGER), +GEN_HANDLER(ori, 0x18, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), +GEN_HANDLER(oris, 0x19, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), +GEN_HANDLER(xori, 0x1A, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), +GEN_HANDLER(xoris, 0x1B, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), +GEN_HANDLER(popcntb, 0x1F, 0x1A, 0x03, 0x0000F801, PPC_POPCNTB), +GEN_HANDLER(popcntw, 0x1F, 0x1A, 0x0b, 0x0000F801, PPC_POPCNTWD), +GEN_HANDLER_E(prtyw, 0x1F, 0x1A, 0x04, 0x0000F801, PPC_NONE, PPC2_ISA205), +#if defined(TARGET_PPC64) +GEN_HANDLER(popcntd, 0x1F, 0x1A, 0x0F, 0x0000F801, PPC_POPCNTWD), +GEN_HANDLER(cntlzd, 0x1F, 0x1A, 0x01, 0x00000000, PPC_64B), +GEN_HANDLER_E(cnttzd, 0x1F, 0x1A, 0x11, 0x00000000, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E(darn, 0x1F, 0x13, 0x17, 0x001CF801, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E(prtyd, 0x1F, 0x1A, 0x05, 0x0000F801, PPC_NONE, PPC2_ISA205), +GEN_HANDLER_E(bpermd, 0x1F, 0x1C, 0x07, 0x00000001, PPC_NONE, PPC2_PERM_ISA206), +#endif +GEN_HANDLER(rlwimi, 0x14, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), +GEN_HANDLER(rlwinm, 0x15, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), +GEN_HANDLER(rlwnm, 0x17, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), +GEN_HANDLER(slw, 0x1F, 0x18, 0x00, 0x00000000, PPC_INTEGER), +GEN_HANDLER(sraw, 0x1F, 0x18, 0x18, 0x00000000, PPC_INTEGER), +GEN_HANDLER(srawi, 0x1F, 0x18, 0x19, 0x00000000, PPC_INTEGER), +GEN_HANDLER(srw, 0x1F, 0x18, 0x10, 0x00000000, PPC_INTEGER), +#if defined(TARGET_PPC64) +GEN_HANDLER(sld, 0x1F, 0x1B, 0x00, 0x00000000, PPC_64B), +GEN_HANDLER(srad, 0x1F, 0x1A, 0x18, 0x00000000, PPC_64B), +GEN_HANDLER2(sradi0, "sradi", 0x1F, 0x1A, 0x19, 0x00000000, PPC_64B), +GEN_HANDLER2(sradi1, "sradi", 0x1F, 0x1B, 0x19, 0x00000000, PPC_64B), +GEN_HANDLER(srd, 0x1F, 0x1B, 0x10, 0x00000000, PPC_64B), +GEN_HANDLER2_E(extswsli0, "extswsli", 0x1F, 0x1A, 0x1B, 0x00000000, + PPC_NONE, PPC2_ISA300), +GEN_HANDLER2_E(extswsli1, "extswsli", 0x1F, 0x1B, 0x1B, 0x00000000, + PPC_NONE, PPC2_ISA300), +#endif +#if defined(TARGET_PPC64) +GEN_HANDLER(ld, 0x3A, 0xFF, 0xFF, 0x00000000, PPC_64B), +GEN_HANDLER(lq, 0x38, 0xFF, 0xFF, 0x00000000, PPC_64BX), +GEN_HANDLER(std, 0x3E, 0xFF, 0xFF, 0x00000000, PPC_64B), +#endif +GEN_HANDLER(lmw, 0x2E, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), +GEN_HANDLER(stmw, 0x2F, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), +GEN_HANDLER(lswi, 0x1F, 0x15, 0x12, 0x00000001, PPC_STRING), +GEN_HANDLER(lswx, 0x1F, 0x15, 0x10, 0x00000001, PPC_STRING), +GEN_HANDLER(stswi, 0x1F, 0x15, 0x16, 0x00000001, PPC_STRING), +GEN_HANDLER(stswx, 0x1F, 0x15, 0x14, 0x00000001, PPC_STRING), +GEN_HANDLER(eieio, 0x1F, 0x16, 0x1A, 0x03FFF801, PPC_MEM_EIEIO), +GEN_HANDLER(isync, 0x13, 0x16, 0x04, 0x03FFF801, PPC_MEM), +GEN_HANDLER_E(lbarx, 0x1F, 0x14, 0x01, 0, PPC_NONE, PPC2_ATOMIC_ISA206), +GEN_HANDLER_E(lharx, 0x1F, 0x14, 0x03, 0, PPC_NONE, PPC2_ATOMIC_ISA206), +GEN_HANDLER(lwarx, 0x1F, 0x14, 0x00, 0x00000000, PPC_RES), +GEN_HANDLER_E(stbcx_, 0x1F, 0x16, 0x15, 0, PPC_NONE, PPC2_ATOMIC_ISA206), +GEN_HANDLER_E(sthcx_, 0x1F, 0x16, 0x16, 0, PPC_NONE, PPC2_ATOMIC_ISA206), +GEN_HANDLER2(stwcx_, "stwcx.", 0x1F, 0x16, 0x04, 0x00000000, PPC_RES), +#if defined(TARGET_PPC64) +GEN_HANDLER(ldarx, 0x1F, 0x14, 0x02, 0x00000000, PPC_64B), +GEN_HANDLER_E(lqarx, 0x1F, 0x14, 0x08, 0, PPC_NONE, PPC2_LSQ_ISA207), +GEN_HANDLER2(stdcx_, "stdcx.", 0x1F, 0x16, 0x06, 0x00000000, PPC_64B), +GEN_HANDLER_E(stqcx_, 0x1F, 0x16, 0x05, 0, PPC_NONE, PPC2_LSQ_ISA207), +#endif +GEN_HANDLER(sync, 0x1F, 0x16, 0x12, 0x039FF801, PPC_MEM_SYNC), +GEN_HANDLER(wait, 0x1F, 0x1E, 0x01, 0x03FFF801, PPC_WAIT), +GEN_HANDLER(b, 0x12, 0xFF, 0xFF, 0x00000000, PPC_FLOW), +GEN_HANDLER(bc, 0x10, 0xFF, 0xFF, 0x00000000, PPC_FLOW), +GEN_HANDLER(bcctr, 0x13, 0x10, 0x10, 0x00000000, PPC_FLOW), +GEN_HANDLER(bclr, 0x13, 0x10, 0x00, 0x00000000, PPC_FLOW), +GEN_HANDLER_E(bctar, 0x13, 0x10, 0x11, 0x0000E000, PPC_NONE, PPC2_BCTAR_ISA207), +GEN_HANDLER(mcrf, 0x13, 0x00, 0xFF, 0x00000001, PPC_INTEGER), +GEN_HANDLER(rfi, 0x13, 0x12, 0x01, 0x03FF8001, PPC_FLOW), +#if defined(TARGET_PPC64) +GEN_HANDLER(rfid, 0x13, 0x12, 0x00, 0x03FF8001, PPC_64B), +GEN_HANDLER_E(doze, 0x13, 0x12, 0x0c, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206), +GEN_HANDLER_E(nap, 0x13, 0x12, 0x0d, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206), +GEN_HANDLER_E(sleep, 0x13, 0x12, 0x0e, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206), +GEN_HANDLER_E(rvwinkle, 0x13, 0x12, 0x0f, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206), +GEN_HANDLER(hrfid, 0x13, 0x12, 0x08, 0x03FF8001, PPC_64H), +#endif +GEN_HANDLER(sc, 0x11, 0xFF, 0xFF, 0x03FFF01D, PPC_FLOW), +GEN_HANDLER(tw, 0x1F, 0x04, 0x00, 0x00000001, PPC_FLOW), +GEN_HANDLER(twi, 0x03, 0xFF, 0xFF, 0x00000000, PPC_FLOW), +#if defined(TARGET_PPC64) +GEN_HANDLER(td, 0x1F, 0x04, 0x02, 0x00000001, PPC_64B), +GEN_HANDLER(tdi, 0x02, 0xFF, 0xFF, 0x00000000, PPC_64B), +#endif +GEN_HANDLER(mcrxr, 0x1F, 0x00, 0x10, 0x007FF801, PPC_MISC), +GEN_HANDLER(mfcr, 0x1F, 0x13, 0x00, 0x00000801, PPC_MISC), +GEN_HANDLER(mfmsr, 0x1F, 0x13, 0x02, 0x001FF801, PPC_MISC), +GEN_HANDLER(mfspr, 0x1F, 0x13, 0x0A, 0x00000001, PPC_MISC), +GEN_HANDLER(mftb, 0x1F, 0x13, 0x0B, 0x00000001, PPC_MFTB), +GEN_HANDLER(mtcrf, 0x1F, 0x10, 0x04, 0x00000801, PPC_MISC), +#if defined(TARGET_PPC64) +GEN_HANDLER(mtmsrd, 0x1F, 0x12, 0x05, 0x001EF801, PPC_64B), +GEN_HANDLER_E(setb, 0x1F, 0x00, 0x04, 0x0003F801, PPC_NONE, PPC2_ISA300), +#endif +GEN_HANDLER(mtmsr, 0x1F, 0x12, 0x04, 0x001EF801, PPC_MISC), +GEN_HANDLER(mtspr, 0x1F, 0x13, 0x0E, 0x00000000, PPC_MISC), +GEN_HANDLER(dcbf, 0x1F, 0x16, 0x02, 0x03C00001, PPC_CACHE), +GEN_HANDLER(dcbi, 0x1F, 0x16, 0x0E, 0x03E00001, PPC_CACHE), +GEN_HANDLER(dcbst, 0x1F, 0x16, 0x01, 0x03E00001, PPC_CACHE), +GEN_HANDLER(dcbt, 0x1F, 0x16, 0x08, 0x00000001, PPC_CACHE), +GEN_HANDLER(dcbtst, 0x1F, 0x16, 0x07, 0x00000001, PPC_CACHE), +GEN_HANDLER_E(dcbtls, 0x1F, 0x06, 0x05, 0x02000001, PPC_BOOKE, PPC2_BOOKE206), +GEN_HANDLER(dcbz, 0x1F, 0x16, 0x1F, 0x03C00001, PPC_CACHE_DCBZ), +GEN_HANDLER(dst, 0x1F, 0x16, 0x0A, 0x01800001, PPC_ALTIVEC), +GEN_HANDLER(dstst, 0x1F, 0x16, 0x0B, 0x02000001, PPC_ALTIVEC), +GEN_HANDLER(dss, 0x1F, 0x16, 0x19, 0x019FF801, PPC_ALTIVEC), +GEN_HANDLER(icbi, 0x1F, 0x16, 0x1E, 0x03E00001, PPC_CACHE_ICBI), +GEN_HANDLER(dcba, 0x1F, 0x16, 0x17, 0x03E00001, PPC_CACHE_DCBA), +GEN_HANDLER(mfsr, 0x1F, 0x13, 0x12, 0x0010F801, PPC_SEGMENT), +GEN_HANDLER(mfsrin, 0x1F, 0x13, 0x14, 0x001F0001, PPC_SEGMENT), +GEN_HANDLER(mtsr, 0x1F, 0x12, 0x06, 0x0010F801, PPC_SEGMENT), +GEN_HANDLER(mtsrin, 0x1F, 0x12, 0x07, 0x001F0001, PPC_SEGMENT), +#if defined(TARGET_PPC64) +GEN_HANDLER2(mfsr_64b, "mfsr", 0x1F, 0x13, 0x12, 0x0010F801, PPC_SEGMENT_64B), +GEN_HANDLER2(mfsrin_64b, "mfsrin", 0x1F, 0x13, 0x14, 0x001F0001, + PPC_SEGMENT_64B), +GEN_HANDLER2(mtsr_64b, "mtsr", 0x1F, 0x12, 0x06, 0x0010F801, PPC_SEGMENT_64B), +GEN_HANDLER2(mtsrin_64b, "mtsrin", 0x1F, 0x12, 0x07, 0x001F0001, + PPC_SEGMENT_64B), +GEN_HANDLER2(slbmte, "slbmte", 0x1F, 0x12, 0x0C, 0x001F0001, PPC_SEGMENT_64B), +GEN_HANDLER2(slbmfee, "slbmfee", 0x1F, 0x13, 0x1C, 0x001F0001, PPC_SEGMENT_64B), +GEN_HANDLER2(slbmfev, "slbmfev", 0x1F, 0x13, 0x1A, 0x001F0001, PPC_SEGMENT_64B), +GEN_HANDLER2(slbfee_, "slbfee.", 0x1F, 0x13, 0x1E, 0x001F0000, PPC_SEGMENT_64B), +#endif +GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_TLBIA), +/* XXX Those instructions will need to be handled differently for + * different ISA versions */ +GEN_HANDLER(tlbiel, 0x1F, 0x12, 0x08, 0x001F0001, PPC_MEM_TLBIE), +GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x001F0001, PPC_MEM_TLBIE), +GEN_HANDLER(tlbsync, 0x1F, 0x16, 0x11, 0x03FFF801, PPC_MEM_TLBSYNC), +#if defined(TARGET_PPC64) +GEN_HANDLER(slbia, 0x1F, 0x12, 0x0F, 0x031FFC01, PPC_SLBI), +GEN_HANDLER(slbie, 0x1F, 0x12, 0x0D, 0x03FF0001, PPC_SLBI), +#endif +GEN_HANDLER(eciwx, 0x1F, 0x16, 0x0D, 0x00000001, PPC_EXTERN), +GEN_HANDLER(ecowx, 0x1F, 0x16, 0x09, 0x00000001, PPC_EXTERN), +GEN_HANDLER(abs, 0x1F, 0x08, 0x0B, 0x0000F800, PPC_POWER_BR), +GEN_HANDLER(abso, 0x1F, 0x08, 0x1B, 0x0000F800, PPC_POWER_BR), +GEN_HANDLER(clcs, 0x1F, 0x10, 0x13, 0x0000F800, PPC_POWER_BR), +GEN_HANDLER(div, 0x1F, 0x0B, 0x0A, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(divo, 0x1F, 0x0B, 0x1A, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(divs, 0x1F, 0x0B, 0x0B, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(divso, 0x1F, 0x0B, 0x1B, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(doz, 0x1F, 0x08, 0x08, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(dozo, 0x1F, 0x08, 0x18, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(dozi, 0x09, 0xFF, 0xFF, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(lscbx, 0x1F, 0x15, 0x08, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(maskg, 0x1F, 0x1D, 0x00, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(maskir, 0x1F, 0x1D, 0x10, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(mul, 0x1F, 0x0B, 0x03, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(mulo, 0x1F, 0x0B, 0x13, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(nabs, 0x1F, 0x08, 0x0F, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(nabso, 0x1F, 0x08, 0x1F, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(rlmi, 0x16, 0xFF, 0xFF, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(rrib, 0x1F, 0x19, 0x10, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(sle, 0x1F, 0x19, 0x04, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(sleq, 0x1F, 0x19, 0x06, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(sliq, 0x1F, 0x18, 0x05, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(slliq, 0x1F, 0x18, 0x07, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(sllq, 0x1F, 0x18, 0x06, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(slq, 0x1F, 0x18, 0x04, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(sraiq, 0x1F, 0x18, 0x1D, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(sraq, 0x1F, 0x18, 0x1C, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(sre, 0x1F, 0x19, 0x14, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(srea, 0x1F, 0x19, 0x1C, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(sreq, 0x1F, 0x19, 0x16, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(sriq, 0x1F, 0x18, 0x15, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(srliq, 0x1F, 0x18, 0x17, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(srlq, 0x1F, 0x18, 0x16, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(srq, 0x1F, 0x18, 0x14, 0x00000000, PPC_POWER_BR), +GEN_HANDLER(dsa, 0x1F, 0x14, 0x13, 0x03FFF801, PPC_602_SPEC), +GEN_HANDLER(esa, 0x1F, 0x14, 0x12, 0x03FFF801, PPC_602_SPEC), +GEN_HANDLER(mfrom, 0x1F, 0x09, 0x08, 0x03E0F801, PPC_602_SPEC), +GEN_HANDLER2(tlbld_6xx, "tlbld", 0x1F, 0x12, 0x1E, 0x03FF0001, PPC_6xx_TLB), +GEN_HANDLER2(tlbli_6xx, "tlbli", 0x1F, 0x12, 0x1F, 0x03FF0001, PPC_6xx_TLB), +GEN_HANDLER2(tlbld_74xx, "tlbld", 0x1F, 0x12, 0x1E, 0x03FF0001, PPC_74xx_TLB), +GEN_HANDLER2(tlbli_74xx, "tlbli", 0x1F, 0x12, 0x1F, 0x03FF0001, PPC_74xx_TLB), +GEN_HANDLER(clf, 0x1F, 0x16, 0x03, 0x03E00000, PPC_POWER), +GEN_HANDLER(cli, 0x1F, 0x16, 0x0F, 0x03E00000, PPC_POWER), +GEN_HANDLER(dclst, 0x1F, 0x16, 0x13, 0x03E00000, PPC_POWER), +GEN_HANDLER(mfsri, 0x1F, 0x13, 0x13, 0x00000001, PPC_POWER), +GEN_HANDLER(rac, 0x1F, 0x12, 0x19, 0x00000001, PPC_POWER), +GEN_HANDLER(rfsvc, 0x13, 0x12, 0x02, 0x03FFF0001, PPC_POWER), +GEN_HANDLER(lfq, 0x38, 0xFF, 0xFF, 0x00000003, PPC_POWER2), +GEN_HANDLER(lfqu, 0x39, 0xFF, 0xFF, 0x00000003, PPC_POWER2), +GEN_HANDLER(lfqux, 0x1F, 0x17, 0x19, 0x00000001, PPC_POWER2), +GEN_HANDLER(lfqx, 0x1F, 0x17, 0x18, 0x00000001, PPC_POWER2), +GEN_HANDLER(stfq, 0x3C, 0xFF, 0xFF, 0x00000003, PPC_POWER2), +GEN_HANDLER(stfqu, 0x3D, 0xFF, 0xFF, 0x00000003, PPC_POWER2), +GEN_HANDLER(stfqux, 0x1F, 0x17, 0x1D, 0x00000001, PPC_POWER2), +GEN_HANDLER(stfqx, 0x1F, 0x17, 0x1C, 0x00000001, PPC_POWER2), +GEN_HANDLER(mfapidi, 0x1F, 0x13, 0x08, 0x0000F801, PPC_MFAPIDI), +GEN_HANDLER(tlbiva, 0x1F, 0x12, 0x18, 0x03FFF801, PPC_TLBIVA), +GEN_HANDLER(mfdcr, 0x1F, 0x03, 0x0A, 0x00000001, PPC_DCR), +GEN_HANDLER(mtdcr, 0x1F, 0x03, 0x0E, 0x00000001, PPC_DCR), +GEN_HANDLER(mfdcrx, 0x1F, 0x03, 0x08, 0x00000000, PPC_DCRX), +GEN_HANDLER(mtdcrx, 0x1F, 0x03, 0x0C, 0x00000000, PPC_DCRX), +GEN_HANDLER(mfdcrux, 0x1F, 0x03, 0x09, 0x00000000, PPC_DCRUX), +GEN_HANDLER(mtdcrux, 0x1F, 0x03, 0x0D, 0x00000000, PPC_DCRUX), +GEN_HANDLER(dccci, 0x1F, 0x06, 0x0E, 0x03E00001, PPC_4xx_COMMON), +GEN_HANDLER(dcread, 0x1F, 0x06, 0x0F, 0x00000001, PPC_4xx_COMMON), +GEN_HANDLER2(icbt_40x, "icbt", 0x1F, 0x06, 0x08, 0x03E00001, PPC_40x_ICBT), +GEN_HANDLER(iccci, 0x1F, 0x06, 0x1E, 0x00000001, PPC_4xx_COMMON), +GEN_HANDLER(icread, 0x1F, 0x06, 0x1F, 0x03E00001, PPC_4xx_COMMON), +GEN_HANDLER2(rfci_40x, "rfci", 0x13, 0x13, 0x01, 0x03FF8001, PPC_40x_EXCP), +GEN_HANDLER_E(rfci, 0x13, 0x13, 0x01, 0x03FF8001, PPC_BOOKE, PPC2_BOOKE206), +GEN_HANDLER(rfdi, 0x13, 0x07, 0x01, 0x03FF8001, PPC_RFDI), +GEN_HANDLER(rfmci, 0x13, 0x06, 0x01, 0x03FF8001, PPC_RFMCI), +GEN_HANDLER2(tlbre_40x, "tlbre", 0x1F, 0x12, 0x1D, 0x00000001, PPC_40x_TLB), +GEN_HANDLER2(tlbsx_40x, "tlbsx", 0x1F, 0x12, 0x1C, 0x00000000, PPC_40x_TLB), +GEN_HANDLER2(tlbwe_40x, "tlbwe", 0x1F, 0x12, 0x1E, 0x00000001, PPC_40x_TLB), +GEN_HANDLER2(tlbre_440, "tlbre", 0x1F, 0x12, 0x1D, 0x00000001, PPC_BOOKE), +GEN_HANDLER2(tlbsx_440, "tlbsx", 0x1F, 0x12, 0x1C, 0x00000000, PPC_BOOKE), +GEN_HANDLER2(tlbwe_440, "tlbwe", 0x1F, 0x12, 0x1E, 0x00000001, PPC_BOOKE), +GEN_HANDLER2_E(tlbre_booke206, "tlbre", 0x1F, 0x12, 0x1D, 0x00000001, + PPC_NONE, PPC2_BOOKE206), +GEN_HANDLER2_E(tlbsx_booke206, "tlbsx", 0x1F, 0x12, 0x1C, 0x00000000, + PPC_NONE, PPC2_BOOKE206), +GEN_HANDLER2_E(tlbwe_booke206, "tlbwe", 0x1F, 0x12, 0x1E, 0x00000001, + PPC_NONE, PPC2_BOOKE206), +GEN_HANDLER2_E(tlbivax_booke206, "tlbivax", 0x1F, 0x12, 0x18, 0x00000001, + PPC_NONE, PPC2_BOOKE206), +GEN_HANDLER2_E(tlbilx_booke206, "tlbilx", 0x1F, 0x12, 0x00, 0x03800001, + PPC_NONE, PPC2_BOOKE206), +GEN_HANDLER2_E(msgsnd, "msgsnd", 0x1F, 0x0E, 0x06, 0x03ff0001, + PPC_NONE, PPC2_PRCNTL), +GEN_HANDLER2_E(msgclr, "msgclr", 0x1F, 0x0E, 0x07, 0x03ff0001, + PPC_NONE, PPC2_PRCNTL), +GEN_HANDLER(wrtee, 0x1F, 0x03, 0x04, 0x000FFC01, PPC_WRTEE), +GEN_HANDLER(wrteei, 0x1F, 0x03, 0x05, 0x000E7C01, PPC_WRTEE), +GEN_HANDLER(dlmzb, 0x1F, 0x0E, 0x02, 0x00000000, PPC_440_SPEC), +GEN_HANDLER_E(mbar, 0x1F, 0x16, 0x1a, 0x001FF801, + PPC_BOOKE, PPC2_BOOKE206), +GEN_HANDLER(msync_4xx, 0x1F, 0x16, 0x12, 0x03FFF801, PPC_BOOKE), +GEN_HANDLER2_E(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001, + PPC_BOOKE, PPC2_BOOKE206), +GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC), +GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC), +GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC), +GEN_HANDLER(mtvscr, 0x04, 0x2, 0x19, 0x03ff0000, PPC_ALTIVEC), +GEN_HANDLER(vmladduhm, 0x04, 0x11, 0xFF, 0x00000000, PPC_ALTIVEC), +#if defined(TARGET_PPC64) +GEN_HANDLER_E(maddhd_maddhdu, 0x04, 0x18, 0xFF, 0x00000000, PPC_NONE, + PPC2_ISA300), +GEN_HANDLER_E(maddld, 0x04, 0x19, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA300), +#endif + +#undef GEN_INT_ARITH_ADD +#undef GEN_INT_ARITH_ADD_CONST +#define GEN_INT_ARITH_ADD(name, opc3, add_ca, compute_ca, compute_ov) \ +GEN_HANDLER(name, 0x1F, 0x0A, opc3, 0x00000000, PPC_INTEGER), +#define GEN_INT_ARITH_ADD_CONST(name, opc3, const_val, \ + add_ca, compute_ca, compute_ov) \ +GEN_HANDLER(name, 0x1F, 0x0A, opc3, 0x0000F800, PPC_INTEGER), +GEN_INT_ARITH_ADD(add, 0x08, 0, 0, 0) +GEN_INT_ARITH_ADD(addo, 0x18, 0, 0, 1) +GEN_INT_ARITH_ADD(addc, 0x00, 0, 1, 0) +GEN_INT_ARITH_ADD(addco, 0x10, 0, 1, 1) +GEN_INT_ARITH_ADD(adde, 0x04, 1, 1, 0) +GEN_INT_ARITH_ADD(addeo, 0x14, 1, 1, 1) +GEN_INT_ARITH_ADD_CONST(addme, 0x07, -1LL, 1, 1, 0) +GEN_INT_ARITH_ADD_CONST(addmeo, 0x17, -1LL, 1, 1, 1) +GEN_INT_ARITH_ADD_CONST(addze, 0x06, 0, 1, 1, 0) +GEN_INT_ARITH_ADD_CONST(addzeo, 0x16, 0, 1, 1, 1) + +#undef GEN_INT_ARITH_DIVW +#define GEN_INT_ARITH_DIVW(name, opc3, sign, compute_ov) \ +GEN_HANDLER(name, 0x1F, 0x0B, opc3, 0x00000000, PPC_INTEGER) +GEN_INT_ARITH_DIVW(divwu, 0x0E, 0, 0), +GEN_INT_ARITH_DIVW(divwuo, 0x1E, 0, 1), +GEN_INT_ARITH_DIVW(divw, 0x0F, 1, 0), +GEN_INT_ARITH_DIVW(divwo, 0x1F, 1, 1), +GEN_HANDLER_E(divwe, 0x1F, 0x0B, 0x0D, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divweo, 0x1F, 0x0B, 0x1D, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divweu, 0x1F, 0x0B, 0x0C, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divweuo, 0x1F, 0x0B, 0x1C, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(modsw, 0x1F, 0x0B, 0x18, 0x00000001, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E(moduw, 0x1F, 0x0B, 0x08, 0x00000001, PPC_NONE, PPC2_ISA300), + +#if defined(TARGET_PPC64) +#undef GEN_INT_ARITH_DIVD +#define GEN_INT_ARITH_DIVD(name, opc3, sign, compute_ov) \ +GEN_HANDLER(name, 0x1F, 0x09, opc3, 0x00000000, PPC_64B) +GEN_INT_ARITH_DIVD(divdu, 0x0E, 0, 0), +GEN_INT_ARITH_DIVD(divduo, 0x1E, 0, 1), +GEN_INT_ARITH_DIVD(divd, 0x0F, 1, 0), +GEN_INT_ARITH_DIVD(divdo, 0x1F, 1, 1), + +GEN_HANDLER_E(divdeu, 0x1F, 0x09, 0x0C, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divdeuo, 0x1F, 0x09, 0x1C, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divde, 0x1F, 0x09, 0x0D, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divdeo, 0x1F, 0x09, 0x1D, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(modsd, 0x1F, 0x09, 0x18, 0x00000001, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E(modud, 0x1F, 0x09, 0x08, 0x00000001, PPC_NONE, PPC2_ISA300), + +#undef GEN_INT_ARITH_MUL_HELPER +#define GEN_INT_ARITH_MUL_HELPER(name, opc3) \ +GEN_HANDLER(name, 0x1F, 0x09, opc3, 0x00000000, PPC_64B) +GEN_INT_ARITH_MUL_HELPER(mulhdu, 0x00), +GEN_INT_ARITH_MUL_HELPER(mulhd, 0x02), +GEN_INT_ARITH_MUL_HELPER(mulldo, 0x17), +#endif + +#undef GEN_INT_ARITH_SUBF +#undef GEN_INT_ARITH_SUBF_CONST +#define GEN_INT_ARITH_SUBF(name, opc3, add_ca, compute_ca, compute_ov) \ +GEN_HANDLER(name, 0x1F, 0x08, opc3, 0x00000000, PPC_INTEGER), +#define GEN_INT_ARITH_SUBF_CONST(name, opc3, const_val, \ + add_ca, compute_ca, compute_ov) \ +GEN_HANDLER(name, 0x1F, 0x08, opc3, 0x0000F800, PPC_INTEGER), +GEN_INT_ARITH_SUBF(subf, 0x01, 0, 0, 0) +GEN_INT_ARITH_SUBF(subfo, 0x11, 0, 0, 1) +GEN_INT_ARITH_SUBF(subfc, 0x00, 0, 1, 0) +GEN_INT_ARITH_SUBF(subfco, 0x10, 0, 1, 1) +GEN_INT_ARITH_SUBF(subfe, 0x04, 1, 1, 0) +GEN_INT_ARITH_SUBF(subfeo, 0x14, 1, 1, 1) +GEN_INT_ARITH_SUBF_CONST(subfme, 0x07, -1LL, 1, 1, 0) +GEN_INT_ARITH_SUBF_CONST(subfmeo, 0x17, -1LL, 1, 1, 1) +GEN_INT_ARITH_SUBF_CONST(subfze, 0x06, 0, 1, 1, 0) +GEN_INT_ARITH_SUBF_CONST(subfzeo, 0x16, 0, 1, 1, 1) + +#undef GEN_LOGICAL1 +#undef GEN_LOGICAL2 +#define GEN_LOGICAL2(name, tcg_op, opc, type) \ +GEN_HANDLER(name, 0x1F, 0x1C, opc, 0x00000000, type) +#define GEN_LOGICAL1(name, tcg_op, opc, type) \ +GEN_HANDLER(name, 0x1F, 0x1A, opc, 0x00000000, type) +GEN_LOGICAL2(and, tcg_gen_and_tl, 0x00, PPC_INTEGER), +GEN_LOGICAL2(andc, tcg_gen_andc_tl, 0x01, PPC_INTEGER), +GEN_LOGICAL2(eqv, tcg_gen_eqv_tl, 0x08, PPC_INTEGER), +GEN_LOGICAL1(extsb, tcg_gen_ext8s_tl, 0x1D, PPC_INTEGER), +GEN_LOGICAL1(extsh, tcg_gen_ext16s_tl, 0x1C, PPC_INTEGER), +GEN_LOGICAL2(nand, tcg_gen_nand_tl, 0x0E, PPC_INTEGER), +GEN_LOGICAL2(nor, tcg_gen_nor_tl, 0x03, PPC_INTEGER), +GEN_LOGICAL2(orc, tcg_gen_orc_tl, 0x0C, PPC_INTEGER), +#if defined(TARGET_PPC64) +GEN_LOGICAL1(extsw, tcg_gen_ext32s_tl, 0x1E, PPC_64B), +#endif + +#if defined(TARGET_PPC64) +#undef GEN_PPC64_R2 +#undef GEN_PPC64_R4 +#define GEN_PPC64_R2(name, opc1, opc2) \ +GEN_HANDLER2(name##0, stringify(name), opc1, opc2, 0xFF, 0x00000000, PPC_64B),\ +GEN_HANDLER2(name##1, stringify(name), opc1, opc2 | 0x10, 0xFF, 0x00000000, \ + PPC_64B) +#define GEN_PPC64_R4(name, opc1, opc2) \ +GEN_HANDLER2(name##0, stringify(name), opc1, opc2, 0xFF, 0x00000000, PPC_64B),\ +GEN_HANDLER2(name##1, stringify(name), opc1, opc2 | 0x01, 0xFF, 0x00000000, \ + PPC_64B), \ +GEN_HANDLER2(name##2, stringify(name), opc1, opc2 | 0x10, 0xFF, 0x00000000, \ + PPC_64B), \ +GEN_HANDLER2(name##3, stringify(name), opc1, opc2 | 0x11, 0xFF, 0x00000000, \ + PPC_64B) +GEN_PPC64_R4(rldicl, 0x1E, 0x00), +GEN_PPC64_R4(rldicr, 0x1E, 0x02), +GEN_PPC64_R4(rldic, 0x1E, 0x04), +GEN_PPC64_R2(rldcl, 0x1E, 0x08), +GEN_PPC64_R2(rldcr, 0x1E, 0x09), +GEN_PPC64_R4(rldimi, 0x1E, 0x06), +#endif + +#undef GEN_LD +#undef GEN_LDU +#undef GEN_LDUX +#undef GEN_LDX_E +#undef GEN_LDS +#define GEN_LD(name, ldop, opc, type) \ +GEN_HANDLER(name, opc, 0xFF, 0xFF, 0x00000000, type), +#define GEN_LDU(name, ldop, opc, type) \ +GEN_HANDLER(name##u, opc, 0xFF, 0xFF, 0x00000000, type), +#define GEN_LDUX(name, ldop, opc2, opc3, type) \ +GEN_HANDLER(name##ux, 0x1F, opc2, opc3, 0x00000001, type), +#define GEN_LDX_E(name, ldop, opc2, opc3, type, type2, chk) \ +GEN_HANDLER_E(name##x, 0x1F, opc2, opc3, 0x00000001, type, type2), +#define GEN_LDS(name, ldop, op, type) \ +GEN_LD(name, ldop, op | 0x20, type) \ +GEN_LDU(name, ldop, op | 0x21, type) \ +GEN_LDUX(name, ldop, 0x17, op | 0x01, type) \ +GEN_LDX(name, ldop, 0x17, op | 0x00, type) + +GEN_LDS(lbz, ld8u, 0x02, PPC_INTEGER) +GEN_LDS(lha, ld16s, 0x0A, PPC_INTEGER) +GEN_LDS(lhz, ld16u, 0x08, PPC_INTEGER) +GEN_LDS(lwz, ld32u, 0x00, PPC_INTEGER) +#if defined(TARGET_PPC64) +GEN_LDUX(lwa, ld32s, 0x15, 0x0B, PPC_64B) +GEN_LDX(lwa, ld32s, 0x15, 0x0A, PPC_64B) +GEN_LDUX(ld, ld64_i64, 0x15, 0x01, PPC_64B) +GEN_LDX(ld, ld64_i64, 0x15, 0x00, PPC_64B) +GEN_LDX_E(ldbr, ld64ur_i64, 0x14, 0x10, PPC_NONE, PPC2_DBRX, CHK_NONE) + +/* HV/P7 and later only */ +GEN_LDX_HVRM(ldcix, ld64_i64, 0x15, 0x1b, PPC_CILDST) +GEN_LDX_HVRM(lwzcix, ld32u, 0x15, 0x18, PPC_CILDST) +GEN_LDX_HVRM(lhzcix, ld16u, 0x15, 0x19, PPC_CILDST) +GEN_LDX_HVRM(lbzcix, ld8u, 0x15, 0x1a, PPC_CILDST) +#endif +GEN_LDX(lhbr, ld16ur, 0x16, 0x18, PPC_INTEGER) +GEN_LDX(lwbr, ld32ur, 0x16, 0x10, PPC_INTEGER) + +#undef GEN_ST +#undef GEN_STU +#undef GEN_STUX +#undef GEN_STX_E +#undef GEN_STS +#define GEN_ST(name, stop, opc, type) \ +GEN_HANDLER(name, opc, 0xFF, 0xFF, 0x00000000, type), +#define GEN_STU(name, stop, opc, type) \ +GEN_HANDLER(stop##u, opc, 0xFF, 0xFF, 0x00000000, type), +#define GEN_STUX(name, stop, opc2, opc3, type) \ +GEN_HANDLER(name##ux, 0x1F, opc2, opc3, 0x00000001, type), +#define GEN_STX_E(name, stop, opc2, opc3, type, type2, chk) \ +GEN_HANDLER_E(name##x, 0x1F, opc2, opc3, 0x00000001, type, type2), +#define GEN_STS(name, stop, op, type) \ +GEN_ST(name, stop, op | 0x20, type) \ +GEN_STU(name, stop, op | 0x21, type) \ +GEN_STUX(name, stop, 0x17, op | 0x01, type) \ +GEN_STX(name, stop, 0x17, op | 0x00, type) + +GEN_STS(stb, st8, 0x06, PPC_INTEGER) +GEN_STS(sth, st16, 0x0C, PPC_INTEGER) +GEN_STS(stw, st32, 0x04, PPC_INTEGER) +#if defined(TARGET_PPC64) +GEN_STUX(std, st64_i64, 0x15, 0x05, PPC_64B) +GEN_STX(std, st64_i64, 0x15, 0x04, PPC_64B) +GEN_STX_E(stdbr, st64r_i64, 0x14, 0x14, PPC_NONE, PPC2_DBRX, CHK_NONE) +GEN_STX_HVRM(stdcix, st64_i64, 0x15, 0x1f, PPC_CILDST) +GEN_STX_HVRM(stwcix, st32, 0x15, 0x1c, PPC_CILDST) +GEN_STX_HVRM(sthcix, st16, 0x15, 0x1d, PPC_CILDST) +GEN_STX_HVRM(stbcix, st8, 0x15, 0x1e, PPC_CILDST) +#endif +GEN_STX(sthbr, st16r, 0x16, 0x1C, PPC_INTEGER) +GEN_STX(stwbr, st32r, 0x16, 0x14, PPC_INTEGER) + +#undef GEN_CRLOGIC +#define GEN_CRLOGIC(name, tcg_op, opc) \ +GEN_HANDLER(name, 0x13, 0x01, opc, 0x00000001, PPC_INTEGER) +GEN_CRLOGIC(crand, tcg_gen_and_i32, 0x08), +GEN_CRLOGIC(crandc, tcg_gen_andc_i32, 0x04), +GEN_CRLOGIC(creqv, tcg_gen_eqv_i32, 0x09), +GEN_CRLOGIC(crnand, tcg_gen_nand_i32, 0x07), +GEN_CRLOGIC(crnor, tcg_gen_nor_i32, 0x01), +GEN_CRLOGIC(cror, tcg_gen_or_i32, 0x0E), +GEN_CRLOGIC(crorc, tcg_gen_orc_i32, 0x0D), +GEN_CRLOGIC(crxor, tcg_gen_xor_i32, 0x06), + +#undef GEN_MAC_HANDLER +#define GEN_MAC_HANDLER(name, opc2, opc3) \ +GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_405_MAC) +GEN_MAC_HANDLER(macchw, 0x0C, 0x05), +GEN_MAC_HANDLER(macchwo, 0x0C, 0x15), +GEN_MAC_HANDLER(macchws, 0x0C, 0x07), +GEN_MAC_HANDLER(macchwso, 0x0C, 0x17), +GEN_MAC_HANDLER(macchwsu, 0x0C, 0x06), +GEN_MAC_HANDLER(macchwsuo, 0x0C, 0x16), +GEN_MAC_HANDLER(macchwu, 0x0C, 0x04), +GEN_MAC_HANDLER(macchwuo, 0x0C, 0x14), +GEN_MAC_HANDLER(machhw, 0x0C, 0x01), +GEN_MAC_HANDLER(machhwo, 0x0C, 0x11), +GEN_MAC_HANDLER(machhws, 0x0C, 0x03), +GEN_MAC_HANDLER(machhwso, 0x0C, 0x13), +GEN_MAC_HANDLER(machhwsu, 0x0C, 0x02), +GEN_MAC_HANDLER(machhwsuo, 0x0C, 0x12), +GEN_MAC_HANDLER(machhwu, 0x0C, 0x00), +GEN_MAC_HANDLER(machhwuo, 0x0C, 0x10), +GEN_MAC_HANDLER(maclhw, 0x0C, 0x0D), +GEN_MAC_HANDLER(maclhwo, 0x0C, 0x1D), +GEN_MAC_HANDLER(maclhws, 0x0C, 0x0F), +GEN_MAC_HANDLER(maclhwso, 0x0C, 0x1F), +GEN_MAC_HANDLER(maclhwu, 0x0C, 0x0C), +GEN_MAC_HANDLER(maclhwuo, 0x0C, 0x1C), +GEN_MAC_HANDLER(maclhwsu, 0x0C, 0x0E), +GEN_MAC_HANDLER(maclhwsuo, 0x0C, 0x1E), +GEN_MAC_HANDLER(nmacchw, 0x0E, 0x05), +GEN_MAC_HANDLER(nmacchwo, 0x0E, 0x15), +GEN_MAC_HANDLER(nmacchws, 0x0E, 0x07), +GEN_MAC_HANDLER(nmacchwso, 0x0E, 0x17), +GEN_MAC_HANDLER(nmachhw, 0x0E, 0x01), +GEN_MAC_HANDLER(nmachhwo, 0x0E, 0x11), +GEN_MAC_HANDLER(nmachhws, 0x0E, 0x03), +GEN_MAC_HANDLER(nmachhwso, 0x0E, 0x13), +GEN_MAC_HANDLER(nmaclhw, 0x0E, 0x0D), +GEN_MAC_HANDLER(nmaclhwo, 0x0E, 0x1D), +GEN_MAC_HANDLER(nmaclhws, 0x0E, 0x0F), +GEN_MAC_HANDLER(nmaclhwso, 0x0E, 0x1F), +GEN_MAC_HANDLER(mulchw, 0x08, 0x05), +GEN_MAC_HANDLER(mulchwu, 0x08, 0x04), +GEN_MAC_HANDLER(mulhhw, 0x08, 0x01), +GEN_MAC_HANDLER(mulhhwu, 0x08, 0x00), +GEN_MAC_HANDLER(mullhw, 0x08, 0x0D), +GEN_MAC_HANDLER(mullhwu, 0x08, 0x0C), + +GEN_HANDLER2_E(tbegin, "tbegin", 0x1F, 0x0E, 0x14, 0x01DFF800, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(tend, "tend", 0x1F, 0x0E, 0x15, 0x01FFF800, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(tabort, "tabort", 0x1F, 0x0E, 0x1C, 0x03E0F800, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(tabortwc, "tabortwc", 0x1F, 0x0E, 0x18, 0x00000000, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(tabortwci, "tabortwci", 0x1F, 0x0E, 0x1A, 0x00000000, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(tabortdc, "tabortdc", 0x1F, 0x0E, 0x19, 0x00000000, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(tabortdci, "tabortdci", 0x1F, 0x0E, 0x1B, 0x00000000, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(tsr, "tsr", 0x1F, 0x0E, 0x17, 0x03DFF800, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(tcheck, "tcheck", 0x1F, 0x0E, 0x16, 0x007FF800, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(treclaim, "treclaim", 0x1F, 0x0E, 0x1D, 0x03E0F800, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(trechkpt, "trechkpt", 0x1F, 0x0E, 0x1F, 0x03FFF800, \ + PPC_NONE, PPC2_TM), + +#include "translate/fp-ops.inc.c" + +#include "translate/vmx-ops.inc.c" + +#include "translate/vsx-ops.inc.c" + +#include "translate/dfp-ops.inc.c" + +#include "translate/spe-ops.inc.c" +}; + +#include "helper_regs.h" +#include "translate_init.c" + +/*****************************************************************************/ +/* Misc PowerPC helpers */ +void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, + int flags) +{ +#define RGPL 4 +#define RFPL 4 + + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + int i; + + cpu_fprintf(f, "NIP " TARGET_FMT_lx " LR " TARGET_FMT_lx " CTR " + TARGET_FMT_lx " XER " TARGET_FMT_lx " CPU#%d\n", + env->nip, env->lr, env->ctr, cpu_read_xer(env), + cs->cpu_index); + cpu_fprintf(f, "MSR " TARGET_FMT_lx " HID0 " TARGET_FMT_lx " HF " + TARGET_FMT_lx " iidx %d didx %d\n", + env->msr, env->spr[SPR_HID0], + env->hflags, env->immu_idx, env->dmmu_idx); +#if !defined(NO_TIMER_DUMP) + cpu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64 +#if !defined(CONFIG_USER_ONLY) + " DECR %08" PRIu32 +#endif + "\n", + cpu_ppc_load_tbu(env), cpu_ppc_load_tbl(env) +#if !defined(CONFIG_USER_ONLY) + , cpu_ppc_load_decr(env) +#endif + ); +#endif + for (i = 0; i < 32; i++) { + if ((i & (RGPL - 1)) == 0) + cpu_fprintf(f, "GPR%02d", i); + cpu_fprintf(f, " %016" PRIx64, ppc_dump_gpr(env, i)); + if ((i & (RGPL - 1)) == (RGPL - 1)) + cpu_fprintf(f, "\n"); + } + cpu_fprintf(f, "CR "); + for (i = 0; i < 8; i++) + cpu_fprintf(f, "%01x", env->crf[i]); + cpu_fprintf(f, " ["); + for (i = 0; i < 8; i++) { + char a = '-'; + if (env->crf[i] & 0x08) + a = 'L'; + else if (env->crf[i] & 0x04) + a = 'G'; + else if (env->crf[i] & 0x02) + a = 'E'; + cpu_fprintf(f, " %c%c", a, env->crf[i] & 0x01 ? 'O' : ' '); + } + cpu_fprintf(f, " ] RES " TARGET_FMT_lx "\n", + env->reserve_addr); + for (i = 0; i < 32; i++) { + if ((i & (RFPL - 1)) == 0) + cpu_fprintf(f, "FPR%02d", i); + cpu_fprintf(f, " %016" PRIx64, *((uint64_t *)&env->fpr[i])); + if ((i & (RFPL - 1)) == (RFPL - 1)) + cpu_fprintf(f, "\n"); + } + cpu_fprintf(f, "FPSCR " TARGET_FMT_lx "\n", env->fpscr); +#if !defined(CONFIG_USER_ONLY) + cpu_fprintf(f, " SRR0 " TARGET_FMT_lx " SRR1 " TARGET_FMT_lx + " PVR " TARGET_FMT_lx " VRSAVE " TARGET_FMT_lx "\n", + env->spr[SPR_SRR0], env->spr[SPR_SRR1], + env->spr[SPR_PVR], env->spr[SPR_VRSAVE]); + + cpu_fprintf(f, "SPRG0 " TARGET_FMT_lx " SPRG1 " TARGET_FMT_lx + " SPRG2 " TARGET_FMT_lx " SPRG3 " TARGET_FMT_lx "\n", + env->spr[SPR_SPRG0], env->spr[SPR_SPRG1], + env->spr[SPR_SPRG2], env->spr[SPR_SPRG3]); + + cpu_fprintf(f, "SPRG4 " TARGET_FMT_lx " SPRG5 " TARGET_FMT_lx + " SPRG6 " TARGET_FMT_lx " SPRG7 " TARGET_FMT_lx "\n", + env->spr[SPR_SPRG4], env->spr[SPR_SPRG5], + env->spr[SPR_SPRG6], env->spr[SPR_SPRG7]); + +#if defined(TARGET_PPC64) + if (env->excp_model == POWERPC_EXCP_POWER7 || + env->excp_model == POWERPC_EXCP_POWER8) { + cpu_fprintf(f, "HSRR0 " TARGET_FMT_lx " HSRR1 " TARGET_FMT_lx "\n", + env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]); + } +#endif + if (env->excp_model == POWERPC_EXCP_BOOKE) { + cpu_fprintf(f, "CSRR0 " TARGET_FMT_lx " CSRR1 " TARGET_FMT_lx + " MCSRR0 " TARGET_FMT_lx " MCSRR1 " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1], + env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1]); + + cpu_fprintf(f, " TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx + " ESR " TARGET_FMT_lx " DEAR " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_TCR], env->spr[SPR_BOOKE_TSR], + env->spr[SPR_BOOKE_ESR], env->spr[SPR_BOOKE_DEAR]); + + cpu_fprintf(f, " PIR " TARGET_FMT_lx " DECAR " TARGET_FMT_lx + " IVPR " TARGET_FMT_lx " EPCR " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_PIR], env->spr[SPR_BOOKE_DECAR], + env->spr[SPR_BOOKE_IVPR], env->spr[SPR_BOOKE_EPCR]); + + cpu_fprintf(f, " MCSR " TARGET_FMT_lx " SPRG8 " TARGET_FMT_lx + " EPR " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_MCSR], env->spr[SPR_BOOKE_SPRG8], + env->spr[SPR_BOOKE_EPR]); + + /* FSL-specific */ + cpu_fprintf(f, " MCAR " TARGET_FMT_lx " PID1 " TARGET_FMT_lx + " PID2 " TARGET_FMT_lx " SVR " TARGET_FMT_lx "\n", + env->spr[SPR_Exxx_MCAR], env->spr[SPR_BOOKE_PID1], + env->spr[SPR_BOOKE_PID2], env->spr[SPR_E500_SVR]); + + /* + * IVORs are left out as they are large and do not change often -- + * they can be read with "p $ivor0", "p $ivor1", etc. + */ + } + +#if defined(TARGET_PPC64) + if (env->flags & POWERPC_FLAG_CFAR) { + cpu_fprintf(f, " CFAR " TARGET_FMT_lx"\n", env->cfar); + } +#endif + + switch (env->mmu_model) { + case POWERPC_MMU_32B: + case POWERPC_MMU_601: + case POWERPC_MMU_SOFT_6xx: + case POWERPC_MMU_SOFT_74xx: +#if defined(TARGET_PPC64) + case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06a: + case POWERPC_MMU_2_07: + case POWERPC_MMU_2_07a: +#endif + cpu_fprintf(f, " SDR1 " TARGET_FMT_lx " DAR " TARGET_FMT_lx + " DSISR " TARGET_FMT_lx "\n", env->spr[SPR_SDR1], + env->spr[SPR_DAR], env->spr[SPR_DSISR]); + break; + case POWERPC_MMU_BOOKE206: + cpu_fprintf(f, " MAS0 " TARGET_FMT_lx " MAS1 " TARGET_FMT_lx + " MAS2 " TARGET_FMT_lx " MAS3 " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_MAS0], env->spr[SPR_BOOKE_MAS1], + env->spr[SPR_BOOKE_MAS2], env->spr[SPR_BOOKE_MAS3]); + + cpu_fprintf(f, " MAS4 " TARGET_FMT_lx " MAS6 " TARGET_FMT_lx + " MAS7 " TARGET_FMT_lx " PID " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_MAS4], env->spr[SPR_BOOKE_MAS6], + env->spr[SPR_BOOKE_MAS7], env->spr[SPR_BOOKE_PID]); + + cpu_fprintf(f, "MMUCFG " TARGET_FMT_lx " TLB0CFG " TARGET_FMT_lx + " TLB1CFG " TARGET_FMT_lx "\n", + env->spr[SPR_MMUCFG], env->spr[SPR_BOOKE_TLB0CFG], + env->spr[SPR_BOOKE_TLB1CFG]); + break; + default: + break; + } +#endif + +#undef RGPL +#undef RFPL +} + +void ppc_cpu_dump_statistics(CPUState *cs, FILE*f, + fprintf_function cpu_fprintf, int flags) +{ +#if defined(DO_PPC_STATISTICS) + PowerPCCPU *cpu = POWERPC_CPU(cs); + opc_handler_t **t1, **t2, **t3, *handler; + int op1, op2, op3; + + t1 = cpu->env.opcodes; + for (op1 = 0; op1 < 64; op1++) { + handler = t1[op1]; + if (is_indirect_opcode(handler)) { + t2 = ind_table(handler); + for (op2 = 0; op2 < 32; op2++) { + handler = t2[op2]; + if (is_indirect_opcode(handler)) { + t3 = ind_table(handler); + for (op3 = 0; op3 < 32; op3++) { + handler = t3[op3]; + if (handler->count == 0) + continue; + cpu_fprintf(f, "%02x %02x %02x (%02x %04d) %16s: " + "%016" PRIx64 " %" PRId64 "\n", + op1, op2, op3, op1, (op3 << 5) | op2, + handler->oname, + handler->count, handler->count); + } + } else { + if (handler->count == 0) + continue; + cpu_fprintf(f, "%02x %02x (%02x %04d) %16s: " + "%016" PRIx64 " %" PRId64 "\n", + op1, op2, op1, op2, handler->oname, + handler->count, handler->count); + } + } + } else { + if (handler->count == 0) + continue; + cpu_fprintf(f, "%02x (%02x ) %16s: %016" PRIx64 + " %" PRId64 "\n", + op1, op1, handler->oname, + handler->count, handler->count); + } + } +#endif +} + +/*****************************************************************************/ +void gen_intermediate_code(CPUPPCState *env, struct TranslationBlock *tb) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + CPUState *cs = CPU(cpu); + DisasContext ctx, *ctxp = &ctx; + opc_handler_t **table, *handler; + target_ulong pc_start; + int num_insns; + int max_insns; + + pc_start = tb->pc; + ctx.nip = pc_start; + ctx.tb = tb; + ctx.exception = POWERPC_EXCP_NONE; + ctx.spr_cb = env->spr_cb; + ctx.pr = msr_pr; + ctx.mem_idx = env->dmmu_idx; + ctx.dr = msr_dr; +#if !defined(CONFIG_USER_ONLY) + ctx.hv = msr_hv || !env->has_hv_mode; +#endif + ctx.insns_flags = env->insns_flags; + ctx.insns_flags2 = env->insns_flags2; + ctx.access_type = -1; + ctx.need_access_type = !(env->mmu_model & POWERPC_MMU_64B); + ctx.le_mode = !!(env->hflags & (1 << MSR_LE)); + ctx.default_tcg_memop_mask = ctx.le_mode ? MO_LE : MO_BE; +#if defined(TARGET_PPC64) + ctx.sf_mode = msr_is_64bit(env, env->msr); + ctx.has_cfar = !!(env->flags & POWERPC_FLAG_CFAR); +#endif + if (env->mmu_model == POWERPC_MMU_32B || + env->mmu_model == POWERPC_MMU_601 || + (env->mmu_model & POWERPC_MMU_64B)) + ctx.lazy_tlb_flush = true; + + ctx.fpu_enabled = !!msr_fp; + if ((env->flags & POWERPC_FLAG_SPE) && msr_spe) + ctx.spe_enabled = !!msr_spe; + else + ctx.spe_enabled = false; + if ((env->flags & POWERPC_FLAG_VRE) && msr_vr) + ctx.altivec_enabled = !!msr_vr; + else + ctx.altivec_enabled = false; + if ((env->flags & POWERPC_FLAG_VSX) && msr_vsx) { + ctx.vsx_enabled = !!msr_vsx; + } else { + ctx.vsx_enabled = false; + } +#if defined(TARGET_PPC64) + if ((env->flags & POWERPC_FLAG_TM) && msr_tm) { + ctx.tm_enabled = !!msr_tm; + } else { + ctx.tm_enabled = false; + } +#endif + if ((env->flags & POWERPC_FLAG_SE) && msr_se) + ctx.singlestep_enabled = CPU_SINGLE_STEP; + else + ctx.singlestep_enabled = 0; + if ((env->flags & POWERPC_FLAG_BE) && msr_be) + ctx.singlestep_enabled |= CPU_BRANCH_STEP; + if (unlikely(cs->singlestep_enabled)) { + ctx.singlestep_enabled |= GDBSTUB_SINGLE_STEP; + } +#if defined (DO_SINGLE_STEP) && 0 + /* Single step trace mode */ + msr_se = 1; +#endif + num_insns = 0; + max_insns = tb->cflags & CF_COUNT_MASK; + if (max_insns == 0) { + max_insns = CF_COUNT_MASK; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } + + gen_tb_start(tb); + tcg_clear_temp_count(); + /* Set env in case of segfault during code fetch */ + while (ctx.exception == POWERPC_EXCP_NONE && !tcg_op_buf_full()) { + tcg_gen_insn_start(ctx.nip); + num_insns++; + + if (unlikely(cpu_breakpoint_test(cs, ctx.nip, BP_ANY))) { + gen_debug_exception(ctxp); + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + ctx.nip += 4; + break; + } + + LOG_DISAS("----------------\n"); + LOG_DISAS("nip=" TARGET_FMT_lx " super=%d ir=%d\n", + ctx.nip, ctx.mem_idx, (int)msr_ir); + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) + gen_io_start(); + if (unlikely(need_byteswap(&ctx))) { + ctx.opcode = bswap32(cpu_ldl_code(env, ctx.nip)); + } else { + ctx.opcode = cpu_ldl_code(env, ctx.nip); + } + LOG_DISAS("translate opcode %08x (%02x %02x %02x %02x) (%s)\n", + ctx.opcode, opc1(ctx.opcode), opc2(ctx.opcode), + opc3(ctx.opcode), opc4(ctx.opcode), + ctx.le_mode ? "little" : "big"); + ctx.nip += 4; + table = env->opcodes; + handler = table[opc1(ctx.opcode)]; + if (is_indirect_opcode(handler)) { + table = ind_table(handler); + handler = table[opc2(ctx.opcode)]; + if (is_indirect_opcode(handler)) { + table = ind_table(handler); + handler = table[opc3(ctx.opcode)]; + if (is_indirect_opcode(handler)) { + table = ind_table(handler); + handler = table[opc4(ctx.opcode)]; + } + } + } + /* Is opcode *REALLY* valid ? */ + if (unlikely(handler->handler == &gen_invalid)) { + qemu_log_mask(LOG_GUEST_ERROR, "invalid/unsupported opcode: " + "%02x - %02x - %02x - %02x (%08x) " + TARGET_FMT_lx " %d\n", + opc1(ctx.opcode), opc2(ctx.opcode), + opc3(ctx.opcode), opc4(ctx.opcode), + ctx.opcode, ctx.nip - 4, (int)msr_ir); + } else { + uint32_t inval; + + if (unlikely(handler->type & (PPC_SPE | PPC_SPE_SINGLE | PPC_SPE_DOUBLE) && Rc(ctx.opcode))) { + inval = handler->inval2; + } else { + inval = handler->inval1; + } + + if (unlikely((ctx.opcode & inval) != 0)) { + qemu_log_mask(LOG_GUEST_ERROR, "invalid bits: %08x for opcode: " + "%02x - %02x - %02x - %02x (%08x) " + TARGET_FMT_lx "\n", ctx.opcode & inval, + opc1(ctx.opcode), opc2(ctx.opcode), + opc3(ctx.opcode), opc4(ctx.opcode), + ctx.opcode, ctx.nip - 4); + gen_inval_exception(ctxp, POWERPC_EXCP_INVAL_INVAL); + break; + } + } + (*(handler->handler))(&ctx); +#if defined(DO_PPC_STATISTICS) + handler->count++; +#endif + /* Check trace mode exceptions */ + if (unlikely(ctx.singlestep_enabled & CPU_SINGLE_STEP && + (ctx.nip <= 0x100 || ctx.nip > 0xF00) && + ctx.exception != POWERPC_SYSCALL && + ctx.exception != POWERPC_EXCP_TRAP && + ctx.exception != POWERPC_EXCP_BRANCH)) { + gen_exception_nip(ctxp, POWERPC_EXCP_TRACE, ctx.nip); + } else if (unlikely(((ctx.nip & (TARGET_PAGE_SIZE - 1)) == 0) || + (cs->singlestep_enabled) || + singlestep || + num_insns >= max_insns)) { + /* if we reach a page boundary or are single stepping, stop + * generation + */ + break; + } + if (tcg_check_temp_count()) { + fprintf(stderr, "Opcode %02x %02x %02x %02x (%08x) leaked " + "temporaries\n", opc1(ctx.opcode), opc2(ctx.opcode), + opc3(ctx.opcode), opc4(ctx.opcode), ctx.opcode); + exit(1); + } + } + if (tb->cflags & CF_LAST_IO) + gen_io_end(); + if (ctx.exception == POWERPC_EXCP_NONE) { + gen_goto_tb(&ctx, 0, ctx.nip); + } else if (ctx.exception != POWERPC_EXCP_BRANCH) { + if (unlikely(cs->singlestep_enabled)) { + gen_debug_exception(ctxp); + } + /* Generate the return instruction */ + tcg_gen_exit_tb(0); + } + gen_tb_end(tb, num_insns); + + tb->size = ctx.nip - pc_start; + tb->icount = num_insns; + +#if defined(DEBUG_DISAS) + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) + && qemu_log_in_addr_range(pc_start)) { + int flags; + flags = env->bfd_mach; + flags |= ctx.le_mode << 16; + qemu_log_lock(); + qemu_log("IN: %s\n", lookup_symbol(pc_start)); + log_target_disas(cs, pc_start, ctx.nip - pc_start, flags); + qemu_log("\n"); + qemu_log_unlock(); + } +#endif +} + +void restore_state_to_opc(CPUPPCState *env, TranslationBlock *tb, + target_ulong *data) +{ + env->nip = data[0]; +} diff --git a/target/ppc/translate/dfp-impl.inc.c b/target/ppc/translate/dfp-impl.inc.c new file mode 100644 index 0000000000..178d3044a7 --- /dev/null +++ b/target/ppc/translate/dfp-impl.inc.c @@ -0,0 +1,232 @@ +/*** Decimal Floating Point ***/ + +static inline TCGv_ptr gen_fprp_ptr(int reg) +{ + TCGv_ptr r = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(r, cpu_env, offsetof(CPUPPCState, fpr[reg])); + return r; +} + +#define GEN_DFP_T_A_B_Rc(name) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_ptr rd, ra, rb; \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + gen_update_nip(ctx, ctx->nip - 4); \ + rd = gen_fprp_ptr(rD(ctx->opcode)); \ + ra = gen_fprp_ptr(rA(ctx->opcode)); \ + rb = gen_fprp_ptr(rB(ctx->opcode)); \ + gen_helper_##name(cpu_env, rd, ra, rb); \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ + tcg_temp_free_ptr(rd); \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rb); \ +} + +#define GEN_DFP_BF_A_B(name) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_ptr ra, rb; \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + gen_update_nip(ctx, ctx->nip - 4); \ + ra = gen_fprp_ptr(rA(ctx->opcode)); \ + rb = gen_fprp_ptr(rB(ctx->opcode)); \ + gen_helper_##name(cpu_crf[crfD(ctx->opcode)], \ + cpu_env, ra, rb); \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rb); \ +} + +#define GEN_DFP_BF_I_B(name) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_i32 uim; \ + TCGv_ptr rb; \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + gen_update_nip(ctx, ctx->nip - 4); \ + uim = tcg_const_i32(UIMM5(ctx->opcode)); \ + rb = gen_fprp_ptr(rB(ctx->opcode)); \ + gen_helper_##name(cpu_crf[crfD(ctx->opcode)], \ + cpu_env, uim, rb); \ + tcg_temp_free_i32(uim); \ + tcg_temp_free_ptr(rb); \ +} + +#define GEN_DFP_BF_A_DCM(name) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_ptr ra; \ + TCGv_i32 dcm; \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + gen_update_nip(ctx, ctx->nip - 4); \ + ra = gen_fprp_ptr(rA(ctx->opcode)); \ + dcm = tcg_const_i32(DCM(ctx->opcode)); \ + gen_helper_##name(cpu_crf[crfD(ctx->opcode)], \ + cpu_env, ra, dcm); \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_i32(dcm); \ +} + +#define GEN_DFP_T_B_U32_U32_Rc(name, u32f1, u32f2) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_ptr rt, rb; \ + TCGv_i32 u32_1, u32_2; \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + gen_update_nip(ctx, ctx->nip - 4); \ + rt = gen_fprp_ptr(rD(ctx->opcode)); \ + rb = gen_fprp_ptr(rB(ctx->opcode)); \ + u32_1 = tcg_const_i32(u32f1(ctx->opcode)); \ + u32_2 = tcg_const_i32(u32f2(ctx->opcode)); \ + gen_helper_##name(cpu_env, rt, rb, u32_1, u32_2); \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ + tcg_temp_free_ptr(rt); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_i32(u32_1); \ + tcg_temp_free_i32(u32_2); \ +} + +#define GEN_DFP_T_A_B_I32_Rc(name, i32fld) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_ptr rt, ra, rb; \ + TCGv_i32 i32; \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + gen_update_nip(ctx, ctx->nip - 4); \ + rt = gen_fprp_ptr(rD(ctx->opcode)); \ + ra = gen_fprp_ptr(rA(ctx->opcode)); \ + rb = gen_fprp_ptr(rB(ctx->opcode)); \ + i32 = tcg_const_i32(i32fld(ctx->opcode)); \ + gen_helper_##name(cpu_env, rt, ra, rb, i32); \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ + tcg_temp_free_ptr(rt); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_i32(i32); \ + } + +#define GEN_DFP_T_B_Rc(name) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_ptr rt, rb; \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + gen_update_nip(ctx, ctx->nip - 4); \ + rt = gen_fprp_ptr(rD(ctx->opcode)); \ + rb = gen_fprp_ptr(rB(ctx->opcode)); \ + gen_helper_##name(cpu_env, rt, rb); \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ + tcg_temp_free_ptr(rt); \ + tcg_temp_free_ptr(rb); \ + } + +#define GEN_DFP_T_FPR_I32_Rc(name, fprfld, i32fld) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_ptr rt, rs; \ + TCGv_i32 i32; \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + gen_update_nip(ctx, ctx->nip - 4); \ + rt = gen_fprp_ptr(rD(ctx->opcode)); \ + rs = gen_fprp_ptr(fprfld(ctx->opcode)); \ + i32 = tcg_const_i32(i32fld(ctx->opcode)); \ + gen_helper_##name(cpu_env, rt, rs, i32); \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ + tcg_temp_free_ptr(rt); \ + tcg_temp_free_ptr(rs); \ + tcg_temp_free_i32(i32); \ +} + +GEN_DFP_T_A_B_Rc(dadd) +GEN_DFP_T_A_B_Rc(daddq) +GEN_DFP_T_A_B_Rc(dsub) +GEN_DFP_T_A_B_Rc(dsubq) +GEN_DFP_T_A_B_Rc(dmul) +GEN_DFP_T_A_B_Rc(dmulq) +GEN_DFP_T_A_B_Rc(ddiv) +GEN_DFP_T_A_B_Rc(ddivq) +GEN_DFP_BF_A_B(dcmpu) +GEN_DFP_BF_A_B(dcmpuq) +GEN_DFP_BF_A_B(dcmpo) +GEN_DFP_BF_A_B(dcmpoq) +GEN_DFP_BF_A_DCM(dtstdc) +GEN_DFP_BF_A_DCM(dtstdcq) +GEN_DFP_BF_A_DCM(dtstdg) +GEN_DFP_BF_A_DCM(dtstdgq) +GEN_DFP_BF_A_B(dtstex) +GEN_DFP_BF_A_B(dtstexq) +GEN_DFP_BF_A_B(dtstsf) +GEN_DFP_BF_A_B(dtstsfq) +GEN_DFP_BF_I_B(dtstsfi) +GEN_DFP_BF_I_B(dtstsfiq) +GEN_DFP_T_B_U32_U32_Rc(dquai, SIMM5, RMC) +GEN_DFP_T_B_U32_U32_Rc(dquaiq, SIMM5, RMC) +GEN_DFP_T_A_B_I32_Rc(dqua, RMC) +GEN_DFP_T_A_B_I32_Rc(dquaq, RMC) +GEN_DFP_T_A_B_I32_Rc(drrnd, RMC) +GEN_DFP_T_A_B_I32_Rc(drrndq, RMC) +GEN_DFP_T_B_U32_U32_Rc(drintx, FPW, RMC) +GEN_DFP_T_B_U32_U32_Rc(drintxq, FPW, RMC) +GEN_DFP_T_B_U32_U32_Rc(drintn, FPW, RMC) +GEN_DFP_T_B_U32_U32_Rc(drintnq, FPW, RMC) +GEN_DFP_T_B_Rc(dctdp) +GEN_DFP_T_B_Rc(dctqpq) +GEN_DFP_T_B_Rc(drsp) +GEN_DFP_T_B_Rc(drdpq) +GEN_DFP_T_B_Rc(dcffix) +GEN_DFP_T_B_Rc(dcffixq) +GEN_DFP_T_B_Rc(dctfix) +GEN_DFP_T_B_Rc(dctfixq) +GEN_DFP_T_FPR_I32_Rc(ddedpd, rB, SP) +GEN_DFP_T_FPR_I32_Rc(ddedpdq, rB, SP) +GEN_DFP_T_FPR_I32_Rc(denbcd, rB, SP) +GEN_DFP_T_FPR_I32_Rc(denbcdq, rB, SP) +GEN_DFP_T_B_Rc(dxex) +GEN_DFP_T_B_Rc(dxexq) +GEN_DFP_T_A_B_Rc(diex) +GEN_DFP_T_A_B_Rc(diexq) +GEN_DFP_T_FPR_I32_Rc(dscli, rA, DCM) +GEN_DFP_T_FPR_I32_Rc(dscliq, rA, DCM) +GEN_DFP_T_FPR_I32_Rc(dscri, rA, DCM) +GEN_DFP_T_FPR_I32_Rc(dscriq, rA, DCM) + +#undef GEN_DFP_T_A_B_Rc +#undef GEN_DFP_BF_A_B +#undef GEN_DFP_BF_A_DCM +#undef GEN_DFP_T_B_U32_U32_Rc +#undef GEN_DFP_T_A_B_I32_Rc +#undef GEN_DFP_T_B_Rc +#undef GEN_DFP_T_FPR_I32_Rc diff --git a/target/ppc/translate/dfp-ops.inc.c b/target/ppc/translate/dfp-ops.inc.c new file mode 100644 index 0000000000..6ef38e5712 --- /dev/null +++ b/target/ppc/translate/dfp-ops.inc.c @@ -0,0 +1,165 @@ +#define _GEN_DFP_LONG(name, op1, op2, mask) \ +GEN_HANDLER_E(name, 0x3B, op1, op2, mask, PPC_NONE, PPC2_DFP) + +#define _GEN_DFP_LONG_300(name, op1, op2, mask) \ +GEN_HANDLER_E(name, 0x3B, op1, op2, mask, PPC_NONE, PPC2_ISA300) + +#define _GEN_DFP_LONGx2(name, op1, op2, mask) \ +GEN_HANDLER_E(name, 0x3B, op1, 0x00 | op2, mask, PPC_NONE, PPC2_DFP), \ +GEN_HANDLER_E(name, 0x3B, op1, 0x10 | op2, mask, PPC_NONE, PPC2_DFP) + +#define _GEN_DFP_LONGx4(name, op1, op2, mask) \ +GEN_HANDLER_E(name, 0x3B, op1, 0x00 | op2, mask, PPC_NONE, PPC2_DFP), \ +GEN_HANDLER_E(name, 0x3B, op1, 0x08 | op2, mask, PPC_NONE, PPC2_DFP), \ +GEN_HANDLER_E(name, 0x3B, op1, 0x10 | op2, mask, PPC_NONE, PPC2_DFP), \ +GEN_HANDLER_E(name, 0x3B, op1, 0x18 | op2, mask, PPC_NONE, PPC2_DFP) + +#define _GEN_DFP_QUAD(name, op1, op2, mask) \ +GEN_HANDLER_E(name, 0x3F, op1, op2, mask, PPC_NONE, PPC2_DFP) + +#define _GEN_DFP_QUAD_300(name, op1, op2, mask) \ +GEN_HANDLER_E(name, 0x3F, op1, op2, mask, PPC_NONE, PPC2_ISA300) + +#define _GEN_DFP_QUADx2(name, op1, op2, mask) \ +GEN_HANDLER_E(name, 0x3F, op1, 0x00 | op2, mask, PPC_NONE, PPC2_DFP), \ +GEN_HANDLER_E(name, 0x3F, op1, 0x10 | op2, mask, PPC_NONE, PPC2_DFP) + +#define _GEN_DFP_QUADx4(name, op1, op2, mask) \ +GEN_HANDLER_E(name, 0x3F, op1, 0x00 | op2, mask, PPC_NONE, PPC2_DFP), \ +GEN_HANDLER_E(name, 0x3F, op1, 0x08 | op2, mask, PPC_NONE, PPC2_DFP), \ +GEN_HANDLER_E(name, 0x3F, op1, 0x10 | op2, mask, PPC_NONE, PPC2_DFP), \ +GEN_HANDLER_E(name, 0x3F, op1, 0x18 | op2, mask, PPC_NONE, PPC2_DFP) + +#define GEN_DFP_T_A_B_Rc(name, op1, op2) \ +_GEN_DFP_LONG(name, op1, op2, 0x00000000) + +#define GEN_DFP_Tp_Ap_Bp_Rc(name, op1, op2) \ +_GEN_DFP_QUAD(name, op1, op2, 0x00210800) + +#define GEN_DFP_Tp_A_Bp_Rc(name, op1, op2) \ +_GEN_DFP_QUAD(name, op1, op2, 0x00200800) + +#define GEN_DFP_T_B_Rc(name, op1, op2) \ +_GEN_DFP_LONG(name, op1, op2, 0x001F0000) + +#define GEN_DFP_Tp_Bp_Rc(name, op1, op2) \ +_GEN_DFP_QUAD(name, op1, op2, 0x003F0800) + +#define GEN_DFP_Tp_B_Rc(name, op1, op2) \ +_GEN_DFP_QUAD(name, op1, op2, 0x003F0000) + +#define GEN_DFP_T_Bp_Rc(name, op1, op2) \ +_GEN_DFP_QUAD(name, op1, op2, 0x001F0800) + +#define GEN_DFP_BF_A_B(name, op1, op2) \ +_GEN_DFP_LONG(name, op1, op2, 0x00000001) + +#define GEN_DFP_BF_A_B_300(name, op1, op2) \ +_GEN_DFP_LONG_300(name, op1, op2, 0x00400001) + +#define GEN_DFP_BF_Ap_Bp(name, op1, op2) \ +_GEN_DFP_QUAD(name, op1, op2, 0x00610801) + +#define GEN_DFP_BF_A_Bp(name, op1, op2) \ +_GEN_DFP_QUAD(name, op1, op2, 0x00600801) + +#define GEN_DFP_BF_A_Bp_300(name, op1, op2) \ +_GEN_DFP_QUAD_300(name, op1, op2, 0x00400001) + +#define GEN_DFP_BF_A_DCM(name, op1, op2) \ +_GEN_DFP_LONGx2(name, op1, op2, 0x00600001) + +#define GEN_DFP_BF_Ap_DCM(name, op1, op2) \ +_GEN_DFP_QUADx2(name, op1, op2, 0x00610001) + +#define GEN_DFP_T_A_B_RMC_Rc(name, op1, op2) \ +_GEN_DFP_LONGx4(name, op1, op2, 0x00000000) + +#define GEN_DFP_Tp_Ap_Bp_RMC_Rc(name, op1, op2) \ +_GEN_DFP_QUADx4(name, op1, op2, 0x02010800) + +#define GEN_DFP_Tp_A_Bp_RMC_Rc(name, op1, op2) \ +_GEN_DFP_QUADx4(name, op1, op2, 0x02000800) + +#define GEN_DFP_TE_T_B_RMC_Rc(name, op1, op2) \ +_GEN_DFP_LONGx4(name, op1, op2, 0x00000000) + +#define GEN_DFP_TE_Tp_Bp_RMC_Rc(name, op1, op2) \ +_GEN_DFP_QUADx4(name, op1, op2, 0x00200800) + +#define GEN_DFP_R_T_B_RMC_Rc(name, op1, op2) \ +_GEN_DFP_LONGx4(name, op1, op2, 0x001E0000) + +#define GEN_DFP_R_Tp_Bp_RMC_Rc(name, op1, op2) \ +_GEN_DFP_QUADx4(name, op1, op2, 0x003E0800) + +#define GEN_DFP_SP_T_B_Rc(name, op1, op2) \ +_GEN_DFP_LONG(name, op1, op2, 0x00070000) + +#define GEN_DFP_SP_Tp_Bp_Rc(name, op1, op2) \ +_GEN_DFP_QUAD(name, op1, op2, 0x00270800) + +#define GEN_DFP_S_T_B_Rc(name, op1, op2) \ +_GEN_DFP_LONG(name, op1, op2, 0x000F0000) + +#define GEN_DFP_S_Tp_Bp_Rc(name, op1, op2) \ +_GEN_DFP_QUAD(name, op1, op2, 0x002F0800) + +#define GEN_DFP_T_A_SH_Rc(name, op1, op2) \ +_GEN_DFP_LONGx2(name, op1, op2, 0x00000000) + +#define GEN_DFP_Tp_Ap_SH_Rc(name, op1, op2) \ +_GEN_DFP_QUADx2(name, op1, op2, 0x00210000) + +GEN_DFP_T_A_B_Rc(dadd, 0x02, 0x00), +GEN_DFP_Tp_Ap_Bp_Rc(daddq, 0x02, 0x00), +GEN_DFP_T_A_B_Rc(dsub, 0x02, 0x10), +GEN_DFP_Tp_Ap_Bp_Rc(dsubq, 0x02, 0x10), +GEN_DFP_T_A_B_Rc(dmul, 0x02, 0x01), +GEN_DFP_Tp_Ap_Bp_Rc(dmulq, 0x02, 0x01), +GEN_DFP_T_A_B_Rc(ddiv, 0x02, 0x11), +GEN_DFP_Tp_Ap_Bp_Rc(ddivq, 0x02, 0x11), +GEN_DFP_BF_A_B(dcmpu, 0x02, 0x14), +GEN_DFP_BF_Ap_Bp(dcmpuq, 0x02, 0x14), +GEN_DFP_BF_A_B(dcmpo, 0x02, 0x04), +GEN_DFP_BF_Ap_Bp(dcmpoq, 0x02, 0x04), +GEN_DFP_BF_A_DCM(dtstdc, 0x02, 0x06), +GEN_DFP_BF_Ap_DCM(dtstdcq, 0x02, 0x06), +GEN_DFP_BF_A_DCM(dtstdg, 0x02, 0x07), +GEN_DFP_BF_Ap_DCM(dtstdgq, 0x02, 0x07), +GEN_DFP_BF_A_B(dtstex, 0x02, 0x05), +GEN_DFP_BF_Ap_Bp(dtstexq, 0x02, 0x05), +GEN_DFP_BF_A_B(dtstsf, 0x02, 0x15), +GEN_DFP_BF_A_Bp(dtstsfq, 0x02, 0x15), +GEN_DFP_BF_A_B_300(dtstsfi, 0x03, 0x15), +GEN_DFP_BF_A_Bp_300(dtstsfiq, 0x03, 0x15), +GEN_DFP_TE_T_B_RMC_Rc(dquai, 0x03, 0x02), +GEN_DFP_TE_Tp_Bp_RMC_Rc(dquaiq, 0x03, 0x02), +GEN_DFP_T_A_B_RMC_Rc(dqua, 0x03, 0x00), +GEN_DFP_Tp_Ap_Bp_RMC_Rc(dquaq, 0x03, 0x00), +GEN_DFP_T_A_B_RMC_Rc(drrnd, 0x03, 0x01), +GEN_DFP_Tp_A_Bp_RMC_Rc(drrndq, 0x03, 0x01), +GEN_DFP_R_T_B_RMC_Rc(drintx, 0x03, 0x03), +GEN_DFP_R_Tp_Bp_RMC_Rc(drintxq, 0x03, 0x03), +GEN_DFP_R_T_B_RMC_Rc(drintn, 0x03, 0x07), +GEN_DFP_R_Tp_Bp_RMC_Rc(drintnq, 0x03, 0x07), +GEN_DFP_T_B_Rc(dctdp, 0x02, 0x08), +GEN_DFP_Tp_B_Rc(dctqpq, 0x02, 0x08), +GEN_DFP_T_B_Rc(drsp, 0x02, 0x18), +GEN_DFP_Tp_Bp_Rc(drdpq, 0x02, 0x18), +GEN_DFP_T_B_Rc(dcffix, 0x02, 0x19), +GEN_DFP_Tp_B_Rc(dcffixq, 0x02, 0x19), +GEN_DFP_T_B_Rc(dctfix, 0x02, 0x09), +GEN_DFP_T_Bp_Rc(dctfixq, 0x02, 0x09), +GEN_DFP_SP_T_B_Rc(ddedpd, 0x02, 0x0a), +GEN_DFP_SP_Tp_Bp_Rc(ddedpdq, 0x02, 0x0a), +GEN_DFP_S_T_B_Rc(denbcd, 0x02, 0x1a), +GEN_DFP_S_Tp_Bp_Rc(denbcdq, 0x02, 0x1a), +GEN_DFP_T_B_Rc(dxex, 0x02, 0x0b), +GEN_DFP_T_Bp_Rc(dxexq, 0x02, 0x0b), +GEN_DFP_T_A_B_Rc(diex, 0x02, 0x1b), +GEN_DFP_Tp_A_Bp_Rc(diexq, 0x02, 0x1b), +GEN_DFP_T_A_SH_Rc(dscli, 0x02, 0x02), +GEN_DFP_Tp_Ap_SH_Rc(dscliq, 0x02, 0x02), +GEN_DFP_T_A_SH_Rc(dscri, 0x02, 0x03), +GEN_DFP_Tp_Ap_SH_Rc(dscriq, 0x02, 0x03), diff --git a/target/ppc/translate/fp-impl.inc.c b/target/ppc/translate/fp-impl.inc.c new file mode 100644 index 0000000000..872af7b56f --- /dev/null +++ b/target/ppc/translate/fp-impl.inc.c @@ -0,0 +1,1070 @@ +/* + * translate-fp.c + * + * Standard FPU translation + */ + +static inline void gen_reset_fpstatus(void) +{ + gen_helper_reset_fpstatus(cpu_env); +} + +static inline void gen_compute_fprf(TCGv_i64 arg) +{ + gen_helper_compute_fprf(cpu_env, arg); + gen_helper_float_check_status(cpu_env); +} + +#if defined(TARGET_PPC64) +static void gen_set_cr1_from_fpscr(DisasContext *ctx) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(tmp, cpu_fpscr); + tcg_gen_shri_i32(cpu_crf[1], tmp, 28); + tcg_temp_free_i32(tmp); +} +#else +static void gen_set_cr1_from_fpscr(DisasContext *ctx) +{ + tcg_gen_shri_tl(cpu_crf[1], cpu_fpscr, 28); +} +#endif + +/*** Floating-Point arithmetic ***/ +#define _GEN_FLOAT_ACB(name, op, op1, op2, isfloat, set_fprf, type) \ +static void gen_f##name(DisasContext *ctx) \ +{ \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + gen_reset_fpstatus(); \ + gen_helper_f##op(cpu_fpr[rD(ctx->opcode)], cpu_env, \ + cpu_fpr[rA(ctx->opcode)], \ + cpu_fpr[rC(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); \ + if (isfloat) { \ + gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, \ + cpu_fpr[rD(ctx->opcode)]); \ + } \ + if (set_fprf) { \ + gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \ + } \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ +} + +#define GEN_FLOAT_ACB(name, op2, set_fprf, type) \ +_GEN_FLOAT_ACB(name, name, 0x3F, op2, 0, set_fprf, type); \ +_GEN_FLOAT_ACB(name##s, name, 0x3B, op2, 1, set_fprf, type); + +#define _GEN_FLOAT_AB(name, op, op1, op2, inval, isfloat, set_fprf, type) \ +static void gen_f##name(DisasContext *ctx) \ +{ \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + gen_reset_fpstatus(); \ + gen_helper_f##op(cpu_fpr[rD(ctx->opcode)], cpu_env, \ + cpu_fpr[rA(ctx->opcode)], \ + cpu_fpr[rB(ctx->opcode)]); \ + if (isfloat) { \ + gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, \ + cpu_fpr[rD(ctx->opcode)]); \ + } \ + if (set_fprf) { \ + gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \ + } \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ +} +#define GEN_FLOAT_AB(name, op2, inval, set_fprf, type) \ +_GEN_FLOAT_AB(name, name, 0x3F, op2, inval, 0, set_fprf, type); \ +_GEN_FLOAT_AB(name##s, name, 0x3B, op2, inval, 1, set_fprf, type); + +#define _GEN_FLOAT_AC(name, op, op1, op2, inval, isfloat, set_fprf, type) \ +static void gen_f##name(DisasContext *ctx) \ +{ \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + gen_reset_fpstatus(); \ + gen_helper_f##op(cpu_fpr[rD(ctx->opcode)], cpu_env, \ + cpu_fpr[rA(ctx->opcode)], \ + cpu_fpr[rC(ctx->opcode)]); \ + if (isfloat) { \ + gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, \ + cpu_fpr[rD(ctx->opcode)]); \ + } \ + if (set_fprf) { \ + gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \ + } \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ +} +#define GEN_FLOAT_AC(name, op2, inval, set_fprf, type) \ +_GEN_FLOAT_AC(name, name, 0x3F, op2, inval, 0, set_fprf, type); \ +_GEN_FLOAT_AC(name##s, name, 0x3B, op2, inval, 1, set_fprf, type); + +#define GEN_FLOAT_B(name, op2, op3, set_fprf, type) \ +static void gen_f##name(DisasContext *ctx) \ +{ \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + gen_reset_fpstatus(); \ + gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_env, \ + cpu_fpr[rB(ctx->opcode)]); \ + if (set_fprf) { \ + gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \ + } \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ +} + +#define GEN_FLOAT_BS(name, op1, op2, set_fprf, type) \ +static void gen_f##name(DisasContext *ctx) \ +{ \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + gen_reset_fpstatus(); \ + gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_env, \ + cpu_fpr[rB(ctx->opcode)]); \ + if (set_fprf) { \ + gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \ + } \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ +} + +/* fadd - fadds */ +GEN_FLOAT_AB(add, 0x15, 0x000007C0, 1, PPC_FLOAT); +/* fdiv - fdivs */ +GEN_FLOAT_AB(div, 0x12, 0x000007C0, 1, PPC_FLOAT); +/* fmul - fmuls */ +GEN_FLOAT_AC(mul, 0x19, 0x0000F800, 1, PPC_FLOAT); + +/* fre */ +GEN_FLOAT_BS(re, 0x3F, 0x18, 1, PPC_FLOAT_EXT); + +/* fres */ +GEN_FLOAT_BS(res, 0x3B, 0x18, 1, PPC_FLOAT_FRES); + +/* frsqrte */ +GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A, 1, PPC_FLOAT_FRSQRTE); + +/* frsqrtes */ +static void gen_frsqrtes(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_reset_fpstatus(); + gen_helper_frsqrte(cpu_fpr[rD(ctx->opcode)], cpu_env, + cpu_fpr[rB(ctx->opcode)]); + gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, + cpu_fpr[rD(ctx->opcode)]); + gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_cr1_from_fpscr(ctx); + } +} + +/* fsel */ +_GEN_FLOAT_ACB(sel, sel, 0x3F, 0x17, 0, 0, PPC_FLOAT_FSEL); +/* fsub - fsubs */ +GEN_FLOAT_AB(sub, 0x14, 0x000007C0, 1, PPC_FLOAT); +/* Optional: */ + +/* fsqrt */ +static void gen_fsqrt(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_reset_fpstatus(); + gen_helper_fsqrt(cpu_fpr[rD(ctx->opcode)], cpu_env, + cpu_fpr[rB(ctx->opcode)]); + gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_cr1_from_fpscr(ctx); + } +} + +static void gen_fsqrts(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_reset_fpstatus(); + gen_helper_fsqrt(cpu_fpr[rD(ctx->opcode)], cpu_env, + cpu_fpr[rB(ctx->opcode)]); + gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, + cpu_fpr[rD(ctx->opcode)]); + gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_cr1_from_fpscr(ctx); + } +} + +/*** Floating-Point multiply-and-add ***/ +/* fmadd - fmadds */ +GEN_FLOAT_ACB(madd, 0x1D, 1, PPC_FLOAT); +/* fmsub - fmsubs */ +GEN_FLOAT_ACB(msub, 0x1C, 1, PPC_FLOAT); +/* fnmadd - fnmadds */ +GEN_FLOAT_ACB(nmadd, 0x1F, 1, PPC_FLOAT); +/* fnmsub - fnmsubs */ +GEN_FLOAT_ACB(nmsub, 0x1E, 1, PPC_FLOAT); + +/*** Floating-Point round & convert ***/ +/* fctiw */ +GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT); +/* fctiwu */ +GEN_FLOAT_B(ctiwu, 0x0E, 0x04, 0, PPC2_FP_CVT_ISA206); +/* fctiwz */ +GEN_FLOAT_B(ctiwz, 0x0F, 0x00, 0, PPC_FLOAT); +/* fctiwuz */ +GEN_FLOAT_B(ctiwuz, 0x0F, 0x04, 0, PPC2_FP_CVT_ISA206); +/* frsp */ +GEN_FLOAT_B(rsp, 0x0C, 0x00, 1, PPC_FLOAT); +/* fcfid */ +GEN_FLOAT_B(cfid, 0x0E, 0x1A, 1, PPC2_FP_CVT_S64); +/* fcfids */ +GEN_FLOAT_B(cfids, 0x0E, 0x1A, 0, PPC2_FP_CVT_ISA206); +/* fcfidu */ +GEN_FLOAT_B(cfidu, 0x0E, 0x1E, 0, PPC2_FP_CVT_ISA206); +/* fcfidus */ +GEN_FLOAT_B(cfidus, 0x0E, 0x1E, 0, PPC2_FP_CVT_ISA206); +/* fctid */ +GEN_FLOAT_B(ctid, 0x0E, 0x19, 0, PPC2_FP_CVT_S64); +/* fctidu */ +GEN_FLOAT_B(ctidu, 0x0E, 0x1D, 0, PPC2_FP_CVT_ISA206); +/* fctidz */ +GEN_FLOAT_B(ctidz, 0x0F, 0x19, 0, PPC2_FP_CVT_S64); +/* fctidu */ +GEN_FLOAT_B(ctiduz, 0x0F, 0x1D, 0, PPC2_FP_CVT_ISA206); + +/* frin */ +GEN_FLOAT_B(rin, 0x08, 0x0C, 1, PPC_FLOAT_EXT); +/* friz */ +GEN_FLOAT_B(riz, 0x08, 0x0D, 1, PPC_FLOAT_EXT); +/* frip */ +GEN_FLOAT_B(rip, 0x08, 0x0E, 1, PPC_FLOAT_EXT); +/* frim */ +GEN_FLOAT_B(rim, 0x08, 0x0F, 1, PPC_FLOAT_EXT); + +static void gen_ftdiv(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_helper_ftdiv(cpu_crf[crfD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)], + cpu_fpr[rB(ctx->opcode)]); +} + +static void gen_ftsqrt(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_helper_ftsqrt(cpu_crf[crfD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); +} + + + +/*** Floating-Point compare ***/ + +/* fcmpo */ +static void gen_fcmpo(DisasContext *ctx) +{ + TCGv_i32 crf; + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_reset_fpstatus(); + crf = tcg_const_i32(crfD(ctx->opcode)); + gen_helper_fcmpo(cpu_env, cpu_fpr[rA(ctx->opcode)], + cpu_fpr[rB(ctx->opcode)], crf); + tcg_temp_free_i32(crf); + gen_helper_float_check_status(cpu_env); +} + +/* fcmpu */ +static void gen_fcmpu(DisasContext *ctx) +{ + TCGv_i32 crf; + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_reset_fpstatus(); + crf = tcg_const_i32(crfD(ctx->opcode)); + gen_helper_fcmpu(cpu_env, cpu_fpr[rA(ctx->opcode)], + cpu_fpr[rB(ctx->opcode)], crf); + tcg_temp_free_i32(crf); + gen_helper_float_check_status(cpu_env); +} + +/*** Floating-point move ***/ +/* fabs */ +/* XXX: beware that fabs never checks for NaNs nor update FPSCR */ +static void gen_fabs(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + tcg_gen_andi_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)], + ~(1ULL << 63)); + if (unlikely(Rc(ctx->opcode))) { + gen_set_cr1_from_fpscr(ctx); + } +} + +/* fmr - fmr. */ +/* XXX: beware that fmr never checks for NaNs nor update FPSCR */ +static void gen_fmr(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + tcg_gen_mov_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode))) { + gen_set_cr1_from_fpscr(ctx); + } +} + +/* fnabs */ +/* XXX: beware that fnabs never checks for NaNs nor update FPSCR */ +static void gen_fnabs(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + tcg_gen_ori_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)], + 1ULL << 63); + if (unlikely(Rc(ctx->opcode))) { + gen_set_cr1_from_fpscr(ctx); + } +} + +/* fneg */ +/* XXX: beware that fneg never checks for NaNs nor update FPSCR */ +static void gen_fneg(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + tcg_gen_xori_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)], + 1ULL << 63); + if (unlikely(Rc(ctx->opcode))) { + gen_set_cr1_from_fpscr(ctx); + } +} + +/* fcpsgn: PowerPC 2.05 specification */ +/* XXX: beware that fcpsgn never checks for NaNs nor update FPSCR */ +static void gen_fcpsgn(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + tcg_gen_deposit_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)], + cpu_fpr[rB(ctx->opcode)], 0, 63); + if (unlikely(Rc(ctx->opcode))) { + gen_set_cr1_from_fpscr(ctx); + } +} + +static void gen_fmrgew(DisasContext *ctx) +{ + TCGv_i64 b0; + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + b0 = tcg_temp_new_i64(); + tcg_gen_shri_i64(b0, cpu_fpr[rB(ctx->opcode)], 32); + tcg_gen_deposit_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)], + b0, 0, 32); + tcg_temp_free_i64(b0); +} + +static void gen_fmrgow(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + tcg_gen_deposit_i64(cpu_fpr[rD(ctx->opcode)], + cpu_fpr[rB(ctx->opcode)], + cpu_fpr[rA(ctx->opcode)], + 32, 32); +} + +/*** Floating-Point status & ctrl register ***/ + +/* mcrfs */ +static void gen_mcrfs(DisasContext *ctx) +{ + TCGv tmp = tcg_temp_new(); + TCGv_i32 tmask; + TCGv_i64 tnew_fpscr = tcg_temp_new_i64(); + int bfa; + int nibble; + int shift; + + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + bfa = crfS(ctx->opcode); + nibble = 7 - bfa; + shift = 4 * nibble; + tcg_gen_shri_tl(tmp, cpu_fpscr, shift); + tcg_gen_trunc_tl_i32(cpu_crf[crfD(ctx->opcode)], tmp); + tcg_gen_andi_i32(cpu_crf[crfD(ctx->opcode)], cpu_crf[crfD(ctx->opcode)], 0xf); + tcg_temp_free(tmp); + tcg_gen_extu_tl_i64(tnew_fpscr, cpu_fpscr); + /* Only the exception bits (including FX) should be cleared if read */ + tcg_gen_andi_i64(tnew_fpscr, tnew_fpscr, ~((0xF << shift) & FP_EX_CLEAR_BITS)); + /* FEX and VX need to be updated, so don't set fpscr directly */ + tmask = tcg_const_i32(1 << nibble); + gen_helper_store_fpscr(cpu_env, tnew_fpscr, tmask); + tcg_temp_free_i32(tmask); + tcg_temp_free_i64(tnew_fpscr); +} + +/* mffs */ +static void gen_mffs(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_reset_fpstatus(); + tcg_gen_extu_tl_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpscr); + if (unlikely(Rc(ctx->opcode))) { + gen_set_cr1_from_fpscr(ctx); + } +} + +/* mtfsb0 */ +static void gen_mtfsb0(DisasContext *ctx) +{ + uint8_t crb; + + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + crb = 31 - crbD(ctx->opcode); + gen_reset_fpstatus(); + if (likely(crb != FPSCR_FEX && crb != FPSCR_VX)) { + TCGv_i32 t0; + t0 = tcg_const_i32(crb); + gen_helper_fpscr_clrbit(cpu_env, t0); + tcg_temp_free_i32(t0); + } + if (unlikely(Rc(ctx->opcode) != 0)) { + tcg_gen_trunc_tl_i32(cpu_crf[1], cpu_fpscr); + tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX); + } +} + +/* mtfsb1 */ +static void gen_mtfsb1(DisasContext *ctx) +{ + uint8_t crb; + + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + crb = 31 - crbD(ctx->opcode); + gen_reset_fpstatus(); + /* XXX: we pretend we can only do IEEE floating-point computations */ + if (likely(crb != FPSCR_FEX && crb != FPSCR_VX && crb != FPSCR_NI)) { + TCGv_i32 t0; + t0 = tcg_const_i32(crb); + gen_helper_fpscr_setbit(cpu_env, t0); + tcg_temp_free_i32(t0); + } + if (unlikely(Rc(ctx->opcode) != 0)) { + tcg_gen_trunc_tl_i32(cpu_crf[1], cpu_fpscr); + tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX); + } + /* We can raise a differed exception */ + gen_helper_float_check_status(cpu_env); +} + +/* mtfsf */ +static void gen_mtfsf(DisasContext *ctx) +{ + TCGv_i32 t0; + int flm, l, w; + + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + flm = FPFLM(ctx->opcode); + l = FPL(ctx->opcode); + w = FPW(ctx->opcode); + if (unlikely(w & !(ctx->insns_flags2 & PPC2_ISA205))) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + return; + } + gen_reset_fpstatus(); + if (l) { + t0 = tcg_const_i32((ctx->insns_flags2 & PPC2_ISA205) ? 0xffff : 0xff); + } else { + t0 = tcg_const_i32(flm << (w * 8)); + } + gen_helper_store_fpscr(cpu_env, cpu_fpr[rB(ctx->opcode)], t0); + tcg_temp_free_i32(t0); + if (unlikely(Rc(ctx->opcode) != 0)) { + tcg_gen_trunc_tl_i32(cpu_crf[1], cpu_fpscr); + tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX); + } + /* We can raise a differed exception */ + gen_helper_float_check_status(cpu_env); +} + +/* mtfsfi */ +static void gen_mtfsfi(DisasContext *ctx) +{ + int bf, sh, w; + TCGv_i64 t0; + TCGv_i32 t1; + + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + w = FPW(ctx->opcode); + bf = FPBF(ctx->opcode); + if (unlikely(w & !(ctx->insns_flags2 & PPC2_ISA205))) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + return; + } + sh = (8 * w) + 7 - bf; + gen_reset_fpstatus(); + t0 = tcg_const_i64(((uint64_t)FPIMM(ctx->opcode)) << (4 * sh)); + t1 = tcg_const_i32(1 << sh); + gen_helper_store_fpscr(cpu_env, t0, t1); + tcg_temp_free_i64(t0); + tcg_temp_free_i32(t1); + if (unlikely(Rc(ctx->opcode) != 0)) { + tcg_gen_trunc_tl_i32(cpu_crf[1], cpu_fpscr); + tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX); + } + /* We can raise a differed exception */ + gen_helper_float_check_status(cpu_env); +} + +/*** Floating-point load ***/ +#define GEN_LDF(name, ldop, opc, type) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_FLOAT); \ + EA = tcg_temp_new(); \ + gen_addr_imm_index(ctx, EA, 0); \ + gen_qemu_##ldop(ctx, cpu_fpr[rD(ctx->opcode)], EA); \ + tcg_temp_free(EA); \ +} + +#define GEN_LDUF(name, ldop, opc, type) \ +static void glue(gen_, name##u)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + if (unlikely(rA(ctx->opcode) == 0)) { \ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_FLOAT); \ + EA = tcg_temp_new(); \ + gen_addr_imm_index(ctx, EA, 0); \ + gen_qemu_##ldop(ctx, cpu_fpr[rD(ctx->opcode)], EA); \ + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \ + tcg_temp_free(EA); \ +} + +#define GEN_LDUXF(name, ldop, opc, type) \ +static void glue(gen_, name##ux)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + if (unlikely(rA(ctx->opcode) == 0)) { \ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_FLOAT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + gen_qemu_##ldop(ctx, cpu_fpr[rD(ctx->opcode)], EA); \ + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \ + tcg_temp_free(EA); \ +} + +#define GEN_LDXF(name, ldop, opc2, opc3, type) \ +static void glue(gen_, name##x)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_FLOAT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + gen_qemu_##ldop(ctx, cpu_fpr[rD(ctx->opcode)], EA); \ + tcg_temp_free(EA); \ +} + +#define GEN_LDFS(name, ldop, op, type) \ +GEN_LDF(name, ldop, op | 0x20, type); \ +GEN_LDUF(name, ldop, op | 0x21, type); \ +GEN_LDUXF(name, ldop, op | 0x01, type); \ +GEN_LDXF(name, ldop, 0x17, op | 0x00, type) + +static inline void gen_qemu_ld32fs(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2) +{ + TCGv t0 = tcg_temp_new(); + TCGv_i32 t1 = tcg_temp_new_i32(); + gen_qemu_ld32u(ctx, t0, arg2); + tcg_gen_trunc_tl_i32(t1, t0); + tcg_temp_free(t0); + gen_helper_float32_to_float64(arg1, cpu_env, t1); + tcg_temp_free_i32(t1); +} + + /* lfd lfdu lfdux lfdx */ +GEN_LDFS(lfd, ld64_i64, 0x12, PPC_FLOAT); + /* lfs lfsu lfsux lfsx */ +GEN_LDFS(lfs, ld32fs, 0x10, PPC_FLOAT); + +/* lfdp */ +static void gen_lfdp(DisasContext *ctx) +{ + TCGv EA; + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_set_access_type(ctx, ACCESS_FLOAT); + EA = tcg_temp_new(); + gen_addr_imm_index(ctx, EA, 0); + /* We only need to swap high and low halves. gen_qemu_ld64_i64 does + necessary 64-bit byteswap already. */ + if (unlikely(ctx->le_mode)) { + gen_qemu_ld64_i64(ctx, cpu_fpr[rD(ctx->opcode) + 1], EA); + tcg_gen_addi_tl(EA, EA, 8); + gen_qemu_ld64_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + } else { + gen_qemu_ld64_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + tcg_gen_addi_tl(EA, EA, 8); + gen_qemu_ld64_i64(ctx, cpu_fpr[rD(ctx->opcode) + 1], EA); + } + tcg_temp_free(EA); +} + +/* lfdpx */ +static void gen_lfdpx(DisasContext *ctx) +{ + TCGv EA; + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_set_access_type(ctx, ACCESS_FLOAT); + EA = tcg_temp_new(); + gen_addr_reg_index(ctx, EA); + /* We only need to swap high and low halves. gen_qemu_ld64_i64 does + necessary 64-bit byteswap already. */ + if (unlikely(ctx->le_mode)) { + gen_qemu_ld64_i64(ctx, cpu_fpr[rD(ctx->opcode) + 1], EA); + tcg_gen_addi_tl(EA, EA, 8); + gen_qemu_ld64_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + } else { + gen_qemu_ld64_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + tcg_gen_addi_tl(EA, EA, 8); + gen_qemu_ld64_i64(ctx, cpu_fpr[rD(ctx->opcode) + 1], EA); + } + tcg_temp_free(EA); +} + +/* lfiwax */ +static void gen_lfiwax(DisasContext *ctx) +{ + TCGv EA; + TCGv t0; + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_set_access_type(ctx, ACCESS_FLOAT); + EA = tcg_temp_new(); + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, EA); + gen_qemu_ld32s(ctx, t0, EA); + tcg_gen_ext_tl_i64(cpu_fpr[rD(ctx->opcode)], t0); + tcg_temp_free(EA); + tcg_temp_free(t0); +} + +/* lfiwzx */ +static void gen_lfiwzx(DisasContext *ctx) +{ + TCGv EA; + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_set_access_type(ctx, ACCESS_FLOAT); + EA = tcg_temp_new(); + gen_addr_reg_index(ctx, EA); + gen_qemu_ld32u_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + tcg_temp_free(EA); +} +/*** Floating-point store ***/ +#define GEN_STF(name, stop, opc, type) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_FLOAT); \ + EA = tcg_temp_new(); \ + gen_addr_imm_index(ctx, EA, 0); \ + gen_qemu_##stop(ctx, cpu_fpr[rS(ctx->opcode)], EA); \ + tcg_temp_free(EA); \ +} + +#define GEN_STUF(name, stop, opc, type) \ +static void glue(gen_, name##u)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + if (unlikely(rA(ctx->opcode) == 0)) { \ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_FLOAT); \ + EA = tcg_temp_new(); \ + gen_addr_imm_index(ctx, EA, 0); \ + gen_qemu_##stop(ctx, cpu_fpr[rS(ctx->opcode)], EA); \ + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \ + tcg_temp_free(EA); \ +} + +#define GEN_STUXF(name, stop, opc, type) \ +static void glue(gen_, name##ux)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + if (unlikely(rA(ctx->opcode) == 0)) { \ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_FLOAT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + gen_qemu_##stop(ctx, cpu_fpr[rS(ctx->opcode)], EA); \ + tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \ + tcg_temp_free(EA); \ +} + +#define GEN_STXF(name, stop, opc2, opc3, type) \ +static void glue(gen_, name##x)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_FLOAT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + gen_qemu_##stop(ctx, cpu_fpr[rS(ctx->opcode)], EA); \ + tcg_temp_free(EA); \ +} + +#define GEN_STFS(name, stop, op, type) \ +GEN_STF(name, stop, op | 0x20, type); \ +GEN_STUF(name, stop, op | 0x21, type); \ +GEN_STUXF(name, stop, op | 0x01, type); \ +GEN_STXF(name, stop, 0x17, op | 0x00, type) + +static inline void gen_qemu_st32fs(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv t1 = tcg_temp_new(); + gen_helper_float64_to_float32(t0, cpu_env, arg1); + tcg_gen_extu_i32_tl(t1, t0); + tcg_temp_free_i32(t0); + gen_qemu_st32(ctx, t1, arg2); + tcg_temp_free(t1); +} + +/* stfd stfdu stfdux stfdx */ +GEN_STFS(stfd, st64_i64, 0x16, PPC_FLOAT); +/* stfs stfsu stfsux stfsx */ +GEN_STFS(stfs, st32fs, 0x14, PPC_FLOAT); + +/* stfdp */ +static void gen_stfdp(DisasContext *ctx) +{ + TCGv EA; + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_set_access_type(ctx, ACCESS_FLOAT); + EA = tcg_temp_new(); + gen_addr_imm_index(ctx, EA, 0); + /* We only need to swap high and low halves. gen_qemu_st64_i64 does + necessary 64-bit byteswap already. */ + if (unlikely(ctx->le_mode)) { + gen_qemu_st64_i64(ctx, cpu_fpr[rD(ctx->opcode) + 1], EA); + tcg_gen_addi_tl(EA, EA, 8); + gen_qemu_st64_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + } else { + gen_qemu_st64_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + tcg_gen_addi_tl(EA, EA, 8); + gen_qemu_st64_i64(ctx, cpu_fpr[rD(ctx->opcode) + 1], EA); + } + tcg_temp_free(EA); +} + +/* stfdpx */ +static void gen_stfdpx(DisasContext *ctx) +{ + TCGv EA; + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_set_access_type(ctx, ACCESS_FLOAT); + EA = tcg_temp_new(); + gen_addr_reg_index(ctx, EA); + /* We only need to swap high and low halves. gen_qemu_st64_i64 does + necessary 64-bit byteswap already. */ + if (unlikely(ctx->le_mode)) { + gen_qemu_st64_i64(ctx, cpu_fpr[rD(ctx->opcode) + 1], EA); + tcg_gen_addi_tl(EA, EA, 8); + gen_qemu_st64_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + } else { + gen_qemu_st64_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + tcg_gen_addi_tl(EA, EA, 8); + gen_qemu_st64_i64(ctx, cpu_fpr[rD(ctx->opcode) + 1], EA); + } + tcg_temp_free(EA); +} + +/* Optional: */ +static inline void gen_qemu_st32fiw(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2) +{ + TCGv t0 = tcg_temp_new(); + tcg_gen_trunc_i64_tl(t0, arg1), + gen_qemu_st32(ctx, t0, arg2); + tcg_temp_free(t0); +} +/* stfiwx */ +GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX); + +/* POWER2 specific instructions */ +/* Quad manipulation (load/store two floats at a time) */ + +/* lfq */ +static void gen_lfq(DisasContext *ctx) +{ + int rd = rD(ctx->opcode); + TCGv t0; + gen_set_access_type(ctx, ACCESS_FLOAT); + t0 = tcg_temp_new(); + gen_addr_imm_index(ctx, t0, 0); + gen_qemu_ld64_i64(ctx, cpu_fpr[rd], t0); + gen_addr_add(ctx, t0, t0, 8); + gen_qemu_ld64_i64(ctx, cpu_fpr[(rd + 1) % 32], t0); + tcg_temp_free(t0); +} + +/* lfqu */ +static void gen_lfqu(DisasContext *ctx) +{ + int ra = rA(ctx->opcode); + int rd = rD(ctx->opcode); + TCGv t0, t1; + gen_set_access_type(ctx, ACCESS_FLOAT); + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + gen_addr_imm_index(ctx, t0, 0); + gen_qemu_ld64_i64(ctx, cpu_fpr[rd], t0); + gen_addr_add(ctx, t1, t0, 8); + gen_qemu_ld64_i64(ctx, cpu_fpr[(rd + 1) % 32], t1); + if (ra != 0) + tcg_gen_mov_tl(cpu_gpr[ra], t0); + tcg_temp_free(t0); + tcg_temp_free(t1); +} + +/* lfqux */ +static void gen_lfqux(DisasContext *ctx) +{ + int ra = rA(ctx->opcode); + int rd = rD(ctx->opcode); + gen_set_access_type(ctx, ACCESS_FLOAT); + TCGv t0, t1; + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + gen_qemu_ld64_i64(ctx, cpu_fpr[rd], t0); + t1 = tcg_temp_new(); + gen_addr_add(ctx, t1, t0, 8); + gen_qemu_ld64_i64(ctx, cpu_fpr[(rd + 1) % 32], t1); + tcg_temp_free(t1); + if (ra != 0) + tcg_gen_mov_tl(cpu_gpr[ra], t0); + tcg_temp_free(t0); +} + +/* lfqx */ +static void gen_lfqx(DisasContext *ctx) +{ + int rd = rD(ctx->opcode); + TCGv t0; + gen_set_access_type(ctx, ACCESS_FLOAT); + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + gen_qemu_ld64_i64(ctx, cpu_fpr[rd], t0); + gen_addr_add(ctx, t0, t0, 8); + gen_qemu_ld64_i64(ctx, cpu_fpr[(rd + 1) % 32], t0); + tcg_temp_free(t0); +} + +/* stfq */ +static void gen_stfq(DisasContext *ctx) +{ + int rd = rD(ctx->opcode); + TCGv t0; + gen_set_access_type(ctx, ACCESS_FLOAT); + t0 = tcg_temp_new(); + gen_addr_imm_index(ctx, t0, 0); + gen_qemu_st64_i64(ctx, cpu_fpr[rd], t0); + gen_addr_add(ctx, t0, t0, 8); + gen_qemu_st64_i64(ctx, cpu_fpr[(rd + 1) % 32], t0); + tcg_temp_free(t0); +} + +/* stfqu */ +static void gen_stfqu(DisasContext *ctx) +{ + int ra = rA(ctx->opcode); + int rd = rD(ctx->opcode); + TCGv t0, t1; + gen_set_access_type(ctx, ACCESS_FLOAT); + t0 = tcg_temp_new(); + gen_addr_imm_index(ctx, t0, 0); + gen_qemu_st64_i64(ctx, cpu_fpr[rd], t0); + t1 = tcg_temp_new(); + gen_addr_add(ctx, t1, t0, 8); + gen_qemu_st64_i64(ctx, cpu_fpr[(rd + 1) % 32], t1); + tcg_temp_free(t1); + if (ra != 0) + tcg_gen_mov_tl(cpu_gpr[ra], t0); + tcg_temp_free(t0); +} + +/* stfqux */ +static void gen_stfqux(DisasContext *ctx) +{ + int ra = rA(ctx->opcode); + int rd = rD(ctx->opcode); + TCGv t0, t1; + gen_set_access_type(ctx, ACCESS_FLOAT); + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + gen_qemu_st64_i64(ctx, cpu_fpr[rd], t0); + t1 = tcg_temp_new(); + gen_addr_add(ctx, t1, t0, 8); + gen_qemu_st64_i64(ctx, cpu_fpr[(rd + 1) % 32], t1); + tcg_temp_free(t1); + if (ra != 0) + tcg_gen_mov_tl(cpu_gpr[ra], t0); + tcg_temp_free(t0); +} + +/* stfqx */ +static void gen_stfqx(DisasContext *ctx) +{ + int rd = rD(ctx->opcode); + TCGv t0; + gen_set_access_type(ctx, ACCESS_FLOAT); + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + gen_qemu_st64_i64(ctx, cpu_fpr[rd], t0); + gen_addr_add(ctx, t0, t0, 8); + gen_qemu_st64_i64(ctx, cpu_fpr[(rd + 1) % 32], t0); + tcg_temp_free(t0); +} + +#undef _GEN_FLOAT_ACB +#undef GEN_FLOAT_ACB +#undef _GEN_FLOAT_AB +#undef GEN_FLOAT_AB +#undef _GEN_FLOAT_AC +#undef GEN_FLOAT_AC +#undef GEN_FLOAT_B +#undef GEN_FLOAT_BS + +#undef GEN_LDF +#undef GEN_LDUF +#undef GEN_LDUXF +#undef GEN_LDXF +#undef GEN_LDFS + +#undef GEN_STF +#undef GEN_STUF +#undef GEN_STUXF +#undef GEN_STXF +#undef GEN_STFS diff --git a/target/ppc/translate/fp-ops.inc.c b/target/ppc/translate/fp-ops.inc.c new file mode 100644 index 0000000000..d36ab4ecc3 --- /dev/null +++ b/target/ppc/translate/fp-ops.inc.c @@ -0,0 +1,111 @@ +#define _GEN_FLOAT_ACB(name, op, op1, op2, isfloat, set_fprf, type) \ +GEN_HANDLER(f##name, op1, op2, 0xFF, 0x00000000, type) +#define GEN_FLOAT_ACB(name, op2, set_fprf, type) \ +_GEN_FLOAT_ACB(name, name, 0x3F, op2, 0, set_fprf, type), \ +_GEN_FLOAT_ACB(name##s, name, 0x3B, op2, 1, set_fprf, type) +#define _GEN_FLOAT_AB(name, op, op1, op2, inval, isfloat, set_fprf, type) \ +GEN_HANDLER(f##name, op1, op2, 0xFF, inval, type) +#define GEN_FLOAT_AB(name, op2, inval, set_fprf, type) \ +_GEN_FLOAT_AB(name, name, 0x3F, op2, inval, 0, set_fprf, type), \ +_GEN_FLOAT_AB(name##s, name, 0x3B, op2, inval, 1, set_fprf, type) +#define _GEN_FLOAT_AC(name, op, op1, op2, inval, isfloat, set_fprf, type) \ +GEN_HANDLER(f##name, op1, op2, 0xFF, inval, type) +#define GEN_FLOAT_AC(name, op2, inval, set_fprf, type) \ +_GEN_FLOAT_AC(name, name, 0x3F, op2, inval, 0, set_fprf, type), \ +_GEN_FLOAT_AC(name##s, name, 0x3B, op2, inval, 1, set_fprf, type) +#define GEN_FLOAT_B(name, op2, op3, set_fprf, type) \ +GEN_HANDLER(f##name, 0x3F, op2, op3, 0x001F0000, type) +#define GEN_FLOAT_BS(name, op1, op2, set_fprf, type) \ +GEN_HANDLER(f##name, op1, op2, 0xFF, 0x001F07C0, type) + +GEN_FLOAT_AB(add, 0x15, 0x000007C0, 1, PPC_FLOAT), +GEN_FLOAT_AB(div, 0x12, 0x000007C0, 1, PPC_FLOAT), +GEN_FLOAT_AC(mul, 0x19, 0x0000F800, 1, PPC_FLOAT), +GEN_FLOAT_BS(re, 0x3F, 0x18, 1, PPC_FLOAT_EXT), +GEN_FLOAT_BS(res, 0x3B, 0x18, 1, PPC_FLOAT_FRES), +GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A, 1, PPC_FLOAT_FRSQRTE), +_GEN_FLOAT_ACB(sel, sel, 0x3F, 0x17, 0, 0, PPC_FLOAT_FSEL), +GEN_FLOAT_AB(sub, 0x14, 0x000007C0, 1, PPC_FLOAT), +GEN_FLOAT_ACB(madd, 0x1D, 1, PPC_FLOAT), +GEN_FLOAT_ACB(msub, 0x1C, 1, PPC_FLOAT), +GEN_FLOAT_ACB(nmadd, 0x1F, 1, PPC_FLOAT), +GEN_FLOAT_ACB(nmsub, 0x1E, 1, PPC_FLOAT), +GEN_HANDLER_E(ftdiv, 0x3F, 0x00, 0x04, 1, PPC_NONE, PPC2_FP_TST_ISA206), +GEN_HANDLER_E(ftsqrt, 0x3F, 0x00, 0x05, 1, PPC_NONE, PPC2_FP_TST_ISA206), +GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT), +GEN_HANDLER_E(fctiwu, 0x3F, 0x0E, 0x04, 0, PPC_NONE, PPC2_FP_CVT_ISA206), +GEN_FLOAT_B(ctiwz, 0x0F, 0x00, 0, PPC_FLOAT), +GEN_HANDLER_E(fctiwuz, 0x3F, 0x0F, 0x04, 0, PPC_NONE, PPC2_FP_CVT_ISA206), +GEN_FLOAT_B(rsp, 0x0C, 0x00, 1, PPC_FLOAT), +GEN_HANDLER_E(fcfid, 0x3F, 0x0E, 0x1A, 0x001F0000, PPC_NONE, PPC2_FP_CVT_S64), +GEN_HANDLER_E(fcfids, 0x3B, 0x0E, 0x1A, 0, PPC_NONE, PPC2_FP_CVT_ISA206), +GEN_HANDLER_E(fcfidu, 0x3F, 0x0E, 0x1E, 0, PPC_NONE, PPC2_FP_CVT_ISA206), +GEN_HANDLER_E(fcfidus, 0x3B, 0x0E, 0x1E, 0, PPC_NONE, PPC2_FP_CVT_ISA206), +GEN_HANDLER_E(fctid, 0x3F, 0x0E, 0x19, 0x001F0000, PPC_NONE, PPC2_FP_CVT_S64), +GEN_HANDLER_E(fctidu, 0x3F, 0x0E, 0x1D, 0, PPC_NONE, PPC2_FP_CVT_ISA206), +GEN_HANDLER_E(fctidz, 0x3F, 0x0F, 0x19, 0x001F0000, PPC_NONE, PPC2_FP_CVT_S64), +GEN_HANDLER_E(fctiduz, 0x3F, 0x0F, 0x1D, 0, PPC_NONE, PPC2_FP_CVT_ISA206), +GEN_FLOAT_B(rin, 0x08, 0x0C, 1, PPC_FLOAT_EXT), +GEN_FLOAT_B(riz, 0x08, 0x0D, 1, PPC_FLOAT_EXT), +GEN_FLOAT_B(rip, 0x08, 0x0E, 1, PPC_FLOAT_EXT), +GEN_FLOAT_B(rim, 0x08, 0x0F, 1, PPC_FLOAT_EXT), + +#define GEN_LDF(name, ldop, opc, type) \ +GEN_HANDLER(name, opc, 0xFF, 0xFF, 0x00000000, type), +#define GEN_LDUF(name, ldop, opc, type) \ +GEN_HANDLER(name##u, opc, 0xFF, 0xFF, 0x00000000, type), +#define GEN_LDUXF(name, ldop, opc, type) \ +GEN_HANDLER(name##ux, 0x1F, 0x17, opc, 0x00000001, type), +#define GEN_LDXF(name, ldop, opc2, opc3, type) \ +GEN_HANDLER(name##x, 0x1F, opc2, opc3, 0x00000001, type), +#define GEN_LDFS(name, ldop, op, type) \ +GEN_LDF(name, ldop, op | 0x20, type) \ +GEN_LDUF(name, ldop, op | 0x21, type) \ +GEN_LDUXF(name, ldop, op | 0x01, type) \ +GEN_LDXF(name, ldop, 0x17, op | 0x00, type) + +GEN_LDFS(lfd, ld64, 0x12, PPC_FLOAT) +GEN_LDFS(lfs, ld32fs, 0x10, PPC_FLOAT) +GEN_HANDLER_E(lfiwax, 0x1f, 0x17, 0x1a, 0x00000001, PPC_NONE, PPC2_ISA205), +GEN_HANDLER_E(lfiwzx, 0x1f, 0x17, 0x1b, 0x1, PPC_NONE, PPC2_FP_CVT_ISA206), +GEN_HANDLER_E(lfdp, 0x39, 0xFF, 0xFF, 0x00200003, PPC_NONE, PPC2_ISA205), +GEN_HANDLER_E(lfdpx, 0x1F, 0x17, 0x18, 0x00200001, PPC_NONE, PPC2_ISA205), + +#define GEN_STF(name, stop, opc, type) \ +GEN_HANDLER(name, opc, 0xFF, 0xFF, 0x00000000, type), +#define GEN_STUF(name, stop, opc, type) \ +GEN_HANDLER(name##u, opc, 0xFF, 0xFF, 0x00000000, type), +#define GEN_STUXF(name, stop, opc, type) \ +GEN_HANDLER(name##ux, 0x1F, 0x17, opc, 0x00000001, type), +#define GEN_STXF(name, stop, opc2, opc3, type) \ +GEN_HANDLER(name##x, 0x1F, opc2, opc3, 0x00000001, type), +#define GEN_STFS(name, stop, op, type) \ +GEN_STF(name, stop, op | 0x20, type) \ +GEN_STUF(name, stop, op | 0x21, type) \ +GEN_STUXF(name, stop, op | 0x01, type) \ +GEN_STXF(name, stop, 0x17, op | 0x00, type) + +GEN_STFS(stfd, st64_i64, 0x16, PPC_FLOAT) +GEN_STFS(stfs, st32fs, 0x14, PPC_FLOAT) +GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX) +GEN_HANDLER_E(stfdp, 0x3D, 0xFF, 0xFF, 0x00200003, PPC_NONE, PPC2_ISA205), +GEN_HANDLER_E(stfdpx, 0x1F, 0x17, 0x1C, 0x00200001, PPC_NONE, PPC2_ISA205), + +GEN_HANDLER(frsqrtes, 0x3B, 0x1A, 0xFF, 0x001F07C0, PPC_FLOAT_FRSQRTES), +GEN_HANDLER(fsqrt, 0x3F, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_FSQRT), +GEN_HANDLER(fsqrts, 0x3B, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_FSQRT), +GEN_HANDLER(fcmpo, 0x3F, 0x00, 0x01, 0x00600001, PPC_FLOAT), +GEN_HANDLER(fcmpu, 0x3F, 0x00, 0x00, 0x00600001, PPC_FLOAT), +GEN_HANDLER(fabs, 0x3F, 0x08, 0x08, 0x001F0000, PPC_FLOAT), +GEN_HANDLER(fmr, 0x3F, 0x08, 0x02, 0x001F0000, PPC_FLOAT), +GEN_HANDLER(fnabs, 0x3F, 0x08, 0x04, 0x001F0000, PPC_FLOAT), +GEN_HANDLER(fneg, 0x3F, 0x08, 0x01, 0x001F0000, PPC_FLOAT), +GEN_HANDLER_E(fcpsgn, 0x3F, 0x08, 0x00, 0x00000000, PPC_NONE, PPC2_ISA205), +GEN_HANDLER_E(fmrgew, 0x3F, 0x06, 0x1E, 0x00000001, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(fmrgow, 0x3F, 0x06, 0x1A, 0x00000001, PPC_NONE, PPC2_VSX207), +GEN_HANDLER(mcrfs, 0x3F, 0x00, 0x02, 0x0063F801, PPC_FLOAT), +GEN_HANDLER(mffs, 0x3F, 0x07, 0x12, 0x001FF800, PPC_FLOAT), +GEN_HANDLER(mtfsb0, 0x3F, 0x06, 0x02, 0x001FF800, PPC_FLOAT), +GEN_HANDLER(mtfsb1, 0x3F, 0x06, 0x01, 0x001FF800, PPC_FLOAT), +GEN_HANDLER(mtfsf, 0x3F, 0x07, 0x16, 0x00000000, PPC_FLOAT), +GEN_HANDLER(mtfsfi, 0x3F, 0x06, 0x04, 0x006e0800, PPC_FLOAT), diff --git a/target/ppc/translate/spe-impl.inc.c b/target/ppc/translate/spe-impl.inc.c new file mode 100644 index 0000000000..8c1c16c63e --- /dev/null +++ b/target/ppc/translate/spe-impl.inc.c @@ -0,0 +1,1229 @@ +/* + * translate-spe.c + * + * Freescale SPE extension translation + */ + +/*** SPE extension ***/ +/* Register moves */ + +static inline void gen_evmra(DisasContext *ctx) +{ + + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + + TCGv_i64 tmp = tcg_temp_new_i64(); + + /* tmp := rA_lo + rA_hi << 32 */ + tcg_gen_concat_tl_i64(tmp, cpu_gpr[rA(ctx->opcode)], cpu_gprh[rA(ctx->opcode)]); + + /* spe_acc := tmp */ + tcg_gen_st_i64(tmp, cpu_env, offsetof(CPUPPCState, spe_acc)); + tcg_temp_free_i64(tmp); + + /* rD := rA */ + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)]); +} + +static inline void gen_load_gpr64(TCGv_i64 t, int reg) +{ + tcg_gen_concat_tl_i64(t, cpu_gpr[reg], cpu_gprh[reg]); +} + +static inline void gen_store_gpr64(int reg, TCGv_i64 t) +{ + tcg_gen_extr_i64_tl(cpu_gpr[reg], cpu_gprh[reg], t); +} + +#define GEN_SPE(name0, name1, opc2, opc3, inval0, inval1, type) \ +static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ +{ \ + if (Rc(ctx->opcode)) \ + gen_##name1(ctx); \ + else \ + gen_##name0(ctx); \ +} + +/* Handler for undefined SPE opcodes */ +static inline void gen_speundef(DisasContext *ctx) +{ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); +} + +/* SPE logic */ +#define GEN_SPEOP_LOGIC2(name, tcg_op) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + if (unlikely(!ctx->spe_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_SPEU); \ + return; \ + } \ + tcg_op(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], \ + cpu_gpr[rB(ctx->opcode)]); \ + tcg_op(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], \ + cpu_gprh[rB(ctx->opcode)]); \ +} + +GEN_SPEOP_LOGIC2(evand, tcg_gen_and_tl); +GEN_SPEOP_LOGIC2(evandc, tcg_gen_andc_tl); +GEN_SPEOP_LOGIC2(evxor, tcg_gen_xor_tl); +GEN_SPEOP_LOGIC2(evor, tcg_gen_or_tl); +GEN_SPEOP_LOGIC2(evnor, tcg_gen_nor_tl); +GEN_SPEOP_LOGIC2(eveqv, tcg_gen_eqv_tl); +GEN_SPEOP_LOGIC2(evorc, tcg_gen_orc_tl); +GEN_SPEOP_LOGIC2(evnand, tcg_gen_nand_tl); + +/* SPE logic immediate */ +#define GEN_SPEOP_TCG_LOGIC_IMM2(name, tcg_opi) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_i32 t0; \ + if (unlikely(!ctx->spe_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_SPEU); \ + return; \ + } \ + t0 = tcg_temp_new_i32(); \ + \ + tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); \ + tcg_opi(t0, t0, rB(ctx->opcode)); \ + tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t0); \ + \ + tcg_gen_trunc_tl_i32(t0, cpu_gprh[rA(ctx->opcode)]); \ + tcg_opi(t0, t0, rB(ctx->opcode)); \ + tcg_gen_extu_i32_tl(cpu_gprh[rD(ctx->opcode)], t0); \ + \ + tcg_temp_free_i32(t0); \ +} +GEN_SPEOP_TCG_LOGIC_IMM2(evslwi, tcg_gen_shli_i32); +GEN_SPEOP_TCG_LOGIC_IMM2(evsrwiu, tcg_gen_shri_i32); +GEN_SPEOP_TCG_LOGIC_IMM2(evsrwis, tcg_gen_sari_i32); +GEN_SPEOP_TCG_LOGIC_IMM2(evrlwi, tcg_gen_rotli_i32); + +/* SPE arithmetic */ +#define GEN_SPEOP_ARITH1(name, tcg_op) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_i32 t0; \ + if (unlikely(!ctx->spe_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_SPEU); \ + return; \ + } \ + t0 = tcg_temp_new_i32(); \ + \ + tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); \ + tcg_op(t0, t0); \ + tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t0); \ + \ + tcg_gen_trunc_tl_i32(t0, cpu_gprh[rA(ctx->opcode)]); \ + tcg_op(t0, t0); \ + tcg_gen_extu_i32_tl(cpu_gprh[rD(ctx->opcode)], t0); \ + \ + tcg_temp_free_i32(t0); \ +} + +static inline void gen_op_evabs(TCGv_i32 ret, TCGv_i32 arg1) +{ + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + + tcg_gen_brcondi_i32(TCG_COND_GE, arg1, 0, l1); + tcg_gen_neg_i32(ret, arg1); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_mov_i32(ret, arg1); + gen_set_label(l2); +} +GEN_SPEOP_ARITH1(evabs, gen_op_evabs); +GEN_SPEOP_ARITH1(evneg, tcg_gen_neg_i32); +GEN_SPEOP_ARITH1(evextsb, tcg_gen_ext8s_i32); +GEN_SPEOP_ARITH1(evextsh, tcg_gen_ext16s_i32); +static inline void gen_op_evrndw(TCGv_i32 ret, TCGv_i32 arg1) +{ + tcg_gen_addi_i32(ret, arg1, 0x8000); + tcg_gen_ext16u_i32(ret, ret); +} +GEN_SPEOP_ARITH1(evrndw, gen_op_evrndw); +GEN_SPEOP_ARITH1(evcntlsw, gen_helper_cntlsw32); +GEN_SPEOP_ARITH1(evcntlzw, gen_helper_cntlzw32); + +#define GEN_SPEOP_ARITH2(name, tcg_op) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_i32 t0, t1; \ + if (unlikely(!ctx->spe_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_SPEU); \ + return; \ + } \ + t0 = tcg_temp_new_i32(); \ + t1 = tcg_temp_new_i32(); \ + \ + tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); \ + tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); \ + tcg_op(t0, t0, t1); \ + tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t0); \ + \ + tcg_gen_trunc_tl_i32(t0, cpu_gprh[rA(ctx->opcode)]); \ + tcg_gen_trunc_tl_i32(t1, cpu_gprh[rB(ctx->opcode)]); \ + tcg_op(t0, t0, t1); \ + tcg_gen_extu_i32_tl(cpu_gprh[rD(ctx->opcode)], t0); \ + \ + tcg_temp_free_i32(t0); \ + tcg_temp_free_i32(t1); \ +} + +static inline void gen_op_evsrwu(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGv_i32 t0 = tcg_temp_local_new_i32(); + + /* No error here: 6 bits are used */ + tcg_gen_andi_i32(t0, arg2, 0x3F); + tcg_gen_brcondi_i32(TCG_COND_GE, t0, 32, l1); + tcg_gen_shr_i32(ret, arg1, t0); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_movi_i32(ret, 0); + gen_set_label(l2); + tcg_temp_free_i32(t0); +} +GEN_SPEOP_ARITH2(evsrwu, gen_op_evsrwu); +static inline void gen_op_evsrws(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGv_i32 t0 = tcg_temp_local_new_i32(); + + /* No error here: 6 bits are used */ + tcg_gen_andi_i32(t0, arg2, 0x3F); + tcg_gen_brcondi_i32(TCG_COND_GE, t0, 32, l1); + tcg_gen_sar_i32(ret, arg1, t0); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_movi_i32(ret, 0); + gen_set_label(l2); + tcg_temp_free_i32(t0); +} +GEN_SPEOP_ARITH2(evsrws, gen_op_evsrws); +static inline void gen_op_evslw(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGv_i32 t0 = tcg_temp_local_new_i32(); + + /* No error here: 6 bits are used */ + tcg_gen_andi_i32(t0, arg2, 0x3F); + tcg_gen_brcondi_i32(TCG_COND_GE, t0, 32, l1); + tcg_gen_shl_i32(ret, arg1, t0); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_movi_i32(ret, 0); + gen_set_label(l2); + tcg_temp_free_i32(t0); +} +GEN_SPEOP_ARITH2(evslw, gen_op_evslw); +static inline void gen_op_evrlw(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_andi_i32(t0, arg2, 0x1F); + tcg_gen_rotl_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); +} +GEN_SPEOP_ARITH2(evrlw, gen_op_evrlw); +static inline void gen_evmergehi(DisasContext *ctx) +{ + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gprh[rB(ctx->opcode)]); + tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)]); +} +GEN_SPEOP_ARITH2(evaddw, tcg_gen_add_i32); +static inline void gen_op_evsubf(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_sub_i32(ret, arg2, arg1); +} +GEN_SPEOP_ARITH2(evsubfw, gen_op_evsubf); + +/* SPE arithmetic immediate */ +#define GEN_SPEOP_ARITH_IMM2(name, tcg_op) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_i32 t0; \ + if (unlikely(!ctx->spe_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_SPEU); \ + return; \ + } \ + t0 = tcg_temp_new_i32(); \ + \ + tcg_gen_trunc_tl_i32(t0, cpu_gpr[rB(ctx->opcode)]); \ + tcg_op(t0, t0, rA(ctx->opcode)); \ + tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t0); \ + \ + tcg_gen_trunc_tl_i32(t0, cpu_gprh[rB(ctx->opcode)]); \ + tcg_op(t0, t0, rA(ctx->opcode)); \ + tcg_gen_extu_i32_tl(cpu_gprh[rD(ctx->opcode)], t0); \ + \ + tcg_temp_free_i32(t0); \ +} +GEN_SPEOP_ARITH_IMM2(evaddiw, tcg_gen_addi_i32); +GEN_SPEOP_ARITH_IMM2(evsubifw, tcg_gen_subi_i32); + +/* SPE comparison */ +#define GEN_SPEOP_COMP(name, tcg_cond) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + if (unlikely(!ctx->spe_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_SPEU); \ + return; \ + } \ + TCGLabel *l1 = gen_new_label(); \ + TCGLabel *l2 = gen_new_label(); \ + TCGLabel *l3 = gen_new_label(); \ + TCGLabel *l4 = gen_new_label(); \ + \ + tcg_gen_ext32s_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); \ + tcg_gen_ext32s_tl(cpu_gpr[rB(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); \ + tcg_gen_ext32s_tl(cpu_gprh[rA(ctx->opcode)], cpu_gprh[rA(ctx->opcode)]); \ + tcg_gen_ext32s_tl(cpu_gprh[rB(ctx->opcode)], cpu_gprh[rB(ctx->opcode)]); \ + \ + tcg_gen_brcond_tl(tcg_cond, cpu_gpr[rA(ctx->opcode)], \ + cpu_gpr[rB(ctx->opcode)], l1); \ + tcg_gen_movi_i32(cpu_crf[crfD(ctx->opcode)], 0); \ + tcg_gen_br(l2); \ + gen_set_label(l1); \ + tcg_gen_movi_i32(cpu_crf[crfD(ctx->opcode)], \ + CRF_CL | CRF_CH_OR_CL | CRF_CH_AND_CL); \ + gen_set_label(l2); \ + tcg_gen_brcond_tl(tcg_cond, cpu_gprh[rA(ctx->opcode)], \ + cpu_gprh[rB(ctx->opcode)], l3); \ + tcg_gen_andi_i32(cpu_crf[crfD(ctx->opcode)], cpu_crf[crfD(ctx->opcode)], \ + ~(CRF_CH | CRF_CH_AND_CL)); \ + tcg_gen_br(l4); \ + gen_set_label(l3); \ + tcg_gen_ori_i32(cpu_crf[crfD(ctx->opcode)], cpu_crf[crfD(ctx->opcode)], \ + CRF_CH | CRF_CH_OR_CL); \ + gen_set_label(l4); \ +} +GEN_SPEOP_COMP(evcmpgtu, TCG_COND_GTU); +GEN_SPEOP_COMP(evcmpgts, TCG_COND_GT); +GEN_SPEOP_COMP(evcmpltu, TCG_COND_LTU); +GEN_SPEOP_COMP(evcmplts, TCG_COND_LT); +GEN_SPEOP_COMP(evcmpeq, TCG_COND_EQ); + +/* SPE misc */ +static inline void gen_brinc(DisasContext *ctx) +{ + /* Note: brinc is usable even if SPE is disabled */ + gen_helper_brinc(cpu_gpr[rD(ctx->opcode)], + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); +} +static inline void gen_evmergelo(DisasContext *ctx) +{ + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); +} +static inline void gen_evmergehilo(DisasContext *ctx) +{ + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)]); +} +static inline void gen_evmergelohi(DisasContext *ctx) +{ + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + if (rD(ctx->opcode) == rA(ctx->opcode)) { + TCGv tmp = tcg_temp_new(); + tcg_gen_mov_tl(tmp, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gprh[rB(ctx->opcode)]); + tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], tmp); + tcg_temp_free(tmp); + } else { + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gprh[rB(ctx->opcode)]); + tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + } +} +static inline void gen_evsplati(DisasContext *ctx) +{ + uint64_t imm = ((int32_t)(rA(ctx->opcode) << 27)) >> 27; + + tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], imm); + tcg_gen_movi_tl(cpu_gprh[rD(ctx->opcode)], imm); +} +static inline void gen_evsplatfi(DisasContext *ctx) +{ + uint64_t imm = rA(ctx->opcode) << 27; + + tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], imm); + tcg_gen_movi_tl(cpu_gprh[rD(ctx->opcode)], imm); +} + +static inline void gen_evsel(DisasContext *ctx) +{ + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGLabel *l3 = gen_new_label(); + TCGLabel *l4 = gen_new_label(); + TCGv_i32 t0 = tcg_temp_local_new_i32(); + + tcg_gen_andi_i32(t0, cpu_crf[ctx->opcode & 0x07], 1 << 3); + tcg_gen_brcondi_i32(TCG_COND_EQ, t0, 0, l1); + tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)]); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rB(ctx->opcode)]); + gen_set_label(l2); + tcg_gen_andi_i32(t0, cpu_crf[ctx->opcode & 0x07], 1 << 2); + tcg_gen_brcondi_i32(TCG_COND_EQ, t0, 0, l3); + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + tcg_gen_br(l4); + gen_set_label(l3); + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + gen_set_label(l4); + tcg_temp_free_i32(t0); +} + +static void gen_evsel0(DisasContext *ctx) +{ + gen_evsel(ctx); +} + +static void gen_evsel1(DisasContext *ctx) +{ + gen_evsel(ctx); +} + +static void gen_evsel2(DisasContext *ctx) +{ + gen_evsel(ctx); +} + +static void gen_evsel3(DisasContext *ctx) +{ + gen_evsel(ctx); +} + +/* Multiply */ + +static inline void gen_evmwumi(DisasContext *ctx) +{ + TCGv_i64 t0, t1; + + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + + /* t0 := rA; t1 := rB */ + tcg_gen_extu_tl_i64(t0, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_ext32u_i64(t0, t0); + tcg_gen_extu_tl_i64(t1, cpu_gpr[rB(ctx->opcode)]); + tcg_gen_ext32u_i64(t1, t1); + + tcg_gen_mul_i64(t0, t0, t1); /* t0 := rA * rB */ + + gen_store_gpr64(rD(ctx->opcode), t0); /* rD := t0 */ + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +} + +static inline void gen_evmwumia(DisasContext *ctx) +{ + TCGv_i64 tmp; + + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + + gen_evmwumi(ctx); /* rD := rA * rB */ + + tmp = tcg_temp_new_i64(); + + /* acc := rD */ + gen_load_gpr64(tmp, rD(ctx->opcode)); + tcg_gen_st_i64(tmp, cpu_env, offsetof(CPUPPCState, spe_acc)); + tcg_temp_free_i64(tmp); +} + +static inline void gen_evmwumiaa(DisasContext *ctx) +{ + TCGv_i64 acc; + TCGv_i64 tmp; + + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + + gen_evmwumi(ctx); /* rD := rA * rB */ + + acc = tcg_temp_new_i64(); + tmp = tcg_temp_new_i64(); + + /* tmp := rD */ + gen_load_gpr64(tmp, rD(ctx->opcode)); + + /* Load acc */ + tcg_gen_ld_i64(acc, cpu_env, offsetof(CPUPPCState, spe_acc)); + + /* acc := tmp + acc */ + tcg_gen_add_i64(acc, acc, tmp); + + /* Store acc */ + tcg_gen_st_i64(acc, cpu_env, offsetof(CPUPPCState, spe_acc)); + + /* rD := acc */ + gen_store_gpr64(rD(ctx->opcode), acc); + + tcg_temp_free_i64(acc); + tcg_temp_free_i64(tmp); +} + +static inline void gen_evmwsmi(DisasContext *ctx) +{ + TCGv_i64 t0, t1; + + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + + /* t0 := rA; t1 := rB */ + tcg_gen_extu_tl_i64(t0, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_ext32s_i64(t0, t0); + tcg_gen_extu_tl_i64(t1, cpu_gpr[rB(ctx->opcode)]); + tcg_gen_ext32s_i64(t1, t1); + + tcg_gen_mul_i64(t0, t0, t1); /* t0 := rA * rB */ + + gen_store_gpr64(rD(ctx->opcode), t0); /* rD := t0 */ + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +} + +static inline void gen_evmwsmia(DisasContext *ctx) +{ + TCGv_i64 tmp; + + gen_evmwsmi(ctx); /* rD := rA * rB */ + + tmp = tcg_temp_new_i64(); + + /* acc := rD */ + gen_load_gpr64(tmp, rD(ctx->opcode)); + tcg_gen_st_i64(tmp, cpu_env, offsetof(CPUPPCState, spe_acc)); + + tcg_temp_free_i64(tmp); +} + +static inline void gen_evmwsmiaa(DisasContext *ctx) +{ + TCGv_i64 acc = tcg_temp_new_i64(); + TCGv_i64 tmp = tcg_temp_new_i64(); + + gen_evmwsmi(ctx); /* rD := rA * rB */ + + acc = tcg_temp_new_i64(); + tmp = tcg_temp_new_i64(); + + /* tmp := rD */ + gen_load_gpr64(tmp, rD(ctx->opcode)); + + /* Load acc */ + tcg_gen_ld_i64(acc, cpu_env, offsetof(CPUPPCState, spe_acc)); + + /* acc := tmp + acc */ + tcg_gen_add_i64(acc, acc, tmp); + + /* Store acc */ + tcg_gen_st_i64(acc, cpu_env, offsetof(CPUPPCState, spe_acc)); + + /* rD := acc */ + gen_store_gpr64(rD(ctx->opcode), acc); + + tcg_temp_free_i64(acc); + tcg_temp_free_i64(tmp); +} + +GEN_SPE(evaddw, speundef, 0x00, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE); //// +GEN_SPE(evaddiw, speundef, 0x01, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE); +GEN_SPE(evsubfw, speundef, 0x02, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE); //// +GEN_SPE(evsubifw, speundef, 0x03, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE); +GEN_SPE(evabs, evneg, 0x04, 0x08, 0x0000F800, 0x0000F800, PPC_SPE); //// +GEN_SPE(evextsb, evextsh, 0x05, 0x08, 0x0000F800, 0x0000F800, PPC_SPE); //// +GEN_SPE(evrndw, evcntlzw, 0x06, 0x08, 0x0000F800, 0x0000F800, PPC_SPE); //// +GEN_SPE(evcntlsw, brinc, 0x07, 0x08, 0x0000F800, 0x00000000, PPC_SPE); // +GEN_SPE(evmra, speundef, 0x02, 0x13, 0x0000F800, 0xFFFFFFFF, PPC_SPE); +GEN_SPE(speundef, evand, 0x08, 0x08, 0xFFFFFFFF, 0x00000000, PPC_SPE); //// +GEN_SPE(evandc, speundef, 0x09, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE); //// +GEN_SPE(evxor, evor, 0x0B, 0x08, 0x00000000, 0x00000000, PPC_SPE); //// +GEN_SPE(evnor, eveqv, 0x0C, 0x08, 0x00000000, 0x00000000, PPC_SPE); //// +GEN_SPE(evmwumi, evmwsmi, 0x0C, 0x11, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(evmwumia, evmwsmia, 0x1C, 0x11, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(evmwumiaa, evmwsmiaa, 0x0C, 0x15, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evorc, 0x0D, 0x08, 0xFFFFFFFF, 0x00000000, PPC_SPE); //// +GEN_SPE(evnand, speundef, 0x0F, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE); //// +GEN_SPE(evsrwu, evsrws, 0x10, 0x08, 0x00000000, 0x00000000, PPC_SPE); //// +GEN_SPE(evsrwiu, evsrwis, 0x11, 0x08, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(evslw, speundef, 0x12, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE); //// +GEN_SPE(evslwi, speundef, 0x13, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE); +GEN_SPE(evrlw, evsplati, 0x14, 0x08, 0x00000000, 0x0000F800, PPC_SPE); // +GEN_SPE(evrlwi, evsplatfi, 0x15, 0x08, 0x00000000, 0x0000F800, PPC_SPE); +GEN_SPE(evmergehi, evmergelo, 0x16, 0x08, 0x00000000, 0x00000000, PPC_SPE); //// +GEN_SPE(evmergehilo, evmergelohi, 0x17, 0x08, 0x00000000, 0x00000000, PPC_SPE); //// +GEN_SPE(evcmpgtu, evcmpgts, 0x18, 0x08, 0x00600000, 0x00600000, PPC_SPE); //// +GEN_SPE(evcmpltu, evcmplts, 0x19, 0x08, 0x00600000, 0x00600000, PPC_SPE); //// +GEN_SPE(evcmpeq, speundef, 0x1A, 0x08, 0x00600000, 0xFFFFFFFF, PPC_SPE); //// + +/* SPE load and stores */ +static inline void gen_addr_spe_imm_index(DisasContext *ctx, TCGv EA, int sh) +{ + target_ulong uimm = rB(ctx->opcode); + + if (rA(ctx->opcode) == 0) { + tcg_gen_movi_tl(EA, uimm << sh); + } else { + tcg_gen_addi_tl(EA, cpu_gpr[rA(ctx->opcode)], uimm << sh); + if (NARROW_MODE(ctx)) { + tcg_gen_ext32u_tl(EA, EA); + } + } +} + +static inline void gen_op_evldd(DisasContext *ctx, TCGv addr) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + gen_qemu_ld64_i64(ctx, t0, addr); + gen_store_gpr64(rD(ctx->opcode), t0); + tcg_temp_free_i64(t0); +} + +static inline void gen_op_evldw(DisasContext *ctx, TCGv addr) +{ + gen_qemu_ld32u(ctx, cpu_gprh[rD(ctx->opcode)], addr); + gen_addr_add(ctx, addr, addr, 4); + gen_qemu_ld32u(ctx, cpu_gpr[rD(ctx->opcode)], addr); +} + +static inline void gen_op_evldh(DisasContext *ctx, TCGv addr) +{ + TCGv t0 = tcg_temp_new(); + gen_qemu_ld16u(ctx, t0, addr); + tcg_gen_shli_tl(cpu_gprh[rD(ctx->opcode)], t0, 16); + gen_addr_add(ctx, addr, addr, 2); + gen_qemu_ld16u(ctx, t0, addr); + tcg_gen_or_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rD(ctx->opcode)], t0); + gen_addr_add(ctx, addr, addr, 2); + gen_qemu_ld16u(ctx, t0, addr); + tcg_gen_shli_tl(cpu_gprh[rD(ctx->opcode)], t0, 16); + gen_addr_add(ctx, addr, addr, 2); + gen_qemu_ld16u(ctx, t0, addr); + tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t0); + tcg_temp_free(t0); +} + +static inline void gen_op_evlhhesplat(DisasContext *ctx, TCGv addr) +{ + TCGv t0 = tcg_temp_new(); + gen_qemu_ld16u(ctx, t0, addr); + tcg_gen_shli_tl(t0, t0, 16); + tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], t0); + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0); + tcg_temp_free(t0); +} + +static inline void gen_op_evlhhousplat(DisasContext *ctx, TCGv addr) +{ + TCGv t0 = tcg_temp_new(); + gen_qemu_ld16u(ctx, t0, addr); + tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], t0); + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0); + tcg_temp_free(t0); +} + +static inline void gen_op_evlhhossplat(DisasContext *ctx, TCGv addr) +{ + TCGv t0 = tcg_temp_new(); + gen_qemu_ld16s(ctx, t0, addr); + tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], t0); + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0); + tcg_temp_free(t0); +} + +static inline void gen_op_evlwhe(DisasContext *ctx, TCGv addr) +{ + TCGv t0 = tcg_temp_new(); + gen_qemu_ld16u(ctx, t0, addr); + tcg_gen_shli_tl(cpu_gprh[rD(ctx->opcode)], t0, 16); + gen_addr_add(ctx, addr, addr, 2); + gen_qemu_ld16u(ctx, t0, addr); + tcg_gen_shli_tl(cpu_gpr[rD(ctx->opcode)], t0, 16); + tcg_temp_free(t0); +} + +static inline void gen_op_evlwhou(DisasContext *ctx, TCGv addr) +{ + gen_qemu_ld16u(ctx, cpu_gprh[rD(ctx->opcode)], addr); + gen_addr_add(ctx, addr, addr, 2); + gen_qemu_ld16u(ctx, cpu_gpr[rD(ctx->opcode)], addr); +} + +static inline void gen_op_evlwhos(DisasContext *ctx, TCGv addr) +{ + gen_qemu_ld16s(ctx, cpu_gprh[rD(ctx->opcode)], addr); + gen_addr_add(ctx, addr, addr, 2); + gen_qemu_ld16s(ctx, cpu_gpr[rD(ctx->opcode)], addr); +} + +static inline void gen_op_evlwwsplat(DisasContext *ctx, TCGv addr) +{ + TCGv t0 = tcg_temp_new(); + gen_qemu_ld32u(ctx, t0, addr); + tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], t0); + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0); + tcg_temp_free(t0); +} + +static inline void gen_op_evlwhsplat(DisasContext *ctx, TCGv addr) +{ + TCGv t0 = tcg_temp_new(); + gen_qemu_ld16u(ctx, t0, addr); + tcg_gen_shli_tl(cpu_gprh[rD(ctx->opcode)], t0, 16); + tcg_gen_or_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rD(ctx->opcode)], t0); + gen_addr_add(ctx, addr, addr, 2); + gen_qemu_ld16u(ctx, t0, addr); + tcg_gen_shli_tl(cpu_gpr[rD(ctx->opcode)], t0, 16); + tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gprh[rD(ctx->opcode)], t0); + tcg_temp_free(t0); +} + +static inline void gen_op_evstdd(DisasContext *ctx, TCGv addr) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + gen_load_gpr64(t0, rS(ctx->opcode)); + gen_qemu_st64_i64(ctx, t0, addr); + tcg_temp_free_i64(t0); +} + +static inline void gen_op_evstdw(DisasContext *ctx, TCGv addr) +{ + gen_qemu_st32(ctx, cpu_gprh[rS(ctx->opcode)], addr); + gen_addr_add(ctx, addr, addr, 4); + gen_qemu_st32(ctx, cpu_gpr[rS(ctx->opcode)], addr); +} + +static inline void gen_op_evstdh(DisasContext *ctx, TCGv addr) +{ + TCGv t0 = tcg_temp_new(); + tcg_gen_shri_tl(t0, cpu_gprh[rS(ctx->opcode)], 16); + gen_qemu_st16(ctx, t0, addr); + gen_addr_add(ctx, addr, addr, 2); + gen_qemu_st16(ctx, cpu_gprh[rS(ctx->opcode)], addr); + gen_addr_add(ctx, addr, addr, 2); + tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], 16); + gen_qemu_st16(ctx, t0, addr); + tcg_temp_free(t0); + gen_addr_add(ctx, addr, addr, 2); + gen_qemu_st16(ctx, cpu_gpr[rS(ctx->opcode)], addr); +} + +static inline void gen_op_evstwhe(DisasContext *ctx, TCGv addr) +{ + TCGv t0 = tcg_temp_new(); + tcg_gen_shri_tl(t0, cpu_gprh[rS(ctx->opcode)], 16); + gen_qemu_st16(ctx, t0, addr); + gen_addr_add(ctx, addr, addr, 2); + tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], 16); + gen_qemu_st16(ctx, t0, addr); + tcg_temp_free(t0); +} + +static inline void gen_op_evstwho(DisasContext *ctx, TCGv addr) +{ + gen_qemu_st16(ctx, cpu_gprh[rS(ctx->opcode)], addr); + gen_addr_add(ctx, addr, addr, 2); + gen_qemu_st16(ctx, cpu_gpr[rS(ctx->opcode)], addr); +} + +static inline void gen_op_evstwwe(DisasContext *ctx, TCGv addr) +{ + gen_qemu_st32(ctx, cpu_gprh[rS(ctx->opcode)], addr); +} + +static inline void gen_op_evstwwo(DisasContext *ctx, TCGv addr) +{ + gen_qemu_st32(ctx, cpu_gpr[rS(ctx->opcode)], addr); +} + +#define GEN_SPEOP_LDST(name, opc2, sh) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv t0; \ + if (unlikely(!ctx->spe_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_SPEU); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + t0 = tcg_temp_new(); \ + if (Rc(ctx->opcode)) { \ + gen_addr_spe_imm_index(ctx, t0, sh); \ + } else { \ + gen_addr_reg_index(ctx, t0); \ + } \ + gen_op_##name(ctx, t0); \ + tcg_temp_free(t0); \ +} + +GEN_SPEOP_LDST(evldd, 0x00, 3); +GEN_SPEOP_LDST(evldw, 0x01, 3); +GEN_SPEOP_LDST(evldh, 0x02, 3); +GEN_SPEOP_LDST(evlhhesplat, 0x04, 1); +GEN_SPEOP_LDST(evlhhousplat, 0x06, 1); +GEN_SPEOP_LDST(evlhhossplat, 0x07, 1); +GEN_SPEOP_LDST(evlwhe, 0x08, 2); +GEN_SPEOP_LDST(evlwhou, 0x0A, 2); +GEN_SPEOP_LDST(evlwhos, 0x0B, 2); +GEN_SPEOP_LDST(evlwwsplat, 0x0C, 2); +GEN_SPEOP_LDST(evlwhsplat, 0x0E, 2); + +GEN_SPEOP_LDST(evstdd, 0x10, 3); +GEN_SPEOP_LDST(evstdw, 0x11, 3); +GEN_SPEOP_LDST(evstdh, 0x12, 3); +GEN_SPEOP_LDST(evstwhe, 0x18, 2); +GEN_SPEOP_LDST(evstwho, 0x1A, 2); +GEN_SPEOP_LDST(evstwwe, 0x1C, 2); +GEN_SPEOP_LDST(evstwwo, 0x1E, 2); + +/* Multiply and add - TODO */ +#if 0 +GEN_SPE(speundef, evmhessf, 0x01, 0x10, 0xFFFFFFFF, 0x00000000, PPC_SPE);// +GEN_SPE(speundef, evmhossf, 0x03, 0x10, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(evmheumi, evmhesmi, 0x04, 0x10, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhesmf, 0x05, 0x10, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(evmhoumi, evmhosmi, 0x06, 0x10, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhosmf, 0x07, 0x10, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhessfa, 0x11, 0x10, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhossfa, 0x13, 0x10, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(evmheumia, evmhesmia, 0x14, 0x10, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhesmfa, 0x15, 0x10, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(evmhoumia, evmhosmia, 0x16, 0x10, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhosmfa, 0x17, 0x10, 0xFFFFFFFF, 0x00000000, PPC_SPE); + +GEN_SPE(speundef, evmwhssf, 0x03, 0x11, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(evmwlumi, speundef, 0x04, 0x11, 0x00000000, 0xFFFFFFFF, PPC_SPE); +GEN_SPE(evmwhumi, evmwhsmi, 0x06, 0x11, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmwhsmf, 0x07, 0x11, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmwssf, 0x09, 0x11, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmwsmf, 0x0D, 0x11, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmwhssfa, 0x13, 0x11, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(evmwlumia, speundef, 0x14, 0x11, 0x00000000, 0xFFFFFFFF, PPC_SPE); +GEN_SPE(evmwhumia, evmwhsmia, 0x16, 0x11, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmwhsmfa, 0x17, 0x11, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmwssfa, 0x19, 0x11, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmwsmfa, 0x1D, 0x11, 0xFFFFFFFF, 0x00000000, PPC_SPE); + +GEN_SPE(evadduiaaw, evaddsiaaw, 0x00, 0x13, 0x0000F800, 0x0000F800, PPC_SPE); +GEN_SPE(evsubfusiaaw, evsubfssiaaw, 0x01, 0x13, 0x0000F800, 0x0000F800, PPC_SPE); +GEN_SPE(evaddumiaaw, evaddsmiaaw, 0x04, 0x13, 0x0000F800, 0x0000F800, PPC_SPE); +GEN_SPE(evsubfumiaaw, evsubfsmiaaw, 0x05, 0x13, 0x0000F800, 0x0000F800, PPC_SPE); +GEN_SPE(evdivws, evdivwu, 0x06, 0x13, 0x00000000, 0x00000000, PPC_SPE); + +GEN_SPE(evmheusiaaw, evmhessiaaw, 0x00, 0x14, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhessfaaw, 0x01, 0x14, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(evmhousiaaw, evmhossiaaw, 0x02, 0x14, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhossfaaw, 0x03, 0x14, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(evmheumiaaw, evmhesmiaaw, 0x04, 0x14, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhesmfaaw, 0x05, 0x14, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(evmhoumiaaw, evmhosmiaaw, 0x06, 0x14, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhosmfaaw, 0x07, 0x14, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(evmhegumiaa, evmhegsmiaa, 0x14, 0x14, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhegsmfaa, 0x15, 0x14, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(evmhogumiaa, evmhogsmiaa, 0x16, 0x14, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhogsmfaa, 0x17, 0x14, 0xFFFFFFFF, 0x00000000, PPC_SPE); + +GEN_SPE(evmwlusiaaw, evmwlssiaaw, 0x00, 0x15, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(evmwlumiaaw, evmwlsmiaaw, 0x04, 0x15, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmwssfaa, 0x09, 0x15, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmwsmfaa, 0x0D, 0x15, 0xFFFFFFFF, 0x00000000, PPC_SPE); + +GEN_SPE(evmheusianw, evmhessianw, 0x00, 0x16, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhessfanw, 0x01, 0x16, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(evmhousianw, evmhossianw, 0x02, 0x16, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhossfanw, 0x03, 0x16, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(evmheumianw, evmhesmianw, 0x04, 0x16, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhesmfanw, 0x05, 0x16, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(evmhoumianw, evmhosmianw, 0x06, 0x16, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhosmfanw, 0x07, 0x16, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(evmhegumian, evmhegsmian, 0x14, 0x16, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhegsmfan, 0x15, 0x16, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(evmhigumian, evmhigsmian, 0x16, 0x16, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmhogsmfan, 0x17, 0x16, 0xFFFFFFFF, 0x00000000, PPC_SPE); + +GEN_SPE(evmwlusianw, evmwlssianw, 0x00, 0x17, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(evmwlumianw, evmwlsmianw, 0x04, 0x17, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmwssfan, 0x09, 0x17, 0xFFFFFFFF, 0x00000000, PPC_SPE); +GEN_SPE(evmwumian, evmwsmian, 0x0C, 0x17, 0x00000000, 0x00000000, PPC_SPE); +GEN_SPE(speundef, evmwsmfan, 0x0D, 0x17, 0xFFFFFFFF, 0x00000000, PPC_SPE); +#endif + +/*** SPE floating-point extension ***/ +#define GEN_SPEFPUOP_CONV_32_32(name) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_i32 t0 = tcg_temp_new_i32(); \ + tcg_gen_trunc_tl_i32(t0, cpu_gpr[rB(ctx->opcode)]); \ + gen_helper_##name(t0, cpu_env, t0); \ + tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t0); \ + tcg_temp_free_i32(t0); \ +} +#define GEN_SPEFPUOP_CONV_32_64(name) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_i64 t0 = tcg_temp_new_i64(); \ + TCGv_i32 t1 = tcg_temp_new_i32(); \ + gen_load_gpr64(t0, rB(ctx->opcode)); \ + gen_helper_##name(t1, cpu_env, t0); \ + tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t1); \ + tcg_temp_free_i64(t0); \ + tcg_temp_free_i32(t1); \ +} +#define GEN_SPEFPUOP_CONV_64_32(name) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_i64 t0 = tcg_temp_new_i64(); \ + TCGv_i32 t1 = tcg_temp_new_i32(); \ + tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); \ + gen_helper_##name(t0, cpu_env, t1); \ + gen_store_gpr64(rD(ctx->opcode), t0); \ + tcg_temp_free_i64(t0); \ + tcg_temp_free_i32(t1); \ +} +#define GEN_SPEFPUOP_CONV_64_64(name) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_i64 t0 = tcg_temp_new_i64(); \ + gen_load_gpr64(t0, rB(ctx->opcode)); \ + gen_helper_##name(t0, cpu_env, t0); \ + gen_store_gpr64(rD(ctx->opcode), t0); \ + tcg_temp_free_i64(t0); \ +} +#define GEN_SPEFPUOP_ARITH2_32_32(name) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_i32 t0, t1; \ + if (unlikely(!ctx->spe_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_SPEU); \ + return; \ + } \ + t0 = tcg_temp_new_i32(); \ + t1 = tcg_temp_new_i32(); \ + tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); \ + tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); \ + gen_helper_##name(t0, cpu_env, t0, t1); \ + tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t0); \ + \ + tcg_temp_free_i32(t0); \ + tcg_temp_free_i32(t1); \ +} +#define GEN_SPEFPUOP_ARITH2_64_64(name) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_i64 t0, t1; \ + if (unlikely(!ctx->spe_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_SPEU); \ + return; \ + } \ + t0 = tcg_temp_new_i64(); \ + t1 = tcg_temp_new_i64(); \ + gen_load_gpr64(t0, rA(ctx->opcode)); \ + gen_load_gpr64(t1, rB(ctx->opcode)); \ + gen_helper_##name(t0, cpu_env, t0, t1); \ + gen_store_gpr64(rD(ctx->opcode), t0); \ + tcg_temp_free_i64(t0); \ + tcg_temp_free_i64(t1); \ +} +#define GEN_SPEFPUOP_COMP_32(name) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_i32 t0, t1; \ + if (unlikely(!ctx->spe_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_SPEU); \ + return; \ + } \ + t0 = tcg_temp_new_i32(); \ + t1 = tcg_temp_new_i32(); \ + \ + tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); \ + tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); \ + gen_helper_##name(cpu_crf[crfD(ctx->opcode)], cpu_env, t0, t1); \ + \ + tcg_temp_free_i32(t0); \ + tcg_temp_free_i32(t1); \ +} +#define GEN_SPEFPUOP_COMP_64(name) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_i64 t0, t1; \ + if (unlikely(!ctx->spe_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_SPEU); \ + return; \ + } \ + t0 = tcg_temp_new_i64(); \ + t1 = tcg_temp_new_i64(); \ + gen_load_gpr64(t0, rA(ctx->opcode)); \ + gen_load_gpr64(t1, rB(ctx->opcode)); \ + gen_helper_##name(cpu_crf[crfD(ctx->opcode)], cpu_env, t0, t1); \ + tcg_temp_free_i64(t0); \ + tcg_temp_free_i64(t1); \ +} + +/* Single precision floating-point vectors operations */ +/* Arithmetic */ +GEN_SPEFPUOP_ARITH2_64_64(evfsadd); +GEN_SPEFPUOP_ARITH2_64_64(evfssub); +GEN_SPEFPUOP_ARITH2_64_64(evfsmul); +GEN_SPEFPUOP_ARITH2_64_64(evfsdiv); +static inline void gen_evfsabs(DisasContext *ctx) +{ + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + tcg_gen_andi_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], + ~0x80000000); + tcg_gen_andi_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], + ~0x80000000); +} +static inline void gen_evfsnabs(DisasContext *ctx) +{ + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + tcg_gen_ori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], + 0x80000000); + tcg_gen_ori_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], + 0x80000000); +} +static inline void gen_evfsneg(DisasContext *ctx) +{ + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + tcg_gen_xori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], + 0x80000000); + tcg_gen_xori_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], + 0x80000000); +} + +/* Conversion */ +GEN_SPEFPUOP_CONV_64_64(evfscfui); +GEN_SPEFPUOP_CONV_64_64(evfscfsi); +GEN_SPEFPUOP_CONV_64_64(evfscfuf); +GEN_SPEFPUOP_CONV_64_64(evfscfsf); +GEN_SPEFPUOP_CONV_64_64(evfsctui); +GEN_SPEFPUOP_CONV_64_64(evfsctsi); +GEN_SPEFPUOP_CONV_64_64(evfsctuf); +GEN_SPEFPUOP_CONV_64_64(evfsctsf); +GEN_SPEFPUOP_CONV_64_64(evfsctuiz); +GEN_SPEFPUOP_CONV_64_64(evfsctsiz); + +/* Comparison */ +GEN_SPEFPUOP_COMP_64(evfscmpgt); +GEN_SPEFPUOP_COMP_64(evfscmplt); +GEN_SPEFPUOP_COMP_64(evfscmpeq); +GEN_SPEFPUOP_COMP_64(evfststgt); +GEN_SPEFPUOP_COMP_64(evfststlt); +GEN_SPEFPUOP_COMP_64(evfststeq); + +/* Opcodes definitions */ +GEN_SPE(evfsadd, evfssub, 0x00, 0x0A, 0x00000000, 0x00000000, PPC_SPE_SINGLE); // +GEN_SPE(evfsabs, evfsnabs, 0x02, 0x0A, 0x0000F800, 0x0000F800, PPC_SPE_SINGLE); // +GEN_SPE(evfsneg, speundef, 0x03, 0x0A, 0x0000F800, 0xFFFFFFFF, PPC_SPE_SINGLE); // +GEN_SPE(evfsmul, evfsdiv, 0x04, 0x0A, 0x00000000, 0x00000000, PPC_SPE_SINGLE); // +GEN_SPE(evfscmpgt, evfscmplt, 0x06, 0x0A, 0x00600000, 0x00600000, PPC_SPE_SINGLE); // +GEN_SPE(evfscmpeq, speundef, 0x07, 0x0A, 0x00600000, 0xFFFFFFFF, PPC_SPE_SINGLE); // +GEN_SPE(evfscfui, evfscfsi, 0x08, 0x0A, 0x00180000, 0x00180000, PPC_SPE_SINGLE); // +GEN_SPE(evfscfuf, evfscfsf, 0x09, 0x0A, 0x00180000, 0x00180000, PPC_SPE_SINGLE); // +GEN_SPE(evfsctui, evfsctsi, 0x0A, 0x0A, 0x00180000, 0x00180000, PPC_SPE_SINGLE); // +GEN_SPE(evfsctuf, evfsctsf, 0x0B, 0x0A, 0x00180000, 0x00180000, PPC_SPE_SINGLE); // +GEN_SPE(evfsctuiz, speundef, 0x0C, 0x0A, 0x00180000, 0xFFFFFFFF, PPC_SPE_SINGLE); // +GEN_SPE(evfsctsiz, speundef, 0x0D, 0x0A, 0x00180000, 0xFFFFFFFF, PPC_SPE_SINGLE); // +GEN_SPE(evfststgt, evfststlt, 0x0E, 0x0A, 0x00600000, 0x00600000, PPC_SPE_SINGLE); // +GEN_SPE(evfststeq, speundef, 0x0F, 0x0A, 0x00600000, 0xFFFFFFFF, PPC_SPE_SINGLE); // + +/* Single precision floating-point operations */ +/* Arithmetic */ +GEN_SPEFPUOP_ARITH2_32_32(efsadd); +GEN_SPEFPUOP_ARITH2_32_32(efssub); +GEN_SPEFPUOP_ARITH2_32_32(efsmul); +GEN_SPEFPUOP_ARITH2_32_32(efsdiv); +static inline void gen_efsabs(DisasContext *ctx) +{ + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + tcg_gen_andi_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], (target_long)~0x80000000LL); +} +static inline void gen_efsnabs(DisasContext *ctx) +{ + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + tcg_gen_ori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x80000000); +} +static inline void gen_efsneg(DisasContext *ctx) +{ + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + tcg_gen_xori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x80000000); +} + +/* Conversion */ +GEN_SPEFPUOP_CONV_32_32(efscfui); +GEN_SPEFPUOP_CONV_32_32(efscfsi); +GEN_SPEFPUOP_CONV_32_32(efscfuf); +GEN_SPEFPUOP_CONV_32_32(efscfsf); +GEN_SPEFPUOP_CONV_32_32(efsctui); +GEN_SPEFPUOP_CONV_32_32(efsctsi); +GEN_SPEFPUOP_CONV_32_32(efsctuf); +GEN_SPEFPUOP_CONV_32_32(efsctsf); +GEN_SPEFPUOP_CONV_32_32(efsctuiz); +GEN_SPEFPUOP_CONV_32_32(efsctsiz); +GEN_SPEFPUOP_CONV_32_64(efscfd); + +/* Comparison */ +GEN_SPEFPUOP_COMP_32(efscmpgt); +GEN_SPEFPUOP_COMP_32(efscmplt); +GEN_SPEFPUOP_COMP_32(efscmpeq); +GEN_SPEFPUOP_COMP_32(efststgt); +GEN_SPEFPUOP_COMP_32(efststlt); +GEN_SPEFPUOP_COMP_32(efststeq); + +/* Opcodes definitions */ +GEN_SPE(efsadd, efssub, 0x00, 0x0B, 0x00000000, 0x00000000, PPC_SPE_SINGLE); // +GEN_SPE(efsabs, efsnabs, 0x02, 0x0B, 0x0000F800, 0x0000F800, PPC_SPE_SINGLE); // +GEN_SPE(efsneg, speundef, 0x03, 0x0B, 0x0000F800, 0xFFFFFFFF, PPC_SPE_SINGLE); // +GEN_SPE(efsmul, efsdiv, 0x04, 0x0B, 0x00000000, 0x00000000, PPC_SPE_SINGLE); // +GEN_SPE(efscmpgt, efscmplt, 0x06, 0x0B, 0x00600000, 0x00600000, PPC_SPE_SINGLE); // +GEN_SPE(efscmpeq, efscfd, 0x07, 0x0B, 0x00600000, 0x00180000, PPC_SPE_SINGLE); // +GEN_SPE(efscfui, efscfsi, 0x08, 0x0B, 0x00180000, 0x00180000, PPC_SPE_SINGLE); // +GEN_SPE(efscfuf, efscfsf, 0x09, 0x0B, 0x00180000, 0x00180000, PPC_SPE_SINGLE); // +GEN_SPE(efsctui, efsctsi, 0x0A, 0x0B, 0x00180000, 0x00180000, PPC_SPE_SINGLE); // +GEN_SPE(efsctuf, efsctsf, 0x0B, 0x0B, 0x00180000, 0x00180000, PPC_SPE_SINGLE); // +GEN_SPE(efsctuiz, speundef, 0x0C, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_SINGLE); // +GEN_SPE(efsctsiz, speundef, 0x0D, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_SINGLE); // +GEN_SPE(efststgt, efststlt, 0x0E, 0x0B, 0x00600000, 0x00600000, PPC_SPE_SINGLE); // +GEN_SPE(efststeq, speundef, 0x0F, 0x0B, 0x00600000, 0xFFFFFFFF, PPC_SPE_SINGLE); // + +/* Double precision floating-point operations */ +/* Arithmetic */ +GEN_SPEFPUOP_ARITH2_64_64(efdadd); +GEN_SPEFPUOP_ARITH2_64_64(efdsub); +GEN_SPEFPUOP_ARITH2_64_64(efdmul); +GEN_SPEFPUOP_ARITH2_64_64(efddiv); +static inline void gen_efdabs(DisasContext *ctx) +{ + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + tcg_gen_andi_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], + ~0x80000000); +} +static inline void gen_efdnabs(DisasContext *ctx) +{ + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + tcg_gen_ori_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], + 0x80000000); +} +static inline void gen_efdneg(DisasContext *ctx) +{ + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_SPEU); + return; + } + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + tcg_gen_xori_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], + 0x80000000); +} + +/* Conversion */ +GEN_SPEFPUOP_CONV_64_32(efdcfui); +GEN_SPEFPUOP_CONV_64_32(efdcfsi); +GEN_SPEFPUOP_CONV_64_32(efdcfuf); +GEN_SPEFPUOP_CONV_64_32(efdcfsf); +GEN_SPEFPUOP_CONV_32_64(efdctui); +GEN_SPEFPUOP_CONV_32_64(efdctsi); +GEN_SPEFPUOP_CONV_32_64(efdctuf); +GEN_SPEFPUOP_CONV_32_64(efdctsf); +GEN_SPEFPUOP_CONV_32_64(efdctuiz); +GEN_SPEFPUOP_CONV_32_64(efdctsiz); +GEN_SPEFPUOP_CONV_64_32(efdcfs); +GEN_SPEFPUOP_CONV_64_64(efdcfuid); +GEN_SPEFPUOP_CONV_64_64(efdcfsid); +GEN_SPEFPUOP_CONV_64_64(efdctuidz); +GEN_SPEFPUOP_CONV_64_64(efdctsidz); + +/* Comparison */ +GEN_SPEFPUOP_COMP_64(efdcmpgt); +GEN_SPEFPUOP_COMP_64(efdcmplt); +GEN_SPEFPUOP_COMP_64(efdcmpeq); +GEN_SPEFPUOP_COMP_64(efdtstgt); +GEN_SPEFPUOP_COMP_64(efdtstlt); +GEN_SPEFPUOP_COMP_64(efdtsteq); + +/* Opcodes definitions */ +GEN_SPE(efdadd, efdsub, 0x10, 0x0B, 0x00000000, 0x00000000, PPC_SPE_DOUBLE); // +GEN_SPE(efdcfuid, efdcfsid, 0x11, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE); // +GEN_SPE(efdabs, efdnabs, 0x12, 0x0B, 0x0000F800, 0x0000F800, PPC_SPE_DOUBLE); // +GEN_SPE(efdneg, speundef, 0x13, 0x0B, 0x0000F800, 0xFFFFFFFF, PPC_SPE_DOUBLE); // +GEN_SPE(efdmul, efddiv, 0x14, 0x0B, 0x00000000, 0x00000000, PPC_SPE_DOUBLE); // +GEN_SPE(efdctuidz, efdctsidz, 0x15, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE); // +GEN_SPE(efdcmpgt, efdcmplt, 0x16, 0x0B, 0x00600000, 0x00600000, PPC_SPE_DOUBLE); // +GEN_SPE(efdcmpeq, efdcfs, 0x17, 0x0B, 0x00600000, 0x00180000, PPC_SPE_DOUBLE); // +GEN_SPE(efdcfui, efdcfsi, 0x18, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE); // +GEN_SPE(efdcfuf, efdcfsf, 0x19, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE); // +GEN_SPE(efdctui, efdctsi, 0x1A, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE); // +GEN_SPE(efdctuf, efdctsf, 0x1B, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE); // +GEN_SPE(efdctuiz, speundef, 0x1C, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_DOUBLE); // +GEN_SPE(efdctsiz, speundef, 0x1D, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_DOUBLE); // +GEN_SPE(efdtstgt, efdtstlt, 0x1E, 0x0B, 0x00600000, 0x00600000, PPC_SPE_DOUBLE); // +GEN_SPE(efdtsteq, speundef, 0x1F, 0x0B, 0x00600000, 0xFFFFFFFF, PPC_SPE_DOUBLE); // + +#undef GEN_SPE +#undef GEN_SPEOP_LDST diff --git a/target/ppc/translate/spe-ops.inc.c b/target/ppc/translate/spe-ops.inc.c new file mode 100644 index 0000000000..7efe8b8746 --- /dev/null +++ b/target/ppc/translate/spe-ops.inc.c @@ -0,0 +1,105 @@ +GEN_HANDLER2(evsel0, "evsel", 0x04, 0x1c, 0x09, 0x00000000, PPC_SPE), +GEN_HANDLER2(evsel1, "evsel", 0x04, 0x1d, 0x09, 0x00000000, PPC_SPE), +GEN_HANDLER2(evsel2, "evsel", 0x04, 0x1e, 0x09, 0x00000000, PPC_SPE), +GEN_HANDLER2(evsel3, "evsel", 0x04, 0x1f, 0x09, 0x00000000, PPC_SPE), + +#define GEN_SPE(name0, name1, opc2, opc3, inval0, inval1, type) \ + GEN_OPCODE_DUAL(name0##_##name1, 0x04, opc2, opc3, inval0, inval1, type, PPC_NONE) +GEN_SPE(evaddw, speundef, 0x00, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE), +GEN_SPE(evaddiw, speundef, 0x01, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE), +GEN_SPE(evsubfw, speundef, 0x02, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE), +GEN_SPE(evsubifw, speundef, 0x03, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE), +GEN_SPE(evabs, evneg, 0x04, 0x08, 0x0000F800, 0x0000F800, PPC_SPE), +GEN_SPE(evextsb, evextsh, 0x05, 0x08, 0x0000F800, 0x0000F800, PPC_SPE), +GEN_SPE(evrndw, evcntlzw, 0x06, 0x08, 0x0000F800, 0x0000F800, PPC_SPE), +GEN_SPE(evcntlsw, brinc, 0x07, 0x08, 0x0000F800, 0x00000000, PPC_SPE), +GEN_SPE(evmra, speundef, 0x02, 0x13, 0x0000F800, 0xFFFFFFFF, PPC_SPE), +GEN_SPE(speundef, evand, 0x08, 0x08, 0xFFFFFFFF, 0x00000000, PPC_SPE), +GEN_SPE(evandc, speundef, 0x09, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE), +GEN_SPE(evxor, evor, 0x0B, 0x08, 0x00000000, 0x00000000, PPC_SPE), +GEN_SPE(evnor, eveqv, 0x0C, 0x08, 0x00000000, 0x00000000, PPC_SPE), +GEN_SPE(evmwumi, evmwsmi, 0x0C, 0x11, 0x00000000, 0x00000000, PPC_SPE), +GEN_SPE(evmwumia, evmwsmia, 0x1C, 0x11, 0x00000000, 0x00000000, PPC_SPE), +GEN_SPE(evmwumiaa, evmwsmiaa, 0x0C, 0x15, 0x00000000, 0x00000000, PPC_SPE), +GEN_SPE(speundef, evorc, 0x0D, 0x08, 0xFFFFFFFF, 0x00000000, PPC_SPE), +GEN_SPE(evnand, speundef, 0x0F, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE), +GEN_SPE(evsrwu, evsrws, 0x10, 0x08, 0x00000000, 0x00000000, PPC_SPE), +GEN_SPE(evsrwiu, evsrwis, 0x11, 0x08, 0x00000000, 0x00000000, PPC_SPE), +GEN_SPE(evslw, speundef, 0x12, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE), +GEN_SPE(evslwi, speundef, 0x13, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE), +GEN_SPE(evrlw, evsplati, 0x14, 0x08, 0x00000000, 0x0000F800, PPC_SPE), +GEN_SPE(evrlwi, evsplatfi, 0x15, 0x08, 0x00000000, 0x0000F800, PPC_SPE), +GEN_SPE(evmergehi, evmergelo, 0x16, 0x08, 0x00000000, 0x00000000, PPC_SPE), +GEN_SPE(evmergehilo, evmergelohi, 0x17, 0x08, 0x00000000, 0x00000000, PPC_SPE), +GEN_SPE(evcmpgtu, evcmpgts, 0x18, 0x08, 0x00600000, 0x00600000, PPC_SPE), +GEN_SPE(evcmpltu, evcmplts, 0x19, 0x08, 0x00600000, 0x00600000, PPC_SPE), +GEN_SPE(evcmpeq, speundef, 0x1A, 0x08, 0x00600000, 0xFFFFFFFF, PPC_SPE), + +GEN_SPE(evfsadd, evfssub, 0x00, 0x0A, 0x00000000, 0x00000000, PPC_SPE_SINGLE), +GEN_SPE(evfsabs, evfsnabs, 0x02, 0x0A, 0x0000F800, 0x0000F800, PPC_SPE_SINGLE), +GEN_SPE(evfsneg, speundef, 0x03, 0x0A, 0x0000F800, 0xFFFFFFFF, PPC_SPE_SINGLE), +GEN_SPE(evfsmul, evfsdiv, 0x04, 0x0A, 0x00000000, 0x00000000, PPC_SPE_SINGLE), +GEN_SPE(evfscmpgt, evfscmplt, 0x06, 0x0A, 0x00600000, 0x00600000, PPC_SPE_SINGLE), +GEN_SPE(evfscmpeq, speundef, 0x07, 0x0A, 0x00600000, 0xFFFFFFFF, PPC_SPE_SINGLE), +GEN_SPE(evfscfui, evfscfsi, 0x08, 0x0A, 0x00180000, 0x00180000, PPC_SPE_SINGLE), +GEN_SPE(evfscfuf, evfscfsf, 0x09, 0x0A, 0x00180000, 0x00180000, PPC_SPE_SINGLE), +GEN_SPE(evfsctui, evfsctsi, 0x0A, 0x0A, 0x00180000, 0x00180000, PPC_SPE_SINGLE), +GEN_SPE(evfsctuf, evfsctsf, 0x0B, 0x0A, 0x00180000, 0x00180000, PPC_SPE_SINGLE), +GEN_SPE(evfsctuiz, speundef, 0x0C, 0x0A, 0x00180000, 0xFFFFFFFF, PPC_SPE_SINGLE), +GEN_SPE(evfsctsiz, speundef, 0x0D, 0x0A, 0x00180000, 0xFFFFFFFF, PPC_SPE_SINGLE), +GEN_SPE(evfststgt, evfststlt, 0x0E, 0x0A, 0x00600000, 0x00600000, PPC_SPE_SINGLE), +GEN_SPE(evfststeq, speundef, 0x0F, 0x0A, 0x00600000, 0xFFFFFFFF, PPC_SPE_SINGLE), + +GEN_SPE(efsadd, efssub, 0x00, 0x0B, 0x00000000, 0x00000000, PPC_SPE_SINGLE), +GEN_SPE(efsabs, efsnabs, 0x02, 0x0B, 0x0000F800, 0x0000F800, PPC_SPE_SINGLE), +GEN_SPE(efsneg, speundef, 0x03, 0x0B, 0x0000F800, 0xFFFFFFFF, PPC_SPE_SINGLE), +GEN_SPE(efsmul, efsdiv, 0x04, 0x0B, 0x00000000, 0x00000000, PPC_SPE_SINGLE), +GEN_SPE(efscmpgt, efscmplt, 0x06, 0x0B, 0x00600000, 0x00600000, PPC_SPE_SINGLE), +GEN_SPE(efscmpeq, efscfd, 0x07, 0x0B, 0x00600000, 0x00180000, PPC_SPE_SINGLE), +GEN_SPE(efscfui, efscfsi, 0x08, 0x0B, 0x00180000, 0x00180000, PPC_SPE_SINGLE), +GEN_SPE(efscfuf, efscfsf, 0x09, 0x0B, 0x00180000, 0x00180000, PPC_SPE_SINGLE), +GEN_SPE(efsctui, efsctsi, 0x0A, 0x0B, 0x00180000, 0x00180000, PPC_SPE_SINGLE), +GEN_SPE(efsctuf, efsctsf, 0x0B, 0x0B, 0x00180000, 0x00180000, PPC_SPE_SINGLE), +GEN_SPE(efsctuiz, speundef, 0x0C, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_SINGLE), +GEN_SPE(efsctsiz, speundef, 0x0D, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_SINGLE), +GEN_SPE(efststgt, efststlt, 0x0E, 0x0B, 0x00600000, 0x00600000, PPC_SPE_SINGLE), +GEN_SPE(efststeq, speundef, 0x0F, 0x0B, 0x00600000, 0xFFFFFFFF, PPC_SPE_SINGLE), + +GEN_SPE(efdadd, efdsub, 0x10, 0x0B, 0x00000000, 0x00000000, PPC_SPE_DOUBLE), +GEN_SPE(efdcfuid, efdcfsid, 0x11, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE), +GEN_SPE(efdabs, efdnabs, 0x12, 0x0B, 0x0000F800, 0x0000F800, PPC_SPE_DOUBLE), +GEN_SPE(efdneg, speundef, 0x13, 0x0B, 0x0000F800, 0xFFFFFFFF, PPC_SPE_DOUBLE), +GEN_SPE(efdmul, efddiv, 0x14, 0x0B, 0x00000000, 0x00000000, PPC_SPE_DOUBLE), +GEN_SPE(efdctuidz, efdctsidz, 0x15, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE), +GEN_SPE(efdcmpgt, efdcmplt, 0x16, 0x0B, 0x00600000, 0x00600000, PPC_SPE_DOUBLE), +GEN_SPE(efdcmpeq, efdcfs, 0x17, 0x0B, 0x00600000, 0x00180000, PPC_SPE_DOUBLE), +GEN_SPE(efdcfui, efdcfsi, 0x18, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE), +GEN_SPE(efdcfuf, efdcfsf, 0x19, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE), +GEN_SPE(efdctui, efdctsi, 0x1A, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE), +GEN_SPE(efdctuf, efdctsf, 0x1B, 0x0B, 0x00180000, 0x00180000, PPC_SPE_DOUBLE), +GEN_SPE(efdctuiz, speundef, 0x1C, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_DOUBLE), +GEN_SPE(efdctsiz, speundef, 0x1D, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_DOUBLE), +GEN_SPE(efdtstgt, efdtstlt, 0x1E, 0x0B, 0x00600000, 0x00600000, PPC_SPE_DOUBLE), +GEN_SPE(efdtsteq, speundef, 0x1F, 0x0B, 0x00600000, 0xFFFFFFFF, PPC_SPE_DOUBLE), + +#define GEN_SPEOP_LDST(name, opc2, sh) \ +GEN_HANDLER(name, 0x04, opc2, 0x0C, 0x00000000, PPC_SPE) +GEN_SPEOP_LDST(evldd, 0x00, 3), +GEN_SPEOP_LDST(evldw, 0x01, 3), +GEN_SPEOP_LDST(evldh, 0x02, 3), +GEN_SPEOP_LDST(evlhhesplat, 0x04, 1), +GEN_SPEOP_LDST(evlhhousplat, 0x06, 1), +GEN_SPEOP_LDST(evlhhossplat, 0x07, 1), +GEN_SPEOP_LDST(evlwhe, 0x08, 2), +GEN_SPEOP_LDST(evlwhou, 0x0A, 2), +GEN_SPEOP_LDST(evlwhos, 0x0B, 2), +GEN_SPEOP_LDST(evlwwsplat, 0x0C, 2), +GEN_SPEOP_LDST(evlwhsplat, 0x0E, 2), + +GEN_SPEOP_LDST(evstdd, 0x10, 3), +GEN_SPEOP_LDST(evstdw, 0x11, 3), +GEN_SPEOP_LDST(evstdh, 0x12, 3), +GEN_SPEOP_LDST(evstwhe, 0x18, 2), +GEN_SPEOP_LDST(evstwho, 0x1A, 2), +GEN_SPEOP_LDST(evstwwe, 0x1C, 2), +GEN_SPEOP_LDST(evstwwo, 0x1E, 2), diff --git a/target/ppc/translate/vmx-impl.inc.c b/target/ppc/translate/vmx-impl.inc.c new file mode 100644 index 0000000000..7143eb3a39 --- /dev/null +++ b/target/ppc/translate/vmx-impl.inc.c @@ -0,0 +1,1113 @@ +/* + * translate/vmx-impl.c + * + * Altivec/VMX translation + */ + +/*** Altivec vector extension ***/ +/* Altivec registers moves */ + +static inline TCGv_ptr gen_avr_ptr(int reg) +{ + TCGv_ptr r = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(r, cpu_env, offsetof(CPUPPCState, avr[reg])); + return r; +} + +#define GEN_VR_LDX(name, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + tcg_gen_andi_tl(EA, EA, ~0xf); \ + /* We only need to swap high and low halves. gen_qemu_ld64_i64 does \ + necessary 64-bit byteswap already. */ \ + if (ctx->le_mode) { \ + gen_qemu_ld64_i64(ctx, cpu_avrl[rD(ctx->opcode)], EA); \ + tcg_gen_addi_tl(EA, EA, 8); \ + gen_qemu_ld64_i64(ctx, cpu_avrh[rD(ctx->opcode)], EA); \ + } else { \ + gen_qemu_ld64_i64(ctx, cpu_avrh[rD(ctx->opcode)], EA); \ + tcg_gen_addi_tl(EA, EA, 8); \ + gen_qemu_ld64_i64(ctx, cpu_avrl[rD(ctx->opcode)], EA); \ + } \ + tcg_temp_free(EA); \ +} + +#define GEN_VR_STX(name, opc2, opc3) \ +static void gen_st##name(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + tcg_gen_andi_tl(EA, EA, ~0xf); \ + /* We only need to swap high and low halves. gen_qemu_st64_i64 does \ + necessary 64-bit byteswap already. */ \ + if (ctx->le_mode) { \ + gen_qemu_st64_i64(ctx, cpu_avrl[rD(ctx->opcode)], EA); \ + tcg_gen_addi_tl(EA, EA, 8); \ + gen_qemu_st64_i64(ctx, cpu_avrh[rD(ctx->opcode)], EA); \ + } else { \ + gen_qemu_st64_i64(ctx, cpu_avrh[rD(ctx->opcode)], EA); \ + tcg_gen_addi_tl(EA, EA, 8); \ + gen_qemu_st64_i64(ctx, cpu_avrl[rD(ctx->opcode)], EA); \ + } \ + tcg_temp_free(EA); \ +} + +#define GEN_VR_LVE(name, opc2, opc3, size) \ +static void gen_lve##name(DisasContext *ctx) \ + { \ + TCGv EA; \ + TCGv_ptr rs; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + if (size > 1) { \ + tcg_gen_andi_tl(EA, EA, ~(size - 1)); \ + } \ + rs = gen_avr_ptr(rS(ctx->opcode)); \ + gen_helper_lve##name(cpu_env, rs, EA); \ + tcg_temp_free(EA); \ + tcg_temp_free_ptr(rs); \ + } + +#define GEN_VR_STVE(name, opc2, opc3, size) \ +static void gen_stve##name(DisasContext *ctx) \ + { \ + TCGv EA; \ + TCGv_ptr rs; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + if (size > 1) { \ + tcg_gen_andi_tl(EA, EA, ~(size - 1)); \ + } \ + rs = gen_avr_ptr(rS(ctx->opcode)); \ + gen_helper_stve##name(cpu_env, rs, EA); \ + tcg_temp_free(EA); \ + tcg_temp_free_ptr(rs); \ + } + +GEN_VR_LDX(lvx, 0x07, 0x03); +/* As we don't emulate the cache, lvxl is stricly equivalent to lvx */ +GEN_VR_LDX(lvxl, 0x07, 0x0B); + +GEN_VR_LVE(bx, 0x07, 0x00, 1); +GEN_VR_LVE(hx, 0x07, 0x01, 2); +GEN_VR_LVE(wx, 0x07, 0x02, 4); + +GEN_VR_STX(svx, 0x07, 0x07); +/* As we don't emulate the cache, stvxl is stricly equivalent to stvx */ +GEN_VR_STX(svxl, 0x07, 0x0F); + +GEN_VR_STVE(bx, 0x07, 0x04, 1); +GEN_VR_STVE(hx, 0x07, 0x05, 2); +GEN_VR_STVE(wx, 0x07, 0x06, 4); + +static void gen_lvsl(DisasContext *ctx) +{ + TCGv_ptr rd; + TCGv EA; + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + EA = tcg_temp_new(); + gen_addr_reg_index(ctx, EA); + rd = gen_avr_ptr(rD(ctx->opcode)); + gen_helper_lvsl(rd, EA); + tcg_temp_free(EA); + tcg_temp_free_ptr(rd); +} + +static void gen_lvsr(DisasContext *ctx) +{ + TCGv_ptr rd; + TCGv EA; + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + EA = tcg_temp_new(); + gen_addr_reg_index(ctx, EA); + rd = gen_avr_ptr(rD(ctx->opcode)); + gen_helper_lvsr(rd, EA); + tcg_temp_free(EA); + tcg_temp_free_ptr(rd); +} + +static void gen_mfvscr(DisasContext *ctx) +{ + TCGv_i32 t; + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + tcg_gen_movi_i64(cpu_avrh[rD(ctx->opcode)], 0); + t = tcg_temp_new_i32(); + tcg_gen_ld_i32(t, cpu_env, offsetof(CPUPPCState, vscr)); + tcg_gen_extu_i32_i64(cpu_avrl[rD(ctx->opcode)], t); + tcg_temp_free_i32(t); +} + +static void gen_mtvscr(DisasContext *ctx) +{ + TCGv_ptr p; + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + p = gen_avr_ptr(rB(ctx->opcode)); + gen_helper_mtvscr(cpu_env, p); + tcg_temp_free_ptr(p); +} + +#define GEN_VX_VMUL10(name, add_cin, ret_carry) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv_i64 t0 = tcg_temp_new_i64(); \ + TCGv_i64 t1 = tcg_temp_new_i64(); \ + TCGv_i64 t2 = tcg_temp_new_i64(); \ + TCGv_i64 ten, z; \ + \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + \ + ten = tcg_const_i64(10); \ + z = tcg_const_i64(0); \ + \ + if (add_cin) { \ + tcg_gen_mulu2_i64(t0, t1, cpu_avrl[rA(ctx->opcode)], ten); \ + tcg_gen_andi_i64(t2, cpu_avrl[rB(ctx->opcode)], 0xF); \ + tcg_gen_add2_i64(cpu_avrl[rD(ctx->opcode)], t2, t0, t1, t2, z); \ + } else { \ + tcg_gen_mulu2_i64(cpu_avrl[rD(ctx->opcode)], t2, \ + cpu_avrl[rA(ctx->opcode)], ten); \ + } \ + \ + if (ret_carry) { \ + tcg_gen_mulu2_i64(t0, t1, cpu_avrh[rA(ctx->opcode)], ten); \ + tcg_gen_add2_i64(t0, cpu_avrl[rD(ctx->opcode)], t0, t1, t2, z); \ + tcg_gen_movi_i64(cpu_avrh[rD(ctx->opcode)], 0); \ + } else { \ + tcg_gen_mul_i64(t0, cpu_avrh[rA(ctx->opcode)], ten); \ + tcg_gen_add_i64(cpu_avrh[rD(ctx->opcode)], t0, t2); \ + } \ + \ + tcg_temp_free_i64(t0); \ + tcg_temp_free_i64(t1); \ + tcg_temp_free_i64(t2); \ + tcg_temp_free_i64(ten); \ + tcg_temp_free_i64(z); \ +} \ + +GEN_VX_VMUL10(vmul10uq, 0, 0); +GEN_VX_VMUL10(vmul10euq, 1, 0); +GEN_VX_VMUL10(vmul10cuq, 0, 1); +GEN_VX_VMUL10(vmul10ecuq, 1, 1); + +/* Logical operations */ +#define GEN_VX_LOGICAL(name, tcg_op, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + tcg_op(cpu_avrh[rD(ctx->opcode)], cpu_avrh[rA(ctx->opcode)], cpu_avrh[rB(ctx->opcode)]); \ + tcg_op(cpu_avrl[rD(ctx->opcode)], cpu_avrl[rA(ctx->opcode)], cpu_avrl[rB(ctx->opcode)]); \ +} + +GEN_VX_LOGICAL(vand, tcg_gen_and_i64, 2, 16); +GEN_VX_LOGICAL(vandc, tcg_gen_andc_i64, 2, 17); +GEN_VX_LOGICAL(vor, tcg_gen_or_i64, 2, 18); +GEN_VX_LOGICAL(vxor, tcg_gen_xor_i64, 2, 19); +GEN_VX_LOGICAL(vnor, tcg_gen_nor_i64, 2, 20); +GEN_VX_LOGICAL(veqv, tcg_gen_eqv_i64, 2, 26); +GEN_VX_LOGICAL(vnand, tcg_gen_nand_i64, 2, 22); +GEN_VX_LOGICAL(vorc, tcg_gen_orc_i64, 2, 21); + +#define GEN_VXFORM(name, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv_ptr ra, rb, rd; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + ra = gen_avr_ptr(rA(ctx->opcode)); \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + gen_helper_##name (rd, ra, rb); \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rd); \ +} + +#define GEN_VXFORM_ENV(name, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv_ptr ra, rb, rd; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + ra = gen_avr_ptr(rA(ctx->opcode)); \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + gen_helper_##name(cpu_env, rd, ra, rb); \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rd); \ +} + +#define GEN_VXFORM3(name, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv_ptr ra, rb, rc, rd; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + ra = gen_avr_ptr(rA(ctx->opcode)); \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rc = gen_avr_ptr(rC(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + gen_helper_##name(rd, ra, rb, rc); \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rc); \ + tcg_temp_free_ptr(rd); \ +} + +/* + * Support for Altivec instruction pairs that use bit 31 (Rc) as + * an opcode bit. In general, these pairs come from different + * versions of the ISA, so we must also support a pair of flags for + * each instruction. + */ +#define GEN_VXFORM_DUAL(name0, flg0, flg2_0, name1, flg1, flg2_1) \ +static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ +{ \ + if ((Rc(ctx->opcode) == 0) && \ + ((ctx->insns_flags & flg0) || (ctx->insns_flags2 & flg2_0))) { \ + gen_##name0(ctx); \ + } else if ((Rc(ctx->opcode) == 1) && \ + ((ctx->insns_flags & flg1) || (ctx->insns_flags2 & flg2_1))) { \ + gen_##name1(ctx); \ + } else { \ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ + } \ +} + +/* Adds support to provide invalid mask */ +#define GEN_VXFORM_DUAL_EXT(name0, flg0, flg2_0, inval0, \ + name1, flg1, flg2_1, inval1) \ +static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ +{ \ + if ((Rc(ctx->opcode) == 0) && \ + ((ctx->insns_flags & flg0) || (ctx->insns_flags2 & flg2_0)) && \ + !(ctx->opcode & inval0)) { \ + gen_##name0(ctx); \ + } else if ((Rc(ctx->opcode) == 1) && \ + ((ctx->insns_flags & flg1) || (ctx->insns_flags2 & flg2_1)) && \ + !(ctx->opcode & inval1)) { \ + gen_##name1(ctx); \ + } else { \ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ + } \ +} + +GEN_VXFORM(vaddubm, 0, 0); +GEN_VXFORM_DUAL_EXT(vaddubm, PPC_ALTIVEC, PPC_NONE, 0, \ + vmul10cuq, PPC_NONE, PPC2_ISA300, 0x0000F800) +GEN_VXFORM(vadduhm, 0, 1); +GEN_VXFORM_DUAL(vadduhm, PPC_ALTIVEC, PPC_NONE, \ + vmul10ecuq, PPC_NONE, PPC2_ISA300) +GEN_VXFORM(vadduwm, 0, 2); +GEN_VXFORM(vaddudm, 0, 3); +GEN_VXFORM(vsububm, 0, 16); +GEN_VXFORM(vsubuhm, 0, 17); +GEN_VXFORM(vsubuwm, 0, 18); +GEN_VXFORM(vsubudm, 0, 19); +GEN_VXFORM(vmaxub, 1, 0); +GEN_VXFORM(vmaxuh, 1, 1); +GEN_VXFORM(vmaxuw, 1, 2); +GEN_VXFORM(vmaxud, 1, 3); +GEN_VXFORM(vmaxsb, 1, 4); +GEN_VXFORM(vmaxsh, 1, 5); +GEN_VXFORM(vmaxsw, 1, 6); +GEN_VXFORM(vmaxsd, 1, 7); +GEN_VXFORM(vminub, 1, 8); +GEN_VXFORM(vminuh, 1, 9); +GEN_VXFORM(vminuw, 1, 10); +GEN_VXFORM(vminud, 1, 11); +GEN_VXFORM(vminsb, 1, 12); +GEN_VXFORM(vminsh, 1, 13); +GEN_VXFORM(vminsw, 1, 14); +GEN_VXFORM(vminsd, 1, 15); +GEN_VXFORM(vavgub, 1, 16); +GEN_VXFORM(vabsdub, 1, 16); +GEN_VXFORM_DUAL(vavgub, PPC_ALTIVEC, PPC_NONE, \ + vabsdub, PPC_NONE, PPC2_ISA300) +GEN_VXFORM(vavguh, 1, 17); +GEN_VXFORM(vabsduh, 1, 17); +GEN_VXFORM_DUAL(vavguh, PPC_ALTIVEC, PPC_NONE, \ + vabsduh, PPC_NONE, PPC2_ISA300) +GEN_VXFORM(vavguw, 1, 18); +GEN_VXFORM(vabsduw, 1, 18); +GEN_VXFORM_DUAL(vavguw, PPC_ALTIVEC, PPC_NONE, \ + vabsduw, PPC_NONE, PPC2_ISA300) +GEN_VXFORM(vavgsb, 1, 20); +GEN_VXFORM(vavgsh, 1, 21); +GEN_VXFORM(vavgsw, 1, 22); +GEN_VXFORM(vmrghb, 6, 0); +GEN_VXFORM(vmrghh, 6, 1); +GEN_VXFORM(vmrghw, 6, 2); +GEN_VXFORM(vmrglb, 6, 4); +GEN_VXFORM(vmrglh, 6, 5); +GEN_VXFORM(vmrglw, 6, 6); + +static void gen_vmrgew(DisasContext *ctx) +{ + TCGv_i64 tmp; + int VT, VA, VB; + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + VT = rD(ctx->opcode); + VA = rA(ctx->opcode); + VB = rB(ctx->opcode); + tmp = tcg_temp_new_i64(); + tcg_gen_shri_i64(tmp, cpu_avrh[VB], 32); + tcg_gen_deposit_i64(cpu_avrh[VT], cpu_avrh[VA], tmp, 0, 32); + tcg_gen_shri_i64(tmp, cpu_avrl[VB], 32); + tcg_gen_deposit_i64(cpu_avrl[VT], cpu_avrl[VA], tmp, 0, 32); + tcg_temp_free_i64(tmp); +} + +static void gen_vmrgow(DisasContext *ctx) +{ + int VT, VA, VB; + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + VT = rD(ctx->opcode); + VA = rA(ctx->opcode); + VB = rB(ctx->opcode); + + tcg_gen_deposit_i64(cpu_avrh[VT], cpu_avrh[VB], cpu_avrh[VA], 32, 32); + tcg_gen_deposit_i64(cpu_avrl[VT], cpu_avrl[VB], cpu_avrl[VA], 32, 32); +} + +GEN_VXFORM(vmuloub, 4, 0); +GEN_VXFORM(vmulouh, 4, 1); +GEN_VXFORM(vmulouw, 4, 2); +GEN_VXFORM(vmuluwm, 4, 2); +GEN_VXFORM_DUAL(vmulouw, PPC_ALTIVEC, PPC_NONE, + vmuluwm, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM(vmulosb, 4, 4); +GEN_VXFORM(vmulosh, 4, 5); +GEN_VXFORM(vmulosw, 4, 6); +GEN_VXFORM(vmuleub, 4, 8); +GEN_VXFORM(vmuleuh, 4, 9); +GEN_VXFORM(vmuleuw, 4, 10); +GEN_VXFORM(vmulesb, 4, 12); +GEN_VXFORM(vmulesh, 4, 13); +GEN_VXFORM(vmulesw, 4, 14); +GEN_VXFORM(vslb, 2, 4); +GEN_VXFORM(vslh, 2, 5); +GEN_VXFORM(vslw, 2, 6); +GEN_VXFORM(vrlwnm, 2, 6); +GEN_VXFORM_DUAL(vslw, PPC_ALTIVEC, PPC_NONE, \ + vrlwnm, PPC_NONE, PPC2_ISA300) +GEN_VXFORM(vsld, 2, 23); +GEN_VXFORM(vsrb, 2, 8); +GEN_VXFORM(vsrh, 2, 9); +GEN_VXFORM(vsrw, 2, 10); +GEN_VXFORM(vsrd, 2, 27); +GEN_VXFORM(vsrab, 2, 12); +GEN_VXFORM(vsrah, 2, 13); +GEN_VXFORM(vsraw, 2, 14); +GEN_VXFORM(vsrad, 2, 15); +GEN_VXFORM(vsrv, 2, 28); +GEN_VXFORM(vslv, 2, 29); +GEN_VXFORM(vslo, 6, 16); +GEN_VXFORM(vsro, 6, 17); +GEN_VXFORM(vaddcuw, 0, 6); +GEN_VXFORM(vsubcuw, 0, 22); +GEN_VXFORM_ENV(vaddubs, 0, 8); +GEN_VXFORM_DUAL_EXT(vaddubs, PPC_ALTIVEC, PPC_NONE, 0, \ + vmul10uq, PPC_NONE, PPC2_ISA300, 0x0000F800) +GEN_VXFORM_ENV(vadduhs, 0, 9); +GEN_VXFORM_DUAL(vadduhs, PPC_ALTIVEC, PPC_NONE, \ + vmul10euq, PPC_NONE, PPC2_ISA300) +GEN_VXFORM_ENV(vadduws, 0, 10); +GEN_VXFORM_ENV(vaddsbs, 0, 12); +GEN_VXFORM_ENV(vaddshs, 0, 13); +GEN_VXFORM_ENV(vaddsws, 0, 14); +GEN_VXFORM_ENV(vsububs, 0, 24); +GEN_VXFORM_ENV(vsubuhs, 0, 25); +GEN_VXFORM_ENV(vsubuws, 0, 26); +GEN_VXFORM_ENV(vsubsbs, 0, 28); +GEN_VXFORM_ENV(vsubshs, 0, 29); +GEN_VXFORM_ENV(vsubsws, 0, 30); +GEN_VXFORM(vadduqm, 0, 4); +GEN_VXFORM(vaddcuq, 0, 5); +GEN_VXFORM3(vaddeuqm, 30, 0); +GEN_VXFORM3(vaddecuq, 30, 0); +GEN_VXFORM_DUAL(vaddeuqm, PPC_NONE, PPC2_ALTIVEC_207, \ + vaddecuq, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM(vsubuqm, 0, 20); +GEN_VXFORM(vsubcuq, 0, 21); +GEN_VXFORM3(vsubeuqm, 31, 0); +GEN_VXFORM3(vsubecuq, 31, 0); +GEN_VXFORM_DUAL(vsubeuqm, PPC_NONE, PPC2_ALTIVEC_207, \ + vsubecuq, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM(vrlb, 2, 0); +GEN_VXFORM(vrlh, 2, 1); +GEN_VXFORM(vrlw, 2, 2); +GEN_VXFORM(vrlwmi, 2, 2); +GEN_VXFORM_DUAL(vrlw, PPC_ALTIVEC, PPC_NONE, \ + vrlwmi, PPC_NONE, PPC2_ISA300) +GEN_VXFORM(vrld, 2, 3); +GEN_VXFORM(vrldmi, 2, 3); +GEN_VXFORM_DUAL(vrld, PPC_NONE, PPC2_ALTIVEC_207, \ + vrldmi, PPC_NONE, PPC2_ISA300) +GEN_VXFORM(vsl, 2, 7); +GEN_VXFORM(vrldnm, 2, 7); +GEN_VXFORM_DUAL(vsl, PPC_ALTIVEC, PPC_NONE, \ + vrldnm, PPC_NONE, PPC2_ISA300) +GEN_VXFORM(vsr, 2, 11); +GEN_VXFORM_ENV(vpkuhum, 7, 0); +GEN_VXFORM_ENV(vpkuwum, 7, 1); +GEN_VXFORM_ENV(vpkudum, 7, 17); +GEN_VXFORM_ENV(vpkuhus, 7, 2); +GEN_VXFORM_ENV(vpkuwus, 7, 3); +GEN_VXFORM_ENV(vpkudus, 7, 19); +GEN_VXFORM_ENV(vpkshus, 7, 4); +GEN_VXFORM_ENV(vpkswus, 7, 5); +GEN_VXFORM_ENV(vpksdus, 7, 21); +GEN_VXFORM_ENV(vpkshss, 7, 6); +GEN_VXFORM_ENV(vpkswss, 7, 7); +GEN_VXFORM_ENV(vpksdss, 7, 23); +GEN_VXFORM(vpkpx, 7, 12); +GEN_VXFORM_ENV(vsum4ubs, 4, 24); +GEN_VXFORM_ENV(vsum4sbs, 4, 28); +GEN_VXFORM_ENV(vsum4shs, 4, 25); +GEN_VXFORM_ENV(vsum2sws, 4, 26); +GEN_VXFORM_ENV(vsumsws, 4, 30); +GEN_VXFORM_ENV(vaddfp, 5, 0); +GEN_VXFORM_ENV(vsubfp, 5, 1); +GEN_VXFORM_ENV(vmaxfp, 5, 16); +GEN_VXFORM_ENV(vminfp, 5, 17); + +#define GEN_VXRFORM1(opname, name, str, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ + { \ + TCGv_ptr ra, rb, rd; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + ra = gen_avr_ptr(rA(ctx->opcode)); \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + gen_helper_##opname(cpu_env, rd, ra, rb); \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rd); \ + } + +#define GEN_VXRFORM(name, opc2, opc3) \ + GEN_VXRFORM1(name, name, #name, opc2, opc3) \ + GEN_VXRFORM1(name##_dot, name##_, #name ".", opc2, (opc3 | (0x1 << 4))) + +/* + * Support for Altivec instructions that use bit 31 (Rc) as an opcode + * bit but also use bit 21 as an actual Rc bit. In general, thse pairs + * come from different versions of the ISA, so we must also support a + * pair of flags for each instruction. + */ +#define GEN_VXRFORM_DUAL(name0, flg0, flg2_0, name1, flg1, flg2_1) \ +static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ +{ \ + if ((Rc(ctx->opcode) == 0) && \ + ((ctx->insns_flags & flg0) || (ctx->insns_flags2 & flg2_0))) { \ + if (Rc21(ctx->opcode) == 0) { \ + gen_##name0(ctx); \ + } else { \ + gen_##name0##_(ctx); \ + } \ + } else if ((Rc(ctx->opcode) == 1) && \ + ((ctx->insns_flags & flg1) || (ctx->insns_flags2 & flg2_1))) { \ + if (Rc21(ctx->opcode) == 0) { \ + gen_##name1(ctx); \ + } else { \ + gen_##name1##_(ctx); \ + } \ + } else { \ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ + } \ +} + +GEN_VXRFORM(vcmpequb, 3, 0) +GEN_VXRFORM(vcmpequh, 3, 1) +GEN_VXRFORM(vcmpequw, 3, 2) +GEN_VXRFORM(vcmpequd, 3, 3) +GEN_VXRFORM(vcmpnezb, 3, 4) +GEN_VXRFORM(vcmpnezh, 3, 5) +GEN_VXRFORM(vcmpnezw, 3, 6) +GEN_VXRFORM(vcmpgtsb, 3, 12) +GEN_VXRFORM(vcmpgtsh, 3, 13) +GEN_VXRFORM(vcmpgtsw, 3, 14) +GEN_VXRFORM(vcmpgtsd, 3, 15) +GEN_VXRFORM(vcmpgtub, 3, 8) +GEN_VXRFORM(vcmpgtuh, 3, 9) +GEN_VXRFORM(vcmpgtuw, 3, 10) +GEN_VXRFORM(vcmpgtud, 3, 11) +GEN_VXRFORM(vcmpeqfp, 3, 3) +GEN_VXRFORM(vcmpgefp, 3, 7) +GEN_VXRFORM(vcmpgtfp, 3, 11) +GEN_VXRFORM(vcmpbfp, 3, 15) +GEN_VXRFORM(vcmpneb, 3, 0) +GEN_VXRFORM(vcmpneh, 3, 1) +GEN_VXRFORM(vcmpnew, 3, 2) + +GEN_VXRFORM_DUAL(vcmpequb, PPC_ALTIVEC, PPC_NONE, \ + vcmpneb, PPC_NONE, PPC2_ISA300) +GEN_VXRFORM_DUAL(vcmpequh, PPC_ALTIVEC, PPC_NONE, \ + vcmpneh, PPC_NONE, PPC2_ISA300) +GEN_VXRFORM_DUAL(vcmpequw, PPC_ALTIVEC, PPC_NONE, \ + vcmpnew, PPC_NONE, PPC2_ISA300) +GEN_VXRFORM_DUAL(vcmpeqfp, PPC_ALTIVEC, PPC_NONE, \ + vcmpequd, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXRFORM_DUAL(vcmpbfp, PPC_ALTIVEC, PPC_NONE, \ + vcmpgtsd, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXRFORM_DUAL(vcmpgtfp, PPC_ALTIVEC, PPC_NONE, \ + vcmpgtud, PPC_NONE, PPC2_ALTIVEC_207) + +#define GEN_VXFORM_SIMM(name, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ + { \ + TCGv_ptr rd; \ + TCGv_i32 simm; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + simm = tcg_const_i32(SIMM5(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + gen_helper_##name (rd, simm); \ + tcg_temp_free_i32(simm); \ + tcg_temp_free_ptr(rd); \ + } + +GEN_VXFORM_SIMM(vspltisb, 6, 12); +GEN_VXFORM_SIMM(vspltish, 6, 13); +GEN_VXFORM_SIMM(vspltisw, 6, 14); + +#define GEN_VXFORM_NOA(name, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ + { \ + TCGv_ptr rb, rd; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + gen_helper_##name (rd, rb); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rd); \ + } + +#define GEN_VXFORM_NOA_ENV(name, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ + { \ + TCGv_ptr rb, rd; \ + \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + gen_helper_##name(cpu_env, rd, rb); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rd); \ + } + +#define GEN_VXFORM_NOA_2(name, opc2, opc3, opc4) \ +static void glue(gen_, name)(DisasContext *ctx) \ + { \ + TCGv_ptr rb, rd; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + gen_helper_##name(rd, rb); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rd); \ + } + +#define GEN_VXFORM_NOA_3(name, opc2, opc3, opc4) \ +static void glue(gen_, name)(DisasContext *ctx) \ + { \ + TCGv_ptr rb; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + gen_helper_##name(cpu_gpr[rD(ctx->opcode)], rb); \ + tcg_temp_free_ptr(rb); \ + } +GEN_VXFORM_NOA(vupkhsb, 7, 8); +GEN_VXFORM_NOA(vupkhsh, 7, 9); +GEN_VXFORM_NOA(vupkhsw, 7, 25); +GEN_VXFORM_NOA(vupklsb, 7, 10); +GEN_VXFORM_NOA(vupklsh, 7, 11); +GEN_VXFORM_NOA(vupklsw, 7, 27); +GEN_VXFORM_NOA(vupkhpx, 7, 13); +GEN_VXFORM_NOA(vupklpx, 7, 15); +GEN_VXFORM_NOA_ENV(vrefp, 5, 4); +GEN_VXFORM_NOA_ENV(vrsqrtefp, 5, 5); +GEN_VXFORM_NOA_ENV(vexptefp, 5, 6); +GEN_VXFORM_NOA_ENV(vlogefp, 5, 7); +GEN_VXFORM_NOA_ENV(vrfim, 5, 11); +GEN_VXFORM_NOA_ENV(vrfin, 5, 8); +GEN_VXFORM_NOA_ENV(vrfip, 5, 10); +GEN_VXFORM_NOA_ENV(vrfiz, 5, 9); +GEN_VXFORM_NOA(vprtybw, 1, 24); +GEN_VXFORM_NOA(vprtybd, 1, 24); +GEN_VXFORM_NOA(vprtybq, 1, 24); + +#define GEN_VXFORM_SIMM(name, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ + { \ + TCGv_ptr rd; \ + TCGv_i32 simm; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + simm = tcg_const_i32(SIMM5(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + gen_helper_##name (rd, simm); \ + tcg_temp_free_i32(simm); \ + tcg_temp_free_ptr(rd); \ + } + +#define GEN_VXFORM_UIMM(name, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ + { \ + TCGv_ptr rb, rd; \ + TCGv_i32 uimm; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + uimm = tcg_const_i32(UIMM5(ctx->opcode)); \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + gen_helper_##name (rd, rb, uimm); \ + tcg_temp_free_i32(uimm); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rd); \ + } + +#define GEN_VXFORM_UIMM_ENV(name, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ + { \ + TCGv_ptr rb, rd; \ + TCGv_i32 uimm; \ + \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + uimm = tcg_const_i32(UIMM5(ctx->opcode)); \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + gen_helper_##name(cpu_env, rd, rb, uimm); \ + tcg_temp_free_i32(uimm); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rd); \ + } + +#define GEN_VXFORM_UIMM_SPLAT(name, opc2, opc3, splat_max) \ +static void glue(gen_, name)(DisasContext *ctx) \ + { \ + TCGv_ptr rb, rd; \ + uint8_t uimm = UIMM4(ctx->opcode); \ + TCGv_i32 t0 = tcg_temp_new_i32(); \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + if (uimm > splat_max) { \ + uimm = 0; \ + } \ + tcg_gen_movi_i32(t0, uimm); \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + gen_helper_##name(rd, rb, t0); \ + tcg_temp_free_i32(t0); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rd); \ + } + +GEN_VXFORM_UIMM(vspltb, 6, 8); +GEN_VXFORM_UIMM(vsplth, 6, 9); +GEN_VXFORM_UIMM(vspltw, 6, 10); +GEN_VXFORM_UIMM_SPLAT(vextractub, 6, 8, 15); +GEN_VXFORM_UIMM_SPLAT(vextractuh, 6, 9, 14); +GEN_VXFORM_UIMM_SPLAT(vextractuw, 6, 10, 12); +GEN_VXFORM_UIMM_SPLAT(vextractd, 6, 11, 8); +GEN_VXFORM_UIMM_SPLAT(vinsertb, 6, 12, 15); +GEN_VXFORM_UIMM_SPLAT(vinserth, 6, 13, 14); +GEN_VXFORM_UIMM_SPLAT(vinsertw, 6, 14, 12); +GEN_VXFORM_UIMM_SPLAT(vinsertd, 6, 15, 8); +GEN_VXFORM_UIMM_ENV(vcfux, 5, 12); +GEN_VXFORM_UIMM_ENV(vcfsx, 5, 13); +GEN_VXFORM_UIMM_ENV(vctuxs, 5, 14); +GEN_VXFORM_UIMM_ENV(vctsxs, 5, 15); +GEN_VXFORM_DUAL(vspltb, PPC_ALTIVEC, PPC_NONE, + vextractub, PPC_NONE, PPC2_ISA300); +GEN_VXFORM_DUAL(vsplth, PPC_ALTIVEC, PPC_NONE, + vextractuh, PPC_NONE, PPC2_ISA300); +GEN_VXFORM_DUAL(vspltw, PPC_ALTIVEC, PPC_NONE, + vextractuw, PPC_NONE, PPC2_ISA300); +GEN_VXFORM_DUAL(vspltisb, PPC_ALTIVEC, PPC_NONE, + vinsertb, PPC_NONE, PPC2_ISA300); +GEN_VXFORM_DUAL(vspltish, PPC_ALTIVEC, PPC_NONE, + vinserth, PPC_NONE, PPC2_ISA300); +GEN_VXFORM_DUAL(vspltisw, PPC_ALTIVEC, PPC_NONE, + vinsertw, PPC_NONE, PPC2_ISA300); + +static void gen_vsldoi(DisasContext *ctx) +{ + TCGv_ptr ra, rb, rd; + TCGv_i32 sh; + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + ra = gen_avr_ptr(rA(ctx->opcode)); + rb = gen_avr_ptr(rB(ctx->opcode)); + rd = gen_avr_ptr(rD(ctx->opcode)); + sh = tcg_const_i32(VSH(ctx->opcode)); + gen_helper_vsldoi (rd, ra, rb, sh); + tcg_temp_free_ptr(ra); + tcg_temp_free_ptr(rb); + tcg_temp_free_ptr(rd); + tcg_temp_free_i32(sh); +} + +#define GEN_VAFORM_PAIRED(name0, name1, opc2) \ +static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ + { \ + TCGv_ptr ra, rb, rc, rd; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + ra = gen_avr_ptr(rA(ctx->opcode)); \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rc = gen_avr_ptr(rC(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + if (Rc(ctx->opcode)) { \ + gen_helper_##name1(cpu_env, rd, ra, rb, rc); \ + } else { \ + gen_helper_##name0(cpu_env, rd, ra, rb, rc); \ + } \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rc); \ + tcg_temp_free_ptr(rd); \ + } + +GEN_VAFORM_PAIRED(vmhaddshs, vmhraddshs, 16) + +static void gen_vmladduhm(DisasContext *ctx) +{ + TCGv_ptr ra, rb, rc, rd; + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + ra = gen_avr_ptr(rA(ctx->opcode)); + rb = gen_avr_ptr(rB(ctx->opcode)); + rc = gen_avr_ptr(rC(ctx->opcode)); + rd = gen_avr_ptr(rD(ctx->opcode)); + gen_helper_vmladduhm(rd, ra, rb, rc); + tcg_temp_free_ptr(ra); + tcg_temp_free_ptr(rb); + tcg_temp_free_ptr(rc); + tcg_temp_free_ptr(rd); +} + +static void gen_vpermr(DisasContext *ctx) +{ + TCGv_ptr ra, rb, rc, rd; + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + ra = gen_avr_ptr(rA(ctx->opcode)); + rb = gen_avr_ptr(rB(ctx->opcode)); + rc = gen_avr_ptr(rC(ctx->opcode)); + rd = gen_avr_ptr(rD(ctx->opcode)); + gen_helper_vpermr(cpu_env, rd, ra, rb, rc); + tcg_temp_free_ptr(ra); + tcg_temp_free_ptr(rb); + tcg_temp_free_ptr(rc); + tcg_temp_free_ptr(rd); +} + +GEN_VAFORM_PAIRED(vmsumubm, vmsummbm, 18) +GEN_VAFORM_PAIRED(vmsumuhm, vmsumuhs, 19) +GEN_VAFORM_PAIRED(vmsumshm, vmsumshs, 20) +GEN_VAFORM_PAIRED(vsel, vperm, 21) +GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23) + +GEN_VXFORM_NOA(vclzb, 1, 28) +GEN_VXFORM_NOA(vclzh, 1, 29) +GEN_VXFORM_NOA(vclzw, 1, 30) +GEN_VXFORM_NOA(vclzd, 1, 31) +GEN_VXFORM_NOA_2(vnegw, 1, 24, 6) +GEN_VXFORM_NOA_2(vnegd, 1, 24, 7) +GEN_VXFORM_NOA_2(vextsb2w, 1, 24, 16) +GEN_VXFORM_NOA_2(vextsh2w, 1, 24, 17) +GEN_VXFORM_NOA_2(vextsb2d, 1, 24, 24) +GEN_VXFORM_NOA_2(vextsh2d, 1, 24, 25) +GEN_VXFORM_NOA_2(vextsw2d, 1, 24, 26) +GEN_VXFORM_NOA_2(vctzb, 1, 24, 28) +GEN_VXFORM_NOA_2(vctzh, 1, 24, 29) +GEN_VXFORM_NOA_2(vctzw, 1, 24, 30) +GEN_VXFORM_NOA_2(vctzd, 1, 24, 31) +GEN_VXFORM_NOA_3(vclzlsbb, 1, 24, 0) +GEN_VXFORM_NOA_3(vctzlsbb, 1, 24, 1) +GEN_VXFORM_NOA(vpopcntb, 1, 28) +GEN_VXFORM_NOA(vpopcnth, 1, 29) +GEN_VXFORM_NOA(vpopcntw, 1, 30) +GEN_VXFORM_NOA(vpopcntd, 1, 31) +GEN_VXFORM_DUAL(vclzb, PPC_NONE, PPC2_ALTIVEC_207, \ + vpopcntb, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vclzh, PPC_NONE, PPC2_ALTIVEC_207, \ + vpopcnth, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vclzw, PPC_NONE, PPC2_ALTIVEC_207, \ + vpopcntw, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vclzd, PPC_NONE, PPC2_ALTIVEC_207, \ + vpopcntd, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM(vbpermd, 6, 23); +GEN_VXFORM(vbpermq, 6, 21); +GEN_VXFORM_NOA(vgbbd, 6, 20); +GEN_VXFORM(vpmsumb, 4, 16) +GEN_VXFORM(vpmsumh, 4, 17) +GEN_VXFORM(vpmsumw, 4, 18) +GEN_VXFORM(vpmsumd, 4, 19) + +#define GEN_BCD(op) \ +static void gen_##op(DisasContext *ctx) \ +{ \ + TCGv_ptr ra, rb, rd; \ + TCGv_i32 ps; \ + \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + \ + ra = gen_avr_ptr(rA(ctx->opcode)); \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + \ + ps = tcg_const_i32((ctx->opcode & 0x200) != 0); \ + \ + gen_helper_##op(cpu_crf[6], rd, ra, rb, ps); \ + \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rd); \ + tcg_temp_free_i32(ps); \ +} + +#define GEN_BCD2(op) \ +static void gen_##op(DisasContext *ctx) \ +{ \ + TCGv_ptr rd, rb; \ + TCGv_i32 ps; \ + \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + \ + ps = tcg_const_i32((ctx->opcode & 0x200) != 0); \ + \ + gen_helper_##op(cpu_crf[6], rd, rb, ps); \ + \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rd); \ + tcg_temp_free_i32(ps); \ +} + +GEN_BCD(bcdadd) +GEN_BCD(bcdsub) +GEN_BCD2(bcdcfn) +GEN_BCD2(bcdctn) +GEN_BCD2(bcdcfz) +GEN_BCD2(bcdctz) + +static void gen_xpnd04_1(DisasContext *ctx) +{ + switch (opc4(ctx->opcode)) { + case 4: + gen_bcdctz(ctx); + break; + case 5: + gen_bcdctn(ctx); + break; + case 6: + gen_bcdcfz(ctx); + break; + case 7: + gen_bcdcfn(ctx); + break; + default: + gen_invalid(ctx); + break; + } +} + +static void gen_xpnd04_2(DisasContext *ctx) +{ + switch (opc4(ctx->opcode)) { + case 4: + gen_bcdctz(ctx); + break; + case 6: + gen_bcdcfz(ctx); + break; + case 7: + gen_bcdcfn(ctx); + break; + default: + gen_invalid(ctx); + break; + } +} + +GEN_VXFORM_DUAL(vsubcuw, PPC_ALTIVEC, PPC_NONE, \ + xpnd04_1, PPC_NONE, PPC2_ISA300) +GEN_VXFORM_DUAL(vsubsws, PPC_ALTIVEC, PPC_NONE, \ + xpnd04_2, PPC_NONE, PPC2_ISA300) + +GEN_VXFORM_DUAL(vsububm, PPC_ALTIVEC, PPC_NONE, \ + bcdadd, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vsububs, PPC_ALTIVEC, PPC_NONE, \ + bcdadd, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vsubuhm, PPC_ALTIVEC, PPC_NONE, \ + bcdsub, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vsubuhs, PPC_ALTIVEC, PPC_NONE, \ + bcdsub, PPC_NONE, PPC2_ALTIVEC_207) + +static void gen_vsbox(DisasContext *ctx) +{ + TCGv_ptr ra, rd; + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + ra = gen_avr_ptr(rA(ctx->opcode)); + rd = gen_avr_ptr(rD(ctx->opcode)); + gen_helper_vsbox(rd, ra); + tcg_temp_free_ptr(ra); + tcg_temp_free_ptr(rd); +} + +GEN_VXFORM(vcipher, 4, 20) +GEN_VXFORM(vcipherlast, 4, 20) +GEN_VXFORM(vncipher, 4, 21) +GEN_VXFORM(vncipherlast, 4, 21) + +GEN_VXFORM_DUAL(vcipher, PPC_NONE, PPC2_ALTIVEC_207, + vcipherlast, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vncipher, PPC_NONE, PPC2_ALTIVEC_207, + vncipherlast, PPC_NONE, PPC2_ALTIVEC_207) + +#define VSHASIGMA(op) \ +static void gen_##op(DisasContext *ctx) \ +{ \ + TCGv_ptr ra, rd; \ + TCGv_i32 st_six; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + ra = gen_avr_ptr(rA(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + st_six = tcg_const_i32(rB(ctx->opcode)); \ + gen_helper_##op(rd, ra, st_six); \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rd); \ + tcg_temp_free_i32(st_six); \ +} + +VSHASIGMA(vshasigmaw) +VSHASIGMA(vshasigmad) + +GEN_VXFORM3(vpermxor, 22, 0xFF) +GEN_VXFORM_DUAL(vsldoi, PPC_ALTIVEC, PPC_NONE, + vpermxor, PPC_NONE, PPC2_ALTIVEC_207) + +#undef GEN_VR_LDX +#undef GEN_VR_STX +#undef GEN_VR_LVE +#undef GEN_VR_STVE + +#undef GEN_VX_LOGICAL +#undef GEN_VX_LOGICAL_207 +#undef GEN_VXFORM +#undef GEN_VXFORM_207 +#undef GEN_VXFORM_DUAL +#undef GEN_VXRFORM_DUAL +#undef GEN_VXRFORM1 +#undef GEN_VXRFORM +#undef GEN_VXFORM_SIMM +#undef GEN_VXFORM_NOA +#undef GEN_VXFORM_UIMM +#undef GEN_VAFORM_PAIRED + +#undef GEN_BCD2 diff --git a/target/ppc/translate/vmx-ops.inc.c b/target/ppc/translate/vmx-ops.inc.c new file mode 100644 index 0000000000..f02b3bed50 --- /dev/null +++ b/target/ppc/translate/vmx-ops.inc.c @@ -0,0 +1,294 @@ +#define GEN_VR_LDX(name, opc2, opc3) \ +GEN_HANDLER(name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) +#define GEN_VR_STX(name, opc2, opc3) \ +GEN_HANDLER(st##name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) +#define GEN_VR_LVE(name, opc2, opc3) \ + GEN_HANDLER(lve##name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) +#define GEN_VR_STVE(name, opc2, opc3) \ + GEN_HANDLER(stve##name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) +GEN_VR_LDX(lvx, 0x07, 0x03), +GEN_VR_LDX(lvxl, 0x07, 0x0B), +GEN_VR_LVE(bx, 0x07, 0x00), +GEN_VR_LVE(hx, 0x07, 0x01), +GEN_VR_LVE(wx, 0x07, 0x02), +GEN_VR_STX(svx, 0x07, 0x07), +GEN_VR_STX(svxl, 0x07, 0x0F), +GEN_VR_STVE(bx, 0x07, 0x04), +GEN_VR_STVE(hx, 0x07, 0x05), +GEN_VR_STVE(wx, 0x07, 0x06), + +#define GEN_VX_LOGICAL(name, tcg_op, opc2, opc3) \ +GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC) + +#define GEN_VX_LOGICAL_207(name, tcg_op, opc2, opc3) \ +GEN_HANDLER_E(name, 0x04, opc2, opc3, 0x00000000, PPC_NONE, PPC2_ALTIVEC_207) + +GEN_VX_LOGICAL(vand, tcg_gen_and_i64, 2, 16), +GEN_VX_LOGICAL(vandc, tcg_gen_andc_i64, 2, 17), +GEN_VX_LOGICAL(vor, tcg_gen_or_i64, 2, 18), +GEN_VX_LOGICAL(vxor, tcg_gen_xor_i64, 2, 19), +GEN_VX_LOGICAL(vnor, tcg_gen_nor_i64, 2, 20), +GEN_VX_LOGICAL_207(veqv, tcg_gen_eqv_i64, 2, 26), +GEN_VX_LOGICAL_207(vnand, tcg_gen_nand_i64, 2, 22), +GEN_VX_LOGICAL_207(vorc, tcg_gen_orc_i64, 2, 21), + +#define GEN_VXFORM(name, opc2, opc3) \ +GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC) + +#define GEN_VXFORM_207(name, opc2, opc3) \ +GEN_HANDLER_E(name, 0x04, opc2, opc3, 0x00000000, PPC_NONE, PPC2_ALTIVEC_207) + +#define GEN_VXFORM_300(name, opc2, opc3) \ +GEN_HANDLER_E(name, 0x04, opc2, opc3, 0x00000000, PPC_NONE, PPC2_ISA300) + +#define GEN_VXFORM_300_EXT(name, opc2, opc3, inval) \ +GEN_HANDLER_E(name, 0x04, opc2, opc3, inval, PPC_NONE, PPC2_ISA300) + +#define GEN_VXFORM_300_EO(name, opc2, opc3, opc4) \ +GEN_HANDLER_E_2(name, 0x04, opc2, opc3, opc4, 0x00000000, PPC_NONE, \ + PPC2_ISA300) + +#define GEN_VXFORM_DUAL(name0, name1, opc2, opc3, type0, type1) \ +GEN_HANDLER_E(name0##_##name1, 0x4, opc2, opc3, 0x00000000, type0, type1) + +#define GEN_VXRFORM_DUAL(name0, name1, opc2, opc3, tp0, tp1) \ +GEN_HANDLER_E(name0##_##name1, 0x4, opc2, opc3, 0x00000000, tp0, tp1), \ +GEN_HANDLER_E(name0##_##name1, 0x4, opc2, (opc3 | 0x10), 0x00000000, tp0, tp1), + +GEN_VXFORM_DUAL(vaddubm, vmul10cuq, 0, 0, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_DUAL(vadduhm, vmul10ecuq, 0, 1, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM(vadduwm, 0, 2), +GEN_VXFORM_207(vaddudm, 0, 3), +GEN_VXFORM_DUAL(vsububm, bcdadd, 0, 16, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_DUAL(vsubuhm, bcdsub, 0, 17, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM(vsubuwm, 0, 18), +GEN_VXFORM_207(vsubudm, 0, 19), +GEN_VXFORM(vmaxub, 1, 0), +GEN_VXFORM(vmaxuh, 1, 1), +GEN_VXFORM(vmaxuw, 1, 2), +GEN_VXFORM_207(vmaxud, 1, 3), +GEN_VXFORM(vmaxsb, 1, 4), +GEN_VXFORM(vmaxsh, 1, 5), +GEN_VXFORM(vmaxsw, 1, 6), +GEN_VXFORM_207(vmaxsd, 1, 7), +GEN_VXFORM(vminub, 1, 8), +GEN_VXFORM(vminuh, 1, 9), +GEN_VXFORM(vminuw, 1, 10), +GEN_VXFORM_207(vminud, 1, 11), +GEN_VXFORM(vminsb, 1, 12), +GEN_VXFORM(vminsh, 1, 13), +GEN_VXFORM(vminsw, 1, 14), +GEN_VXFORM_207(vminsd, 1, 15), +GEN_VXFORM_DUAL(vavgub, vabsdub, 1, 16, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_DUAL(vavguh, vabsduh, 1, 17, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_DUAL(vavguw, vabsduw, 1, 18, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM(vavgsb, 1, 20), +GEN_VXFORM(vavgsh, 1, 21), +GEN_VXFORM(vavgsw, 1, 22), +GEN_VXFORM(vmrghb, 6, 0), +GEN_VXFORM(vmrghh, 6, 1), +GEN_VXFORM(vmrghw, 6, 2), +GEN_VXFORM(vmrglb, 6, 4), +GEN_VXFORM(vmrglh, 6, 5), +GEN_VXFORM(vmrglw, 6, 6), +GEN_VXFORM_207(vmrgew, 6, 30), +GEN_VXFORM_207(vmrgow, 6, 26), +GEN_VXFORM(vmuloub, 4, 0), +GEN_VXFORM(vmulouh, 4, 1), +GEN_VXFORM_DUAL(vmulouw, vmuluwm, 4, 2, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM(vmulosb, 4, 4), +GEN_VXFORM(vmulosh, 4, 5), +GEN_VXFORM_207(vmulosw, 4, 6), +GEN_VXFORM(vmuleub, 4, 8), +GEN_VXFORM(vmuleuh, 4, 9), +GEN_VXFORM_207(vmuleuw, 4, 10), +GEN_VXFORM(vmulesb, 4, 12), +GEN_VXFORM(vmulesh, 4, 13), +GEN_VXFORM_207(vmulesw, 4, 14), +GEN_VXFORM(vslb, 2, 4), +GEN_VXFORM(vslh, 2, 5), +GEN_VXFORM_DUAL(vslw, vrlwnm, 2, 6, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_207(vsld, 2, 23), +GEN_VXFORM(vsrb, 2, 8), +GEN_VXFORM(vsrh, 2, 9), +GEN_VXFORM(vsrw, 2, 10), +GEN_VXFORM_207(vsrd, 2, 27), +GEN_VXFORM(vsrab, 2, 12), +GEN_VXFORM(vsrah, 2, 13), +GEN_VXFORM(vsraw, 2, 14), +GEN_VXFORM_207(vsrad, 2, 15), +GEN_VXFORM_300(vsrv, 2, 28), +GEN_VXFORM_300(vslv, 2, 29), +GEN_VXFORM(vslo, 6, 16), +GEN_VXFORM(vsro, 6, 17), +GEN_VXFORM(vaddcuw, 0, 6), +GEN_HANDLER_E_2(vprtybw, 0x4, 0x1, 0x18, 8, 0, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E_2(vprtybd, 0x4, 0x1, 0x18, 9, 0, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E_2(vprtybq, 0x4, 0x1, 0x18, 10, 0, PPC_NONE, PPC2_ISA300), + +GEN_VXFORM_DUAL(vsubcuw, xpnd04_1, 0, 22, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_DUAL(vaddubs, vmul10uq, 0, 8, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_DUAL(vadduhs, vmul10euq, 0, 9, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM(vadduws, 0, 10), +GEN_VXFORM(vaddsbs, 0, 12), +GEN_VXFORM(vaddshs, 0, 13), +GEN_VXFORM(vaddsws, 0, 14), +GEN_VXFORM_DUAL(vsububs, bcdadd, 0, 24, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_DUAL(vsubuhs, bcdsub, 0, 25, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM(vsubuws, 0, 26), +GEN_VXFORM(vsubsbs, 0, 28), +GEN_VXFORM(vsubshs, 0, 29), +GEN_VXFORM_DUAL(vsubsws, xpnd04_2, 0, 30, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_207(vadduqm, 0, 4), +GEN_VXFORM_207(vaddcuq, 0, 5), +GEN_VXFORM_DUAL(vaddeuqm, vaddecuq, 30, 0xFF, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_207(vsubuqm, 0, 20), +GEN_VXFORM_207(vsubcuq, 0, 21), +GEN_VXFORM_DUAL(vsubeuqm, vsubecuq, 31, 0xFF, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM(vrlb, 2, 0), +GEN_VXFORM(vrlh, 2, 1), +GEN_VXFORM_DUAL(vrlw, vrlwmi, 2, 2, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_DUAL(vrld, vrldmi, 2, 3, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_DUAL(vsl, vrldnm, 2, 7, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM(vsr, 2, 11), +GEN_VXFORM(vpkuhum, 7, 0), +GEN_VXFORM(vpkuwum, 7, 1), +GEN_VXFORM_207(vpkudum, 7, 17), +GEN_VXFORM(vpkuhus, 7, 2), +GEN_VXFORM(vpkuwus, 7, 3), +GEN_VXFORM_207(vpkudus, 7, 19), +GEN_VXFORM(vpkshus, 7, 4), +GEN_VXFORM(vpkswus, 7, 5), +GEN_VXFORM_207(vpksdus, 7, 21), +GEN_VXFORM(vpkshss, 7, 6), +GEN_VXFORM(vpkswss, 7, 7), +GEN_VXFORM_207(vpksdss, 7, 23), +GEN_VXFORM(vpkpx, 7, 12), +GEN_VXFORM(vsum4ubs, 4, 24), +GEN_VXFORM(vsum4sbs, 4, 28), +GEN_VXFORM(vsum4shs, 4, 25), +GEN_VXFORM(vsum2sws, 4, 26), +GEN_VXFORM(vsumsws, 4, 30), +GEN_VXFORM(vaddfp, 5, 0), +GEN_VXFORM(vsubfp, 5, 1), +GEN_VXFORM(vmaxfp, 5, 16), +GEN_VXFORM(vminfp, 5, 17), + +#define GEN_VXRFORM1(opname, name, str, opc2, opc3) \ + GEN_HANDLER2(name, str, 0x4, opc2, opc3, 0x00000000, PPC_ALTIVEC), +#define GEN_VXRFORM1_300(opname, name, str, opc2, opc3) \ +GEN_HANDLER2_E(name, str, 0x4, opc2, opc3, 0x00000000, PPC_NONE, PPC2_ISA300), +#define GEN_VXRFORM(name, opc2, opc3) \ + GEN_VXRFORM1(name, name, #name, opc2, opc3) \ + GEN_VXRFORM1(name##_dot, name##_, #name ".", opc2, (opc3 | (0x1 << 4))) +#define GEN_VXRFORM_300(name, opc2, opc3) \ + GEN_VXRFORM1_300(name, name, #name, opc2, opc3) \ + GEN_VXRFORM1_300(name##_dot, name##_, #name ".", opc2, (opc3 | (0x1 << 4))) + +GEN_VXRFORM_300(vcmpnezb, 3, 4) +GEN_VXRFORM_300(vcmpnezh, 3, 5) +GEN_VXRFORM_300(vcmpnezw, 3, 6) +GEN_VXRFORM(vcmpgtsb, 3, 12) +GEN_VXRFORM(vcmpgtsh, 3, 13) +GEN_VXRFORM(vcmpgtsw, 3, 14) +GEN_VXRFORM(vcmpgtub, 3, 8) +GEN_VXRFORM(vcmpgtuh, 3, 9) +GEN_VXRFORM(vcmpgtuw, 3, 10) +GEN_VXRFORM_DUAL(vcmpeqfp, vcmpequd, 3, 3, PPC_ALTIVEC, PPC_NONE) +GEN_VXRFORM(vcmpgefp, 3, 7) +GEN_VXRFORM_DUAL(vcmpgtfp, vcmpgtud, 3, 11, PPC_ALTIVEC, PPC_NONE) +GEN_VXRFORM_DUAL(vcmpbfp, vcmpgtsd, 3, 15, PPC_ALTIVEC, PPC_NONE) +GEN_VXRFORM_DUAL(vcmpequb, vcmpneb, 3, 0, PPC_ALTIVEC, PPC_NONE) +GEN_VXRFORM_DUAL(vcmpequh, vcmpneh, 3, 1, PPC_ALTIVEC, PPC_NONE) +GEN_VXRFORM_DUAL(vcmpequw, vcmpnew, 3, 2, PPC_ALTIVEC, PPC_NONE) + +#define GEN_VXFORM_DUAL_INV(name0, name1, opc2, opc3, inval0, inval1, type) \ +GEN_OPCODE_DUAL(name0##_##name1, 0x04, opc2, opc3, inval0, inval1, type, \ + PPC_NONE) +GEN_VXFORM_DUAL_INV(vspltb, vextractub, 6, 8, 0x00000000, 0x100000, + PPC_ALTIVEC), +GEN_VXFORM_DUAL_INV(vsplth, vextractuh, 6, 9, 0x00000000, 0x100000, + PPC_ALTIVEC), +GEN_VXFORM_DUAL_INV(vspltw, vextractuw, 6, 10, 0x00000000, 0x100000, + PPC_ALTIVEC), +GEN_VXFORM_300_EXT(vextractd, 6, 11, 0x100000), +GEN_VXFORM_DUAL_INV(vspltisb, vinsertb, 6, 12, 0x00000000, 0x100000, + PPC_ALTIVEC), +GEN_VXFORM_DUAL_INV(vspltish, vinserth, 6, 13, 0x00000000, 0x100000, + PPC_ALTIVEC), +GEN_VXFORM_DUAL_INV(vspltisw, vinsertw, 6, 14, 0x00000000, 0x100000, + PPC_ALTIVEC), +GEN_VXFORM_300_EXT(vinsertd, 6, 15, 0x100000), +GEN_VXFORM_300_EO(vnegw, 0x01, 0x18, 0x06), +GEN_VXFORM_300_EO(vnegd, 0x01, 0x18, 0x07), +GEN_VXFORM_300_EO(vextsb2w, 0x01, 0x18, 0x10), +GEN_VXFORM_300_EO(vextsh2w, 0x01, 0x18, 0x11), +GEN_VXFORM_300_EO(vextsb2d, 0x01, 0x18, 0x18), +GEN_VXFORM_300_EO(vextsh2d, 0x01, 0x18, 0x19), +GEN_VXFORM_300_EO(vextsw2d, 0x01, 0x18, 0x1A), +GEN_VXFORM_300_EO(vctzb, 0x01, 0x18, 0x1C), +GEN_VXFORM_300_EO(vctzh, 0x01, 0x18, 0x1D), +GEN_VXFORM_300_EO(vctzw, 0x01, 0x18, 0x1E), +GEN_VXFORM_300_EO(vctzd, 0x01, 0x18, 0x1F), +GEN_VXFORM_300_EO(vclzlsbb, 0x01, 0x18, 0x0), +GEN_VXFORM_300_EO(vctzlsbb, 0x01, 0x18, 0x1), +GEN_VXFORM_300(vpermr, 0x1D, 0xFF), + +#define GEN_VXFORM_NOA(name, opc2, opc3) \ + GEN_HANDLER(name, 0x04, opc2, opc3, 0x001f0000, PPC_ALTIVEC) +GEN_VXFORM_NOA(vupkhsb, 7, 8), +GEN_VXFORM_NOA(vupkhsh, 7, 9), +GEN_VXFORM_207(vupkhsw, 7, 25), +GEN_VXFORM_NOA(vupklsb, 7, 10), +GEN_VXFORM_NOA(vupklsh, 7, 11), +GEN_VXFORM_207(vupklsw, 7, 27), +GEN_VXFORM_NOA(vupkhpx, 7, 13), +GEN_VXFORM_NOA(vupklpx, 7, 15), +GEN_VXFORM_NOA(vrefp, 5, 4), +GEN_VXFORM_NOA(vrsqrtefp, 5, 5), +GEN_VXFORM_NOA(vexptefp, 5, 6), +GEN_VXFORM_NOA(vlogefp, 5, 7), +GEN_VXFORM_NOA(vrfim, 5, 11), +GEN_VXFORM_NOA(vrfin, 5, 8), +GEN_VXFORM_NOA(vrfip, 5, 10), +GEN_VXFORM_NOA(vrfiz, 5, 9), + +#define GEN_VXFORM_UIMM(name, opc2, opc3) \ + GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC) +GEN_VXFORM_UIMM(vcfux, 5, 12), +GEN_VXFORM_UIMM(vcfsx, 5, 13), +GEN_VXFORM_UIMM(vctuxs, 5, 14), +GEN_VXFORM_UIMM(vctsxs, 5, 15), + + +#define GEN_VAFORM_PAIRED(name0, name1, opc2) \ + GEN_HANDLER(name0##_##name1, 0x04, opc2, 0xFF, 0x00000000, PPC_ALTIVEC) +GEN_VAFORM_PAIRED(vmhaddshs, vmhraddshs, 16), +GEN_VAFORM_PAIRED(vmsumubm, vmsummbm, 18), +GEN_VAFORM_PAIRED(vmsumuhm, vmsumuhs, 19), +GEN_VAFORM_PAIRED(vmsumshm, vmsumshs, 20), +GEN_VAFORM_PAIRED(vsel, vperm, 21), +GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23), + +GEN_VXFORM_DUAL(vclzb, vpopcntb, 1, 28, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_DUAL(vclzh, vpopcnth, 1, 29, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_DUAL(vclzw, vpopcntw, 1, 30, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_DUAL(vclzd, vpopcntd, 1, 31, PPC_NONE, PPC2_ALTIVEC_207), + +GEN_VXFORM_300(vbpermd, 6, 23), +GEN_VXFORM_207(vbpermq, 6, 21), +GEN_VXFORM_207(vgbbd, 6, 20), +GEN_VXFORM_207(vpmsumb, 4, 16), +GEN_VXFORM_207(vpmsumh, 4, 17), +GEN_VXFORM_207(vpmsumw, 4, 18), +GEN_VXFORM_207(vpmsumd, 4, 19), + +GEN_VXFORM_207(vsbox, 4, 23), + +GEN_VXFORM_DUAL(vcipher, vcipherlast, 4, 20, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_DUAL(vncipher, vncipherlast, 4, 21, PPC_NONE, PPC2_ALTIVEC_207), + +GEN_VXFORM_207(vshasigmaw, 1, 26), +GEN_VXFORM_207(vshasigmad, 1, 27), + +GEN_VXFORM_DUAL(vsldoi, vpermxor, 22, 0xFF, PPC_ALTIVEC, PPC_NONE), diff --git a/target/ppc/translate/vsx-impl.inc.c b/target/ppc/translate/vsx-impl.inc.c new file mode 100644 index 0000000000..5a27be4bd4 --- /dev/null +++ b/target/ppc/translate/vsx-impl.inc.c @@ -0,0 +1,1009 @@ +/*** VSX extension ***/ + +static inline TCGv_i64 cpu_vsrh(int n) +{ + if (n < 32) { + return cpu_fpr[n]; + } else { + return cpu_avrh[n-32]; + } +} + +static inline TCGv_i64 cpu_vsrl(int n) +{ + if (n < 32) { + return cpu_vsr[n]; + } else { + return cpu_avrl[n-32]; + } +} + +#define VSX_LOAD_SCALAR(name, operation) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + gen_qemu_##operation(ctx, cpu_vsrh(xT(ctx->opcode)), EA); \ + /* NOTE: cpu_vsrl is undefined */ \ + tcg_temp_free(EA); \ +} + +VSX_LOAD_SCALAR(lxsdx, ld64_i64) +VSX_LOAD_SCALAR(lxsiwax, ld32s_i64) +VSX_LOAD_SCALAR(lxsibzx, ld8u_i64) +VSX_LOAD_SCALAR(lxsihzx, ld16u_i64) +VSX_LOAD_SCALAR(lxsiwzx, ld32u_i64) +VSX_LOAD_SCALAR(lxsspx, ld32fs) + +static void gen_lxvd2x(DisasContext *ctx) +{ + TCGv EA; + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + gen_set_access_type(ctx, ACCESS_INT); + EA = tcg_temp_new(); + gen_addr_reg_index(ctx, EA); + gen_qemu_ld64_i64(ctx, cpu_vsrh(xT(ctx->opcode)), EA); + tcg_gen_addi_tl(EA, EA, 8); + gen_qemu_ld64_i64(ctx, cpu_vsrl(xT(ctx->opcode)), EA); + tcg_temp_free(EA); +} + +static void gen_lxvdsx(DisasContext *ctx) +{ + TCGv EA; + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + gen_set_access_type(ctx, ACCESS_INT); + EA = tcg_temp_new(); + gen_addr_reg_index(ctx, EA); + gen_qemu_ld64_i64(ctx, cpu_vsrh(xT(ctx->opcode)), EA); + tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrh(xT(ctx->opcode))); + tcg_temp_free(EA); +} + +static void gen_lxvw4x(DisasContext *ctx) +{ + TCGv EA; + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + gen_set_access_type(ctx, ACCESS_INT); + EA = tcg_temp_new(); + + gen_addr_reg_index(ctx, EA); + if (ctx->le_mode) { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_qemu_ld_i64(t0, EA, ctx->mem_idx, MO_LEQ); + tcg_gen_shri_i64(t1, t0, 32); + tcg_gen_deposit_i64(xth, t1, t0, 32, 32); + tcg_gen_addi_tl(EA, EA, 8); + tcg_gen_qemu_ld_i64(t0, EA, ctx->mem_idx, MO_LEQ); + tcg_gen_shri_i64(t1, t0, 32); + tcg_gen_deposit_i64(xtl, t1, t0, 32, 32); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } else { + tcg_gen_qemu_ld_i64(xth, EA, ctx->mem_idx, MO_BEQ); + tcg_gen_addi_tl(EA, EA, 8); + tcg_gen_qemu_ld_i64(xtl, EA, ctx->mem_idx, MO_BEQ); + } + tcg_temp_free(EA); +} + +static void gen_bswap16x8(TCGv_i64 outh, TCGv_i64 outl, + TCGv_i64 inh, TCGv_i64 inl) +{ + TCGv_i64 mask = tcg_const_i64(0x00FF00FF00FF00FF); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + /* outh = ((inh & mask) << 8) | ((inh >> 8) & mask) */ + tcg_gen_and_i64(t0, inh, mask); + tcg_gen_shli_i64(t0, t0, 8); + tcg_gen_shri_i64(t1, inh, 8); + tcg_gen_and_i64(t1, t1, mask); + tcg_gen_or_i64(outh, t0, t1); + + /* outl = ((inl & mask) << 8) | ((inl >> 8) & mask) */ + tcg_gen_and_i64(t0, inl, mask); + tcg_gen_shli_i64(t0, t0, 8); + tcg_gen_shri_i64(t1, inl, 8); + tcg_gen_and_i64(t1, t1, mask); + tcg_gen_or_i64(outl, t0, t1); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(mask); +} + +static void gen_bswap32x4(TCGv_i64 outh, TCGv_i64 outl, + TCGv_i64 inh, TCGv_i64 inl) +{ + TCGv_i64 hi = tcg_temp_new_i64(); + TCGv_i64 lo = tcg_temp_new_i64(); + + tcg_gen_bswap64_i64(hi, inh); + tcg_gen_bswap64_i64(lo, inl); + tcg_gen_shri_i64(outh, hi, 32); + tcg_gen_deposit_i64(outh, outh, hi, 32, 32); + tcg_gen_shri_i64(outl, lo, 32); + tcg_gen_deposit_i64(outl, outl, lo, 32, 32); + + tcg_temp_free_i64(hi); + tcg_temp_free_i64(lo); +} +static void gen_lxvh8x(DisasContext *ctx) +{ + TCGv EA; + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + gen_set_access_type(ctx, ACCESS_INT); + + EA = tcg_temp_new(); + gen_addr_reg_index(ctx, EA); + tcg_gen_qemu_ld_i64(xth, EA, ctx->mem_idx, MO_BEQ); + tcg_gen_addi_tl(EA, EA, 8); + tcg_gen_qemu_ld_i64(xtl, EA, ctx->mem_idx, MO_BEQ); + if (ctx->le_mode) { + gen_bswap16x8(xth, xtl, xth, xtl); + } + tcg_temp_free(EA); +} + +static void gen_lxvb16x(DisasContext *ctx) +{ + TCGv EA; + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + gen_set_access_type(ctx, ACCESS_INT); + EA = tcg_temp_new(); + gen_addr_reg_index(ctx, EA); + tcg_gen_qemu_ld_i64(xth, EA, ctx->mem_idx, MO_BEQ); + tcg_gen_addi_tl(EA, EA, 8); + tcg_gen_qemu_ld_i64(xtl, EA, ctx->mem_idx, MO_BEQ); + tcg_temp_free(EA); +} + +#define VSX_STORE_SCALAR(name, operation) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + gen_qemu_##operation(ctx, cpu_vsrh(xS(ctx->opcode)), EA); \ + tcg_temp_free(EA); \ +} + +VSX_STORE_SCALAR(stxsdx, st64_i64) + +VSX_STORE_SCALAR(stxsibx, st8_i64) +VSX_STORE_SCALAR(stxsihx, st16_i64) +VSX_STORE_SCALAR(stxsiwx, st32_i64) +VSX_STORE_SCALAR(stxsspx, st32fs) + +static void gen_stxvd2x(DisasContext *ctx) +{ + TCGv EA; + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + gen_set_access_type(ctx, ACCESS_INT); + EA = tcg_temp_new(); + gen_addr_reg_index(ctx, EA); + gen_qemu_st64_i64(ctx, cpu_vsrh(xS(ctx->opcode)), EA); + tcg_gen_addi_tl(EA, EA, 8); + gen_qemu_st64_i64(ctx, cpu_vsrl(xS(ctx->opcode)), EA); + tcg_temp_free(EA); +} + +static void gen_stxvw4x(DisasContext *ctx) +{ + TCGv_i64 xsh = cpu_vsrh(xS(ctx->opcode)); + TCGv_i64 xsl = cpu_vsrl(xS(ctx->opcode)); + TCGv EA; + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + gen_set_access_type(ctx, ACCESS_INT); + EA = tcg_temp_new(); + gen_addr_reg_index(ctx, EA); + if (ctx->le_mode) { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_shri_i64(t0, xsh, 32); + tcg_gen_deposit_i64(t1, t0, xsh, 32, 32); + tcg_gen_qemu_st_i64(t1, EA, ctx->mem_idx, MO_LEQ); + tcg_gen_addi_tl(EA, EA, 8); + tcg_gen_shri_i64(t0, xsl, 32); + tcg_gen_deposit_i64(t1, t0, xsl, 32, 32); + tcg_gen_qemu_st_i64(t1, EA, ctx->mem_idx, MO_LEQ); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } else { + tcg_gen_qemu_st_i64(xsh, EA, ctx->mem_idx, MO_BEQ); + tcg_gen_addi_tl(EA, EA, 8); + tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEQ); + } + tcg_temp_free(EA); +} + +static void gen_stxvh8x(DisasContext *ctx) +{ + TCGv_i64 xsh = cpu_vsrh(xS(ctx->opcode)); + TCGv_i64 xsl = cpu_vsrl(xS(ctx->opcode)); + TCGv EA; + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + gen_set_access_type(ctx, ACCESS_INT); + EA = tcg_temp_new(); + gen_addr_reg_index(ctx, EA); + if (ctx->le_mode) { + TCGv_i64 outh = tcg_temp_new_i64(); + TCGv_i64 outl = tcg_temp_new_i64(); + + gen_bswap16x8(outh, outl, xsh, xsl); + tcg_gen_qemu_st_i64(outh, EA, ctx->mem_idx, MO_BEQ); + tcg_gen_addi_tl(EA, EA, 8); + tcg_gen_qemu_st_i64(outl, EA, ctx->mem_idx, MO_BEQ); + tcg_temp_free_i64(outh); + tcg_temp_free_i64(outl); + } else { + tcg_gen_qemu_st_i64(xsh, EA, ctx->mem_idx, MO_BEQ); + tcg_gen_addi_tl(EA, EA, 8); + tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEQ); + } + tcg_temp_free(EA); +} + +static void gen_stxvb16x(DisasContext *ctx) +{ + TCGv_i64 xsh = cpu_vsrh(xS(ctx->opcode)); + TCGv_i64 xsl = cpu_vsrl(xS(ctx->opcode)); + TCGv EA; + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + gen_set_access_type(ctx, ACCESS_INT); + EA = tcg_temp_new(); + gen_addr_reg_index(ctx, EA); + tcg_gen_qemu_st_i64(xsh, EA, ctx->mem_idx, MO_BEQ); + tcg_gen_addi_tl(EA, EA, 8); + tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEQ); + tcg_temp_free(EA); +} + +#define MV_VSRW(name, tcgop1, tcgop2, target, source) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + if (xS(ctx->opcode) < 32) { \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + } else { \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + } \ + TCGv_i64 tmp = tcg_temp_new_i64(); \ + tcg_gen_##tcgop1(tmp, source); \ + tcg_gen_##tcgop2(target, tmp); \ + tcg_temp_free_i64(tmp); \ +} + + +MV_VSRW(mfvsrwz, ext32u_i64, trunc_i64_tl, cpu_gpr[rA(ctx->opcode)], \ + cpu_vsrh(xS(ctx->opcode))) +MV_VSRW(mtvsrwa, extu_tl_i64, ext32s_i64, cpu_vsrh(xT(ctx->opcode)), \ + cpu_gpr[rA(ctx->opcode)]) +MV_VSRW(mtvsrwz, extu_tl_i64, ext32u_i64, cpu_vsrh(xT(ctx->opcode)), \ + cpu_gpr[rA(ctx->opcode)]) + +#if defined(TARGET_PPC64) +#define MV_VSRD(name, target, source) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + if (xS(ctx->opcode) < 32) { \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + } else { \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + } \ + tcg_gen_mov_i64(target, source); \ +} + +MV_VSRD(mfvsrd, cpu_gpr[rA(ctx->opcode)], cpu_vsrh(xS(ctx->opcode))) +MV_VSRD(mtvsrd, cpu_vsrh(xT(ctx->opcode)), cpu_gpr[rA(ctx->opcode)]) + +static void gen_mfvsrld(DisasContext *ctx) +{ + if (xS(ctx->opcode) < 32) { + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + } else { + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + } + + tcg_gen_mov_i64(cpu_gpr[rA(ctx->opcode)], cpu_vsrl(xS(ctx->opcode))); +} + +static void gen_mtvsrdd(DisasContext *ctx) +{ + if (xT(ctx->opcode) < 32) { + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + } else { + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + } + + if (!rA(ctx->opcode)) { + tcg_gen_movi_i64(cpu_vsrh(xT(ctx->opcode)), 0); + } else { + tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_gpr[rA(ctx->opcode)]); + } + + tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_gpr[rB(ctx->opcode)]); +} + +static void gen_mtvsrws(DisasContext *ctx) +{ + if (xT(ctx->opcode) < 32) { + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + } else { + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + } + + tcg_gen_deposit_i64(cpu_vsrl(xT(ctx->opcode)), cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rA(ctx->opcode)], 32, 32); + tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_vsrl(xT(ctx->opcode))); +} + +#endif + +static void gen_xxpermdi(DisasContext *ctx) +{ + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + + if (unlikely((xT(ctx->opcode) == xA(ctx->opcode)) || + (xT(ctx->opcode) == xB(ctx->opcode)))) { + TCGv_i64 xh, xl; + + xh = tcg_temp_new_i64(); + xl = tcg_temp_new_i64(); + + if ((DM(ctx->opcode) & 2) == 0) { + tcg_gen_mov_i64(xh, cpu_vsrh(xA(ctx->opcode))); + } else { + tcg_gen_mov_i64(xh, cpu_vsrl(xA(ctx->opcode))); + } + if ((DM(ctx->opcode) & 1) == 0) { + tcg_gen_mov_i64(xl, cpu_vsrh(xB(ctx->opcode))); + } else { + tcg_gen_mov_i64(xl, cpu_vsrl(xB(ctx->opcode))); + } + + tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), xh); + tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), xl); + + tcg_temp_free_i64(xh); + tcg_temp_free_i64(xl); + } else { + if ((DM(ctx->opcode) & 2) == 0) { + tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_vsrh(xA(ctx->opcode))); + } else { + tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_vsrl(xA(ctx->opcode))); + } + if ((DM(ctx->opcode) & 1) == 0) { + tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrh(xB(ctx->opcode))); + } else { + tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrl(xB(ctx->opcode))); + } + } +} + +#define OP_ABS 1 +#define OP_NABS 2 +#define OP_NEG 3 +#define OP_CPSGN 4 +#define SGN_MASK_DP 0x8000000000000000ull +#define SGN_MASK_SP 0x8000000080000000ull + +#define VSX_SCALAR_MOVE(name, op, sgn_mask) \ +static void glue(gen_, name)(DisasContext * ctx) \ + { \ + TCGv_i64 xb, sgm; \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + xb = tcg_temp_new_i64(); \ + sgm = tcg_temp_new_i64(); \ + tcg_gen_mov_i64(xb, cpu_vsrh(xB(ctx->opcode))); \ + tcg_gen_movi_i64(sgm, sgn_mask); \ + switch (op) { \ + case OP_ABS: { \ + tcg_gen_andc_i64(xb, xb, sgm); \ + break; \ + } \ + case OP_NABS: { \ + tcg_gen_or_i64(xb, xb, sgm); \ + break; \ + } \ + case OP_NEG: { \ + tcg_gen_xor_i64(xb, xb, sgm); \ + break; \ + } \ + case OP_CPSGN: { \ + TCGv_i64 xa = tcg_temp_new_i64(); \ + tcg_gen_mov_i64(xa, cpu_vsrh(xA(ctx->opcode))); \ + tcg_gen_and_i64(xa, xa, sgm); \ + tcg_gen_andc_i64(xb, xb, sgm); \ + tcg_gen_or_i64(xb, xb, xa); \ + tcg_temp_free_i64(xa); \ + break; \ + } \ + } \ + tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), xb); \ + tcg_temp_free_i64(xb); \ + tcg_temp_free_i64(sgm); \ + } + +VSX_SCALAR_MOVE(xsabsdp, OP_ABS, SGN_MASK_DP) +VSX_SCALAR_MOVE(xsnabsdp, OP_NABS, SGN_MASK_DP) +VSX_SCALAR_MOVE(xsnegdp, OP_NEG, SGN_MASK_DP) +VSX_SCALAR_MOVE(xscpsgndp, OP_CPSGN, SGN_MASK_DP) + +#define VSX_VECTOR_MOVE(name, op, sgn_mask) \ +static void glue(gen_, name)(DisasContext * ctx) \ + { \ + TCGv_i64 xbh, xbl, sgm; \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + xbh = tcg_temp_new_i64(); \ + xbl = tcg_temp_new_i64(); \ + sgm = tcg_temp_new_i64(); \ + tcg_gen_mov_i64(xbh, cpu_vsrh(xB(ctx->opcode))); \ + tcg_gen_mov_i64(xbl, cpu_vsrl(xB(ctx->opcode))); \ + tcg_gen_movi_i64(sgm, sgn_mask); \ + switch (op) { \ + case OP_ABS: { \ + tcg_gen_andc_i64(xbh, xbh, sgm); \ + tcg_gen_andc_i64(xbl, xbl, sgm); \ + break; \ + } \ + case OP_NABS: { \ + tcg_gen_or_i64(xbh, xbh, sgm); \ + tcg_gen_or_i64(xbl, xbl, sgm); \ + break; \ + } \ + case OP_NEG: { \ + tcg_gen_xor_i64(xbh, xbh, sgm); \ + tcg_gen_xor_i64(xbl, xbl, sgm); \ + break; \ + } \ + case OP_CPSGN: { \ + TCGv_i64 xah = tcg_temp_new_i64(); \ + TCGv_i64 xal = tcg_temp_new_i64(); \ + tcg_gen_mov_i64(xah, cpu_vsrh(xA(ctx->opcode))); \ + tcg_gen_mov_i64(xal, cpu_vsrl(xA(ctx->opcode))); \ + tcg_gen_and_i64(xah, xah, sgm); \ + tcg_gen_and_i64(xal, xal, sgm); \ + tcg_gen_andc_i64(xbh, xbh, sgm); \ + tcg_gen_andc_i64(xbl, xbl, sgm); \ + tcg_gen_or_i64(xbh, xbh, xah); \ + tcg_gen_or_i64(xbl, xbl, xal); \ + tcg_temp_free_i64(xah); \ + tcg_temp_free_i64(xal); \ + break; \ + } \ + } \ + tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), xbh); \ + tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), xbl); \ + tcg_temp_free_i64(xbh); \ + tcg_temp_free_i64(xbl); \ + tcg_temp_free_i64(sgm); \ + } + +VSX_VECTOR_MOVE(xvabsdp, OP_ABS, SGN_MASK_DP) +VSX_VECTOR_MOVE(xvnabsdp, OP_NABS, SGN_MASK_DP) +VSX_VECTOR_MOVE(xvnegdp, OP_NEG, SGN_MASK_DP) +VSX_VECTOR_MOVE(xvcpsgndp, OP_CPSGN, SGN_MASK_DP) +VSX_VECTOR_MOVE(xvabssp, OP_ABS, SGN_MASK_SP) +VSX_VECTOR_MOVE(xvnabssp, OP_NABS, SGN_MASK_SP) +VSX_VECTOR_MOVE(xvnegsp, OP_NEG, SGN_MASK_SP) +VSX_VECTOR_MOVE(xvcpsgnsp, OP_CPSGN, SGN_MASK_SP) + +#define GEN_VSX_HELPER_2(name, op1, op2, inval, type) \ +static void gen_##name(DisasContext * ctx) \ +{ \ + TCGv_i32 opc; \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + opc = tcg_const_i32(ctx->opcode); \ + gen_helper_##name(cpu_env, opc); \ + tcg_temp_free_i32(opc); \ +} + +#define GEN_VSX_HELPER_XT_XB_ENV(name, op1, op2, inval, type) \ +static void gen_##name(DisasContext * ctx) \ +{ \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + gen_helper_##name(cpu_vsrh(xT(ctx->opcode)), cpu_env, \ + cpu_vsrh(xB(ctx->opcode))); \ +} + +GEN_VSX_HELPER_2(xsadddp, 0x00, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xssubdp, 0x00, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmuldp, 0x00, 0x06, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsdivdp, 0x00, 0x07, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsredp, 0x14, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xssqrtdp, 0x16, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrsqrtedp, 0x14, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xstdivdp, 0x14, 0x07, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xstsqrtdp, 0x14, 0x06, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmaddadp, 0x04, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmaddmdp, 0x04, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmsubadp, 0x04, 0x06, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmsubmdp, 0x04, 0x07, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsnmaddadp, 0x04, 0x14, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsnmaddmdp, 0x04, 0x15, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsnmsubadp, 0x04, 0x16, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsnmsubmdp, 0x04, 0x17, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscmpeqdp, 0x0C, 0x00, 0, PPC2_ISA300) +GEN_VSX_HELPER_2(xscmpgtdp, 0x0C, 0x01, 0, PPC2_ISA300) +GEN_VSX_HELPER_2(xscmpgedp, 0x0C, 0x02, 0, PPC2_ISA300) +GEN_VSX_HELPER_2(xscmpnedp, 0x0C, 0x03, 0, PPC2_ISA300) +GEN_VSX_HELPER_2(xscmpodp, 0x0C, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscmpudp, 0x0C, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmaxdp, 0x00, 0x14, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmindp, 0x00, 0x15, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvdpsp, 0x12, 0x10, 0, PPC2_VSX) +GEN_VSX_HELPER_XT_XB_ENV(xscvdpspn, 0x16, 0x10, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xscvspdp, 0x12, 0x14, 0, PPC2_VSX) +GEN_VSX_HELPER_XT_XB_ENV(xscvspdpn, 0x16, 0x14, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xscvdpsxds, 0x10, 0x15, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvdpsxws, 0x10, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvdpuxds, 0x10, 0x14, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvdpuxws, 0x10, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvsxddp, 0x10, 0x17, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvuxddp, 0x10, 0x16, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrdpi, 0x12, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrdpic, 0x16, 0x06, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrdpim, 0x12, 0x07, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrdpip, 0x12, 0x06, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrdpiz, 0x12, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_XT_XB_ENV(xsrsp, 0x12, 0x11, 0, PPC2_VSX207) + +GEN_VSX_HELPER_2(xsaddsp, 0x00, 0x00, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xssubsp, 0x00, 0x01, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsmulsp, 0x00, 0x02, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsdivsp, 0x00, 0x03, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsresp, 0x14, 0x01, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xssqrtsp, 0x16, 0x00, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsrsqrtesp, 0x14, 0x00, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsmaddasp, 0x04, 0x00, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsmaddmsp, 0x04, 0x01, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsmsubasp, 0x04, 0x02, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsmsubmsp, 0x04, 0x03, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsnmaddasp, 0x04, 0x10, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsnmaddmsp, 0x04, 0x11, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsnmsubasp, 0x04, 0x12, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsnmsubmsp, 0x04, 0x13, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xscvsxdsp, 0x10, 0x13, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xscvuxdsp, 0x10, 0x12, 0, PPC2_VSX207) + +GEN_VSX_HELPER_2(xvadddp, 0x00, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvsubdp, 0x00, 0x0D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmuldp, 0x00, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvdivdp, 0x00, 0x0F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvredp, 0x14, 0x0D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvsqrtdp, 0x16, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrsqrtedp, 0x14, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvtdivdp, 0x14, 0x0F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvtsqrtdp, 0x14, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaddadp, 0x04, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaddmdp, 0x04, 0x0D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmsubadp, 0x04, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmsubmdp, 0x04, 0x0F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmaddadp, 0x04, 0x1C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmaddmdp, 0x04, 0x1D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmsubadp, 0x04, 0x1E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmsubmdp, 0x04, 0x1F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaxdp, 0x00, 0x1C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmindp, 0x00, 0x1D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpeqdp, 0x0C, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpgtdp, 0x0C, 0x0D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpgedp, 0x0C, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpnedp, 0x0C, 0x0F, 0, PPC2_ISA300) +GEN_VSX_HELPER_2(xvcvdpsp, 0x12, 0x18, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvdpsxds, 0x10, 0x1D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvdpsxws, 0x10, 0x0D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvdpuxds, 0x10, 0x1C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvdpuxws, 0x10, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvsxddp, 0x10, 0x1F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvuxddp, 0x10, 0x1E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvsxwdp, 0x10, 0x0F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvuxwdp, 0x10, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrdpi, 0x12, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrdpic, 0x16, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrdpim, 0x12, 0x0F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrdpip, 0x12, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrdpiz, 0x12, 0x0D, 0, PPC2_VSX) + +GEN_VSX_HELPER_2(xvaddsp, 0x00, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvsubsp, 0x00, 0x09, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmulsp, 0x00, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvdivsp, 0x00, 0x0B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvresp, 0x14, 0x09, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvsqrtsp, 0x16, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrsqrtesp, 0x14, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvtdivsp, 0x14, 0x0B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvtsqrtsp, 0x14, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaddasp, 0x04, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaddmsp, 0x04, 0x09, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmsubasp, 0x04, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmsubmsp, 0x04, 0x0B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmaddasp, 0x04, 0x18, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmaddmsp, 0x04, 0x19, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmsubasp, 0x04, 0x1A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmsubmsp, 0x04, 0x1B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaxsp, 0x00, 0x18, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvminsp, 0x00, 0x19, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpeqsp, 0x0C, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpgtsp, 0x0C, 0x09, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpgesp, 0x0C, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpnesp, 0x0C, 0x0B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvspdp, 0x12, 0x1C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvspsxds, 0x10, 0x19, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvspsxws, 0x10, 0x09, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvspuxds, 0x10, 0x18, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvspuxws, 0x10, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvsxdsp, 0x10, 0x1B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvuxdsp, 0x10, 0x1A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvsxwsp, 0x10, 0x0B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvuxwsp, 0x10, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrspi, 0x12, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrspic, 0x16, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrspim, 0x12, 0x0B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrspip, 0x12, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrspiz, 0x12, 0x09, 0, PPC2_VSX) + +static void gen_xxbrd(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); + TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + tcg_gen_bswap64_i64(xth, xbh); + tcg_gen_bswap64_i64(xtl, xbl); +} + +static void gen_xxbrh(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); + TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + gen_bswap16x8(xth, xtl, xbh, xbl); +} + +static void gen_xxbrq(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); + TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + TCGv_i64 t0 = tcg_temp_new_i64(); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + tcg_gen_bswap64_i64(t0, xbl); + tcg_gen_bswap64_i64(xtl, xbh); + tcg_gen_mov_i64(xth, t0); + tcg_temp_free_i64(t0); +} + +static void gen_xxbrw(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); + TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + gen_bswap32x4(xth, xtl, xbh, xbl); +} + +#define VSX_LOGICAL(name, tcg_op) \ +static void glue(gen_, name)(DisasContext * ctx) \ + { \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + tcg_op(cpu_vsrh(xT(ctx->opcode)), cpu_vsrh(xA(ctx->opcode)), \ + cpu_vsrh(xB(ctx->opcode))); \ + tcg_op(cpu_vsrl(xT(ctx->opcode)), cpu_vsrl(xA(ctx->opcode)), \ + cpu_vsrl(xB(ctx->opcode))); \ + } + +VSX_LOGICAL(xxland, tcg_gen_and_i64) +VSX_LOGICAL(xxlandc, tcg_gen_andc_i64) +VSX_LOGICAL(xxlor, tcg_gen_or_i64) +VSX_LOGICAL(xxlxor, tcg_gen_xor_i64) +VSX_LOGICAL(xxlnor, tcg_gen_nor_i64) +VSX_LOGICAL(xxleqv, tcg_gen_eqv_i64) +VSX_LOGICAL(xxlnand, tcg_gen_nand_i64) +VSX_LOGICAL(xxlorc, tcg_gen_orc_i64) + +#define VSX_XXMRG(name, high) \ +static void glue(gen_, name)(DisasContext * ctx) \ + { \ + TCGv_i64 a0, a1, b0, b1; \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + a0 = tcg_temp_new_i64(); \ + a1 = tcg_temp_new_i64(); \ + b0 = tcg_temp_new_i64(); \ + b1 = tcg_temp_new_i64(); \ + if (high) { \ + tcg_gen_mov_i64(a0, cpu_vsrh(xA(ctx->opcode))); \ + tcg_gen_mov_i64(a1, cpu_vsrh(xA(ctx->opcode))); \ + tcg_gen_mov_i64(b0, cpu_vsrh(xB(ctx->opcode))); \ + tcg_gen_mov_i64(b1, cpu_vsrh(xB(ctx->opcode))); \ + } else { \ + tcg_gen_mov_i64(a0, cpu_vsrl(xA(ctx->opcode))); \ + tcg_gen_mov_i64(a1, cpu_vsrl(xA(ctx->opcode))); \ + tcg_gen_mov_i64(b0, cpu_vsrl(xB(ctx->opcode))); \ + tcg_gen_mov_i64(b1, cpu_vsrl(xB(ctx->opcode))); \ + } \ + tcg_gen_shri_i64(a0, a0, 32); \ + tcg_gen_shri_i64(b0, b0, 32); \ + tcg_gen_deposit_i64(cpu_vsrh(xT(ctx->opcode)), \ + b0, a0, 32, 32); \ + tcg_gen_deposit_i64(cpu_vsrl(xT(ctx->opcode)), \ + b1, a1, 32, 32); \ + tcg_temp_free_i64(a0); \ + tcg_temp_free_i64(a1); \ + tcg_temp_free_i64(b0); \ + tcg_temp_free_i64(b1); \ + } + +VSX_XXMRG(xxmrghw, 1) +VSX_XXMRG(xxmrglw, 0) + +static void gen_xxsel(DisasContext * ctx) +{ + TCGv_i64 a, b, c; + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + a = tcg_temp_new_i64(); + b = tcg_temp_new_i64(); + c = tcg_temp_new_i64(); + + tcg_gen_mov_i64(a, cpu_vsrh(xA(ctx->opcode))); + tcg_gen_mov_i64(b, cpu_vsrh(xB(ctx->opcode))); + tcg_gen_mov_i64(c, cpu_vsrh(xC(ctx->opcode))); + + tcg_gen_and_i64(b, b, c); + tcg_gen_andc_i64(a, a, c); + tcg_gen_or_i64(cpu_vsrh(xT(ctx->opcode)), a, b); + + tcg_gen_mov_i64(a, cpu_vsrl(xA(ctx->opcode))); + tcg_gen_mov_i64(b, cpu_vsrl(xB(ctx->opcode))); + tcg_gen_mov_i64(c, cpu_vsrl(xC(ctx->opcode))); + + tcg_gen_and_i64(b, b, c); + tcg_gen_andc_i64(a, a, c); + tcg_gen_or_i64(cpu_vsrl(xT(ctx->opcode)), a, b); + + tcg_temp_free_i64(a); + tcg_temp_free_i64(b); + tcg_temp_free_i64(c); +} + +static void gen_xxspltw(DisasContext *ctx) +{ + TCGv_i64 b, b2; + TCGv_i64 vsr = (UIM(ctx->opcode) & 2) ? + cpu_vsrl(xB(ctx->opcode)) : + cpu_vsrh(xB(ctx->opcode)); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + + b = tcg_temp_new_i64(); + b2 = tcg_temp_new_i64(); + + if (UIM(ctx->opcode) & 1) { + tcg_gen_ext32u_i64(b, vsr); + } else { + tcg_gen_shri_i64(b, vsr, 32); + } + + tcg_gen_shli_i64(b2, b, 32); + tcg_gen_or_i64(cpu_vsrh(xT(ctx->opcode)), b, b2); + tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrh(xT(ctx->opcode))); + + tcg_temp_free_i64(b); + tcg_temp_free_i64(b2); +} + +#define pattern(x) (((x) & 0xff) * (~(uint64_t)0 / 0xff)) + +static void gen_xxspltib(DisasContext *ctx) +{ + unsigned char uim8 = IMM8(ctx->opcode); + if (xS(ctx->opcode) < 32) { + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + } else { + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + } + tcg_gen_movi_i64(cpu_vsrh(xT(ctx->opcode)), pattern(uim8)); + tcg_gen_movi_i64(cpu_vsrl(xT(ctx->opcode)), pattern(uim8)); +} + +static void gen_xxsldwi(DisasContext *ctx) +{ + TCGv_i64 xth, xtl; + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + xth = tcg_temp_new_i64(); + xtl = tcg_temp_new_i64(); + + switch (SHW(ctx->opcode)) { + case 0: { + tcg_gen_mov_i64(xth, cpu_vsrh(xA(ctx->opcode))); + tcg_gen_mov_i64(xtl, cpu_vsrl(xA(ctx->opcode))); + break; + } + case 1: { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_mov_i64(xth, cpu_vsrh(xA(ctx->opcode))); + tcg_gen_shli_i64(xth, xth, 32); + tcg_gen_mov_i64(t0, cpu_vsrl(xA(ctx->opcode))); + tcg_gen_shri_i64(t0, t0, 32); + tcg_gen_or_i64(xth, xth, t0); + tcg_gen_mov_i64(xtl, cpu_vsrl(xA(ctx->opcode))); + tcg_gen_shli_i64(xtl, xtl, 32); + tcg_gen_mov_i64(t0, cpu_vsrh(xB(ctx->opcode))); + tcg_gen_shri_i64(t0, t0, 32); + tcg_gen_or_i64(xtl, xtl, t0); + tcg_temp_free_i64(t0); + break; + } + case 2: { + tcg_gen_mov_i64(xth, cpu_vsrl(xA(ctx->opcode))); + tcg_gen_mov_i64(xtl, cpu_vsrh(xB(ctx->opcode))); + break; + } + case 3: { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_mov_i64(xth, cpu_vsrl(xA(ctx->opcode))); + tcg_gen_shli_i64(xth, xth, 32); + tcg_gen_mov_i64(t0, cpu_vsrh(xB(ctx->opcode))); + tcg_gen_shri_i64(t0, t0, 32); + tcg_gen_or_i64(xth, xth, t0); + tcg_gen_mov_i64(xtl, cpu_vsrh(xB(ctx->opcode))); + tcg_gen_shli_i64(xtl, xtl, 32); + tcg_gen_mov_i64(t0, cpu_vsrl(xB(ctx->opcode))); + tcg_gen_shri_i64(t0, t0, 32); + tcg_gen_or_i64(xtl, xtl, t0); + tcg_temp_free_i64(t0); + break; + } + } + + tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), xth); + tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), xtl); + + tcg_temp_free_i64(xth); + tcg_temp_free_i64(xtl); +} + +#undef GEN_XX2FORM +#undef GEN_XX3FORM +#undef GEN_XX2IFORM +#undef GEN_XX3_RC_FORM +#undef GEN_XX3FORM_DM +#undef VSX_LOGICAL diff --git a/target/ppc/translate/vsx-ops.inc.c b/target/ppc/translate/vsx-ops.inc.c new file mode 100644 index 0000000000..3d9104155a --- /dev/null +++ b/target/ppc/translate/vsx-ops.inc.c @@ -0,0 +1,300 @@ +GEN_HANDLER_E(lxsdx, 0x1F, 0x0C, 0x12, 0, PPC_NONE, PPC2_VSX), +GEN_HANDLER_E(lxsiwax, 0x1F, 0x0C, 0x02, 0, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(lxsiwzx, 0x1F, 0x0C, 0x00, 0, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(lxsibzx, 0x1F, 0x0D, 0x18, 0, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E(lxsihzx, 0x1F, 0x0D, 0x19, 0, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E(lxsspx, 0x1F, 0x0C, 0x10, 0, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(lxvd2x, 0x1F, 0x0C, 0x1A, 0, PPC_NONE, PPC2_VSX), +GEN_HANDLER_E(lxvdsx, 0x1F, 0x0C, 0x0A, 0, PPC_NONE, PPC2_VSX), +GEN_HANDLER_E(lxvw4x, 0x1F, 0x0C, 0x18, 0, PPC_NONE, PPC2_VSX), +GEN_HANDLER_E(lxvh8x, 0x1F, 0x0C, 0x19, 0, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E(lxvb16x, 0x1F, 0x0C, 0x1B, 0, PPC_NONE, PPC2_ISA300), + +GEN_HANDLER_E(stxsdx, 0x1F, 0xC, 0x16, 0, PPC_NONE, PPC2_VSX), +GEN_HANDLER_E(stxsibx, 0x1F, 0xD, 0x1C, 0, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E(stxsihx, 0x1F, 0xD, 0x1D, 0, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E(stxsiwx, 0x1F, 0xC, 0x04, 0, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(stxsspx, 0x1F, 0xC, 0x14, 0, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(stxvd2x, 0x1F, 0xC, 0x1E, 0, PPC_NONE, PPC2_VSX), +GEN_HANDLER_E(stxvw4x, 0x1F, 0xC, 0x1C, 0, PPC_NONE, PPC2_VSX), +GEN_HANDLER_E(stxvh8x, 0x1F, 0x0C, 0x1D, 0, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E(stxvb16x, 0x1F, 0x0C, 0x1F, 0, PPC_NONE, PPC2_ISA300), + +GEN_HANDLER_E(mfvsrwz, 0x1F, 0x13, 0x03, 0x0000F800, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(mtvsrwa, 0x1F, 0x13, 0x06, 0x0000F800, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(mtvsrwz, 0x1F, 0x13, 0x07, 0x0000F800, PPC_NONE, PPC2_VSX207), +#if defined(TARGET_PPC64) +GEN_HANDLER_E(mfvsrd, 0x1F, 0x13, 0x01, 0x0000F800, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(mtvsrd, 0x1F, 0x13, 0x05, 0x0000F800, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(mfvsrld, 0X1F, 0x13, 0x09, 0x0000F800, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E(mtvsrdd, 0X1F, 0x13, 0x0D, 0x0, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E(mtvsrws, 0x1F, 0x13, 0x0C, 0x0000F800, PPC_NONE, PPC2_ISA300), +#endif + +#define GEN_XX1FORM(name, opc2, opc3, fl2) \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2) + +#define GEN_XX2FORM(name, opc2, opc3, fl2) \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2) + +#define GEN_XX2FORM_EO(name, opc2, opc3, opc4, fl2) \ +GEN_HANDLER2_E_2(name, #name, 0x3C, opc2 | 0, opc3, opc4, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E_2(name, #name, 0x3C, opc2 | 1, opc3, opc4, 0, PPC_NONE, fl2) + +#define GEN_XX3FORM(name, opc2, opc3, fl2) \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 2, opc3, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 3, opc3, 0, PPC_NONE, fl2) + +#define GEN_XX2IFORM(name, opc2, opc3, fl2) \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 1, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 1, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 2, opc3, 1, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 3, opc3, 1, PPC_NONE, fl2) + +#define GEN_XX3_RC_FORM(name, opc2, opc3, fl2) \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x00, opc3 | 0x00, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x01, opc3 | 0x00, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x02, opc3 | 0x00, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x03, opc3 | 0x00, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x00, opc3 | 0x10, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x01, opc3 | 0x10, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x02, opc3 | 0x10, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x03, opc3 | 0x10, 0, PPC_NONE, fl2) + +#define GEN_XX3FORM_DM(name, opc2, opc3) \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x00, opc3|0x00, 0, PPC_NONE, PPC2_VSX),\ +GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x01, opc3|0x00, 0, PPC_NONE, PPC2_VSX),\ +GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x02, opc3|0x00, 0, PPC_NONE, PPC2_VSX),\ +GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x03, opc3|0x00, 0, PPC_NONE, PPC2_VSX),\ +GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x00, opc3|0x04, 0, PPC_NONE, PPC2_VSX),\ +GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x01, opc3|0x04, 0, PPC_NONE, PPC2_VSX),\ +GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x02, opc3|0x04, 0, PPC_NONE, PPC2_VSX),\ +GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x03, opc3|0x04, 0, PPC_NONE, PPC2_VSX),\ +GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x00, opc3|0x08, 0, PPC_NONE, PPC2_VSX),\ +GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x01, opc3|0x08, 0, PPC_NONE, PPC2_VSX),\ +GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x02, opc3|0x08, 0, PPC_NONE, PPC2_VSX),\ +GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x03, opc3|0x08, 0, PPC_NONE, PPC2_VSX),\ +GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x00, opc3|0x0C, 0, PPC_NONE, PPC2_VSX),\ +GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x01, opc3|0x0C, 0, PPC_NONE, PPC2_VSX),\ +GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x02, opc3|0x0C, 0, PPC_NONE, PPC2_VSX),\ +GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x03, opc3|0x0C, 0, PPC_NONE, PPC2_VSX) + +GEN_XX2FORM(xsabsdp, 0x12, 0x15, PPC2_VSX), +GEN_XX2FORM(xsnabsdp, 0x12, 0x16, PPC2_VSX), +GEN_XX2FORM(xsnegdp, 0x12, 0x17, PPC2_VSX), +GEN_XX3FORM(xscpsgndp, 0x00, 0x16, PPC2_VSX), + +GEN_XX2FORM(xvabsdp, 0x12, 0x1D, PPC2_VSX), +GEN_XX2FORM(xvnabsdp, 0x12, 0x1E, PPC2_VSX), +GEN_XX2FORM(xvnegdp, 0x12, 0x1F, PPC2_VSX), +GEN_XX3FORM(xvcpsgndp, 0x00, 0x1E, PPC2_VSX), +GEN_XX2FORM(xvabssp, 0x12, 0x19, PPC2_VSX), +GEN_XX2FORM(xvnabssp, 0x12, 0x1A, PPC2_VSX), +GEN_XX2FORM(xvnegsp, 0x12, 0x1B, PPC2_VSX), +GEN_XX3FORM(xvcpsgnsp, 0x00, 0x1A, PPC2_VSX), + +GEN_XX3FORM(xsadddp, 0x00, 0x04, PPC2_VSX), +GEN_XX3FORM(xssubdp, 0x00, 0x05, PPC2_VSX), +GEN_XX3FORM(xsmuldp, 0x00, 0x06, PPC2_VSX), +GEN_XX3FORM(xsdivdp, 0x00, 0x07, PPC2_VSX), +GEN_XX2FORM(xsredp, 0x14, 0x05, PPC2_VSX), +GEN_XX2FORM(xssqrtdp, 0x16, 0x04, PPC2_VSX), +GEN_XX2FORM(xsrsqrtedp, 0x14, 0x04, PPC2_VSX), +GEN_XX3FORM(xstdivdp, 0x14, 0x07, PPC2_VSX), +GEN_XX2FORM(xstsqrtdp, 0x14, 0x06, PPC2_VSX), +GEN_XX3FORM(xsmaddadp, 0x04, 0x04, PPC2_VSX), +GEN_XX3FORM(xsmaddmdp, 0x04, 0x05, PPC2_VSX), +GEN_XX3FORM(xsmsubadp, 0x04, 0x06, PPC2_VSX), +GEN_XX3FORM(xsmsubmdp, 0x04, 0x07, PPC2_VSX), +GEN_XX3FORM(xsnmaddadp, 0x04, 0x14, PPC2_VSX), +GEN_XX3FORM(xsnmaddmdp, 0x04, 0x15, PPC2_VSX), +GEN_XX3FORM(xsnmsubadp, 0x04, 0x16, PPC2_VSX), +GEN_XX3FORM(xsnmsubmdp, 0x04, 0x17, PPC2_VSX), +GEN_XX3FORM(xscmpeqdp, 0x0C, 0x00, PPC2_ISA300), +GEN_XX3FORM(xscmpgtdp, 0x0C, 0x01, PPC2_ISA300), +GEN_XX3FORM(xscmpgedp, 0x0C, 0x02, PPC2_ISA300), +GEN_XX3FORM(xscmpnedp, 0x0C, 0x03, PPC2_ISA300), +GEN_XX2IFORM(xscmpodp, 0x0C, 0x05, PPC2_VSX), +GEN_XX2IFORM(xscmpudp, 0x0C, 0x04, PPC2_VSX), +GEN_XX3FORM(xsmaxdp, 0x00, 0x14, PPC2_VSX), +GEN_XX3FORM(xsmindp, 0x00, 0x15, PPC2_VSX), +GEN_XX2FORM(xscvdpsp, 0x12, 0x10, PPC2_VSX), +GEN_XX2FORM(xscvdpspn, 0x16, 0x10, PPC2_VSX207), +GEN_XX2FORM(xscvspdp, 0x12, 0x14, PPC2_VSX), +GEN_XX2FORM(xscvspdpn, 0x16, 0x14, PPC2_VSX207), +GEN_XX2FORM(xscvdpsxds, 0x10, 0x15, PPC2_VSX), +GEN_XX2FORM(xscvdpsxws, 0x10, 0x05, PPC2_VSX), +GEN_XX2FORM(xscvdpuxds, 0x10, 0x14, PPC2_VSX), +GEN_XX2FORM(xscvdpuxws, 0x10, 0x04, PPC2_VSX), +GEN_XX2FORM(xscvsxddp, 0x10, 0x17, PPC2_VSX), +GEN_XX2FORM(xscvuxddp, 0x10, 0x16, PPC2_VSX), +GEN_XX2FORM(xsrdpi, 0x12, 0x04, PPC2_VSX), +GEN_XX2FORM(xsrdpic, 0x16, 0x06, PPC2_VSX), +GEN_XX2FORM(xsrdpim, 0x12, 0x07, PPC2_VSX), +GEN_XX2FORM(xsrdpip, 0x12, 0x06, PPC2_VSX), +GEN_XX2FORM(xsrdpiz, 0x12, 0x05, PPC2_VSX), + +GEN_XX3FORM(xsaddsp, 0x00, 0x00, PPC2_VSX207), +GEN_XX3FORM(xssubsp, 0x00, 0x01, PPC2_VSX207), +GEN_XX3FORM(xsmulsp, 0x00, 0x02, PPC2_VSX207), +GEN_XX3FORM(xsdivsp, 0x00, 0x03, PPC2_VSX207), +GEN_XX2FORM(xsresp, 0x14, 0x01, PPC2_VSX207), +GEN_XX2FORM(xsrsp, 0x12, 0x11, PPC2_VSX207), +GEN_XX2FORM(xssqrtsp, 0x16, 0x00, PPC2_VSX207), +GEN_XX2FORM(xsrsqrtesp, 0x14, 0x00, PPC2_VSX207), +GEN_XX3FORM(xsmaddasp, 0x04, 0x00, PPC2_VSX207), +GEN_XX3FORM(xsmaddmsp, 0x04, 0x01, PPC2_VSX207), +GEN_XX3FORM(xsmsubasp, 0x04, 0x02, PPC2_VSX207), +GEN_XX3FORM(xsmsubmsp, 0x04, 0x03, PPC2_VSX207), +GEN_XX3FORM(xsnmaddasp, 0x04, 0x10, PPC2_VSX207), +GEN_XX3FORM(xsnmaddmsp, 0x04, 0x11, PPC2_VSX207), +GEN_XX3FORM(xsnmsubasp, 0x04, 0x12, PPC2_VSX207), +GEN_XX3FORM(xsnmsubmsp, 0x04, 0x13, PPC2_VSX207), +GEN_XX2FORM(xscvsxdsp, 0x10, 0x13, PPC2_VSX207), +GEN_XX2FORM(xscvuxdsp, 0x10, 0x12, PPC2_VSX207), + +GEN_XX3FORM(xvadddp, 0x00, 0x0C, PPC2_VSX), +GEN_XX3FORM(xvsubdp, 0x00, 0x0D, PPC2_VSX), +GEN_XX3FORM(xvmuldp, 0x00, 0x0E, PPC2_VSX), +GEN_XX3FORM(xvdivdp, 0x00, 0x0F, PPC2_VSX), +GEN_XX2FORM(xvredp, 0x14, 0x0D, PPC2_VSX), +GEN_XX2FORM(xvsqrtdp, 0x16, 0x0C, PPC2_VSX), +GEN_XX2FORM(xvrsqrtedp, 0x14, 0x0C, PPC2_VSX), +GEN_XX3FORM(xvtdivdp, 0x14, 0x0F, PPC2_VSX), +GEN_XX2FORM(xvtsqrtdp, 0x14, 0x0E, PPC2_VSX), +GEN_XX3FORM(xvmaddadp, 0x04, 0x0C, PPC2_VSX), +GEN_XX3FORM(xvmaddmdp, 0x04, 0x0D, PPC2_VSX), +GEN_XX3FORM(xvmsubadp, 0x04, 0x0E, PPC2_VSX), +GEN_XX3FORM(xvmsubmdp, 0x04, 0x0F, PPC2_VSX), +GEN_XX3FORM(xvnmaddadp, 0x04, 0x1C, PPC2_VSX), +GEN_XX3FORM(xvnmaddmdp, 0x04, 0x1D, PPC2_VSX), +GEN_XX3FORM(xvnmsubadp, 0x04, 0x1E, PPC2_VSX), +GEN_XX3FORM(xvnmsubmdp, 0x04, 0x1F, PPC2_VSX), +GEN_XX3FORM(xvmaxdp, 0x00, 0x1C, PPC2_VSX), +GEN_XX3FORM(xvmindp, 0x00, 0x1D, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpeqdp, 0x0C, 0x0C, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpgtdp, 0x0C, 0x0D, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpgedp, 0x0C, 0x0E, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpnedp, 0x0C, 0x0F, PPC2_ISA300), +GEN_XX2FORM(xvcvdpsp, 0x12, 0x18, PPC2_VSX), +GEN_XX2FORM(xvcvdpsxds, 0x10, 0x1D, PPC2_VSX), +GEN_XX2FORM(xvcvdpsxws, 0x10, 0x0D, PPC2_VSX), +GEN_XX2FORM(xvcvdpuxds, 0x10, 0x1C, PPC2_VSX), +GEN_XX2FORM(xvcvdpuxws, 0x10, 0x0C, PPC2_VSX), +GEN_XX2FORM(xvcvsxddp, 0x10, 0x1F, PPC2_VSX), +GEN_XX2FORM(xvcvuxddp, 0x10, 0x1E, PPC2_VSX), +GEN_XX2FORM(xvcvsxwdp, 0x10, 0x0F, PPC2_VSX), +GEN_XX2FORM(xvcvuxwdp, 0x10, 0x0E, PPC2_VSX), +GEN_XX2FORM(xvrdpi, 0x12, 0x0C, PPC2_VSX), +GEN_XX2FORM(xvrdpic, 0x16, 0x0E, PPC2_VSX), +GEN_XX2FORM(xvrdpim, 0x12, 0x0F, PPC2_VSX), +GEN_XX2FORM(xvrdpip, 0x12, 0x0E, PPC2_VSX), +GEN_XX2FORM(xvrdpiz, 0x12, 0x0D, PPC2_VSX), + +GEN_XX3FORM(xvaddsp, 0x00, 0x08, PPC2_VSX), +GEN_XX3FORM(xvsubsp, 0x00, 0x09, PPC2_VSX), +GEN_XX3FORM(xvmulsp, 0x00, 0x0A, PPC2_VSX), +GEN_XX3FORM(xvdivsp, 0x00, 0x0B, PPC2_VSX), +GEN_XX2FORM(xvresp, 0x14, 0x09, PPC2_VSX), +GEN_XX2FORM(xvsqrtsp, 0x16, 0x08, PPC2_VSX), +GEN_XX2FORM(xvrsqrtesp, 0x14, 0x08, PPC2_VSX), +GEN_XX3FORM(xvtdivsp, 0x14, 0x0B, PPC2_VSX), +GEN_XX2FORM(xvtsqrtsp, 0x14, 0x0A, PPC2_VSX), +GEN_XX3FORM(xvmaddasp, 0x04, 0x08, PPC2_VSX), +GEN_XX3FORM(xvmaddmsp, 0x04, 0x09, PPC2_VSX), +GEN_XX3FORM(xvmsubasp, 0x04, 0x0A, PPC2_VSX), +GEN_XX3FORM(xvmsubmsp, 0x04, 0x0B, PPC2_VSX), +GEN_XX3FORM(xvnmaddasp, 0x04, 0x18, PPC2_VSX), +GEN_XX3FORM(xvnmaddmsp, 0x04, 0x19, PPC2_VSX), +GEN_XX3FORM(xvnmsubasp, 0x04, 0x1A, PPC2_VSX), +GEN_XX3FORM(xvnmsubmsp, 0x04, 0x1B, PPC2_VSX), +GEN_XX3FORM(xvmaxsp, 0x00, 0x18, PPC2_VSX), +GEN_XX3FORM(xvminsp, 0x00, 0x19, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpeqsp, 0x0C, 0x08, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpgtsp, 0x0C, 0x09, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpgesp, 0x0C, 0x0A, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpnesp, 0x0C, 0x0B, PPC2_ISA300), +GEN_XX2FORM(xvcvspdp, 0x12, 0x1C, PPC2_VSX), +GEN_XX2FORM(xvcvspsxds, 0x10, 0x19, PPC2_VSX), +GEN_XX2FORM(xvcvspsxws, 0x10, 0x09, PPC2_VSX), +GEN_XX2FORM(xvcvspuxds, 0x10, 0x18, PPC2_VSX), +GEN_XX2FORM(xvcvspuxws, 0x10, 0x08, PPC2_VSX), +GEN_XX2FORM(xvcvsxdsp, 0x10, 0x1B, PPC2_VSX), +GEN_XX2FORM(xvcvuxdsp, 0x10, 0x1A, PPC2_VSX), +GEN_XX2FORM(xvcvsxwsp, 0x10, 0x0B, PPC2_VSX), +GEN_XX2FORM(xvcvuxwsp, 0x10, 0x0A, PPC2_VSX), +GEN_XX2FORM(xvrspi, 0x12, 0x08, PPC2_VSX), +GEN_XX2FORM(xvrspic, 0x16, 0x0A, PPC2_VSX), +GEN_XX2FORM(xvrspim, 0x12, 0x0B, PPC2_VSX), +GEN_XX2FORM(xvrspip, 0x12, 0x0A, PPC2_VSX), +GEN_XX2FORM(xvrspiz, 0x12, 0x09, PPC2_VSX), +GEN_XX2FORM_EO(xxbrh, 0x16, 0x1D, 0x07, PPC2_ISA300), +GEN_XX2FORM_EO(xxbrw, 0x16, 0x1D, 0x0F, PPC2_ISA300), +GEN_XX2FORM_EO(xxbrd, 0x16, 0x1D, 0x17, PPC2_ISA300), +GEN_XX2FORM_EO(xxbrq, 0x16, 0x1D, 0x1F, PPC2_ISA300), + +#define VSX_LOGICAL(name, opc2, opc3, fl2) \ +GEN_XX3FORM(name, opc2, opc3, fl2) + +VSX_LOGICAL(xxland, 0x8, 0x10, PPC2_VSX), +VSX_LOGICAL(xxlandc, 0x8, 0x11, PPC2_VSX), +VSX_LOGICAL(xxlor, 0x8, 0x12, PPC2_VSX), +VSX_LOGICAL(xxlxor, 0x8, 0x13, PPC2_VSX), +VSX_LOGICAL(xxlnor, 0x8, 0x14, PPC2_VSX), +VSX_LOGICAL(xxleqv, 0x8, 0x17, PPC2_VSX207), +VSX_LOGICAL(xxlnand, 0x8, 0x16, PPC2_VSX207), +VSX_LOGICAL(xxlorc, 0x8, 0x15, PPC2_VSX207), +GEN_XX3FORM(xxmrghw, 0x08, 0x02, PPC2_VSX), +GEN_XX3FORM(xxmrglw, 0x08, 0x06, PPC2_VSX), +GEN_XX2FORM(xxspltw, 0x08, 0x0A, PPC2_VSX), +GEN_XX1FORM(xxspltib, 0x08, 0x0B, PPC2_ISA300), +GEN_XX3FORM_DM(xxsldwi, 0x08, 0x00), + +#define GEN_XXSEL_ROW(opc3) \ +GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x18, opc3, 0, PPC_NONE, PPC2_VSX), \ +GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x19, opc3, 0, PPC_NONE, PPC2_VSX), \ +GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1A, opc3, 0, PPC_NONE, PPC2_VSX), \ +GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1B, opc3, 0, PPC_NONE, PPC2_VSX), \ +GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1C, opc3, 0, PPC_NONE, PPC2_VSX), \ +GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1D, opc3, 0, PPC_NONE, PPC2_VSX), \ +GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1E, opc3, 0, PPC_NONE, PPC2_VSX), \ +GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1F, opc3, 0, PPC_NONE, PPC2_VSX), \ + +GEN_XXSEL_ROW(0x00) +GEN_XXSEL_ROW(0x01) +GEN_XXSEL_ROW(0x02) +GEN_XXSEL_ROW(0x03) +GEN_XXSEL_ROW(0x04) +GEN_XXSEL_ROW(0x05) +GEN_XXSEL_ROW(0x06) +GEN_XXSEL_ROW(0x07) +GEN_XXSEL_ROW(0x08) +GEN_XXSEL_ROW(0x09) +GEN_XXSEL_ROW(0x0A) +GEN_XXSEL_ROW(0x0B) +GEN_XXSEL_ROW(0x0C) +GEN_XXSEL_ROW(0x0D) +GEN_XXSEL_ROW(0x0E) +GEN_XXSEL_ROW(0x0F) +GEN_XXSEL_ROW(0x10) +GEN_XXSEL_ROW(0x11) +GEN_XXSEL_ROW(0x12) +GEN_XXSEL_ROW(0x13) +GEN_XXSEL_ROW(0x14) +GEN_XXSEL_ROW(0x15) +GEN_XXSEL_ROW(0x16) +GEN_XXSEL_ROW(0x17) +GEN_XXSEL_ROW(0x18) +GEN_XXSEL_ROW(0x19) +GEN_XXSEL_ROW(0x1A) +GEN_XXSEL_ROW(0x1B) +GEN_XXSEL_ROW(0x1C) +GEN_XXSEL_ROW(0x1D) +GEN_XXSEL_ROW(0x1E) +GEN_XXSEL_ROW(0x1F) + +GEN_XX3FORM_DM(xxpermdi, 0x08, 0x01), diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c new file mode 100644 index 0000000000..626e03186c --- /dev/null +++ b/target/ppc/translate_init.c @@ -0,0 +1,10601 @@ +/* + * PowerPC CPU initialization for qemu. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * Copyright 2011 Freescale Semiconductor, Inc. + * + * 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 "qemu/osdep.h" +#include "disas/bfd.h" +#include "exec/gdbstub.h" +#include "sysemu/kvm.h" +#include "kvm_ppc.h" +#include "sysemu/arch_init.h" +#include "sysemu/cpus.h" +#include "cpu-models.h" +#include "mmu-hash32.h" +#include "mmu-hash64.h" +#include "qemu/error-report.h" +#include "qapi/visitor.h" +#include "hw/qdev-properties.h" +#include "hw/ppc/ppc.h" + +//#define PPC_DUMP_CPU +//#define PPC_DEBUG_SPR +//#define PPC_DUMP_SPR_ACCESSES +/* #define USE_APPLE_GDB */ + +/* Generic callbacks: + * do nothing but store/retrieve spr value + */ +static void spr_load_dump_spr(int sprn) +{ +#ifdef PPC_DUMP_SPR_ACCESSES + TCGv_i32 t0 = tcg_const_i32(sprn); + gen_helper_load_dump_spr(cpu_env, t0); + tcg_temp_free_i32(t0); +#endif +} + +static void spr_read_generic (DisasContext *ctx, int gprn, int sprn) +{ + gen_load_spr(cpu_gpr[gprn], sprn); + spr_load_dump_spr(sprn); +} + +static void spr_store_dump_spr(int sprn) +{ +#ifdef PPC_DUMP_SPR_ACCESSES + TCGv_i32 t0 = tcg_const_i32(sprn); + gen_helper_store_dump_spr(cpu_env, t0); + tcg_temp_free_i32(t0); +#endif +} + +static void spr_write_generic (DisasContext *ctx, int sprn, int gprn) +{ + gen_store_spr(sprn, cpu_gpr[gprn]); + spr_store_dump_spr(sprn); +} + +#if !defined(CONFIG_USER_ONLY) +static void spr_write_generic32(DisasContext *ctx, int sprn, int gprn) +{ +#ifdef TARGET_PPC64 + TCGv t0 = tcg_temp_new(); + tcg_gen_ext32u_tl(t0, cpu_gpr[gprn]); + gen_store_spr(sprn, t0); + tcg_temp_free(t0); + spr_store_dump_spr(sprn); +#else + spr_write_generic(ctx, sprn, gprn); +#endif +} + +static void spr_write_clear (DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + gen_load_spr(t0, sprn); + tcg_gen_neg_tl(t1, cpu_gpr[gprn]); + tcg_gen_and_tl(t0, t0, t1); + gen_store_spr(sprn, t0); + tcg_temp_free(t0); + tcg_temp_free(t1); +} + +static void spr_access_nop(DisasContext *ctx, int sprn, int gprn) +{ +} + +#endif + +/* SPR common to all PowerPC */ +/* XER */ +static void spr_read_xer (DisasContext *ctx, int gprn, int sprn) +{ + gen_read_xer(cpu_gpr[gprn]); +} + +static void spr_write_xer (DisasContext *ctx, int sprn, int gprn) +{ + gen_write_xer(cpu_gpr[gprn]); +} + +/* LR */ +static void spr_read_lr (DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_mov_tl(cpu_gpr[gprn], cpu_lr); +} + +static void spr_write_lr (DisasContext *ctx, int sprn, int gprn) +{ + tcg_gen_mov_tl(cpu_lr, cpu_gpr[gprn]); +} + +/* CFAR */ +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +static void spr_read_cfar (DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_mov_tl(cpu_gpr[gprn], cpu_cfar); +} + +static void spr_write_cfar (DisasContext *ctx, int sprn, int gprn) +{ + tcg_gen_mov_tl(cpu_cfar, cpu_gpr[gprn]); +} +#endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */ + +/* CTR */ +static void spr_read_ctr (DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_mov_tl(cpu_gpr[gprn], cpu_ctr); +} + +static void spr_write_ctr (DisasContext *ctx, int sprn, int gprn) +{ + tcg_gen_mov_tl(cpu_ctr, cpu_gpr[gprn]); +} + +/* User read access to SPR */ +/* USPRx */ +/* UMMCRx */ +/* UPMCx */ +/* USIA */ +/* UDECR */ +static void spr_read_ureg (DisasContext *ctx, int gprn, int sprn) +{ + gen_load_spr(cpu_gpr[gprn], sprn + 0x10); +} + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +static void spr_write_ureg(DisasContext *ctx, int sprn, int gprn) +{ + gen_store_spr(sprn + 0x10, cpu_gpr[gprn]); +} +#endif + +/* SPR common to all non-embedded PowerPC */ +/* DECR */ +#if !defined(CONFIG_USER_ONLY) +static void spr_read_decr (DisasContext *ctx, int gprn, int sprn) +{ + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_load_decr(cpu_gpr[gprn], cpu_env); + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_end(); + gen_stop_exception(ctx); + } +} + +static void spr_write_decr (DisasContext *ctx, int sprn, int gprn) +{ + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_store_decr(cpu_env, cpu_gpr[gprn]); + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_end(); + gen_stop_exception(ctx); + } +} +#endif + +/* SPR common to all non-embedded PowerPC, except 601 */ +/* Time base */ +static void spr_read_tbl (DisasContext *ctx, int gprn, int sprn) +{ + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_load_tbl(cpu_gpr[gprn], cpu_env); + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_end(); + gen_stop_exception(ctx); + } +} + +static void spr_read_tbu (DisasContext *ctx, int gprn, int sprn) +{ + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_load_tbu(cpu_gpr[gprn], cpu_env); + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_end(); + gen_stop_exception(ctx); + } +} + +__attribute__ (( unused )) +static void spr_read_atbl (DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_load_atbl(cpu_gpr[gprn], cpu_env); +} + +__attribute__ (( unused )) +static void spr_read_atbu (DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_load_atbu(cpu_gpr[gprn], cpu_env); +} + +#if !defined(CONFIG_USER_ONLY) +static void spr_write_tbl (DisasContext *ctx, int sprn, int gprn) +{ + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_store_tbl(cpu_env, cpu_gpr[gprn]); + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_end(); + gen_stop_exception(ctx); + } +} + +static void spr_write_tbu (DisasContext *ctx, int sprn, int gprn) +{ + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_store_tbu(cpu_env, cpu_gpr[gprn]); + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_end(); + gen_stop_exception(ctx); + } +} + +__attribute__ (( unused )) +static void spr_write_atbl (DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_atbl(cpu_env, cpu_gpr[gprn]); +} + +__attribute__ (( unused )) +static void spr_write_atbu (DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_atbu(cpu_env, cpu_gpr[gprn]); +} + +#if defined(TARGET_PPC64) +__attribute__ (( unused )) +static void spr_read_purr (DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_load_purr(cpu_gpr[gprn], cpu_env); +} + +/* HDECR */ +static void spr_read_hdecr(DisasContext *ctx, int gprn, int sprn) +{ + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_load_hdecr(cpu_gpr[gprn], cpu_env); + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_end(); + gen_stop_exception(ctx); + } +} + +static void spr_write_hdecr(DisasContext *ctx, int sprn, int gprn) +{ + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_store_hdecr(cpu_env, cpu_gpr[gprn]); + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_end(); + gen_stop_exception(ctx); + } +} + +#endif +#endif + +#if !defined(CONFIG_USER_ONLY) +/* IBAT0U...IBAT0U */ +/* IBAT0L...IBAT7L */ +static void spr_read_ibat (DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, IBAT[sprn & 1][(sprn - SPR_IBAT0U) / 2])); +} + +static void spr_read_ibat_h (DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, IBAT[sprn & 1][((sprn - SPR_IBAT4U) / 2) + 4])); +} + +static void spr_write_ibatu (DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); + gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +static void spr_write_ibatu_h (DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4U) / 2) + 4); + gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +static void spr_write_ibatl (DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0L) / 2); + gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +static void spr_write_ibatl_h (DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4L) / 2) + 4); + gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +/* DBAT0U...DBAT7U */ +/* DBAT0L...DBAT7L */ +static void spr_read_dbat (DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, DBAT[sprn & 1][(sprn - SPR_DBAT0U) / 2])); +} + +static void spr_read_dbat_h (DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, DBAT[sprn & 1][((sprn - SPR_DBAT4U) / 2) + 4])); +} + +static void spr_write_dbatu (DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0U) / 2); + gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +static void spr_write_dbatu_h (DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4U) / 2) + 4); + gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +static void spr_write_dbatl (DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0L) / 2); + gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +static void spr_write_dbatl_h (DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4L) / 2) + 4); + gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +/* SDR1 */ +static void spr_write_sdr1 (DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_sdr1(cpu_env, cpu_gpr[gprn]); +} + +/* 64 bits PowerPC specific SPRs */ +#if defined(TARGET_PPC64) +static void spr_read_hior (DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, excp_prefix)); +} + +static void spr_write_hior (DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0x3FFFFF00000ULL); + tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, excp_prefix)); + tcg_temp_free(t0); +} +#endif +#endif + +/* PowerPC 601 specific registers */ +/* RTC */ +static void spr_read_601_rtcl (DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_load_601_rtcl(cpu_gpr[gprn], cpu_env); +} + +static void spr_read_601_rtcu (DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_load_601_rtcu(cpu_gpr[gprn], cpu_env); +} + +#if !defined(CONFIG_USER_ONLY) +static void spr_write_601_rtcu (DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_601_rtcu(cpu_env, cpu_gpr[gprn]); +} + +static void spr_write_601_rtcl (DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_601_rtcl(cpu_env, cpu_gpr[gprn]); +} + +static void spr_write_hid0_601 (DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_hid0_601(cpu_env, cpu_gpr[gprn]); + /* Must stop the translation as endianness may have changed */ + gen_stop_exception(ctx); +} +#endif + +/* Unified bats */ +#if !defined(CONFIG_USER_ONLY) +static void spr_read_601_ubat (DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, IBAT[sprn & 1][(sprn - SPR_IBAT0U) / 2])); +} + +static void spr_write_601_ubatu (DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); + gen_helper_store_601_batl(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +static void spr_write_601_ubatl (DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); + gen_helper_store_601_batu(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} +#endif + +/* PowerPC 40x specific registers */ +#if !defined(CONFIG_USER_ONLY) +static void spr_read_40x_pit (DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_load_40x_pit(cpu_gpr[gprn], cpu_env); +} + +static void spr_write_40x_pit (DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_40x_pit(cpu_env, cpu_gpr[gprn]); +} + +static void spr_write_40x_dbcr0 (DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_40x_dbcr0(cpu_env, cpu_gpr[gprn]); + /* We must stop translation as we may have rebooted */ + gen_stop_exception(ctx); +} + +static void spr_write_40x_sler (DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_40x_sler(cpu_env, cpu_gpr[gprn]); +} + +static void spr_write_booke_tcr (DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_booke_tcr(cpu_env, cpu_gpr[gprn]); +} + +static void spr_write_booke_tsr (DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_booke_tsr(cpu_env, cpu_gpr[gprn]); +} +#endif + +/* PowerPC 403 specific registers */ +/* PBL1 / PBU1 / PBL2 / PBU2 */ +#if !defined(CONFIG_USER_ONLY) +static void spr_read_403_pbr (DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, pb[sprn - SPR_403_PBL1])); +} + +static void spr_write_403_pbr (DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32(sprn - SPR_403_PBL1); + gen_helper_store_403_pbr(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +static void spr_write_pir (DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0xF); + gen_store_spr(SPR_PIR, t0); + tcg_temp_free(t0); +} +#endif + +/* SPE specific registers */ +static void spr_read_spefscr (DisasContext *ctx, int gprn, int sprn) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_ld_i32(t0, cpu_env, offsetof(CPUPPCState, spe_fscr)); + tcg_gen_extu_i32_tl(cpu_gpr[gprn], t0); + tcg_temp_free_i32(t0); +} + +static void spr_write_spefscr (DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t0, cpu_gpr[gprn]); + tcg_gen_st_i32(t0, cpu_env, offsetof(CPUPPCState, spe_fscr)); + tcg_temp_free_i32(t0); +} + +#if !defined(CONFIG_USER_ONLY) +/* Callback used to write the exception vector base */ +static void spr_write_excp_prefix (DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUPPCState, ivpr_mask)); + tcg_gen_and_tl(t0, t0, cpu_gpr[gprn]); + tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, excp_prefix)); + gen_store_spr(sprn, t0); + tcg_temp_free(t0); +} + +static void spr_write_excp_vector (DisasContext *ctx, int sprn, int gprn) +{ + int sprn_offs; + + if (sprn >= SPR_BOOKE_IVOR0 && sprn <= SPR_BOOKE_IVOR15) { + sprn_offs = sprn - SPR_BOOKE_IVOR0; + } else if (sprn >= SPR_BOOKE_IVOR32 && sprn <= SPR_BOOKE_IVOR37) { + sprn_offs = sprn - SPR_BOOKE_IVOR32 + 32; + } else if (sprn >= SPR_BOOKE_IVOR38 && sprn <= SPR_BOOKE_IVOR42) { + sprn_offs = sprn - SPR_BOOKE_IVOR38 + 38; + } else { + printf("Trying to write an unknown exception vector %d %03x\n", + sprn, sprn); + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + return; + } + + TCGv t0 = tcg_temp_new(); + tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUPPCState, ivor_mask)); + tcg_gen_and_tl(t0, t0, cpu_gpr[gprn]); + tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, excp_vectors[sprn_offs])); + gen_store_spr(sprn, t0); + tcg_temp_free(t0); +} +#endif + +static inline void vscr_init (CPUPPCState *env, uint32_t val) +{ + env->vscr = val; + /* Altivec always uses round-to-nearest */ + set_float_rounding_mode(float_round_nearest_even, &env->vec_status); + set_flush_to_zero(vscr_nj, &env->vec_status); +} + +#ifdef CONFIG_USER_ONLY +#define spr_register_kvm(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, one_reg_id, initial_value) \ + _spr_register(env, num, name, uea_read, uea_write, initial_value) +#define spr_register_kvm_hv(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, hea_read, hea_write, \ + one_reg_id, initial_value) \ + _spr_register(env, num, name, uea_read, uea_write, initial_value) +#else +#if !defined(CONFIG_KVM) +#define spr_register_kvm(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, one_reg_id, initial_value) \ + _spr_register(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, oea_read, oea_write, initial_value) +#define spr_register_kvm_hv(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, hea_read, hea_write, \ + one_reg_id, initial_value) \ + _spr_register(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, hea_read, hea_write, initial_value) +#else +#define spr_register_kvm(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, one_reg_id, initial_value) \ + _spr_register(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, oea_read, oea_write, \ + one_reg_id, initial_value) +#define spr_register_kvm_hv(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, hea_read, hea_write, \ + one_reg_id, initial_value) \ + _spr_register(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, hea_read, hea_write, \ + one_reg_id, initial_value) +#endif +#endif + +#define spr_register(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, initial_value) \ + spr_register_kvm(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, 0, initial_value) + +#define spr_register_hv(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, hea_read, hea_write, \ + initial_value) \ + spr_register_kvm_hv(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, hea_read, hea_write, \ + 0, initial_value) + +static inline void _spr_register(CPUPPCState *env, int num, + const char *name, + void (*uea_read)(DisasContext *ctx, int gprn, int sprn), + void (*uea_write)(DisasContext *ctx, int sprn, int gprn), +#if !defined(CONFIG_USER_ONLY) + + void (*oea_read)(DisasContext *ctx, int gprn, int sprn), + void (*oea_write)(DisasContext *ctx, int sprn, int gprn), + void (*hea_read)(DisasContext *opaque, int gprn, int sprn), + void (*hea_write)(DisasContext *opaque, int sprn, int gprn), +#endif +#if defined(CONFIG_KVM) + uint64_t one_reg_id, +#endif + target_ulong initial_value) +{ + ppc_spr_t *spr; + + spr = &env->spr_cb[num]; + if (spr->name != NULL ||env-> spr[num] != 0x00000000 || +#if !defined(CONFIG_USER_ONLY) + spr->oea_read != NULL || spr->oea_write != NULL || +#endif + spr->uea_read != NULL || spr->uea_write != NULL) { + printf("Error: Trying to register SPR %d (%03x) twice !\n", num, num); + exit(1); + } +#if defined(PPC_DEBUG_SPR) + printf("*** register spr %d (%03x) %s val " TARGET_FMT_lx "\n", num, num, + name, initial_value); +#endif + spr->name = name; + spr->uea_read = uea_read; + spr->uea_write = uea_write; +#if !defined(CONFIG_USER_ONLY) + spr->oea_read = oea_read; + spr->oea_write = oea_write; + spr->hea_read = hea_read; + spr->hea_write = hea_write; +#endif +#if defined(CONFIG_KVM) + spr->one_reg_id = one_reg_id, +#endif + env->spr[num] = spr->default_value = initial_value; +} + +/* Generic PowerPC SPRs */ +static void gen_spr_generic (CPUPPCState *env) +{ + /* Integer processing */ + spr_register(env, SPR_XER, "XER", + &spr_read_xer, &spr_write_xer, + &spr_read_xer, &spr_write_xer, + 0x00000000); + /* Branch contol */ + spr_register(env, SPR_LR, "LR", + &spr_read_lr, &spr_write_lr, + &spr_read_lr, &spr_write_lr, + 0x00000000); + spr_register(env, SPR_CTR, "CTR", + &spr_read_ctr, &spr_write_ctr, + &spr_read_ctr, &spr_write_ctr, + 0x00000000); + /* Interrupt processing */ + spr_register(env, SPR_SRR0, "SRR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SRR1, "SRR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Processor control */ + spr_register(env, SPR_SPRG0, "SPRG0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SPRG1, "SPRG1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SPRG2, "SPRG2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SPRG3, "SPRG3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +/* SPR common to all non-embedded PowerPC, including 601 */ +static void gen_spr_ne_601 (CPUPPCState *env) +{ + /* Exception processing */ + spr_register_kvm(env, SPR_DSISR, "DSISR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DSISR, 0x00000000); + spr_register_kvm(env, SPR_DAR, "DAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DAR, 0x00000000); + /* Timer */ + spr_register(env, SPR_DECR, "DECR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_decr, &spr_write_decr, + 0x00000000); + /* Memory management */ + spr_register(env, SPR_SDR1, "SDR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_sdr1, + 0x00000000); +} + +/* BATs 0-3 */ +static void gen_low_BATs (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + spr_register(env, SPR_IBAT0U, "IBAT0U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat, &spr_write_ibatu, + 0x00000000); + spr_register(env, SPR_IBAT0L, "IBAT0L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat, &spr_write_ibatl, + 0x00000000); + spr_register(env, SPR_IBAT1U, "IBAT1U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat, &spr_write_ibatu, + 0x00000000); + spr_register(env, SPR_IBAT1L, "IBAT1L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat, &spr_write_ibatl, + 0x00000000); + spr_register(env, SPR_IBAT2U, "IBAT2U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat, &spr_write_ibatu, + 0x00000000); + spr_register(env, SPR_IBAT2L, "IBAT2L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat, &spr_write_ibatl, + 0x00000000); + spr_register(env, SPR_IBAT3U, "IBAT3U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat, &spr_write_ibatu, + 0x00000000); + spr_register(env, SPR_IBAT3L, "IBAT3L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat, &spr_write_ibatl, + 0x00000000); + spr_register(env, SPR_DBAT0U, "DBAT0U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat, &spr_write_dbatu, + 0x00000000); + spr_register(env, SPR_DBAT0L, "DBAT0L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat, &spr_write_dbatl, + 0x00000000); + spr_register(env, SPR_DBAT1U, "DBAT1U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat, &spr_write_dbatu, + 0x00000000); + spr_register(env, SPR_DBAT1L, "DBAT1L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat, &spr_write_dbatl, + 0x00000000); + spr_register(env, SPR_DBAT2U, "DBAT2U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat, &spr_write_dbatu, + 0x00000000); + spr_register(env, SPR_DBAT2L, "DBAT2L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat, &spr_write_dbatl, + 0x00000000); + spr_register(env, SPR_DBAT3U, "DBAT3U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat, &spr_write_dbatu, + 0x00000000); + spr_register(env, SPR_DBAT3L, "DBAT3L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat, &spr_write_dbatl, + 0x00000000); + env->nb_BATs += 4; +#endif +} + +/* BATs 4-7 */ +static void gen_high_BATs (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + spr_register(env, SPR_IBAT4U, "IBAT4U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat_h, &spr_write_ibatu_h, + 0x00000000); + spr_register(env, SPR_IBAT4L, "IBAT4L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat_h, &spr_write_ibatl_h, + 0x00000000); + spr_register(env, SPR_IBAT5U, "IBAT5U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat_h, &spr_write_ibatu_h, + 0x00000000); + spr_register(env, SPR_IBAT5L, "IBAT5L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat_h, &spr_write_ibatl_h, + 0x00000000); + spr_register(env, SPR_IBAT6U, "IBAT6U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat_h, &spr_write_ibatu_h, + 0x00000000); + spr_register(env, SPR_IBAT6L, "IBAT6L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat_h, &spr_write_ibatl_h, + 0x00000000); + spr_register(env, SPR_IBAT7U, "IBAT7U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat_h, &spr_write_ibatu_h, + 0x00000000); + spr_register(env, SPR_IBAT7L, "IBAT7L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat_h, &spr_write_ibatl_h, + 0x00000000); + spr_register(env, SPR_DBAT4U, "DBAT4U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat_h, &spr_write_dbatu_h, + 0x00000000); + spr_register(env, SPR_DBAT4L, "DBAT4L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat_h, &spr_write_dbatl_h, + 0x00000000); + spr_register(env, SPR_DBAT5U, "DBAT5U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat_h, &spr_write_dbatu_h, + 0x00000000); + spr_register(env, SPR_DBAT5L, "DBAT5L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat_h, &spr_write_dbatl_h, + 0x00000000); + spr_register(env, SPR_DBAT6U, "DBAT6U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat_h, &spr_write_dbatu_h, + 0x00000000); + spr_register(env, SPR_DBAT6L, "DBAT6L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat_h, &spr_write_dbatl_h, + 0x00000000); + spr_register(env, SPR_DBAT7U, "DBAT7U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat_h, &spr_write_dbatu_h, + 0x00000000); + spr_register(env, SPR_DBAT7L, "DBAT7L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat_h, &spr_write_dbatl_h, + 0x00000000); + env->nb_BATs += 4; +#endif +} + +/* Generic PowerPC time base */ +static void gen_tbl (CPUPPCState *env) +{ + spr_register(env, SPR_VTBL, "TBL", + &spr_read_tbl, SPR_NOACCESS, + &spr_read_tbl, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_TBL, "TBL", + &spr_read_tbl, SPR_NOACCESS, + &spr_read_tbl, &spr_write_tbl, + 0x00000000); + spr_register(env, SPR_VTBU, "TBU", + &spr_read_tbu, SPR_NOACCESS, + &spr_read_tbu, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_TBU, "TBU", + &spr_read_tbu, SPR_NOACCESS, + &spr_read_tbu, &spr_write_tbu, + 0x00000000); +} + +/* Softare table search registers */ +static void gen_6xx_7xx_soft_tlb (CPUPPCState *env, int nb_tlbs, int nb_ways) +{ +#if !defined(CONFIG_USER_ONLY) + env->nb_tlb = nb_tlbs; + env->nb_ways = nb_ways; + env->id_tlbs = 1; + env->tlb_type = TLB_6XX; + spr_register(env, SPR_DMISS, "DMISS", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_DCMP, "DCMP", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_HASH1, "HASH1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_HASH2, "HASH2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_IMISS, "IMISS", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_ICMP, "ICMP", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_RPA, "RPA", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +#endif +} + +/* SPR common to MPC755 and G2 */ +static void gen_spr_G2_755 (CPUPPCState *env) +{ + /* SGPRs */ + spr_register(env, SPR_SPRG4, "SPRG4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SPRG5, "SPRG5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SPRG6, "SPRG6", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SPRG7, "SPRG7", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +/* SPR common to all 7xx PowerPC implementations */ +static void gen_spr_7xx (CPUPPCState *env) +{ + /* Breakpoints */ + /* XXX : not implemented */ + spr_register_kvm(env, SPR_DABR, "DABR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DABR, 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_IABR, "IABR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Cache management */ + /* XXX : not implemented */ + spr_register(env, SPR_ICTC, "ICTC", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Performance monitors */ + /* XXX : not implemented */ + spr_register(env, SPR_7XX_MMCR0, "MMCR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_MMCR1, "MMCR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC1, "PMC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC2, "PMC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC3, "PMC3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC4, "PMC4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_SIAR, "SIAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UMMCR0, "UMMCR0", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UMMCR1, "UMMCR1", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UPMC1, "UPMC1", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UPMC2, "UPMC2", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UPMC3, "UPMC3", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UPMC4, "UPMC4", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_USIAR, "USIAR", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* External access control */ + /* XXX : not implemented */ + spr_register(env, SPR_EAR, "EAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +#ifdef TARGET_PPC64 +#ifndef CONFIG_USER_ONLY +static void spr_write_amr(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + /* Note, the HV=1 PR=0 case is handled earlier by simply using + * spr_write_generic for HV mode in the SPR table + */ + + /* Build insertion mask into t1 based on context */ + if (ctx->pr) { + gen_load_spr(t1, SPR_UAMOR); + } else { + gen_load_spr(t1, SPR_AMOR); + } + + /* Mask new bits into t2 */ + tcg_gen_and_tl(t2, t1, cpu_gpr[gprn]); + + /* Load AMR and clear new bits in t0 */ + gen_load_spr(t0, SPR_AMR); + tcg_gen_andc_tl(t0, t0, t1); + + /* Or'in new bits and write it out */ + tcg_gen_or_tl(t0, t0, t2); + gen_store_spr(SPR_AMR, t0); + spr_store_dump_spr(SPR_AMR); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); +} + +static void spr_write_uamor(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + /* Note, the HV=1 case is handled earlier by simply using + * spr_write_generic for HV mode in the SPR table + */ + + /* Build insertion mask into t1 based on context */ + gen_load_spr(t1, SPR_AMOR); + + /* Mask new bits into t2 */ + tcg_gen_and_tl(t2, t1, cpu_gpr[gprn]); + + /* Load AMR and clear new bits in t0 */ + gen_load_spr(t0, SPR_UAMOR); + tcg_gen_andc_tl(t0, t0, t1); + + /* Or'in new bits and write it out */ + tcg_gen_or_tl(t0, t0, t2); + gen_store_spr(SPR_UAMOR, t0); + spr_store_dump_spr(SPR_UAMOR); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); +} + +static void spr_write_iamr(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + /* Note, the HV=1 case is handled earlier by simply using + * spr_write_generic for HV mode in the SPR table + */ + + /* Build insertion mask into t1 based on context */ + gen_load_spr(t1, SPR_AMOR); + + /* Mask new bits into t2 */ + tcg_gen_and_tl(t2, t1, cpu_gpr[gprn]); + + /* Load AMR and clear new bits in t0 */ + gen_load_spr(t0, SPR_IAMR); + tcg_gen_andc_tl(t0, t0, t1); + + /* Or'in new bits and write it out */ + tcg_gen_or_tl(t0, t0, t2); + gen_store_spr(SPR_IAMR, t0); + spr_store_dump_spr(SPR_IAMR); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); +} +#endif /* CONFIG_USER_ONLY */ + +static void gen_spr_amr(CPUPPCState *env, bool has_iamr) +{ +#ifndef CONFIG_USER_ONLY + /* Virtual Page Class Key protection */ + /* The AMR is accessible either via SPR 13 or SPR 29. 13 is + * userspace accessible, 29 is privileged. So we only need to set + * the kvm ONE_REG id on one of them, we use 29 */ + spr_register(env, SPR_UAMR, "UAMR", + &spr_read_generic, &spr_write_amr, + &spr_read_generic, &spr_write_amr, + 0); + spr_register_kvm_hv(env, SPR_AMR, "AMR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_amr, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_AMR, 0); + spr_register_kvm_hv(env, SPR_UAMOR, "UAMOR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_uamor, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_UAMOR, 0); + spr_register_hv(env, SPR_AMOR, "AMOR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0); + if (has_iamr) { + spr_register_kvm_hv(env, SPR_IAMR, "IAMR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_iamr, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_IAMR, 0); + } +#endif /* !CONFIG_USER_ONLY */ +} +#endif /* TARGET_PPC64 */ + +#ifndef CONFIG_USER_ONLY +static void spr_read_thrm(DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_fixup_thrm(cpu_env); + gen_load_spr(cpu_gpr[gprn], sprn); + spr_load_dump_spr(sprn); +} +#endif /* !CONFIG_USER_ONLY */ + +static void gen_spr_thrm (CPUPPCState *env) +{ + /* Thermal management */ + /* XXX : not implemented */ + spr_register(env, SPR_THRM1, "THRM1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_thrm, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_THRM2, "THRM2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_thrm, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_THRM3, "THRM3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_thrm, &spr_write_generic, + 0x00000000); +} + +/* SPR specific to PowerPC 604 implementation */ +static void gen_spr_604 (CPUPPCState *env) +{ + /* Processor identification */ + spr_register(env, SPR_PIR, "PIR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_pir, + 0x00000000); + /* Breakpoints */ + /* XXX : not implemented */ + spr_register(env, SPR_IABR, "IABR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register_kvm(env, SPR_DABR, "DABR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DABR, 0x00000000); + /* Performance counters */ + /* XXX : not implemented */ + spr_register(env, SPR_7XX_MMCR0, "MMCR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC1, "PMC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC2, "PMC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_SIAR, "SIAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_SDA, "SDA", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + /* External access control */ + /* XXX : not implemented */ + spr_register(env, SPR_EAR, "EAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +/* SPR specific to PowerPC 603 implementation */ +static void gen_spr_603 (CPUPPCState *env) +{ + /* External access control */ + /* XXX : not implemented */ + spr_register(env, SPR_EAR, "EAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Breakpoints */ + /* XXX : not implemented */ + spr_register(env, SPR_IABR, "IABR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + +} + +/* SPR specific to PowerPC G2 implementation */ +static void gen_spr_G2 (CPUPPCState *env) +{ + /* Memory base address */ + /* MBAR */ + /* XXX : not implemented */ + spr_register(env, SPR_MBAR, "MBAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Exception processing */ + spr_register(env, SPR_BOOKE_CSRR0, "CSRR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_CSRR1, "CSRR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Breakpoints */ + /* XXX : not implemented */ + spr_register(env, SPR_DABR, "DABR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_DABR2, "DABR2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_IABR, "IABR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_IABR2, "IABR2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_IBCR, "IBCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_DBCR, "DBCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +/* SPR specific to PowerPC 602 implementation */ +static void gen_spr_602 (CPUPPCState *env) +{ + /* ESA registers */ + /* XXX : not implemented */ + spr_register(env, SPR_SER, "SER", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_SEBR, "SEBR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_ESASRR, "ESASRR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Floating point status */ + /* XXX : not implemented */ + spr_register(env, SPR_SP, "SP", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_LT, "LT", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Watchdog timer */ + /* XXX : not implemented */ + spr_register(env, SPR_TCR, "TCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Interrupt base */ + spr_register(env, SPR_IBR, "IBR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_IABR, "IABR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +/* SPR specific to PowerPC 601 implementation */ +static void gen_spr_601 (CPUPPCState *env) +{ + /* Multiplication/division register */ + /* MQ */ + spr_register(env, SPR_MQ, "MQ", + &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* RTC registers */ + spr_register(env, SPR_601_RTCU, "RTCU", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, &spr_write_601_rtcu, + 0x00000000); + spr_register(env, SPR_601_VRTCU, "RTCU", + &spr_read_601_rtcu, SPR_NOACCESS, + &spr_read_601_rtcu, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_601_RTCL, "RTCL", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, &spr_write_601_rtcl, + 0x00000000); + spr_register(env, SPR_601_VRTCL, "RTCL", + &spr_read_601_rtcl, SPR_NOACCESS, + &spr_read_601_rtcl, SPR_NOACCESS, + 0x00000000); + /* Timer */ +#if 0 /* ? */ + spr_register(env, SPR_601_UDECR, "UDECR", + &spr_read_decr, SPR_NOACCESS, + &spr_read_decr, SPR_NOACCESS, + 0x00000000); +#endif + /* External access control */ + /* XXX : not implemented */ + spr_register(env, SPR_EAR, "EAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ +#if !defined(CONFIG_USER_ONLY) + spr_register(env, SPR_IBAT0U, "IBAT0U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_601_ubat, &spr_write_601_ubatu, + 0x00000000); + spr_register(env, SPR_IBAT0L, "IBAT0L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_601_ubat, &spr_write_601_ubatl, + 0x00000000); + spr_register(env, SPR_IBAT1U, "IBAT1U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_601_ubat, &spr_write_601_ubatu, + 0x00000000); + spr_register(env, SPR_IBAT1L, "IBAT1L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_601_ubat, &spr_write_601_ubatl, + 0x00000000); + spr_register(env, SPR_IBAT2U, "IBAT2U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_601_ubat, &spr_write_601_ubatu, + 0x00000000); + spr_register(env, SPR_IBAT2L, "IBAT2L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_601_ubat, &spr_write_601_ubatl, + 0x00000000); + spr_register(env, SPR_IBAT3U, "IBAT3U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_601_ubat, &spr_write_601_ubatu, + 0x00000000); + spr_register(env, SPR_IBAT3L, "IBAT3L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_601_ubat, &spr_write_601_ubatl, + 0x00000000); + env->nb_BATs = 4; +#endif +} + +static void gen_spr_74xx (CPUPPCState *env) +{ + /* Processor identification */ + spr_register(env, SPR_PIR, "PIR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_pir, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_74XX_MMCR2, "MMCR2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_74XX_UMMCR2, "UMMCR2", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* XXX: not implemented */ + spr_register(env, SPR_BAMR, "BAMR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MSSCR0, "MSSCR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Altivec */ + spr_register(env, SPR_VRSAVE, "VRSAVE", + &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_L2CR, "L2CR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, spr_access_nop, + 0x00000000); + /* Not strictly an SPR */ + vscr_init(env, 0x00010000); +} + +static void gen_l3_ctrl (CPUPPCState *env) +{ + /* L3CR */ + /* XXX : not implemented */ + spr_register(env, SPR_L3CR, "L3CR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* L3ITCR0 */ + /* XXX : not implemented */ + spr_register(env, SPR_L3ITCR0, "L3ITCR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* L3PM */ + /* XXX : not implemented */ + spr_register(env, SPR_L3PM, "L3PM", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +static void gen_74xx_soft_tlb (CPUPPCState *env, int nb_tlbs, int nb_ways) +{ +#if !defined(CONFIG_USER_ONLY) + env->nb_tlb = nb_tlbs; + env->nb_ways = nb_ways; + env->id_tlbs = 1; + env->tlb_type = TLB_6XX; + /* XXX : not implemented */ + spr_register(env, SPR_PTEHI, "PTEHI", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_PTELO, "PTELO", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_TLBMISS, "TLBMISS", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +#endif +} + +#if !defined(CONFIG_USER_ONLY) +static void spr_write_e500_l1csr0 (DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + + tcg_gen_andi_tl(t0, cpu_gpr[gprn], L1CSR0_DCE | L1CSR0_CPE); + gen_store_spr(sprn, t0); + tcg_temp_free(t0); +} + +static void spr_write_e500_l1csr1(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + + tcg_gen_andi_tl(t0, cpu_gpr[gprn], L1CSR1_ICE | L1CSR1_CPE); + gen_store_spr(sprn, t0); + tcg_temp_free(t0); +} + +static void spr_write_booke206_mmucsr0 (DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_booke206_tlbflush(cpu_env, cpu_gpr[gprn]); +} + +static void spr_write_booke_pid (DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32(sprn); + gen_helper_booke_setpid(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} +#endif + +static void gen_spr_usprgh (CPUPPCState *env) +{ + spr_register(env, SPR_USPRG4, "USPRG4", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_USPRG5, "USPRG5", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_USPRG6, "USPRG6", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_USPRG7, "USPRG7", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); +} + +/* PowerPC BookE SPR */ +static void gen_spr_BookE (CPUPPCState *env, uint64_t ivor_mask) +{ + const char *ivor_names[64] = { + "IVOR0", "IVOR1", "IVOR2", "IVOR3", + "IVOR4", "IVOR5", "IVOR6", "IVOR7", + "IVOR8", "IVOR9", "IVOR10", "IVOR11", + "IVOR12", "IVOR13", "IVOR14", "IVOR15", + "IVOR16", "IVOR17", "IVOR18", "IVOR19", + "IVOR20", "IVOR21", "IVOR22", "IVOR23", + "IVOR24", "IVOR25", "IVOR26", "IVOR27", + "IVOR28", "IVOR29", "IVOR30", "IVOR31", + "IVOR32", "IVOR33", "IVOR34", "IVOR35", + "IVOR36", "IVOR37", "IVOR38", "IVOR39", + "IVOR40", "IVOR41", "IVOR42", "IVOR43", + "IVOR44", "IVOR45", "IVOR46", "IVOR47", + "IVOR48", "IVOR49", "IVOR50", "IVOR51", + "IVOR52", "IVOR53", "IVOR54", "IVOR55", + "IVOR56", "IVOR57", "IVOR58", "IVOR59", + "IVOR60", "IVOR61", "IVOR62", "IVOR63", + }; +#define SPR_BOOKE_IVORxx (-1) + int ivor_sprn[64] = { + SPR_BOOKE_IVOR0, SPR_BOOKE_IVOR1, SPR_BOOKE_IVOR2, SPR_BOOKE_IVOR3, + SPR_BOOKE_IVOR4, SPR_BOOKE_IVOR5, SPR_BOOKE_IVOR6, SPR_BOOKE_IVOR7, + SPR_BOOKE_IVOR8, SPR_BOOKE_IVOR9, SPR_BOOKE_IVOR10, SPR_BOOKE_IVOR11, + SPR_BOOKE_IVOR12, SPR_BOOKE_IVOR13, SPR_BOOKE_IVOR14, SPR_BOOKE_IVOR15, + SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, + SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, + SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, + SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, + SPR_BOOKE_IVOR32, SPR_BOOKE_IVOR33, SPR_BOOKE_IVOR34, SPR_BOOKE_IVOR35, + SPR_BOOKE_IVOR36, SPR_BOOKE_IVOR37, SPR_BOOKE_IVOR38, SPR_BOOKE_IVOR39, + SPR_BOOKE_IVOR40, SPR_BOOKE_IVOR41, SPR_BOOKE_IVOR42, SPR_BOOKE_IVORxx, + SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, + SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, + SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, + SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, + SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, + }; + int i; + + /* Interrupt processing */ + spr_register(env, SPR_BOOKE_CSRR0, "CSRR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_CSRR1, "CSRR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Debug */ + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_IAC1, "IAC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_IAC2, "IAC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DAC1, "DAC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DAC2, "DAC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DBCR0, "DBCR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_40x_dbcr0, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DBCR1, "DBCR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DBCR2, "DBCR2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DBSR, "DBSR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_clear, + 0x00000000); + spr_register(env, SPR_BOOKE_DEAR, "DEAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_ESR, "ESR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_IVPR, "IVPR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_excp_prefix, + 0x00000000); + /* Exception vectors */ + for (i = 0; i < 64; i++) { + if (ivor_mask & (1ULL << i)) { + if (ivor_sprn[i] == SPR_BOOKE_IVORxx) { + fprintf(stderr, "ERROR: IVOR %d SPR is not defined\n", i); + exit(1); + } + spr_register(env, ivor_sprn[i], ivor_names[i], + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_excp_vector, + 0x00000000); + } + } + spr_register(env, SPR_BOOKE_PID, "PID", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_booke_pid, + 0x00000000); + spr_register(env, SPR_BOOKE_TCR, "TCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_booke_tcr, + 0x00000000); + spr_register(env, SPR_BOOKE_TSR, "TSR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_booke_tsr, + 0x00000000); + /* Timer */ + spr_register(env, SPR_DECR, "DECR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_decr, &spr_write_decr, + 0x00000000); + spr_register(env, SPR_BOOKE_DECAR, "DECAR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, &spr_write_generic, + 0x00000000); + /* SPRGs */ + spr_register(env, SPR_USPRG0, "USPRG0", + &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SPRG4, "SPRG4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SPRG5, "SPRG5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SPRG6, "SPRG6", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SPRG7, "SPRG7", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +static inline uint32_t gen_tlbncfg(uint32_t assoc, uint32_t minsize, + uint32_t maxsize, uint32_t flags, + uint32_t nentries) +{ + return (assoc << TLBnCFG_ASSOC_SHIFT) | + (minsize << TLBnCFG_MINSIZE_SHIFT) | + (maxsize << TLBnCFG_MAXSIZE_SHIFT) | + flags | nentries; +} + +/* BookE 2.06 storage control registers */ +static void gen_spr_BookE206(CPUPPCState *env, uint32_t mas_mask, + uint32_t *tlbncfg) +{ +#if !defined(CONFIG_USER_ONLY) + const char *mas_names[8] = { + "MAS0", "MAS1", "MAS2", "MAS3", "MAS4", "MAS5", "MAS6", "MAS7", + }; + int mas_sprn[8] = { + SPR_BOOKE_MAS0, SPR_BOOKE_MAS1, SPR_BOOKE_MAS2, SPR_BOOKE_MAS3, + SPR_BOOKE_MAS4, SPR_BOOKE_MAS5, SPR_BOOKE_MAS6, SPR_BOOKE_MAS7, + }; + int i; + + /* TLB assist registers */ + /* XXX : not implemented */ + for (i = 0; i < 8; i++) { + void (*uea_write)(DisasContext *ctx, int sprn, int gprn) = &spr_write_generic32; + if (i == 2 && (mas_mask & (1 << i)) && (env->insns_flags & PPC_64B)) { + uea_write = &spr_write_generic; + } + if (mas_mask & (1 << i)) { + spr_register(env, mas_sprn[i], mas_names[i], + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, uea_write, + 0x00000000); + } + } + if (env->nb_pids > 1) { + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_PID1, "PID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_booke_pid, + 0x00000000); + } + if (env->nb_pids > 2) { + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_PID2, "PID2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_booke_pid, + 0x00000000); + } + /* XXX : not implemented */ + spr_register(env, SPR_MMUCFG, "MMUCFG", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); /* TOFIX */ + switch (env->nb_ways) { + case 4: + spr_register(env, SPR_BOOKE_TLB3CFG, "TLB3CFG", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + tlbncfg[3]); + /* Fallthru */ + case 3: + spr_register(env, SPR_BOOKE_TLB2CFG, "TLB2CFG", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + tlbncfg[2]); + /* Fallthru */ + case 2: + spr_register(env, SPR_BOOKE_TLB1CFG, "TLB1CFG", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + tlbncfg[1]); + /* Fallthru */ + case 1: + spr_register(env, SPR_BOOKE_TLB0CFG, "TLB0CFG", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + tlbncfg[0]); + /* Fallthru */ + case 0: + default: + break; + } +#endif + + gen_spr_usprgh(env); +} + +/* SPR specific to PowerPC 440 implementation */ +static void gen_spr_440 (CPUPPCState *env) +{ + /* Cache control */ + /* XXX : not implemented */ + spr_register(env, SPR_440_DNV0, "DNV0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_DNV1, "DNV1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_DNV2, "DNV2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_DNV3, "DNV3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_DTV0, "DTV0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_DTV1, "DTV1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_DTV2, "DTV2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_DTV3, "DTV3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_DVLIM, "DVLIM", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_INV0, "INV0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_INV1, "INV1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_INV2, "INV2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_INV3, "INV3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_ITV0, "ITV0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_ITV1, "ITV1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_ITV2, "ITV2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_ITV3, "ITV3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_IVLIM, "IVLIM", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Cache debug */ + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DCDBTRH, "DCDBTRH", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DCDBTRL, "DCDBTRL", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_ICDBDR, "ICDBDR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_ICDBTRH, "ICDBTRH", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_ICDBTRL, "ICDBTRL", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_DBDR, "DBDR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Processor control */ + spr_register(env, SPR_4xx_CCR0, "CCR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_440_RSTCFG, "RSTCFG", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + /* Storage control */ + spr_register(env, SPR_440_MMUCR, "MMUCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +/* SPR shared between PowerPC 40x implementations */ +static void gen_spr_40x (CPUPPCState *env) +{ + /* Cache */ + /* not emulated, as QEMU do not emulate caches */ + spr_register(env, SPR_40x_DCCR, "DCCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* not emulated, as QEMU do not emulate caches */ + spr_register(env, SPR_40x_ICCR, "ICCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* not emulated, as QEMU do not emulate caches */ + spr_register(env, SPR_BOOKE_ICDBDR, "ICDBDR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + /* Exception */ + spr_register(env, SPR_40x_DEAR, "DEAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_40x_ESR, "ESR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_40x_EVPR, "EVPR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_excp_prefix, + 0x00000000); + spr_register(env, SPR_40x_SRR2, "SRR2", + &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_40x_SRR3, "SRR3", + &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Timers */ + spr_register(env, SPR_40x_PIT, "PIT", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_40x_pit, &spr_write_40x_pit, + 0x00000000); + spr_register(env, SPR_40x_TCR, "TCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_booke_tcr, + 0x00000000); + spr_register(env, SPR_40x_TSR, "TSR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_booke_tsr, + 0x00000000); +} + +/* SPR specific to PowerPC 405 implementation */ +static void gen_spr_405 (CPUPPCState *env) +{ + /* MMU */ + spr_register(env, SPR_40x_PID, "PID", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_4xx_CCR0, "CCR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00700000); + /* Debug interface */ + /* XXX : not implemented */ + spr_register(env, SPR_40x_DBCR0, "DBCR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_40x_dbcr0, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_405_DBCR1, "DBCR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_40x_DBSR, "DBSR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_clear, + /* Last reset was system reset */ + 0x00000300); + /* XXX : not implemented */ + spr_register(env, SPR_40x_DAC1, "DAC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_40x_DAC2, "DAC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_405_DVC1, "DVC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_405_DVC2, "DVC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_40x_IAC1, "IAC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_40x_IAC2, "IAC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_405_IAC3, "IAC3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_405_IAC4, "IAC4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Storage control */ + /* XXX: TODO: not implemented */ + spr_register(env, SPR_405_SLER, "SLER", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_40x_sler, + 0x00000000); + spr_register(env, SPR_40x_ZPR, "ZPR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_405_SU0R, "SU0R", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* SPRG */ + spr_register(env, SPR_USPRG0, "USPRG0", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_SPRG4, "SPRG4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SPRG5, "SPRG5", + SPR_NOACCESS, SPR_NOACCESS, + spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SPRG6, "SPRG6", + SPR_NOACCESS, SPR_NOACCESS, + spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SPRG7, "SPRG7", + SPR_NOACCESS, SPR_NOACCESS, + spr_read_generic, &spr_write_generic, + 0x00000000); + gen_spr_usprgh(env); +} + +/* SPR shared between PowerPC 401 & 403 implementations */ +static void gen_spr_401_403 (CPUPPCState *env) +{ + /* Time base */ + spr_register(env, SPR_403_VTBL, "TBL", + &spr_read_tbl, SPR_NOACCESS, + &spr_read_tbl, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_403_TBL, "TBL", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, &spr_write_tbl, + 0x00000000); + spr_register(env, SPR_403_VTBU, "TBU", + &spr_read_tbu, SPR_NOACCESS, + &spr_read_tbu, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_403_TBU, "TBU", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, &spr_write_tbu, + 0x00000000); + /* Debug */ + /* not emulated, as QEMU do not emulate caches */ + spr_register(env, SPR_403_CDBCR, "CDBCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +/* SPR specific to PowerPC 401 implementation */ +static void gen_spr_401 (CPUPPCState *env) +{ + /* Debug interface */ + /* XXX : not implemented */ + spr_register(env, SPR_40x_DBCR0, "DBCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_40x_dbcr0, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_40x_DBSR, "DBSR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_clear, + /* Last reset was system reset */ + 0x00000300); + /* XXX : not implemented */ + spr_register(env, SPR_40x_DAC1, "DAC", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_40x_IAC1, "IAC", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Storage control */ + /* XXX: TODO: not implemented */ + spr_register(env, SPR_405_SLER, "SLER", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_40x_sler, + 0x00000000); + /* not emulated, as QEMU never does speculative access */ + spr_register(env, SPR_40x_SGR, "SGR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0xFFFFFFFF); + /* not emulated, as QEMU do not emulate caches */ + spr_register(env, SPR_40x_DCWR, "DCWR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +static void gen_spr_401x2 (CPUPPCState *env) +{ + gen_spr_401(env); + spr_register(env, SPR_40x_PID, "PID", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_40x_ZPR, "ZPR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +/* SPR specific to PowerPC 403 implementation */ +static void gen_spr_403 (CPUPPCState *env) +{ + /* Debug interface */ + /* XXX : not implemented */ + spr_register(env, SPR_40x_DBCR0, "DBCR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_40x_dbcr0, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_40x_DBSR, "DBSR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_clear, + /* Last reset was system reset */ + 0x00000300); + /* XXX : not implemented */ + spr_register(env, SPR_40x_DAC1, "DAC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_40x_DAC2, "DAC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_40x_IAC1, "IAC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_40x_IAC2, "IAC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +static void gen_spr_403_real (CPUPPCState *env) +{ + spr_register(env, SPR_403_PBL1, "PBL1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_403_pbr, &spr_write_403_pbr, + 0x00000000); + spr_register(env, SPR_403_PBU1, "PBU1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_403_pbr, &spr_write_403_pbr, + 0x00000000); + spr_register(env, SPR_403_PBL2, "PBL2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_403_pbr, &spr_write_403_pbr, + 0x00000000); + spr_register(env, SPR_403_PBU2, "PBU2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_403_pbr, &spr_write_403_pbr, + 0x00000000); +} + +static void gen_spr_403_mmu (CPUPPCState *env) +{ + /* MMU */ + spr_register(env, SPR_40x_PID, "PID", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_40x_ZPR, "ZPR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +/* SPR specific to PowerPC compression coprocessor extension */ +static void gen_spr_compress (CPUPPCState *env) +{ + /* XXX : not implemented */ + spr_register(env, SPR_401_SKR, "SKR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +static void gen_spr_5xx_8xx (CPUPPCState *env) +{ + /* Exception processing */ + spr_register_kvm(env, SPR_DSISR, "DSISR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DSISR, 0x00000000); + spr_register_kvm(env, SPR_DAR, "DAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DAR, 0x00000000); + /* Timer */ + spr_register(env, SPR_DECR, "DECR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_decr, &spr_write_decr, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_EIE, "EIE", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_EID, "EID", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_NRI, "NRI", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_CMPA, "CMPA", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_CMPB, "CMPB", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_CMPC, "CMPC", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_CMPD, "CMPD", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_ECR, "ECR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_DER, "DER", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_COUNTA, "COUNTA", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_COUNTB, "COUNTB", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_CMPE, "CMPE", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_CMPF, "CMPF", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_CMPG, "CMPG", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_CMPH, "CMPH", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_LCTRL1, "LCTRL1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_LCTRL2, "LCTRL2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_BAR, "BAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_DPDR, "DPDR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_IMMR, "IMMR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +static void gen_spr_5xx (CPUPPCState *env) +{ + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_MI_GRA, "MI_GRA", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_L2U_GRA, "L2U_GRA", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RPCU_BBCMCR, "L2U_BBCMCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_L2U_MCR, "L2U_MCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_MI_RBA0, "MI_RBA0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_MI_RBA1, "MI_RBA1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_MI_RBA2, "MI_RBA2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_MI_RBA3, "MI_RBA3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_L2U_RBA0, "L2U_RBA0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_L2U_RBA1, "L2U_RBA1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_L2U_RBA2, "L2U_RBA2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_L2U_RBA3, "L2U_RBA3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_MI_RA0, "MI_RA0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_MI_RA1, "MI_RA1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_MI_RA2, "MI_RA2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_MI_RA3, "MI_RA3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_L2U_RA0, "L2U_RA0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_L2U_RA1, "L2U_RA1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_L2U_RA2, "L2U_RA2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_L2U_RA3, "L2U_RA3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_RCPU_FPECR, "FPECR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +static void gen_spr_8xx (CPUPPCState *env) +{ + /* XXX : not implemented */ + spr_register(env, SPR_MPC_IC_CST, "IC_CST", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_IC_ADR, "IC_ADR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_IC_DAT, "IC_DAT", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_DC_CST, "DC_CST", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_DC_ADR, "DC_ADR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_DC_DAT, "DC_DAT", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MI_CTR, "MI_CTR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MI_AP, "MI_AP", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MI_EPN, "MI_EPN", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MI_TWC, "MI_TWC", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MI_RPN, "MI_RPN", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MI_DBCAM, "MI_DBCAM", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MI_DBRAM0, "MI_DBRAM0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MI_DBRAM1, "MI_DBRAM1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MD_CTR, "MD_CTR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MD_CASID, "MD_CASID", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MD_AP, "MD_AP", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MD_EPN, "MD_EPN", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MD_TWB, "MD_TWB", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MD_TWC, "MD_TWC", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MD_RPN, "MD_RPN", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MD_TW, "MD_TW", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MD_DBCAM, "MD_DBCAM", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MD_DBRAM0, "MD_DBRAM0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MPC_MD_DBRAM1, "MD_DBRAM1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +// XXX: TODO +/* + * AMR => SPR 29 (Power 2.04) + * CTRL => SPR 136 (Power 2.04) + * CTRL => SPR 152 (Power 2.04) + * SCOMC => SPR 276 (64 bits ?) + * SCOMD => SPR 277 (64 bits ?) + * TBU40 => SPR 286 (Power 2.04 hypv) + * HSPRG0 => SPR 304 (Power 2.04 hypv) + * HSPRG1 => SPR 305 (Power 2.04 hypv) + * HDSISR => SPR 306 (Power 2.04 hypv) + * HDAR => SPR 307 (Power 2.04 hypv) + * PURR => SPR 309 (Power 2.04 hypv) + * HDEC => SPR 310 (Power 2.04 hypv) + * HIOR => SPR 311 (hypv) + * RMOR => SPR 312 (970) + * HRMOR => SPR 313 (Power 2.04 hypv) + * HSRR0 => SPR 314 (Power 2.04 hypv) + * HSRR1 => SPR 315 (Power 2.04 hypv) + * LPIDR => SPR 317 (970) + * EPR => SPR 702 (Power 2.04 emb) + * perf => 768-783 (Power 2.04) + * perf => 784-799 (Power 2.04) + * PPR => SPR 896 (Power 2.04) + * EPLC => SPR 947 (Power 2.04 emb) + * EPSC => SPR 948 (Power 2.04 emb) + * DABRX => 1015 (Power 2.04 hypv) + * FPECR => SPR 1022 (?) + * ... and more (thermal management, performance counters, ...) + */ + +/*****************************************************************************/ +/* Exception vectors models */ +static void init_excp_4xx_real (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_CRITICAL] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_PIT] = 0x00001000; + env->excp_vectors[POWERPC_EXCP_FIT] = 0x00001010; + env->excp_vectors[POWERPC_EXCP_WDT] = 0x00001020; + env->excp_vectors[POWERPC_EXCP_DEBUG] = 0x00002000; + env->ivor_mask = 0x0000FFF0UL; + env->ivpr_mask = 0xFFFF0000UL; + /* Hardware reset vector */ + env->hreset_vector = 0xFFFFFFFCUL; +#endif +} + +static void init_excp_4xx_softmmu (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_CRITICAL] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_PIT] = 0x00001000; + env->excp_vectors[POWERPC_EXCP_FIT] = 0x00001010; + env->excp_vectors[POWERPC_EXCP_WDT] = 0x00001020; + env->excp_vectors[POWERPC_EXCP_DTLB] = 0x00001100; + env->excp_vectors[POWERPC_EXCP_ITLB] = 0x00001200; + env->excp_vectors[POWERPC_EXCP_DEBUG] = 0x00002000; + env->ivor_mask = 0x0000FFF0UL; + env->ivpr_mask = 0xFFFF0000UL; + /* Hardware reset vector */ + env->hreset_vector = 0xFFFFFFFCUL; +#endif +} + +static void init_excp_MPC5xx (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; + env->excp_vectors[POWERPC_EXCP_FPA] = 0x00000E00; + env->excp_vectors[POWERPC_EXCP_EMUL] = 0x00001000; + env->excp_vectors[POWERPC_EXCP_DABR] = 0x00001C00; + env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001C00; + env->excp_vectors[POWERPC_EXCP_MEXTBR] = 0x00001E00; + env->excp_vectors[POWERPC_EXCP_NMEXTBR] = 0x00001F00; + env->ivor_mask = 0x0000FFF0UL; + env->ivpr_mask = 0xFFFF0000UL; + /* Hardware reset vector */ + env->hreset_vector = 0x00000100UL; +#endif +} + +static void init_excp_MPC8xx (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; + env->excp_vectors[POWERPC_EXCP_FPA] = 0x00000E00; + env->excp_vectors[POWERPC_EXCP_EMUL] = 0x00001000; + env->excp_vectors[POWERPC_EXCP_ITLB] = 0x00001100; + env->excp_vectors[POWERPC_EXCP_DTLB] = 0x00001200; + env->excp_vectors[POWERPC_EXCP_ITLBE] = 0x00001300; + env->excp_vectors[POWERPC_EXCP_DTLBE] = 0x00001400; + env->excp_vectors[POWERPC_EXCP_DABR] = 0x00001C00; + env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001C00; + env->excp_vectors[POWERPC_EXCP_MEXTBR] = 0x00001E00; + env->excp_vectors[POWERPC_EXCP_NMEXTBR] = 0x00001F00; + env->ivor_mask = 0x0000FFF0UL; + env->ivpr_mask = 0xFFFF0000UL; + /* Hardware reset vector */ + env->hreset_vector = 0x00000100UL; +#endif +} + +static void init_excp_G2 (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_CRITICAL] = 0x00000A00; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; + env->excp_vectors[POWERPC_EXCP_IFTLB] = 0x00001000; + env->excp_vectors[POWERPC_EXCP_DLTLB] = 0x00001100; + env->excp_vectors[POWERPC_EXCP_DSTLB] = 0x00001200; + env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300; + env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400; + /* Hardware reset vector */ + env->hreset_vector = 0x00000100UL; +#endif +} + +static void init_excp_e200(CPUPPCState *env, target_ulong ivpr_mask) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000FFC; + env->excp_vectors[POWERPC_EXCP_CRITICAL] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_APU] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_FIT] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_WDT] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_DTLB] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_ITLB] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_DEBUG] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_SPEU] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_EFPDI] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_EFPRI] = 0x00000000; + env->ivor_mask = 0x0000FFF7UL; + env->ivpr_mask = ivpr_mask; + /* Hardware reset vector */ + env->hreset_vector = 0xFFFFFFFCUL; +#endif +} + +static void init_excp_BookE (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_CRITICAL] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_APU] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_FIT] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_WDT] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_DTLB] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_ITLB] = 0x00000000; + env->excp_vectors[POWERPC_EXCP_DEBUG] = 0x00000000; + env->ivor_mask = 0x0000FFF0UL; + env->ivpr_mask = 0xFFFF0000UL; + /* Hardware reset vector */ + env->hreset_vector = 0xFFFFFFFCUL; +#endif +} + +static void init_excp_601 (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_IO] = 0x00000A00; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_RUNM] = 0x00002000; + /* Hardware reset vector */ + env->hreset_vector = 0x00000100UL; +#endif +} + +static void init_excp_602 (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + /* XXX: exception prefix has a special behavior on 602 */ + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; + env->excp_vectors[POWERPC_EXCP_IFTLB] = 0x00001000; + env->excp_vectors[POWERPC_EXCP_DLTLB] = 0x00001100; + env->excp_vectors[POWERPC_EXCP_DSTLB] = 0x00001200; + env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300; + env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400; + env->excp_vectors[POWERPC_EXCP_WDT] = 0x00001500; + env->excp_vectors[POWERPC_EXCP_EMUL] = 0x00001600; + /* Hardware reset vector */ + env->hreset_vector = 0x00000100UL; +#endif +} + +static void init_excp_603 (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; + env->excp_vectors[POWERPC_EXCP_IFTLB] = 0x00001000; + env->excp_vectors[POWERPC_EXCP_DLTLB] = 0x00001100; + env->excp_vectors[POWERPC_EXCP_DSTLB] = 0x00001200; + env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300; + env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400; + /* Hardware reset vector */ + env->hreset_vector = 0x00000100UL; +#endif +} + +static void init_excp_604 (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; + env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00; + env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300; + env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400; + /* Hardware reset vector */ + env->hreset_vector = 0x00000100UL; +#endif +} + +static void init_excp_7x0 (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; + env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00; + env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300; + env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400; + env->excp_vectors[POWERPC_EXCP_THERM] = 0x00001700; + /* Hardware reset vector */ + env->hreset_vector = 0x00000100UL; +#endif +} + +static void init_excp_750cl (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; + env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00; + env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300; + env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400; + /* Hardware reset vector */ + env->hreset_vector = 0x00000100UL; +#endif +} + +static void init_excp_750cx (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; + env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00; + env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300; + env->excp_vectors[POWERPC_EXCP_THERM] = 0x00001700; + /* Hardware reset vector */ + env->hreset_vector = 0x00000100UL; +#endif +} + +/* XXX: Check if this is correct */ +static void init_excp_7x5 (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; + env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00; + env->excp_vectors[POWERPC_EXCP_IFTLB] = 0x00001000; + env->excp_vectors[POWERPC_EXCP_DLTLB] = 0x00001100; + env->excp_vectors[POWERPC_EXCP_DSTLB] = 0x00001200; + env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300; + env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400; + env->excp_vectors[POWERPC_EXCP_THERM] = 0x00001700; + /* Hardware reset vector */ + env->hreset_vector = 0x00000100UL; +#endif +} + +static void init_excp_7400 (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; + env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00; + env->excp_vectors[POWERPC_EXCP_VPU] = 0x00000F20; + env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300; + env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400; + env->excp_vectors[POWERPC_EXCP_VPUA] = 0x00001600; + env->excp_vectors[POWERPC_EXCP_THERM] = 0x00001700; + /* Hardware reset vector */ + env->hreset_vector = 0x00000100UL; +#endif +} + +static void init_excp_7450 (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; + env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00; + env->excp_vectors[POWERPC_EXCP_VPU] = 0x00000F20; + env->excp_vectors[POWERPC_EXCP_IFTLB] = 0x00001000; + env->excp_vectors[POWERPC_EXCP_DLTLB] = 0x00001100; + env->excp_vectors[POWERPC_EXCP_DSTLB] = 0x00001200; + env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300; + env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400; + env->excp_vectors[POWERPC_EXCP_VPUA] = 0x00001600; + /* Hardware reset vector */ + env->hreset_vector = 0x00000100UL; +#endif +} + +#if defined (TARGET_PPC64) +static void init_excp_970 (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; + env->excp_vectors[POWERPC_EXCP_DSEG] = 0x00000380; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; + env->excp_vectors[POWERPC_EXCP_ISEG] = 0x00000480; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_HDECR] = 0x00000980; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; + env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00; + env->excp_vectors[POWERPC_EXCP_VPU] = 0x00000F20; + env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300; + env->excp_vectors[POWERPC_EXCP_MAINT] = 0x00001600; + env->excp_vectors[POWERPC_EXCP_VPUA] = 0x00001700; + env->excp_vectors[POWERPC_EXCP_THERM] = 0x00001800; + /* Hardware reset vector */ + env->hreset_vector = 0x0000000000000100ULL; +#endif +} + +static void init_excp_POWER7 (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; + env->excp_vectors[POWERPC_EXCP_DSEG] = 0x00000380; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; + env->excp_vectors[POWERPC_EXCP_ISEG] = 0x00000480; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_HDECR] = 0x00000980; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; + env->excp_vectors[POWERPC_EXCP_HDSI] = 0x00000E00; + env->excp_vectors[POWERPC_EXCP_HISI] = 0x00000E20; + env->excp_vectors[POWERPC_EXCP_HV_EMU] = 0x00000E40; + env->excp_vectors[POWERPC_EXCP_HV_MAINT] = 0x00000E60; + env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00; + env->excp_vectors[POWERPC_EXCP_VPU] = 0x00000F20; + env->excp_vectors[POWERPC_EXCP_VSXU] = 0x00000F40; + /* Hardware reset vector */ + env->hreset_vector = 0x0000000000000100ULL; +#endif +} + +static void init_excp_POWER8(CPUPPCState *env) +{ + init_excp_POWER7(env); + +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_SDOOR] = 0x00000A00; + env->excp_vectors[POWERPC_EXCP_FU] = 0x00000F60; + env->excp_vectors[POWERPC_EXCP_HV_FU] = 0x00000F80; + env->excp_vectors[POWERPC_EXCP_SDOOR_HV] = 0x00000E80; +#endif +} + +#endif + +/*****************************************************************************/ +/* Power management enable checks */ +static int check_pow_none (CPUPPCState *env) +{ + return 0; +} + +static int check_pow_nocheck (CPUPPCState *env) +{ + return 1; +} + +static int check_pow_hid0 (CPUPPCState *env) +{ + if (env->spr[SPR_HID0] & 0x00E00000) + return 1; + + return 0; +} + +static int check_pow_hid0_74xx (CPUPPCState *env) +{ + if (env->spr[SPR_HID0] & 0x00600000) + return 1; + + return 0; +} + +static bool ppc_cpu_interrupts_big_endian_always(PowerPCCPU *cpu) +{ + return true; +} + +#ifdef TARGET_PPC64 +static bool ppc_cpu_interrupts_big_endian_lpcr(PowerPCCPU *cpu) +{ + return !(cpu->env.spr[SPR_LPCR] & LPCR_ILE); +} +#endif + +/*****************************************************************************/ +/* PowerPC implementations definitions */ + +#define POWERPC_FAMILY(_name) \ + static void \ + glue(glue(ppc_, _name), _cpu_family_class_init)(ObjectClass *, void *); \ + \ + static const TypeInfo \ + glue(glue(ppc_, _name), _cpu_family_type_info) = { \ + .name = stringify(_name) "-family-" TYPE_POWERPC_CPU, \ + .parent = TYPE_POWERPC_CPU, \ + .abstract = true, \ + .class_init = glue(glue(ppc_, _name), _cpu_family_class_init), \ + }; \ + \ + static void glue(glue(ppc_, _name), _cpu_family_register_types)(void) \ + { \ + type_register_static( \ + &glue(glue(ppc_, _name), _cpu_family_type_info)); \ + } \ + \ + type_init(glue(glue(ppc_, _name), _cpu_family_register_types)) \ + \ + static void glue(glue(ppc_, _name), _cpu_family_class_init) + +static void init_proc_401 (CPUPPCState *env) +{ + gen_spr_40x(env); + gen_spr_401_403(env); + gen_spr_401(env); + init_excp_4xx_real(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc40x_irq_init(ppc_env_get_cpu(env)); + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(16, 20, 24, 28); +} + +POWERPC_FAMILY(401)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 401"; + pcc->init_proc = init_proc_401; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_WRTEE | PPC_DCR | + PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | + PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_4xx_COMMON | PPC_40x_EXCP; + pcc->msr_mask = (1ull << MSR_KEY) | + (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_ME) | + (1ull << MSR_DE) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_REAL; + pcc->excp_model = POWERPC_EXCP_40x; + pcc->bus_model = PPC_FLAGS_INPUT_401; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DE | + POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_401x2 (CPUPPCState *env) +{ + gen_spr_40x(env); + gen_spr_401_403(env); + gen_spr_401x2(env); + gen_spr_compress(env); + /* Memory management */ +#if !defined(CONFIG_USER_ONLY) + env->nb_tlb = 64; + env->nb_ways = 1; + env->id_tlbs = 0; + env->tlb_type = TLB_EMB; +#endif + init_excp_4xx_softmmu(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc40x_irq_init(ppc_env_get_cpu(env)); + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(16, 20, 24, 28); +} + +POWERPC_FAMILY(401x2)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 401x2"; + pcc->init_proc = init_proc_401x2; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_DCR | PPC_WRTEE | + PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | + PPC_4xx_COMMON | PPC_40x_EXCP; + pcc->msr_mask = (1ull << 20) | + (1ull << MSR_KEY) | + (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_ME) | + (1ull << MSR_DE) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_SOFT_4xx_Z; + pcc->excp_model = POWERPC_EXCP_40x; + pcc->bus_model = PPC_FLAGS_INPUT_401; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DE | + POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_401x3 (CPUPPCState *env) +{ + gen_spr_40x(env); + gen_spr_401_403(env); + gen_spr_401(env); + gen_spr_401x2(env); + gen_spr_compress(env); + init_excp_4xx_softmmu(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc40x_irq_init(ppc_env_get_cpu(env)); + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(16, 20, 24, 28); +} + +POWERPC_FAMILY(401x3)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 401x3"; + pcc->init_proc = init_proc_401x3; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_DCR | PPC_WRTEE | + PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | + PPC_4xx_COMMON | PPC_40x_EXCP; + pcc->msr_mask = (1ull << 20) | + (1ull << MSR_KEY) | + (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_ME) | + (1ull << MSR_DWE) | + (1ull << MSR_DE) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_SOFT_4xx_Z; + pcc->excp_model = POWERPC_EXCP_40x; + pcc->bus_model = PPC_FLAGS_INPUT_401; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DE | + POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_IOP480 (CPUPPCState *env) +{ + gen_spr_40x(env); + gen_spr_401_403(env); + gen_spr_401x2(env); + gen_spr_compress(env); + /* Memory management */ +#if !defined(CONFIG_USER_ONLY) + env->nb_tlb = 64; + env->nb_ways = 1; + env->id_tlbs = 0; + env->tlb_type = TLB_EMB; +#endif + init_excp_4xx_softmmu(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc40x_irq_init(ppc_env_get_cpu(env)); + + SET_FIT_PERIOD(8, 12, 16, 20); + SET_WDT_PERIOD(16, 20, 24, 28); +} + +POWERPC_FAMILY(IOP480)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "IOP480"; + pcc->init_proc = init_proc_IOP480; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_DCR | PPC_WRTEE | + PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | + PPC_4xx_COMMON | PPC_40x_EXCP; + pcc->msr_mask = (1ull << 20) | + (1ull << MSR_KEY) | + (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_ME) | + (1ull << MSR_DE) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_SOFT_4xx_Z; + pcc->excp_model = POWERPC_EXCP_40x; + pcc->bus_model = PPC_FLAGS_INPUT_401; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DE | + POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_403 (CPUPPCState *env) +{ + gen_spr_40x(env); + gen_spr_401_403(env); + gen_spr_403(env); + gen_spr_403_real(env); + init_excp_4xx_real(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc40x_irq_init(ppc_env_get_cpu(env)); + + SET_FIT_PERIOD(8, 12, 16, 20); + SET_WDT_PERIOD(16, 20, 24, 28); +} + +POWERPC_FAMILY(403)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 403"; + pcc->init_proc = init_proc_403; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_DCR | PPC_WRTEE | + PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | + PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_4xx_COMMON | PPC_40x_EXCP; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_ME) | + (1ull << MSR_PE) | + (1ull << MSR_PX) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_REAL; + pcc->excp_model = POWERPC_EXCP_40x; + pcc->bus_model = PPC_FLAGS_INPUT_401; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_PX | + POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_403GCX (CPUPPCState *env) +{ + gen_spr_40x(env); + gen_spr_401_403(env); + gen_spr_403(env); + gen_spr_403_real(env); + gen_spr_403_mmu(env); + /* Bus access control */ + /* not emulated, as QEMU never does speculative access */ + spr_register(env, SPR_40x_SGR, "SGR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0xFFFFFFFF); + /* not emulated, as QEMU do not emulate caches */ + spr_register(env, SPR_40x_DCWR, "DCWR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ +#if !defined(CONFIG_USER_ONLY) + env->nb_tlb = 64; + env->nb_ways = 1; + env->id_tlbs = 0; + env->tlb_type = TLB_EMB; +#endif + init_excp_4xx_softmmu(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc40x_irq_init(ppc_env_get_cpu(env)); + + SET_FIT_PERIOD(8, 12, 16, 20); + SET_WDT_PERIOD(16, 20, 24, 28); +} + +POWERPC_FAMILY(403GCX)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 403 GCX"; + pcc->init_proc = init_proc_403GCX; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_DCR | PPC_WRTEE | + PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | + PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | + PPC_4xx_COMMON | PPC_40x_EXCP; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_ME) | + (1ull << MSR_PE) | + (1ull << MSR_PX) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_SOFT_4xx_Z; + pcc->excp_model = POWERPC_EXCP_40x; + pcc->bus_model = PPC_FLAGS_INPUT_401; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_PX | + POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_405 (CPUPPCState *env) +{ + /* Time base */ + gen_tbl(env); + gen_spr_40x(env); + gen_spr_405(env); + /* Bus access control */ + /* not emulated, as QEMU never does speculative access */ + spr_register(env, SPR_40x_SGR, "SGR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0xFFFFFFFF); + /* not emulated, as QEMU do not emulate caches */ + spr_register(env, SPR_40x_DCWR, "DCWR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ +#if !defined(CONFIG_USER_ONLY) + env->nb_tlb = 64; + env->nb_ways = 1; + env->id_tlbs = 0; + env->tlb_type = TLB_EMB; +#endif + init_excp_4xx_softmmu(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc40x_irq_init(ppc_env_get_cpu(env)); + + SET_FIT_PERIOD(8, 12, 16, 20); + SET_WDT_PERIOD(16, 20, 24, 28); +} + +POWERPC_FAMILY(405)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 405"; + pcc->init_proc = init_proc_405; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_DCR | PPC_WRTEE | + PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | + PPC_4xx_COMMON | PPC_405_MAC | PPC_40x_EXCP; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_DWE) | + (1ull << MSR_DE) | + (1ull << MSR_IR) | + (1ull << MSR_DR); + pcc->mmu_model = POWERPC_MMU_SOFT_4xx; + pcc->excp_model = POWERPC_EXCP_40x; + pcc->bus_model = PPC_FLAGS_INPUT_405; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DWE | + POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_440EP (CPUPPCState *env) +{ + /* Time base */ + gen_tbl(env); + gen_spr_BookE(env, 0x000000000000FFFFULL); + gen_spr_440(env); + gen_spr_usprgh(env); + /* Processor identification */ + spr_register(env, SPR_BOOKE_PIR, "PIR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_pir, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_IAC3, "IAC3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_IAC4, "IAC4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DVC1, "DVC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DVC2, "DVC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_MCSR, "MCSR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_MCSRR0, "MCSRR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_MCSRR1, "MCSRR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_CCR1, "CCR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ +#if !defined(CONFIG_USER_ONLY) + env->nb_tlb = 64; + env->nb_ways = 1; + env->id_tlbs = 0; + env->tlb_type = TLB_EMB; +#endif + init_excp_BookE(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + ppc40x_irq_init(ppc_env_get_cpu(env)); + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); +} + +POWERPC_FAMILY(440EP)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 440 EP"; + pcc->init_proc = init_proc_440EP; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_FLOAT | PPC_FLOAT_FRES | PPC_FLOAT_FSEL | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_DCR | PPC_WRTEE | PPC_RFMCI | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_MFTB | + PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | + PPC_440_SPEC; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_DWE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR); + pcc->mmu_model = POWERPC_MMU_BOOKE; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DWE | + POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_440GP (CPUPPCState *env) +{ + /* Time base */ + gen_tbl(env); + gen_spr_BookE(env, 0x000000000000FFFFULL); + gen_spr_440(env); + gen_spr_usprgh(env); + /* Processor identification */ + spr_register(env, SPR_BOOKE_PIR, "PIR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_pir, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_IAC3, "IAC3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_IAC4, "IAC4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DVC1, "DVC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DVC2, "DVC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ +#if !defined(CONFIG_USER_ONLY) + env->nb_tlb = 64; + env->nb_ways = 1; + env->id_tlbs = 0; + env->tlb_type = TLB_EMB; +#endif + init_excp_BookE(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* XXX: TODO: allocate internal IRQ controller */ + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); +} + +POWERPC_FAMILY(440GP)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 440 GP"; + pcc->init_proc = init_proc_440GP; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_DCR | PPC_DCRX | PPC_WRTEE | PPC_MFAPIDI | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_TLBIVA | PPC_MFTB | + PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | + PPC_440_SPEC; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_DWE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR); + pcc->mmu_model = POWERPC_MMU_BOOKE; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DWE | + POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_440x4 (CPUPPCState *env) +{ + /* Time base */ + gen_tbl(env); + gen_spr_BookE(env, 0x000000000000FFFFULL); + gen_spr_440(env); + gen_spr_usprgh(env); + /* Processor identification */ + spr_register(env, SPR_BOOKE_PIR, "PIR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_pir, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_IAC3, "IAC3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_IAC4, "IAC4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DVC1, "DVC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DVC2, "DVC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ +#if !defined(CONFIG_USER_ONLY) + env->nb_tlb = 64; + env->nb_ways = 1; + env->id_tlbs = 0; + env->tlb_type = TLB_EMB; +#endif + init_excp_BookE(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* XXX: TODO: allocate internal IRQ controller */ + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); +} + +POWERPC_FAMILY(440x4)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 440x4"; + pcc->init_proc = init_proc_440x4; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_DCR | PPC_WRTEE | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_MFTB | + PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | + PPC_440_SPEC; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_DWE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR); + pcc->mmu_model = POWERPC_MMU_BOOKE; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DWE | + POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_440x5 (CPUPPCState *env) +{ + /* Time base */ + gen_tbl(env); + gen_spr_BookE(env, 0x000000000000FFFFULL); + gen_spr_440(env); + gen_spr_usprgh(env); + /* Processor identification */ + spr_register(env, SPR_BOOKE_PIR, "PIR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_pir, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_IAC3, "IAC3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_IAC4, "IAC4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DVC1, "DVC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DVC2, "DVC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_MCSR, "MCSR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_MCSRR0, "MCSRR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_MCSRR1, "MCSRR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_CCR1, "CCR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ +#if !defined(CONFIG_USER_ONLY) + env->nb_tlb = 64; + env->nb_ways = 1; + env->id_tlbs = 0; + env->tlb_type = TLB_EMB; +#endif + init_excp_BookE(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + ppc40x_irq_init(ppc_env_get_cpu(env)); + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); +} + +POWERPC_FAMILY(440x5)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 440x5"; + pcc->init_proc = init_proc_440x5; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_DCR | PPC_WRTEE | PPC_RFMCI | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_MFTB | + PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | + PPC_440_SPEC; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_DWE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR); + pcc->mmu_model = POWERPC_MMU_BOOKE; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DWE | + POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; +} + +POWERPC_FAMILY(440x5wDFPU)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 440x5 with double precision FPU"; + pcc->init_proc = init_proc_440x5; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_FLOAT | PPC_FLOAT_FSQRT | + PPC_FLOAT_STFIWX | + PPC_DCR | PPC_WRTEE | PPC_RFMCI | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_MFTB | + PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | + PPC_440_SPEC; + pcc->insns_flags2 = PPC2_FP_CVT_S64; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_DWE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR); + pcc->mmu_model = POWERPC_MMU_BOOKE; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DWE | + POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_460 (CPUPPCState *env) +{ + /* Time base */ + gen_tbl(env); + gen_spr_BookE(env, 0x000000000000FFFFULL); + gen_spr_440(env); + gen_spr_usprgh(env); + /* Processor identification */ + spr_register(env, SPR_BOOKE_PIR, "PIR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_pir, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_IAC3, "IAC3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_IAC4, "IAC4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DVC1, "DVC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DVC2, "DVC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_MCSR, "MCSR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_MCSRR0, "MCSRR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_MCSRR1, "MCSRR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_CCR1, "CCR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_DCRIPR, "SPR_DCRIPR", + &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ +#if !defined(CONFIG_USER_ONLY) + env->nb_tlb = 64; + env->nb_ways = 1; + env->id_tlbs = 0; + env->tlb_type = TLB_EMB; +#endif + init_excp_BookE(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* XXX: TODO: allocate internal IRQ controller */ + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); +} + +POWERPC_FAMILY(460)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 460 (guessed)"; + pcc->init_proc = init_proc_460; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_DCR | PPC_DCRX | PPC_DCRUX | + PPC_WRTEE | PPC_MFAPIDI | PPC_MFTB | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_TLBIVA | + PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | + PPC_440_SPEC; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_DWE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR); + pcc->mmu_model = POWERPC_MMU_BOOKE; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DWE | + POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_460F (CPUPPCState *env) +{ + /* Time base */ + gen_tbl(env); + gen_spr_BookE(env, 0x000000000000FFFFULL); + gen_spr_440(env); + gen_spr_usprgh(env); + /* Processor identification */ + spr_register(env, SPR_BOOKE_PIR, "PIR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_pir, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_IAC3, "IAC3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_IAC4, "IAC4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DVC1, "DVC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_DVC2, "DVC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_MCSR, "MCSR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_MCSRR0, "MCSRR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_MCSRR1, "MCSRR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_440_CCR1, "CCR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_DCRIPR, "SPR_DCRIPR", + &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ +#if !defined(CONFIG_USER_ONLY) + env->nb_tlb = 64; + env->nb_ways = 1; + env->id_tlbs = 0; + env->tlb_type = TLB_EMB; +#endif + init_excp_BookE(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* XXX: TODO: allocate internal IRQ controller */ + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); +} + +POWERPC_FAMILY(460F)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 460F (guessed)"; + pcc->init_proc = init_proc_460F; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_FLOAT | PPC_FLOAT_FRES | PPC_FLOAT_FSEL | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | PPC_MFTB | + PPC_DCR | PPC_DCRX | PPC_DCRUX | + PPC_WRTEE | PPC_MFAPIDI | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_TLBIVA | + PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | + PPC_440_SPEC; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_DWE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR); + pcc->mmu_model = POWERPC_MMU_BOOKE; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DWE | + POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_MPC5xx (CPUPPCState *env) +{ + /* Time base */ + gen_tbl(env); + gen_spr_5xx_8xx(env); + gen_spr_5xx(env); + init_excp_MPC5xx(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* XXX: TODO: allocate internal IRQ controller */ +} + +POWERPC_FAMILY(MPC5xx)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "Freescale 5xx cores (aka RCPU)"; + pcc->init_proc = init_proc_MPC5xx; + pcc->check_pow = check_pow_none; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_MEM_EIEIO | PPC_MEM_SYNC | + PPC_CACHE_ICBI | PPC_FLOAT | PPC_FLOAT_STFIWX | + PPC_MFTB; + pcc->msr_mask = (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_REAL; + pcc->excp_model = POWERPC_EXCP_603; + pcc->bus_model = PPC_FLAGS_INPUT_RCPU; + pcc->bfd_mach = bfd_mach_ppc_505; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_MPC8xx (CPUPPCState *env) +{ + /* Time base */ + gen_tbl(env); + gen_spr_5xx_8xx(env); + gen_spr_8xx(env); + init_excp_MPC8xx(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* XXX: TODO: allocate internal IRQ controller */ +} + +POWERPC_FAMILY(MPC8xx)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "Freescale 8xx cores (aka PowerQUICC)"; + pcc->init_proc = init_proc_MPC8xx; + pcc->check_pow = check_pow_none; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_MEM_EIEIO | PPC_MEM_SYNC | + PPC_CACHE_ICBI | PPC_MFTB; + pcc->msr_mask = (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_MPC8xx; + pcc->excp_model = POWERPC_EXCP_603; + pcc->bus_model = PPC_FLAGS_INPUT_RCPU; + pcc->bfd_mach = bfd_mach_ppc_860; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_BUS_CLK; +} + +/* Freescale 82xx cores (aka PowerQUICC-II) */ + +static void init_proc_G2 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_G2_755(env); + gen_spr_G2(env); + /* Time base */ + gen_tbl(env); + /* External access control */ + /* XXX : not implemented */ + spr_register(env, SPR_EAR, "EAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Hardware implementation register */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID2, "HID2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + gen_high_BATs(env); + gen_6xx_7xx_soft_tlb(env, 64, 2); + init_excp_G2(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(G2)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC G2"; + pcc->init_proc = init_proc_G2; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | + PPC_SEGMENT | PPC_EXTERN; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_TGPR) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_AL) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_RI); + pcc->mmu_model = POWERPC_MMU_SOFT_6xx; + pcc->excp_model = POWERPC_EXCP_G2; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_ec603e; + pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_G2LE (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_G2_755(env); + gen_spr_G2(env); + /* Time base */ + gen_tbl(env); + /* External access control */ + /* XXX : not implemented */ + spr_register(env, SPR_EAR, "EAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Hardware implementation register */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID2, "HID2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + + /* Memory management */ + gen_low_BATs(env); + gen_high_BATs(env); + gen_6xx_7xx_soft_tlb(env, 64, 2); + init_excp_G2(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(G2LE)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC G2LE"; + pcc->init_proc = init_proc_G2LE; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | + PPC_SEGMENT | PPC_EXTERN; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_TGPR) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_AL) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_SOFT_6xx; + pcc->excp_model = POWERPC_EXCP_G2; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_ec603e; + pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_e200 (CPUPPCState *env) +{ + /* Time base */ + gen_tbl(env); + gen_spr_BookE(env, 0x000000070000FFFFULL); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_SPEFSCR, "SPEFSCR", + &spr_read_spefscr, &spr_write_spefscr, + &spr_read_spefscr, &spr_write_spefscr, + 0x00000000); + /* Memory management */ + gen_spr_BookE206(env, 0x0000005D, NULL); + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_Exxx_ALTCTXCR, "ALTCTXCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_Exxx_BUCSR, "BUCSR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_Exxx_CTXCR, "CTXCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_Exxx_DBCNT, "DBCNT", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_Exxx_DBCR3, "DBCR3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_Exxx_L1CFG0, "L1CFG0", + &spr_read_generic, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_Exxx_L1CSR0, "L1CSR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_Exxx_L1FINV0, "L1FINV0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_TLB0CFG, "TLB0CFG", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_TLB1CFG, "TLB1CFG", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_IAC3, "IAC3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_IAC4, "IAC4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MMUCSR0, "MMUCSR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); /* TOFIX */ + spr_register(env, SPR_BOOKE_DSRR0, "DSRR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_DSRR1, "DSRR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +#if !defined(CONFIG_USER_ONLY) + env->nb_tlb = 64; + env->nb_ways = 1; + env->id_tlbs = 0; + env->tlb_type = TLB_EMB; +#endif + init_excp_e200(env, 0xFFFF0000UL); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* XXX: TODO: allocate internal IRQ controller */ +} + +POWERPC_FAMILY(e200)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "e200 core"; + pcc->init_proc = init_proc_e200; + pcc->check_pow = check_pow_hid0; + /* XXX: unimplemented instructions: + * dcblc + * dcbtlst + * dcbtstls + * icblc + * icbtls + * tlbivax + * all SPE multiply-accumulate instructions + */ + pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | + PPC_SPE | PPC_SPE_SINGLE | + PPC_WRTEE | PPC_RFDI | + PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_TLBIVAX | + PPC_BOOKE; + pcc->msr_mask = (1ull << MSR_UCLE) | + (1ull << MSR_SPE) | + (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_DWE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR); + pcc->mmu_model = POWERPC_MMU_BOOKE206; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_860; + pcc->flags = POWERPC_FLAG_SPE | POWERPC_FLAG_CE | + POWERPC_FLAG_UBLE | POWERPC_FLAG_DE | + POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_e300 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_603(env); + /* Time base */ + gen_tbl(env); + /* hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID2, "HID2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Breakpoints */ + /* XXX : not implemented */ + spr_register(env, SPR_DABR, "DABR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_DABR2, "DABR2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_IABR2, "IABR2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_IBCR, "IBCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_DBCR, "DBCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + gen_high_BATs(env); + gen_6xx_7xx_soft_tlb(env, 64, 2); + init_excp_603(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(e300)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "e300 core"; + pcc->init_proc = init_proc_e300; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | + PPC_SEGMENT | PPC_EXTERN; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_TGPR) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_AL) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_SOFT_6xx; + pcc->excp_model = POWERPC_EXCP_603; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_603; + pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; +} + +#if !defined(CONFIG_USER_ONLY) +static void spr_write_mas73(DisasContext *ctx, int sprn, int gprn) +{ + TCGv val = tcg_temp_new(); + tcg_gen_ext32u_tl(val, cpu_gpr[gprn]); + gen_store_spr(SPR_BOOKE_MAS3, val); + tcg_gen_shri_tl(val, cpu_gpr[gprn], 32); + gen_store_spr(SPR_BOOKE_MAS7, val); + tcg_temp_free(val); +} + +static void spr_read_mas73(DisasContext *ctx, int gprn, int sprn) +{ + TCGv mas7 = tcg_temp_new(); + TCGv mas3 = tcg_temp_new(); + gen_load_spr(mas7, SPR_BOOKE_MAS7); + tcg_gen_shli_tl(mas7, mas7, 32); + gen_load_spr(mas3, SPR_BOOKE_MAS3); + tcg_gen_or_tl(cpu_gpr[gprn], mas3, mas7); + tcg_temp_free(mas3); + tcg_temp_free(mas7); +} + +#endif + +enum fsl_e500_version { + fsl_e500v1, + fsl_e500v2, + fsl_e500mc, + fsl_e5500, +}; + +static void init_proc_e500 (CPUPPCState *env, int version) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + uint32_t tlbncfg[2]; + uint64_t ivor_mask; + uint64_t ivpr_mask = 0xFFFF0000ULL; + uint32_t l1cfg0 = 0x3800 /* 8 ways */ + | 0x0020; /* 32 kb */ + uint32_t l1cfg1 = 0x3800 /* 8 ways */ + | 0x0020; /* 32 kb */ +#if !defined(CONFIG_USER_ONLY) + int i; +#endif + + /* Time base */ + gen_tbl(env); + /* + * XXX The e500 doesn't implement IVOR7 and IVOR9, but doesn't + * complain when accessing them. + * gen_spr_BookE(env, 0x0000000F0000FD7FULL); + */ + switch (version) { + case fsl_e500v1: + case fsl_e500v2: + default: + ivor_mask = 0x0000000F0000FFFFULL; + break; + case fsl_e500mc: + case fsl_e5500: + ivor_mask = 0x000003FE0000FFFFULL; + break; + } + gen_spr_BookE(env, ivor_mask); + /* Processor identification */ + spr_register(env, SPR_BOOKE_PIR, "PIR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_pir, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_SPEFSCR, "SPEFSCR", + &spr_read_spefscr, &spr_write_spefscr, + &spr_read_spefscr, &spr_write_spefscr, + 0x00000000); +#if !defined(CONFIG_USER_ONLY) + /* Memory management */ + env->nb_pids = 3; + env->nb_ways = 2; + env->id_tlbs = 0; + switch (version) { + case fsl_e500v1: + tlbncfg[0] = gen_tlbncfg(2, 1, 1, 0, 256); + tlbncfg[1] = gen_tlbncfg(16, 1, 9, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16); + break; + case fsl_e500v2: + tlbncfg[0] = gen_tlbncfg(4, 1, 1, 0, 512); + tlbncfg[1] = gen_tlbncfg(16, 1, 12, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16); + break; + case fsl_e500mc: + case fsl_e5500: + tlbncfg[0] = gen_tlbncfg(4, 1, 1, 0, 512); + tlbncfg[1] = gen_tlbncfg(64, 1, 12, TLBnCFG_AVAIL | TLBnCFG_IPROT, 64); + break; + default: + cpu_abort(CPU(cpu), "Unknown CPU: " TARGET_FMT_lx "\n", env->spr[SPR_PVR]); + } +#endif + /* Cache sizes */ + switch (version) { + case fsl_e500v1: + case fsl_e500v2: + env->dcache_line_size = 32; + env->icache_line_size = 32; + break; + case fsl_e500mc: + case fsl_e5500: + env->dcache_line_size = 64; + env->icache_line_size = 64; + l1cfg0 |= 0x1000000; /* 64 byte cache block size */ + l1cfg1 |= 0x1000000; /* 64 byte cache block size */ + break; + default: + cpu_abort(CPU(cpu), "Unknown CPU: " TARGET_FMT_lx "\n", env->spr[SPR_PVR]); + } + gen_spr_BookE206(env, 0x000000DF, tlbncfg); + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_Exxx_BBEAR, "BBEAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_Exxx_BBTAR, "BBTAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_Exxx_MCAR, "MCAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_BOOKE_MCSR, "MCSR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_Exxx_NPIDR, "NPIDR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_Exxx_BUCSR, "BUCSR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_Exxx_L1CFG0, "L1CFG0", + &spr_read_generic, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + l1cfg0); + spr_register(env, SPR_Exxx_L1CFG1, "L1CFG1", + &spr_read_generic, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + l1cfg1); + spr_register(env, SPR_Exxx_L1CSR0, "L1CSR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_e500_l1csr0, + 0x00000000); + spr_register(env, SPR_Exxx_L1CSR1, "L1CSR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_e500_l1csr1, + 0x00000000); + spr_register(env, SPR_BOOKE_MCSRR0, "MCSRR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_MCSRR1, "MCSRR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_MMUCSR0, "MMUCSR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_booke206_mmucsr0, + 0x00000000); + spr_register(env, SPR_BOOKE_EPR, "EPR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + /* XXX better abstract into Emb.xxx features */ + if (version == fsl_e5500) { + spr_register(env, SPR_BOOKE_EPCR, "EPCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_MAS7_MAS3, "MAS7_MAS3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_mas73, &spr_write_mas73, + 0x00000000); + ivpr_mask = (target_ulong)~0xFFFFULL; + } + +#if !defined(CONFIG_USER_ONLY) + env->nb_tlb = 0; + env->tlb_type = TLB_MAS; + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + env->nb_tlb += booke206_tlb_size(env, i); + } +#endif + + init_excp_e200(env, ivpr_mask); + /* Allocate hardware IRQ controller */ + ppce500_irq_init(ppc_env_get_cpu(env)); +} + +static void init_proc_e500v1(CPUPPCState *env) +{ + init_proc_e500(env, fsl_e500v1); +} + +POWERPC_FAMILY(e500v1)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "e500v1 core"; + pcc->init_proc = init_proc_e500v1; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | + PPC_SPE | PPC_SPE_SINGLE | + PPC_WRTEE | PPC_RFDI | + PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_TLBIVAX | PPC_MEM_SYNC; + pcc->insns_flags2 = PPC2_BOOKE206; + pcc->msr_mask = (1ull << MSR_UCLE) | + (1ull << MSR_SPE) | + (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_DWE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR); + pcc->mmu_model = POWERPC_MMU_BOOKE206; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_860; + pcc->flags = POWERPC_FLAG_SPE | POWERPC_FLAG_CE | + POWERPC_FLAG_UBLE | POWERPC_FLAG_DE | + POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_e500v2(CPUPPCState *env) +{ + init_proc_e500(env, fsl_e500v2); +} + +POWERPC_FAMILY(e500v2)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "e500v2 core"; + pcc->init_proc = init_proc_e500v2; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | + PPC_SPE | PPC_SPE_SINGLE | PPC_SPE_DOUBLE | + PPC_WRTEE | PPC_RFDI | + PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_TLBIVAX | PPC_MEM_SYNC; + pcc->insns_flags2 = PPC2_BOOKE206; + pcc->msr_mask = (1ull << MSR_UCLE) | + (1ull << MSR_SPE) | + (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_DWE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR); + pcc->mmu_model = POWERPC_MMU_BOOKE206; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_860; + pcc->flags = POWERPC_FLAG_SPE | POWERPC_FLAG_CE | + POWERPC_FLAG_UBLE | POWERPC_FLAG_DE | + POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_e500mc(CPUPPCState *env) +{ + init_proc_e500(env, fsl_e500mc); +} + +POWERPC_FAMILY(e500mc)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "e500mc core"; + pcc->init_proc = init_proc_e500mc; + pcc->check_pow = check_pow_none; + pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_MFTB | + PPC_WRTEE | PPC_RFDI | PPC_RFMCI | + PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_FLOAT | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_FSEL | + PPC_FLOAT_STFIWX | PPC_WAIT | + PPC_MEM_TLBSYNC | PPC_TLBIVAX | PPC_MEM_SYNC; + pcc->insns_flags2 = PPC2_BOOKE206 | PPC2_PRCNTL; + pcc->msr_mask = (1ull << MSR_GS) | + (1ull << MSR_UCLE) | + (1ull << MSR_CE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PX) | + (1ull << MSR_RI); + pcc->mmu_model = POWERPC_MMU_BOOKE206; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + /* FIXME: figure out the correct flag for e500mc */ + pcc->bfd_mach = bfd_mach_ppc_e500; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} + +#ifdef TARGET_PPC64 +static void init_proc_e5500(CPUPPCState *env) +{ + init_proc_e500(env, fsl_e5500); +} + +POWERPC_FAMILY(e5500)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "e5500 core"; + pcc->init_proc = init_proc_e5500; + pcc->check_pow = check_pow_none; + pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_MFTB | + PPC_WRTEE | PPC_RFDI | PPC_RFMCI | + PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_FLOAT | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_FSEL | + PPC_FLOAT_STFIWX | PPC_WAIT | + PPC_MEM_TLBSYNC | PPC_TLBIVAX | PPC_MEM_SYNC | + PPC_64B | PPC_POPCNTB | PPC_POPCNTWD; + pcc->insns_flags2 = PPC2_BOOKE206 | PPC2_PRCNTL | PPC2_PERM_ISA206 | \ + PPC2_FP_CVT_S64; + pcc->msr_mask = (1ull << MSR_CM) | + (1ull << MSR_GS) | + (1ull << MSR_UCLE) | + (1ull << MSR_CE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PX) | + (1ull << MSR_RI); + pcc->mmu_model = POWERPC_MMU_BOOKE206; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + /* FIXME: figure out the correct flag for e5500 */ + pcc->bfd_mach = bfd_mach_ppc_e500; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} +#endif + +/* Non-embedded PowerPC */ + +/* POWER : same as 601, without mfmsr, mfsr */ +POWERPC_FAMILY(POWER)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "POWER"; + /* pcc->insns_flags = XXX_TODO; */ + /* POWER RSC (from RAD6000) */ + pcc->msr_mask = (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_AL) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR); +} + +#define POWERPC_MSRR_601 (0x0000000000001040ULL) + +static void init_proc_601 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_601(env); + /* Hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_hid0_601, + 0x80010080); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_601_HID2, "HID2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_601_HID5, "HID5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ + init_excp_601(env); + /* XXX: beware that dcache line size is 64 + * but dcbz uses 32 bytes "sectors" + * XXX: this breaks clcs instruction ! + */ + env->dcache_line_size = 32; + env->icache_line_size = 64; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(601)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 601"; + pcc->init_proc = init_proc_601; + pcc->check_pow = check_pow_none; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_POWER_BR | + PPC_FLOAT | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | + PPC_SEGMENT | PPC_EXTERN; + pcc->msr_mask = (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR); + pcc->mmu_model = POWERPC_MMU_601; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif + pcc->excp_model = POWERPC_EXCP_601; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_601; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK; +} + +#define POWERPC_MSRR_601v (0x0000000000001040ULL) + +static void init_proc_601v (CPUPPCState *env) +{ + init_proc_601(env); + /* XXX : not implemented */ + spr_register(env, SPR_601_HID15, "HID15", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +POWERPC_FAMILY(601v)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 601v"; + pcc->init_proc = init_proc_601v; + pcc->check_pow = check_pow_none; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_POWER_BR | + PPC_FLOAT | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | + PPC_SEGMENT | PPC_EXTERN; + pcc->msr_mask = (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR); + pcc->mmu_model = POWERPC_MMU_601; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_601; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK; +} + +static void init_proc_602 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_602(env); + /* Time base */ + gen_tbl(env); + /* hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + gen_6xx_7xx_soft_tlb(env, 64, 2); + init_excp_602(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(602)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 602"; + pcc->init_proc = init_proc_602; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_6xx_TLB | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_602_SPEC; + pcc->msr_mask = (1ull << MSR_VSX) | + (1ull << MSR_SA) | + (1ull << MSR_POW) | + (1ull << MSR_TGPR) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + /* XXX: 602 MMU is quite specific. Should add a special case */ + pcc->mmu_model = POWERPC_MMU_SOFT_6xx; + pcc->excp_model = POWERPC_EXCP_602; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_602; + pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_603 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_603(env); + /* Time base */ + gen_tbl(env); + /* hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + gen_6xx_7xx_soft_tlb(env, 64, 2); + init_excp_603(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(603)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 603"; + pcc->init_proc = init_proc_603; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | + PPC_SEGMENT | PPC_EXTERN; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_TGPR) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_SOFT_6xx; + pcc->excp_model = POWERPC_EXCP_603; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_603; + pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_603E (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_603(env); + /* Time base */ + gen_tbl(env); + /* hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + gen_6xx_7xx_soft_tlb(env, 64, 2); + init_excp_603(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(603E)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 603e"; + pcc->init_proc = init_proc_603E; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | + PPC_SEGMENT | PPC_EXTERN; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_TGPR) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_SOFT_6xx; + pcc->excp_model = POWERPC_EXCP_603E; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_ec603e; + pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_604 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_604(env); + /* Time base */ + gen_tbl(env); + /* Hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + init_excp_604(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(604)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 604"; + pcc->init_proc = init_proc_604; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_EXTERN; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif + pcc->excp_model = POWERPC_EXCP_604; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_604; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_604E (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_604(env); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_MMCR1, "MMCR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC3, "PMC3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC4, "PMC4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Time base */ + gen_tbl(env); + /* Hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + init_excp_604(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(604E)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 604E"; + pcc->init_proc = init_proc_604E; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_EXTERN; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif + pcc->excp_model = POWERPC_EXCP_604; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_604; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_740 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + /* Time base */ + gen_tbl(env); + /* Thermal management */ + gen_spr_thrm(env); + /* Hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + init_excp_7x0(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(740)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 740"; + pcc->init_proc = init_proc_740; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_EXTERN; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif + pcc->excp_model = POWERPC_EXCP_7x0; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_750; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_750 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + /* XXX : not implemented */ + spr_register(env, SPR_L2CR, "L2CR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, spr_access_nop, + 0x00000000); + /* Time base */ + gen_tbl(env); + /* Thermal management */ + gen_spr_thrm(env); + /* Hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + /* XXX: high BATs are also present but are known to be bugged on + * die version 1.x + */ + init_excp_7x0(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(750)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 750"; + pcc->init_proc = init_proc_750; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_EXTERN; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif + pcc->excp_model = POWERPC_EXCP_7x0; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_750; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_750cl (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + /* XXX : not implemented */ + spr_register(env, SPR_L2CR, "L2CR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, spr_access_nop, + 0x00000000); + /* Time base */ + gen_tbl(env); + /* Thermal management */ + /* Those registers are fake on 750CL */ + spr_register(env, SPR_THRM1, "THRM1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_THRM2, "THRM2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_THRM3, "THRM3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX: not implemented */ + spr_register(env, SPR_750_TDCL, "TDCL", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_750_TDCH, "TDCH", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* DMA */ + /* XXX : not implemented */ + spr_register(env, SPR_750_WPAR, "WPAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_750_DMAL, "DMAL", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_750_DMAU, "DMAU", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_750CL_HID2, "HID2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_750CL_HID4, "HID4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Quantization registers */ + /* XXX : not implemented */ + spr_register(env, SPR_750_GQR0, "GQR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_750_GQR1, "GQR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_750_GQR2, "GQR2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_750_GQR3, "GQR3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_750_GQR4, "GQR4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_750_GQR5, "GQR5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_750_GQR6, "GQR6", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_750_GQR7, "GQR7", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + /* PowerPC 750cl has 8 DBATs and 8 IBATs */ + gen_high_BATs(env); + init_excp_750cl(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(750cl)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 750 CL"; + pcc->init_proc = init_proc_750cl; + pcc->check_pow = check_pow_hid0; + /* XXX: not implemented: + * cache lock instructions: + * dcbz_l + * floating point paired instructions + * psq_lux + * psq_lx + * psq_stux + * psq_stx + * ps_abs + * ps_add + * ps_cmpo0 + * ps_cmpo1 + * ps_cmpu0 + * ps_cmpu1 + * ps_div + * ps_madd + * ps_madds0 + * ps_madds1 + * ps_merge00 + * ps_merge01 + * ps_merge10 + * ps_merge11 + * ps_mr + * ps_msub + * ps_mul + * ps_muls0 + * ps_muls1 + * ps_nabs + * ps_neg + * ps_nmadd + * ps_nmsub + * ps_res + * ps_rsqrte + * ps_sel + * ps_sub + * ps_sum0 + * ps_sum1 + */ + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_EXTERN; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif + pcc->excp_model = POWERPC_EXCP_7x0; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_750; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_750cx (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + /* XXX : not implemented */ + spr_register(env, SPR_L2CR, "L2CR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, spr_access_nop, + 0x00000000); + /* Time base */ + gen_tbl(env); + /* Thermal management */ + gen_spr_thrm(env); + /* This register is not implemented but is present for compatibility */ + spr_register(env, SPR_SDA, "SDA", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + /* PowerPC 750cx has 8 DBATs and 8 IBATs */ + gen_high_BATs(env); + init_excp_750cx(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(750cx)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 750CX"; + pcc->init_proc = init_proc_750cx; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_EXTERN; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif + pcc->excp_model = POWERPC_EXCP_7x0; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_750; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_750fx (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + /* XXX : not implemented */ + spr_register(env, SPR_L2CR, "L2CR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, spr_access_nop, + 0x00000000); + /* Time base */ + gen_tbl(env); + /* Thermal management */ + gen_spr_thrm(env); + /* XXX : not implemented */ + spr_register(env, SPR_750_THRM4, "THRM4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_750FX_HID2, "HID2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + /* PowerPC 750fx & 750gx has 8 DBATs and 8 IBATs */ + gen_high_BATs(env); + init_excp_7x0(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(750fx)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 750FX"; + pcc->init_proc = init_proc_750fx; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_EXTERN; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif + pcc->excp_model = POWERPC_EXCP_7x0; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_750; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_750gx (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + /* XXX : not implemented (XXX: different from 750fx) */ + spr_register(env, SPR_L2CR, "L2CR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, spr_access_nop, + 0x00000000); + /* Time base */ + gen_tbl(env); + /* Thermal management */ + gen_spr_thrm(env); + /* XXX : not implemented */ + spr_register(env, SPR_750_THRM4, "THRM4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Hardware implementation registers */ + /* XXX : not implemented (XXX: different from 750fx) */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented (XXX: different from 750fx) */ + spr_register(env, SPR_750FX_HID2, "HID2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + /* PowerPC 750fx & 750gx has 8 DBATs and 8 IBATs */ + gen_high_BATs(env); + init_excp_7x0(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(750gx)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 750GX"; + pcc->init_proc = init_proc_750gx; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_EXTERN; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif + pcc->excp_model = POWERPC_EXCP_7x0; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_750; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_745 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + gen_spr_G2_755(env); + /* Time base */ + gen_tbl(env); + /* Thermal management */ + gen_spr_thrm(env); + /* Hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID2, "HID2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + gen_high_BATs(env); + gen_6xx_7xx_soft_tlb(env, 64, 2); + init_excp_7x5(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(745)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 745"; + pcc->init_proc = init_proc_745; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | + PPC_SEGMENT | PPC_EXTERN; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_SOFT_6xx; + pcc->excp_model = POWERPC_EXCP_7x5; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_750; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_755 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + gen_spr_G2_755(env); + /* Time base */ + gen_tbl(env); + /* L2 cache control */ + /* XXX : not implemented */ + spr_register(env, SPR_L2CR, "L2CR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, spr_access_nop, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_L2PMCR, "L2PMCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Thermal management */ + gen_spr_thrm(env); + /* Hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID2, "HID2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + gen_high_BATs(env); + gen_6xx_7xx_soft_tlb(env, 64, 2); + init_excp_7x5(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(755)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 755"; + pcc->init_proc = init_proc_755; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | + PPC_SEGMENT | PPC_EXTERN; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_SOFT_6xx; + pcc->excp_model = POWERPC_EXCP_7x5; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_750; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_7400 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + /* Time base */ + gen_tbl(env); + /* 74xx specific SPR */ + gen_spr_74xx(env); + /* XXX : not implemented */ + spr_register(env, SPR_UBAMR, "UBAMR", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* XXX: this seems not implemented on all revisions. */ + /* XXX : not implemented */ + spr_register(env, SPR_MSSCR1, "MSSCR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Thermal management */ + gen_spr_thrm(env); + /* Memory management */ + gen_low_BATs(env); + init_excp_7400(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(7400)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 7400 (aka G4)"; + pcc->init_proc = init_proc_7400; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBA | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_MEM_TLBIA | + PPC_SEGMENT | PPC_EXTERN | + PPC_ALTIVEC; + pcc->msr_mask = (1ull << MSR_VR) | + (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif + pcc->excp_model = POWERPC_EXCP_74xx; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_7400; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_7410 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + /* Time base */ + gen_tbl(env); + /* 74xx specific SPR */ + gen_spr_74xx(env); + /* XXX : not implemented */ + spr_register(env, SPR_UBAMR, "UBAMR", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* Thermal management */ + gen_spr_thrm(env); + /* L2PMCR */ + /* XXX : not implemented */ + spr_register(env, SPR_L2PMCR, "L2PMCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* LDSTDB */ + /* XXX : not implemented */ + spr_register(env, SPR_LDSTDB, "LDSTDB", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + init_excp_7400(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(7410)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 7410 (aka G4)"; + pcc->init_proc = init_proc_7410; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBA | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_MEM_TLBIA | + PPC_SEGMENT | PPC_EXTERN | + PPC_ALTIVEC; + pcc->msr_mask = (1ull << MSR_VR) | + (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif + pcc->excp_model = POWERPC_EXCP_74xx; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_7400; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_7440 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + /* Time base */ + gen_tbl(env); + /* 74xx specific SPR */ + gen_spr_74xx(env); + /* XXX : not implemented */ + spr_register(env, SPR_UBAMR, "UBAMR", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* LDSTCR */ + /* XXX : not implemented */ + spr_register(env, SPR_LDSTCR, "LDSTCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* ICTRL */ + /* XXX : not implemented */ + spr_register(env, SPR_ICTRL, "ICTRL", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* MSSSR0 */ + /* XXX : not implemented */ + spr_register(env, SPR_MSSSR0, "MSSSR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* PMC */ + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC5, "PMC5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UPMC5, "UPMC5", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC6, "PMC6", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UPMC6, "UPMC6", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + gen_74xx_soft_tlb(env, 128, 2); + init_excp_7450(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(7440)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 7440 (aka G4)"; + pcc->init_proc = init_proc_7440; + pcc->check_pow = check_pow_hid0_74xx; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBA | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_MEM_TLBIA | PPC_74xx_TLB | + PPC_SEGMENT | PPC_EXTERN | + PPC_ALTIVEC; + pcc->msr_mask = (1ull << MSR_VR) | + (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_SOFT_74xx; + pcc->excp_model = POWERPC_EXCP_74xx; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_7400; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_7450 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + /* Time base */ + gen_tbl(env); + /* 74xx specific SPR */ + gen_spr_74xx(env); + /* Level 3 cache control */ + gen_l3_ctrl(env); + /* L3ITCR1 */ + /* XXX : not implemented */ + spr_register(env, SPR_L3ITCR1, "L3ITCR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* L3ITCR2 */ + /* XXX : not implemented */ + spr_register(env, SPR_L3ITCR2, "L3ITCR2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* L3ITCR3 */ + /* XXX : not implemented */ + spr_register(env, SPR_L3ITCR3, "L3ITCR3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* L3OHCR */ + /* XXX : not implemented */ + spr_register(env, SPR_L3OHCR, "L3OHCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_UBAMR, "UBAMR", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* LDSTCR */ + /* XXX : not implemented */ + spr_register(env, SPR_LDSTCR, "LDSTCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* ICTRL */ + /* XXX : not implemented */ + spr_register(env, SPR_ICTRL, "ICTRL", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* MSSSR0 */ + /* XXX : not implemented */ + spr_register(env, SPR_MSSSR0, "MSSSR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* PMC */ + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC5, "PMC5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UPMC5, "UPMC5", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC6, "PMC6", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UPMC6, "UPMC6", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + gen_74xx_soft_tlb(env, 128, 2); + init_excp_7450(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(7450)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 7450 (aka G4)"; + pcc->init_proc = init_proc_7450; + pcc->check_pow = check_pow_hid0_74xx; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBA | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_MEM_TLBIA | PPC_74xx_TLB | + PPC_SEGMENT | PPC_EXTERN | + PPC_ALTIVEC; + pcc->msr_mask = (1ull << MSR_VR) | + (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_SOFT_74xx; + pcc->excp_model = POWERPC_EXCP_74xx; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_7400; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_7445 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + /* Time base */ + gen_tbl(env); + /* 74xx specific SPR */ + gen_spr_74xx(env); + /* LDSTCR */ + /* XXX : not implemented */ + spr_register(env, SPR_LDSTCR, "LDSTCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* ICTRL */ + /* XXX : not implemented */ + spr_register(env, SPR_ICTRL, "ICTRL", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* MSSSR0 */ + /* XXX : not implemented */ + spr_register(env, SPR_MSSSR0, "MSSSR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* PMC */ + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC5, "PMC5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UPMC5, "UPMC5", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC6, "PMC6", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UPMC6, "UPMC6", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* SPRGs */ + spr_register(env, SPR_SPRG4, "SPRG4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG4, "USPRG4", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_SPRG5, "SPRG5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG5, "USPRG5", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_SPRG6, "SPRG6", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG6, "USPRG6", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_SPRG7, "SPRG7", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG7, "USPRG7", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + gen_high_BATs(env); + gen_74xx_soft_tlb(env, 128, 2); + init_excp_7450(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(7445)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 7445 (aka G4)"; + pcc->init_proc = init_proc_7445; + pcc->check_pow = check_pow_hid0_74xx; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBA | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_MEM_TLBIA | PPC_74xx_TLB | + PPC_SEGMENT | PPC_EXTERN | + PPC_ALTIVEC; + pcc->msr_mask = (1ull << MSR_VR) | + (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_SOFT_74xx; + pcc->excp_model = POWERPC_EXCP_74xx; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_7400; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_7455 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + /* Time base */ + gen_tbl(env); + /* 74xx specific SPR */ + gen_spr_74xx(env); + /* Level 3 cache control */ + gen_l3_ctrl(env); + /* LDSTCR */ + /* XXX : not implemented */ + spr_register(env, SPR_LDSTCR, "LDSTCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* ICTRL */ + /* XXX : not implemented */ + spr_register(env, SPR_ICTRL, "ICTRL", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* MSSSR0 */ + /* XXX : not implemented */ + spr_register(env, SPR_MSSSR0, "MSSSR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* PMC */ + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC5, "PMC5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UPMC5, "UPMC5", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC6, "PMC6", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UPMC6, "UPMC6", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* SPRGs */ + spr_register(env, SPR_SPRG4, "SPRG4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG4, "USPRG4", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_SPRG5, "SPRG5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG5, "USPRG5", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_SPRG6, "SPRG6", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG6, "USPRG6", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_SPRG7, "SPRG7", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG7, "USPRG7", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + gen_high_BATs(env); + gen_74xx_soft_tlb(env, 128, 2); + init_excp_7450(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(7455)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 7455 (aka G4)"; + pcc->init_proc = init_proc_7455; + pcc->check_pow = check_pow_hid0_74xx; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBA | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_MEM_TLBIA | PPC_74xx_TLB | + PPC_SEGMENT | PPC_EXTERN | + PPC_ALTIVEC; + pcc->msr_mask = (1ull << MSR_VR) | + (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_SOFT_74xx; + pcc->excp_model = POWERPC_EXCP_74xx; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_7400; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_7457 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + /* Time base */ + gen_tbl(env); + /* 74xx specific SPR */ + gen_spr_74xx(env); + /* Level 3 cache control */ + gen_l3_ctrl(env); + /* L3ITCR1 */ + /* XXX : not implemented */ + spr_register(env, SPR_L3ITCR1, "L3ITCR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* L3ITCR2 */ + /* XXX : not implemented */ + spr_register(env, SPR_L3ITCR2, "L3ITCR2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* L3ITCR3 */ + /* XXX : not implemented */ + spr_register(env, SPR_L3ITCR3, "L3ITCR3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* L3OHCR */ + /* XXX : not implemented */ + spr_register(env, SPR_L3OHCR, "L3OHCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* LDSTCR */ + /* XXX : not implemented */ + spr_register(env, SPR_LDSTCR, "LDSTCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* ICTRL */ + /* XXX : not implemented */ + spr_register(env, SPR_ICTRL, "ICTRL", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* MSSSR0 */ + /* XXX : not implemented */ + spr_register(env, SPR_MSSSR0, "MSSSR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* PMC */ + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC5, "PMC5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UPMC5, "UPMC5", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC6, "PMC6", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UPMC6, "UPMC6", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* SPRGs */ + spr_register(env, SPR_SPRG4, "SPRG4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG4, "USPRG4", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_SPRG5, "SPRG5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG5, "USPRG5", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_SPRG6, "SPRG6", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG6, "USPRG6", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_SPRG7, "SPRG7", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG7, "USPRG7", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + gen_high_BATs(env); + gen_74xx_soft_tlb(env, 128, 2); + init_excp_7450(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(7457)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 7457 (aka G4)"; + pcc->init_proc = init_proc_7457; + pcc->check_pow = check_pow_hid0_74xx; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBA | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_MEM_TLBIA | PPC_74xx_TLB | + PPC_SEGMENT | PPC_EXTERN | + PPC_ALTIVEC; + pcc->msr_mask = (1ull << MSR_VR) | + (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_SOFT_74xx; + pcc->excp_model = POWERPC_EXCP_74xx; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_7400; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} + +static void init_proc_e600 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + /* Time base */ + gen_tbl(env); + /* 74xx specific SPR */ + gen_spr_74xx(env); + /* XXX : not implemented */ + spr_register(env, SPR_UBAMR, "UBAMR", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_LDSTCR, "LDSTCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_ICTRL, "ICTRL", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MSSSR0, "MSSSR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC5, "PMC5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UPMC5, "UPMC5", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_PMC6, "PMC6", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_7XX_UPMC6, "UPMC6", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* SPRGs */ + spr_register(env, SPR_SPRG4, "SPRG4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG4, "USPRG4", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_SPRG5, "SPRG5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG5, "USPRG5", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_SPRG6, "SPRG6", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG6, "USPRG6", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_SPRG7, "SPRG7", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG7, "USPRG7", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + gen_high_BATs(env); + gen_74xx_soft_tlb(env, 128, 2); + init_excp_7450(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(ppc_env_get_cpu(env)); +} + +POWERPC_FAMILY(e600)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC e600"; + pcc->init_proc = init_proc_e600; + pcc->check_pow = check_pow_hid0_74xx; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBA | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_MEM_TLBIA | PPC_74xx_TLB | + PPC_SEGMENT | PPC_EXTERN | + PPC_ALTIVEC; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = (1ull << MSR_VR) | + (1ull << MSR_POW) | + (1ull << MSR_ILE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_EP) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif + pcc->excp_model = POWERPC_EXCP_74xx; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_7400; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} + +#if defined (TARGET_PPC64) +#if defined(CONFIG_USER_ONLY) +#define POWERPC970_HID5_INIT 0x00000080 +#else +#define POWERPC970_HID5_INIT 0x00000000 +#endif + +enum BOOK3S_CPU_TYPE { + BOOK3S_CPU_970, + BOOK3S_CPU_POWER5PLUS, + BOOK3S_CPU_POWER6, + BOOK3S_CPU_POWER7, + BOOK3S_CPU_POWER8, + BOOK3S_CPU_POWER9 +}; + +static void gen_fscr_facility_check(DisasContext *ctx, int facility_sprn, + int bit, int sprn, int cause) +{ + TCGv_i32 t1 = tcg_const_i32(bit); + TCGv_i32 t2 = tcg_const_i32(sprn); + TCGv_i32 t3 = tcg_const_i32(cause); + + gen_helper_fscr_facility_check(cpu_env, t1, t2, t3); + + tcg_temp_free_i32(t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t1); +} + +static void gen_msr_facility_check(DisasContext *ctx, int facility_sprn, + int bit, int sprn, int cause) +{ + TCGv_i32 t1 = tcg_const_i32(bit); + TCGv_i32 t2 = tcg_const_i32(sprn); + TCGv_i32 t3 = tcg_const_i32(cause); + + gen_helper_msr_facility_check(cpu_env, t1, t2, t3); + + tcg_temp_free_i32(t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t1); +} + +static void spr_read_prev_upper32(DisasContext *ctx, int gprn, int sprn) +{ + TCGv spr_up = tcg_temp_new(); + TCGv spr = tcg_temp_new(); + + gen_load_spr(spr, sprn - 1); + tcg_gen_shri_tl(spr_up, spr, 32); + tcg_gen_ext32u_tl(cpu_gpr[gprn], spr_up); + + tcg_temp_free(spr); + tcg_temp_free(spr_up); +} + +static void spr_write_prev_upper32(DisasContext *ctx, int sprn, int gprn) +{ + TCGv spr = tcg_temp_new(); + + gen_load_spr(spr, sprn - 1); + tcg_gen_deposit_tl(spr, spr, cpu_gpr[gprn], 32, 32); + gen_store_spr(sprn - 1, spr); + + tcg_temp_free(spr); +} + +static int check_pow_970 (CPUPPCState *env) +{ + if (env->spr[SPR_HID0] & (HID0_DEEPNAP | HID0_DOZE | HID0_NAP)) { + return 1; + } + + return 0; +} + +static void gen_spr_970_hid(CPUPPCState *env) +{ + /* Hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_clear, + 0x60000000); + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_970_HID5, "HID5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + POWERPC970_HID5_INIT); +} + +static void gen_spr_970_hior(CPUPPCState *env) +{ + spr_register(env, SPR_HIOR, "SPR_HIOR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_hior, &spr_write_hior, + 0x00000000); +} + +static void gen_spr_book3s_common(CPUPPCState *env) +{ + spr_register(env, SPR_CTRL, "SPR_CTRL", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_UCTRL, "SPR_UCTRL", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); +} + +static void gen_spr_book3s_altivec(CPUPPCState *env) +{ + if (!(env->insns_flags & PPC_ALTIVEC)) { + return; + } + + spr_register_kvm(env, SPR_VRSAVE, "VRSAVE", + &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_VRSAVE, 0x00000000); + + /* Can't find information on what this should be on reset. This + * value is the one used by 74xx processors. */ + vscr_init(env, 0x00010000); +} + +static void gen_spr_book3s_dbg(CPUPPCState *env) +{ + /* + * TODO: different specs define different scopes for these, + * will have to address this: + * 970: super/write and super/read + * powerisa 2.03..2.04: hypv/write and super/read. + * powerisa 2.05 and newer: hypv/write and hypv/read. + */ + spr_register_kvm(env, SPR_DABR, "DABR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DABR, 0x00000000); + spr_register_kvm(env, SPR_DABRX, "DABRX", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DABRX, 0x00000000); +} + +static void gen_spr_book3s_207_dbg(CPUPPCState *env) +{ + spr_register_kvm_hv(env, SPR_DAWR, "DAWR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DAWR, 0x00000000); + spr_register_kvm_hv(env, SPR_DAWRX, "DAWRX", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DAWRX, 0x00000000); + spr_register_kvm_hv(env, SPR_CIABR, "CIABR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_CIABR, 0x00000000); +} + +static void gen_spr_970_dbg(CPUPPCState *env) +{ + /* Breakpoints */ + spr_register(env, SPR_IABR, "IABR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +static void gen_spr_book3s_pmu_sup(CPUPPCState *env) +{ + spr_register_kvm(env, SPR_POWER_MMCR0, "MMCR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_MMCR0, 0x00000000); + spr_register_kvm(env, SPR_POWER_MMCR1, "MMCR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_MMCR1, 0x00000000); + spr_register_kvm(env, SPR_POWER_MMCRA, "MMCRA", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_MMCRA, 0x00000000); + spr_register_kvm(env, SPR_POWER_PMC1, "PMC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_PMC1, 0x00000000); + spr_register_kvm(env, SPR_POWER_PMC2, "PMC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_PMC2, 0x00000000); + spr_register_kvm(env, SPR_POWER_PMC3, "PMC3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_PMC3, 0x00000000); + spr_register_kvm(env, SPR_POWER_PMC4, "PMC4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_PMC4, 0x00000000); + spr_register_kvm(env, SPR_POWER_PMC5, "PMC5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_PMC5, 0x00000000); + spr_register_kvm(env, SPR_POWER_PMC6, "PMC6", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_PMC6, 0x00000000); + spr_register_kvm(env, SPR_POWER_SIAR, "SIAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_SIAR, 0x00000000); + spr_register_kvm(env, SPR_POWER_SDAR, "SDAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_SDAR, 0x00000000); +} + +static void gen_spr_book3s_pmu_user(CPUPPCState *env) +{ + spr_register(env, SPR_POWER_UMMCR0, "UMMCR0", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, &spr_write_ureg, + 0x00000000); + spr_register(env, SPR_POWER_UMMCR1, "UMMCR1", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, &spr_write_ureg, + 0x00000000); + spr_register(env, SPR_POWER_UMMCRA, "UMMCRA", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, &spr_write_ureg, + 0x00000000); + spr_register(env, SPR_POWER_UPMC1, "UPMC1", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, &spr_write_ureg, + 0x00000000); + spr_register(env, SPR_POWER_UPMC2, "UPMC2", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, &spr_write_ureg, + 0x00000000); + spr_register(env, SPR_POWER_UPMC3, "UPMC3", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, &spr_write_ureg, + 0x00000000); + spr_register(env, SPR_POWER_UPMC4, "UPMC4", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, &spr_write_ureg, + 0x00000000); + spr_register(env, SPR_POWER_UPMC5, "UPMC5", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, &spr_write_ureg, + 0x00000000); + spr_register(env, SPR_POWER_UPMC6, "UPMC6", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, &spr_write_ureg, + 0x00000000); + spr_register(env, SPR_POWER_USIAR, "USIAR", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, &spr_write_ureg, + 0x00000000); + spr_register(env, SPR_POWER_USDAR, "USDAR", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, &spr_write_ureg, + 0x00000000); +} + +static void gen_spr_970_pmu_sup(CPUPPCState *env) +{ + spr_register_kvm(env, SPR_970_PMC7, "PMC7", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_PMC7, 0x00000000); + spr_register_kvm(env, SPR_970_PMC8, "PMC8", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_PMC8, 0x00000000); +} + +static void gen_spr_970_pmu_user(CPUPPCState *env) +{ + spr_register(env, SPR_970_UPMC7, "UPMC7", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, &spr_write_ureg, + 0x00000000); + spr_register(env, SPR_970_UPMC8, "UPMC8", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, &spr_write_ureg, + 0x00000000); +} + +static void gen_spr_power8_pmu_sup(CPUPPCState *env) +{ + spr_register_kvm(env, SPR_POWER_MMCR2, "MMCR2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_MMCR2, 0x00000000); + spr_register_kvm(env, SPR_POWER_MMCRS, "MMCRS", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_MMCRS, 0x00000000); + spr_register_kvm(env, SPR_POWER_SIER, "SIER", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_SIER, 0x00000000); + spr_register_kvm(env, SPR_POWER_SPMC1, "SPMC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_SPMC1, 0x00000000); + spr_register_kvm(env, SPR_POWER_SPMC2, "SPMC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_SPMC2, 0x00000000); + spr_register_kvm(env, SPR_TACR, "TACR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_TACR, 0x00000000); + spr_register_kvm(env, SPR_TCSCR, "TCSCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_TCSCR, 0x00000000); + spr_register_kvm(env, SPR_CSIGR, "CSIGR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_CSIGR, 0x00000000); +} + +static void gen_spr_power8_pmu_user(CPUPPCState *env) +{ + spr_register(env, SPR_POWER_UMMCR2, "UMMCR2", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, &spr_write_ureg, + 0x00000000); + spr_register(env, SPR_POWER_USIER, "USIER", + &spr_read_generic, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +static void gen_spr_power5p_ear(CPUPPCState *env) +{ + /* External access control */ + spr_register(env, SPR_EAR, "EAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +#if !defined(CONFIG_USER_ONLY) +static void spr_write_hmer(DisasContext *ctx, int sprn, int gprn) +{ + TCGv hmer = tcg_temp_new(); + + gen_load_spr(hmer, sprn); + tcg_gen_and_tl(hmer, cpu_gpr[gprn], hmer); + gen_store_spr(sprn, hmer); + spr_store_dump_spr(sprn); + tcg_temp_free(hmer); +} + +static void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]); +} + +static void spr_write_970_hid4(DisasContext *ctx, int sprn, int gprn) +{ +#if defined(TARGET_PPC64) + spr_write_generic(ctx, sprn, gprn); + gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]); +#endif +} + +#endif /* !defined(CONFIG_USER_ONLY) */ + +static void gen_spr_970_lpar(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + /* Logical partitionning */ + /* PPC970: HID4 is effectively the LPCR */ + spr_register(env, SPR_970_HID4, "HID4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_970_hid4, + 0x00000000); +#endif +} + +static void gen_spr_power5p_lpar(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + /* Logical partitionning */ + spr_register_kvm_hv(env, SPR_LPCR, "LPCR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_lpcr, + KVM_REG_PPC_LPCR, LPCR_LPES0 | LPCR_LPES1); + spr_register_hv(env, SPR_HDEC, "HDEC", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_hdecr, &spr_write_hdecr, 0); +#endif +} + +static void gen_spr_book3s_ids(CPUPPCState *env) +{ + /* FIXME: Will need to deal with thread vs core only SPRs */ + + /* Processor identification */ + spr_register_hv(env, SPR_PIR, "PIR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, NULL, + 0x00000000); + spr_register_hv(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register_hv(env, SPR_TSCR, "TSCR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register_hv(env, SPR_HMER, "HMER", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_hmer, + 0x00000000); + spr_register_hv(env, SPR_HMEER, "HMEER", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register_hv(env, SPR_TFMR, "TFMR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register_hv(env, SPR_LPIDR, "LPIDR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register_hv(env, SPR_HFSCR, "HFSCR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register_hv(env, SPR_MMCRC, "MMCRC", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register_hv(env, SPR_MMCRH, "MMCRH", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register_hv(env, SPR_HSPRG0, "HSPRG0", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register_hv(env, SPR_HSPRG1, "HSPRG1", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register_hv(env, SPR_HSRR0, "HSRR0", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register_hv(env, SPR_HSRR1, "HSRR1", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register_hv(env, SPR_HDAR, "HDAR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register_hv(env, SPR_HDSISR, "HDSISR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register_hv(env, SPR_RMOR, "RMOR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register_hv(env, SPR_HRMOR, "HRMOR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +static void gen_spr_power8_ids(CPUPPCState *env) +{ + /* Thread identification */ + spr_register(env, SPR_TIR, "TIR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); +} + +static void gen_spr_book3s_purr(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + /* PURR & SPURR: Hack - treat these as aliases for the TB for now */ + spr_register_kvm(env, SPR_PURR, "PURR", + &spr_read_purr, SPR_NOACCESS, + &spr_read_purr, SPR_NOACCESS, + KVM_REG_PPC_PURR, 0x00000000); + spr_register_kvm(env, SPR_SPURR, "SPURR", + &spr_read_purr, SPR_NOACCESS, + &spr_read_purr, SPR_NOACCESS, + KVM_REG_PPC_SPURR, 0x00000000); +#endif +} + +static void gen_spr_power6_dbg(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + spr_register(env, SPR_CFAR, "SPR_CFAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_cfar, &spr_write_cfar, + 0x00000000); +#endif +} + +static void gen_spr_power5p_common(CPUPPCState *env) +{ + spr_register_kvm(env, SPR_PPR, "PPR", + &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_PPR, 0x00000000); +} + +static void gen_spr_power6_common(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + spr_register_kvm(env, SPR_DSCR, "SPR_DSCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DSCR, 0x00000000); +#endif + /* + * Register PCR to report POWERPC_EXCP_PRIV_REG instead of + * POWERPC_EXCP_INVAL_SPR. + */ + spr_register(env, SPR_PCR, "PCR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + 0x00000000); +} + +static void spr_read_tar(DisasContext *ctx, int gprn, int sprn) +{ + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_TAR, sprn, FSCR_IC_TAR); + spr_read_generic(ctx, gprn, sprn); +} + +static void spr_write_tar(DisasContext *ctx, int sprn, int gprn) +{ + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_TAR, sprn, FSCR_IC_TAR); + spr_write_generic(ctx, sprn, gprn); +} + +static void gen_spr_power8_tce_address_control(CPUPPCState *env) +{ + spr_register_kvm(env, SPR_TAR, "TAR", + &spr_read_tar, &spr_write_tar, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_TAR, 0x00000000); +} + +static void spr_read_tm(DisasContext *ctx, int gprn, int sprn) +{ + gen_msr_facility_check(ctx, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); + spr_read_generic(ctx, gprn, sprn); +} + +static void spr_write_tm(DisasContext *ctx, int sprn, int gprn) +{ + gen_msr_facility_check(ctx, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); + spr_write_generic(ctx, sprn, gprn); +} + +static void spr_read_tm_upper32(DisasContext *ctx, int gprn, int sprn) +{ + gen_msr_facility_check(ctx, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); + spr_read_prev_upper32(ctx, gprn, sprn); +} + +static void spr_write_tm_upper32(DisasContext *ctx, int sprn, int gprn) +{ + gen_msr_facility_check(ctx, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); + spr_write_prev_upper32(ctx, sprn, gprn); +} + +static void gen_spr_power8_tm(CPUPPCState *env) +{ + spr_register_kvm(env, SPR_TFHAR, "TFHAR", + &spr_read_tm, &spr_write_tm, + &spr_read_tm, &spr_write_tm, + KVM_REG_PPC_TFHAR, 0x00000000); + spr_register_kvm(env, SPR_TFIAR, "TFIAR", + &spr_read_tm, &spr_write_tm, + &spr_read_tm, &spr_write_tm, + KVM_REG_PPC_TFIAR, 0x00000000); + spr_register_kvm(env, SPR_TEXASR, "TEXASR", + &spr_read_tm, &spr_write_tm, + &spr_read_tm, &spr_write_tm, + KVM_REG_PPC_TEXASR, 0x00000000); + spr_register(env, SPR_TEXASRU, "TEXASRU", + &spr_read_tm_upper32, &spr_write_tm_upper32, + &spr_read_tm_upper32, &spr_write_tm_upper32, + 0x00000000); +} + +static void spr_read_ebb(DisasContext *ctx, int gprn, int sprn) +{ + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); + spr_read_generic(ctx, gprn, sprn); +} + +static void spr_write_ebb(DisasContext *ctx, int sprn, int gprn) +{ + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); + spr_write_generic(ctx, sprn, gprn); +} + +static void spr_read_ebb_upper32(DisasContext *ctx, int gprn, int sprn) +{ + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); + spr_read_prev_upper32(ctx, gprn, sprn); +} + +static void spr_write_ebb_upper32(DisasContext *ctx, int sprn, int gprn) +{ + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); + spr_write_prev_upper32(ctx, sprn, gprn); +} + +static void gen_spr_power8_ebb(CPUPPCState *env) +{ + spr_register(env, SPR_BESCRS, "BESCRS", + &spr_read_ebb, &spr_write_ebb, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BESCRSU, "BESCRSU", + &spr_read_ebb_upper32, &spr_write_ebb_upper32, + &spr_read_prev_upper32, &spr_write_prev_upper32, + 0x00000000); + spr_register(env, SPR_BESCRR, "BESCRR", + &spr_read_ebb, &spr_write_ebb, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BESCRRU, "BESCRRU", + &spr_read_ebb_upper32, &spr_write_ebb_upper32, + &spr_read_prev_upper32, &spr_write_prev_upper32, + 0x00000000); + spr_register_kvm(env, SPR_EBBHR, "EBBHR", + &spr_read_ebb, &spr_write_ebb, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_EBBHR, 0x00000000); + spr_register_kvm(env, SPR_EBBRR, "EBBRR", + &spr_read_ebb, &spr_write_ebb, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_EBBRR, 0x00000000); + spr_register_kvm(env, SPR_BESCR, "BESCR", + &spr_read_ebb, &spr_write_ebb, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_BESCR, 0x00000000); +} + +/* Virtual Time Base */ +static void gen_spr_vtb(CPUPPCState *env) +{ + spr_register(env, SPR_VTB, "VTB", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_tbl, SPR_NOACCESS, + 0x00000000); +} + +static void gen_spr_power8_fscr(CPUPPCState *env) +{ +#if defined(CONFIG_USER_ONLY) + target_ulong initval = 1ULL << FSCR_TAR; +#else + target_ulong initval = 0; +#endif + spr_register_kvm(env, SPR_FSCR, "FSCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_FSCR, initval); +} + +static void gen_spr_power8_pspb(CPUPPCState *env) +{ + spr_register_kvm(env, SPR_PSPB, "PSPB", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic32, + KVM_REG_PPC_PSPB, 0); +} + +static void gen_spr_power8_ic(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + spr_register_hv(env, SPR_IC, "IC", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0); +#endif +} + +static void gen_spr_power8_book4(CPUPPCState *env) +{ + /* Add a number of P8 book4 registers */ +#if !defined(CONFIG_USER_ONLY) + spr_register_kvm(env, SPR_ACOP, "ACOP", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_ACOP, 0); + spr_register_kvm(env, SPR_BOOKS_PID, "PID", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_PID, 0); + spr_register_kvm(env, SPR_WORT, "WORT", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_WORT, 0); +#endif +} + +static void gen_spr_power7_book4(CPUPPCState *env) +{ + /* Add a number of P7 book4 registers */ +#if !defined(CONFIG_USER_ONLY) + spr_register_kvm(env, SPR_ACOP, "ACOP", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_ACOP, 0); + spr_register_kvm(env, SPR_BOOKS_PID, "PID", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_PID, 0); +#endif +} + +static void gen_spr_power8_rpr(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + spr_register_hv(env, SPR_RPR, "RPR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000103070F1F3F); +#endif +} + +static void init_proc_book3s_64(CPUPPCState *env, int version) +{ + gen_spr_ne_601(env); + gen_tbl(env); + gen_spr_book3s_altivec(env); + gen_spr_book3s_pmu_sup(env); + gen_spr_book3s_pmu_user(env); + gen_spr_book3s_common(env); + + switch (version) { + case BOOK3S_CPU_970: + case BOOK3S_CPU_POWER5PLUS: + gen_spr_970_hid(env); + gen_spr_970_hior(env); + gen_low_BATs(env); + gen_spr_970_pmu_sup(env); + gen_spr_970_pmu_user(env); + break; + case BOOK3S_CPU_POWER7: + case BOOK3S_CPU_POWER8: + case BOOK3S_CPU_POWER9: + gen_spr_book3s_ids(env); + gen_spr_amr(env, version >= BOOK3S_CPU_POWER8); + gen_spr_book3s_purr(env); + env->ci_large_pages = true; + break; + default: + g_assert_not_reached(); + } + if (version >= BOOK3S_CPU_POWER5PLUS) { + gen_spr_power5p_common(env); + gen_spr_power5p_lpar(env); + gen_spr_power5p_ear(env); + } else { + gen_spr_970_lpar(env); + } + if (version == BOOK3S_CPU_970) { + gen_spr_970_dbg(env); + } + if (version >= BOOK3S_CPU_POWER6) { + gen_spr_power6_common(env); + gen_spr_power6_dbg(env); + } + if (version == BOOK3S_CPU_POWER7) { + gen_spr_power7_book4(env); + } + if (version >= BOOK3S_CPU_POWER8) { + gen_spr_power8_tce_address_control(env); + gen_spr_power8_ids(env); + gen_spr_power8_ebb(env); + gen_spr_power8_fscr(env); + gen_spr_power8_pmu_sup(env); + gen_spr_power8_pmu_user(env); + gen_spr_power8_tm(env); + gen_spr_power8_pspb(env); + gen_spr_vtb(env); + gen_spr_power8_ic(env); + gen_spr_power8_book4(env); + gen_spr_power8_rpr(env); + } + if (version < BOOK3S_CPU_POWER8) { + gen_spr_book3s_dbg(env); + } else { + gen_spr_book3s_207_dbg(env); + } +#if !defined(CONFIG_USER_ONLY) + switch (version) { + case BOOK3S_CPU_970: + case BOOK3S_CPU_POWER5PLUS: + env->slb_nr = 64; + break; + case BOOK3S_CPU_POWER7: + case BOOK3S_CPU_POWER8: + case BOOK3S_CPU_POWER9: + default: + env->slb_nr = 32; + break; + } +#endif + /* Allocate hardware IRQ controller */ + switch (version) { + case BOOK3S_CPU_970: + case BOOK3S_CPU_POWER5PLUS: + init_excp_970(env); + ppc970_irq_init(ppc_env_get_cpu(env)); + break; + case BOOK3S_CPU_POWER7: + init_excp_POWER7(env); + ppcPOWER7_irq_init(ppc_env_get_cpu(env)); + break; + case BOOK3S_CPU_POWER8: + case BOOK3S_CPU_POWER9: + init_excp_POWER8(env); + ppcPOWER7_irq_init(ppc_env_get_cpu(env)); + break; + default: + g_assert_not_reached(); + } + + env->dcache_line_size = 128; + env->icache_line_size = 128; +} + +static void init_proc_970(CPUPPCState *env) +{ + init_proc_book3s_64(env, BOOK3S_CPU_970); +} + +POWERPC_FAMILY(970)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 970"; + pcc->init_proc = init_proc_970; + pcc->check_pow = check_pow_970; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_64B | PPC_ALTIVEC | + PPC_SEGMENT_64B | PPC_SLBI; + pcc->insns_flags2 = PPC2_FP_CVT_S64; + pcc->msr_mask = (1ull << MSR_SF) | + (1ull << MSR_VR) | + (1ull << MSR_POW) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI); + pcc->mmu_model = POWERPC_MMU_64B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; +#endif + pcc->excp_model = POWERPC_EXCP_970; + pcc->bus_model = PPC_FLAGS_INPUT_970; + pcc->bfd_mach = bfd_mach_ppc64; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; + pcc->l1_dcache_size = 0x8000; + pcc->l1_icache_size = 0x10000; +} + +static void init_proc_power5plus(CPUPPCState *env) +{ + init_proc_book3s_64(env, BOOK3S_CPU_POWER5PLUS); +} + +POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->fw_name = "PowerPC,POWER5"; + dc->desc = "POWER5+"; + pcc->init_proc = init_proc_power5plus; + pcc->check_pow = check_pow_970; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_64B | + PPC_SEGMENT_64B | PPC_SLBI; + pcc->insns_flags2 = PPC2_FP_CVT_S64; + pcc->msr_mask = (1ull << MSR_SF) | + (1ull << MSR_VR) | + (1ull << MSR_POW) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI); + pcc->mmu_model = POWERPC_MMU_2_03; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; +#endif + pcc->excp_model = POWERPC_EXCP_970; + pcc->bus_model = PPC_FLAGS_INPUT_970; + pcc->bfd_mach = bfd_mach_ppc64; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; + pcc->l1_dcache_size = 0x8000; + pcc->l1_icache_size = 0x10000; +} + +static void powerpc_get_compat(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + char *value = (char *)""; + Property *prop = opaque; + uint32_t *max_compat = qdev_get_prop_ptr(DEVICE(obj), prop); + + switch (*max_compat) { + case CPU_POWERPC_LOGICAL_2_05: + value = (char *)"power6"; + break; + case CPU_POWERPC_LOGICAL_2_06: + value = (char *)"power7"; + break; + case CPU_POWERPC_LOGICAL_2_07: + value = (char *)"power8"; + break; + case 0: + break; + default: + error_report("Internal error: compat is set to %x", *max_compat); + abort(); + break; + } + + visit_type_str(v, name, &value, errp); +} + +static void powerpc_set_compat(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + Error *error = NULL; + char *value = NULL; + Property *prop = opaque; + uint32_t *max_compat = qdev_get_prop_ptr(DEVICE(obj), prop); + + visit_type_str(v, name, &value, &error); + if (error) { + error_propagate(errp, error); + return; + } + + if (strcmp(value, "power6") == 0) { + *max_compat = CPU_POWERPC_LOGICAL_2_05; + } else if (strcmp(value, "power7") == 0) { + *max_compat = CPU_POWERPC_LOGICAL_2_06; + } else if (strcmp(value, "power8") == 0) { + *max_compat = CPU_POWERPC_LOGICAL_2_07; + } else { + error_setg(errp, "Invalid compatibility mode \"%s\"", value); + } + + g_free(value); +} + +static PropertyInfo powerpc_compat_propinfo = { + .name = "str", + .description = "compatibility mode, power6/power7/power8", + .get = powerpc_get_compat, + .set = powerpc_set_compat, +}; + +#define DEFINE_PROP_POWERPC_COMPAT(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, powerpc_compat_propinfo, uint32_t) + +static Property powerpc_servercpu_properties[] = { + DEFINE_PROP_POWERPC_COMPAT("compat", PowerPCCPU, max_compat), + DEFINE_PROP_END_OF_LIST(), +}; + +#ifdef CONFIG_SOFTMMU +static const struct ppc_segment_page_sizes POWER7_POWER8_sps = { + .sps = { + { + .page_shift = 12, /* 4K */ + .slb_enc = 0, + .enc = { { .page_shift = 12, .pte_enc = 0 }, + { .page_shift = 16, .pte_enc = 0x7 }, + { .page_shift = 24, .pte_enc = 0x38 }, }, + }, + { + .page_shift = 16, /* 64K */ + .slb_enc = SLB_VSID_64K, + .enc = { { .page_shift = 16, .pte_enc = 0x1 }, + { .page_shift = 24, .pte_enc = 0x8 }, }, + }, + { + .page_shift = 24, /* 16M */ + .slb_enc = SLB_VSID_16M, + .enc = { { .page_shift = 24, .pte_enc = 0 }, }, + }, + { + .page_shift = 34, /* 16G */ + .slb_enc = SLB_VSID_16G, + .enc = { { .page_shift = 34, .pte_enc = 0x3 }, }, + }, + } +}; +#endif /* CONFIG_SOFTMMU */ + +static void init_proc_POWER7 (CPUPPCState *env) +{ + init_proc_book3s_64(env, BOOK3S_CPU_POWER7); +} + +static bool ppc_pvr_match_power7(PowerPCCPUClass *pcc, uint32_t pvr) +{ + if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER7P_BASE) { + return true; + } + if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER7_BASE) { + return true; + } + return false; +} + +static bool cpu_has_work_POWER7(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + if (cs->halted) { + if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) { + return false; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) && + (env->spr[SPR_LPCR] & LPCR_P7_PECE0)) { + return true; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) && + (env->spr[SPR_LPCR] & LPCR_P7_PECE1)) { + return true; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK)) && + (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) { + return true; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HMI)) && + (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) { + return true; + } + if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) { + return true; + } + return false; + } else { + return msr_ee && (cs->interrupt_request & CPU_INTERRUPT_HARD); + } +} + +POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); + + dc->fw_name = "PowerPC,POWER7"; + dc->desc = "POWER7"; + dc->props = powerpc_servercpu_properties; + pcc->pvr_match = ppc_pvr_match_power7; + pcc->pcr_mask = PCR_VEC_DIS | PCR_VSX_DIS | PCR_COMPAT_2_05; + pcc->pcr_supported = PCR_COMPAT_2_06 | PCR_COMPAT_2_05; + pcc->init_proc = init_proc_POWER7; + pcc->check_pow = check_pow_nocheck; + cc->has_work = cpu_has_work_POWER7; + pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_FRSQRTES | + PPC_FLOAT_STFIWX | + PPC_FLOAT_EXT | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC | + PPC_SEGMENT_64B | PPC_SLBI | + PPC_POPCNTB | PPC_POPCNTWD | + PPC_CILDST; + pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205 | + PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | + PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | + PPC2_FP_TST_ISA206 | PPC2_FP_CVT_S64 | + PPC2_PM_ISA206; + pcc->msr_mask = (1ull << MSR_SF) | + (1ull << MSR_VR) | + (1ull << MSR_VSX) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_2_06; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; + pcc->sps = &POWER7_POWER8_sps; +#endif + pcc->excp_model = POWERPC_EXCP_POWER7; + pcc->bus_model = PPC_FLAGS_INPUT_POWER7; + pcc->bfd_mach = bfd_mach_ppc64; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR | + POWERPC_FLAG_VSX; + pcc->l1_dcache_size = 0x8000; + pcc->l1_icache_size = 0x8000; + pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; +} + +static void init_proc_POWER8(CPUPPCState *env) +{ + init_proc_book3s_64(env, BOOK3S_CPU_POWER8); +} + +static bool ppc_pvr_match_power8(PowerPCCPUClass *pcc, uint32_t pvr) +{ + if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER8NVL_BASE) { + return true; + } + if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER8E_BASE) { + return true; + } + if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER8_BASE) { + return true; + } + return false; +} + +static bool cpu_has_work_POWER8(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + if (cs->halted) { + if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) { + return false; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE2)) { + return true; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE3)) { + return true; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK)) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) { + return true; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HMI)) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) { + return true; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DOORBELL)) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE0)) { + return true; + } + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HDOORBELL)) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE1)) { + return true; + } + if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) { + return true; + } + return false; + } else { + return msr_ee && (cs->interrupt_request & CPU_INTERRUPT_HARD); + } +} + +POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); + + dc->fw_name = "PowerPC,POWER8"; + dc->desc = "POWER8"; + dc->props = powerpc_servercpu_properties; + pcc->pvr_match = ppc_pvr_match_power8; + pcc->pcr_mask = PCR_TM_DIS | PCR_COMPAT_2_06 | PCR_COMPAT_2_05; + pcc->pcr_supported = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05; + pcc->init_proc = init_proc_POWER8; + pcc->check_pow = check_pow_nocheck; + cc->has_work = cpu_has_work_POWER8; + pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_FRSQRTES | + PPC_FLOAT_STFIWX | + PPC_FLOAT_EXT | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC | + PPC_SEGMENT_64B | PPC_SLBI | + PPC_POPCNTB | PPC_POPCNTWD | + PPC_CILDST; + pcc->insns_flags2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX | + PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | + PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | + PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 | + PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | + PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | + PPC2_TM | PPC2_PM_ISA206; + pcc->msr_mask = (1ull << MSR_SF) | + (1ull << MSR_SHV) | + (1ull << MSR_TM) | + (1ull << MSR_VR) | + (1ull << MSR_VSX) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->mmu_model = POWERPC_MMU_2_07; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; + pcc->sps = &POWER7_POWER8_sps; +#endif + pcc->excp_model = POWERPC_EXCP_POWER8; + pcc->bus_model = PPC_FLAGS_INPUT_POWER7; + pcc->bfd_mach = bfd_mach_ppc64; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR | + POWERPC_FLAG_VSX | POWERPC_FLAG_TM; + pcc->l1_dcache_size = 0x8000; + pcc->l1_icache_size = 0x8000; + pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; +} +static void init_proc_POWER9(CPUPPCState *env) +{ + init_proc_book3s_64(env, BOOK3S_CPU_POWER9); +} + +static bool ppc_pvr_match_power9(PowerPCCPUClass *pcc, uint32_t pvr) +{ + if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER9_BASE) { + return true; + } + return false; +} + +POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->fw_name = "PowerPC,POWER9"; + dc->desc = "POWER9"; + dc->props = powerpc_servercpu_properties; + pcc->pvr_match = ppc_pvr_match_power9; + pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06 | PCR_COMPAT_2_07; + pcc->init_proc = init_proc_POWER9; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_FRSQRTES | + PPC_FLOAT_STFIWX | + PPC_FLOAT_EXT | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_64B | PPC_64BX | PPC_ALTIVEC | + PPC_SEGMENT_64B | PPC_SLBI | + PPC_POPCNTB | PPC_POPCNTWD | + PPC_CILDST; + pcc->insns_flags2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX | + PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | + PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | + PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 | + PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | + PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | + PPC2_TM | PPC2_PM_ISA206 | PPC2_ISA300; + pcc->msr_mask = (1ull << MSR_SF) | + (1ull << MSR_TM) | + (1ull << MSR_VR) | + (1ull << MSR_VSX) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + /* Using 2.07 defines until new radix model is added. */ + pcc->mmu_model = POWERPC_MMU_2_07; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; + /* segment page size remain the same */ + pcc->sps = &POWER7_POWER8_sps; +#endif + pcc->excp_model = POWERPC_EXCP_POWER8; + pcc->bus_model = PPC_FLAGS_INPUT_POWER7; + pcc->bfd_mach = bfd_mach_ppc64; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR | + POWERPC_FLAG_VSX | POWERPC_FLAG_TM; + pcc->l1_dcache_size = 0x8000; + pcc->l1_icache_size = 0x8000; + pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; +} + +#if !defined(CONFIG_USER_ONLY) + +void cpu_ppc_set_papr(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + ppc_spr_t *lpcr = &env->spr_cb[SPR_LPCR]; + ppc_spr_t *amor = &env->spr_cb[SPR_AMOR]; + + /* PAPR always has exception vectors in RAM not ROM. To ensure this, + * MSR[IP] should never be set. + * + * We also disallow setting of MSR_HV + */ + env->msr_mask &= ~((1ull << MSR_EP) | MSR_HVB); + + /* Set emulated LPCR to not send interrupts to hypervisor. Note that + * under KVM, the actual HW LPCR will be set differently by KVM itself, + * the settings below ensure proper operations with TCG in absence of + * a real hypervisor. + * + * Clearing VPM0 will also cause us to use RMOR in mmu-hash64.c for + * real mode accesses, which thankfully defaults to 0 and isn't + * accessible in guest mode. + */ + lpcr->default_value &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV); + lpcr->default_value |= LPCR_LPES0 | LPCR_LPES1; + + /* Set RMLS to the max (ie, 16G) */ + lpcr->default_value &= ~LPCR_RMLS; + lpcr->default_value |= 1ull << LPCR_RMLS_SHIFT; + + /* P7 and P8 has slightly different PECE bits, mostly because P8 adds + * bit 47 and 48 which are reserved on P7. Here we set them all, which + * will work as expected for both implementations + */ + lpcr->default_value |= LPCR_P8_PECE0 | LPCR_P8_PECE1 | LPCR_P8_PECE2 | + LPCR_P8_PECE3 | LPCR_P8_PECE4; + + /* We should be followed by a CPU reset but update the active value + * just in case... + */ + env->spr[SPR_LPCR] = lpcr->default_value; + + /* Set a full AMOR so guest can use the AMR as it sees fit */ + env->spr[SPR_AMOR] = amor->default_value = 0xffffffffffffffffull; + + /* Update some env bits based on new LPCR value */ + ppc_hash64_update_rmls(env); + ppc_hash64_update_vrma(env); + + /* Tell KVM that we're in PAPR mode */ + if (kvm_enabled()) { + kvmppc_set_papr(cpu); + } +} + +#endif /* !defined(CONFIG_USER_ONLY) */ + +#endif /* defined (TARGET_PPC64) */ + +/*****************************************************************************/ +/* Generic CPU instantiation routine */ +static void init_ppc_proc(PowerPCCPU *cpu) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + CPUPPCState *env = &cpu->env; +#if !defined(CONFIG_USER_ONLY) + int i; + + env->irq_inputs = NULL; + /* Set all exception vectors to an invalid address */ + for (i = 0; i < POWERPC_EXCP_NB; i++) + env->excp_vectors[i] = (target_ulong)(-1ULL); + env->ivor_mask = 0x00000000; + env->ivpr_mask = 0x00000000; + /* Default MMU definitions */ + env->nb_BATs = 0; + env->nb_tlb = 0; + env->nb_ways = 0; + env->tlb_type = TLB_NONE; +#endif + /* Register SPR common to all PowerPC implementations */ + gen_spr_generic(env); + spr_register(env, SPR_PVR, "PVR", + /* Linux permits userspace to read PVR */ +#if defined(CONFIG_LINUX_USER) + &spr_read_generic, +#else + SPR_NOACCESS, +#endif + SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + pcc->pvr); + /* Register SVR if it's defined to anything else than POWERPC_SVR_NONE */ + if (pcc->svr != POWERPC_SVR_NONE) { + if (pcc->svr & POWERPC_SVR_E500) { + spr_register(env, SPR_E500_SVR, "SVR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + pcc->svr & ~POWERPC_SVR_E500); + } else { + spr_register(env, SPR_SVR, "SVR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + pcc->svr); + } + } + /* PowerPC implementation specific initialisations (SPRs, timers, ...) */ + (*pcc->init_proc)(env); + + /* MSR bits & flags consistency checks */ + if (env->msr_mask & (1 << 25)) { + switch (env->flags & (POWERPC_FLAG_SPE | POWERPC_FLAG_VRE)) { + case POWERPC_FLAG_SPE: + case POWERPC_FLAG_VRE: + break; + default: + fprintf(stderr, "PowerPC MSR definition inconsistency\n" + "Should define POWERPC_FLAG_SPE or POWERPC_FLAG_VRE\n"); + exit(1); + } + } else if (env->flags & (POWERPC_FLAG_SPE | POWERPC_FLAG_VRE)) { + fprintf(stderr, "PowerPC MSR definition inconsistency\n" + "Should not define POWERPC_FLAG_SPE nor POWERPC_FLAG_VRE\n"); + exit(1); + } + if (env->msr_mask & (1 << 17)) { + switch (env->flags & (POWERPC_FLAG_TGPR | POWERPC_FLAG_CE)) { + case POWERPC_FLAG_TGPR: + case POWERPC_FLAG_CE: + break; + default: + fprintf(stderr, "PowerPC MSR definition inconsistency\n" + "Should define POWERPC_FLAG_TGPR or POWERPC_FLAG_CE\n"); + exit(1); + } + } else if (env->flags & (POWERPC_FLAG_TGPR | POWERPC_FLAG_CE)) { + fprintf(stderr, "PowerPC MSR definition inconsistency\n" + "Should not define POWERPC_FLAG_TGPR nor POWERPC_FLAG_CE\n"); + exit(1); + } + if (env->msr_mask & (1 << 10)) { + switch (env->flags & (POWERPC_FLAG_SE | POWERPC_FLAG_DWE | + POWERPC_FLAG_UBLE)) { + case POWERPC_FLAG_SE: + case POWERPC_FLAG_DWE: + case POWERPC_FLAG_UBLE: + break; + default: + fprintf(stderr, "PowerPC MSR definition inconsistency\n" + "Should define POWERPC_FLAG_SE or POWERPC_FLAG_DWE or " + "POWERPC_FLAG_UBLE\n"); + exit(1); + } + } else if (env->flags & (POWERPC_FLAG_SE | POWERPC_FLAG_DWE | + POWERPC_FLAG_UBLE)) { + fprintf(stderr, "PowerPC MSR definition inconsistency\n" + "Should not define POWERPC_FLAG_SE nor POWERPC_FLAG_DWE nor " + "POWERPC_FLAG_UBLE\n"); + exit(1); + } + if (env->msr_mask & (1 << 9)) { + switch (env->flags & (POWERPC_FLAG_BE | POWERPC_FLAG_DE)) { + case POWERPC_FLAG_BE: + case POWERPC_FLAG_DE: + break; + default: + fprintf(stderr, "PowerPC MSR definition inconsistency\n" + "Should define POWERPC_FLAG_BE or POWERPC_FLAG_DE\n"); + exit(1); + } + } else if (env->flags & (POWERPC_FLAG_BE | POWERPC_FLAG_DE)) { + fprintf(stderr, "PowerPC MSR definition inconsistency\n" + "Should not define POWERPC_FLAG_BE nor POWERPC_FLAG_DE\n"); + exit(1); + } + if (env->msr_mask & (1 << 2)) { + switch (env->flags & (POWERPC_FLAG_PX | POWERPC_FLAG_PMM)) { + case POWERPC_FLAG_PX: + case POWERPC_FLAG_PMM: + break; + default: + fprintf(stderr, "PowerPC MSR definition inconsistency\n" + "Should define POWERPC_FLAG_PX or POWERPC_FLAG_PMM\n"); + exit(1); + } + } else if (env->flags & (POWERPC_FLAG_PX | POWERPC_FLAG_PMM)) { + fprintf(stderr, "PowerPC MSR definition inconsistency\n" + "Should not define POWERPC_FLAG_PX nor POWERPC_FLAG_PMM\n"); + exit(1); + } + if ((env->flags & (POWERPC_FLAG_RTC_CLK | POWERPC_FLAG_BUS_CLK)) == 0) { + fprintf(stderr, "PowerPC flags inconsistency\n" + "Should define the time-base and decrementer clock source\n"); + exit(1); + } + /* Allocate TLBs buffer when needed */ +#if !defined(CONFIG_USER_ONLY) + if (env->nb_tlb != 0) { + int nb_tlb = env->nb_tlb; + if (env->id_tlbs != 0) + nb_tlb *= 2; + switch (env->tlb_type) { + case TLB_6XX: + env->tlb.tlb6 = g_malloc0(nb_tlb * sizeof(ppc6xx_tlb_t)); + break; + case TLB_EMB: + env->tlb.tlbe = g_malloc0(nb_tlb * sizeof(ppcemb_tlb_t)); + break; + case TLB_MAS: + env->tlb.tlbm = g_malloc0(nb_tlb * sizeof(ppcmas_tlb_t)); + break; + } + /* Pre-compute some useful values */ + env->tlb_per_way = env->nb_tlb / env->nb_ways; + } + if (env->irq_inputs == NULL) { + fprintf(stderr, "WARNING: no internal IRQ controller registered.\n" + " Attempt QEMU to crash very soon !\n"); + } +#endif + if (env->check_pow == NULL) { + fprintf(stderr, "WARNING: no power management check handler " + "registered.\n" + " Attempt QEMU to crash very soon !\n"); + } +} + +#if defined(PPC_DUMP_CPU) +static void dump_ppc_sprs (CPUPPCState *env) +{ + ppc_spr_t *spr; +#if !defined(CONFIG_USER_ONLY) + uint32_t sr, sw; +#endif + uint32_t ur, uw; + int i, j, n; + + printf("Special purpose registers:\n"); + for (i = 0; i < 32; i++) { + for (j = 0; j < 32; j++) { + n = (i << 5) | j; + spr = &env->spr_cb[n]; + uw = spr->uea_write != NULL && spr->uea_write != SPR_NOACCESS; + ur = spr->uea_read != NULL && spr->uea_read != SPR_NOACCESS; +#if !defined(CONFIG_USER_ONLY) + sw = spr->oea_write != NULL && spr->oea_write != SPR_NOACCESS; + sr = spr->oea_read != NULL && spr->oea_read != SPR_NOACCESS; + if (sw || sr || uw || ur) { + printf("SPR: %4d (%03x) %-8s s%c%c u%c%c\n", + (i << 5) | j, (i << 5) | j, spr->name, + sw ? 'w' : '-', sr ? 'r' : '-', + uw ? 'w' : '-', ur ? 'r' : '-'); + } +#else + if (uw || ur) { + printf("SPR: %4d (%03x) %-8s u%c%c\n", + (i << 5) | j, (i << 5) | j, spr->name, + uw ? 'w' : '-', ur ? 'r' : '-'); + } +#endif + } + } + fflush(stdout); + fflush(stderr); +} +#endif + +/*****************************************************************************/ + +/* Opcode types */ +enum { + PPC_DIRECT = 0, /* Opcode routine */ + PPC_INDIRECT = 1, /* Indirect opcode table */ +}; + +#define PPC_OPCODE_MASK 0x3 + +static inline int is_indirect_opcode (void *handler) +{ + return ((uintptr_t)handler & PPC_OPCODE_MASK) == PPC_INDIRECT; +} + +static inline opc_handler_t **ind_table(void *handler) +{ + return (opc_handler_t **)((uintptr_t)handler & ~PPC_OPCODE_MASK); +} + +/* Instruction table creation */ +/* Opcodes tables creation */ +static void fill_new_table (opc_handler_t **table, int len) +{ + int i; + + for (i = 0; i < len; i++) + table[i] = &invalid_handler; +} + +static int create_new_table (opc_handler_t **table, unsigned char idx) +{ + opc_handler_t **tmp; + + tmp = g_new(opc_handler_t *, PPC_CPU_INDIRECT_OPCODES_LEN); + fill_new_table(tmp, PPC_CPU_INDIRECT_OPCODES_LEN); + table[idx] = (opc_handler_t *)((uintptr_t)tmp | PPC_INDIRECT); + + return 0; +} + +static int insert_in_table (opc_handler_t **table, unsigned char idx, + opc_handler_t *handler) +{ + if (table[idx] != &invalid_handler) + return -1; + table[idx] = handler; + + return 0; +} + +static int register_direct_insn (opc_handler_t **ppc_opcodes, + unsigned char idx, opc_handler_t *handler) +{ + if (insert_in_table(ppc_opcodes, idx, handler) < 0) { + printf("*** ERROR: opcode %02x already assigned in main " + "opcode table\n", idx); +#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU) + printf(" Registered handler '%s' - new handler '%s'\n", + ppc_opcodes[idx]->oname, handler->oname); +#endif + return -1; + } + + return 0; +} + +static int register_ind_in_table (opc_handler_t **table, + unsigned char idx1, unsigned char idx2, + opc_handler_t *handler) +{ + if (table[idx1] == &invalid_handler) { + if (create_new_table(table, idx1) < 0) { + printf("*** ERROR: unable to create indirect table " + "idx=%02x\n", idx1); + return -1; + } + } else { + if (!is_indirect_opcode(table[idx1])) { + printf("*** ERROR: idx %02x already assigned to a direct " + "opcode\n", idx1); +#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU) + printf(" Registered handler '%s' - new handler '%s'\n", + ind_table(table[idx1])[idx2]->oname, handler->oname); +#endif + return -1; + } + } + if (handler != NULL && + insert_in_table(ind_table(table[idx1]), idx2, handler) < 0) { + printf("*** ERROR: opcode %02x already assigned in " + "opcode table %02x\n", idx2, idx1); +#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU) + printf(" Registered handler '%s' - new handler '%s'\n", + ind_table(table[idx1])[idx2]->oname, handler->oname); +#endif + return -1; + } + + return 0; +} + +static int register_ind_insn (opc_handler_t **ppc_opcodes, + unsigned char idx1, unsigned char idx2, + opc_handler_t *handler) +{ + return register_ind_in_table(ppc_opcodes, idx1, idx2, handler); +} + +static int register_dblind_insn (opc_handler_t **ppc_opcodes, + unsigned char idx1, unsigned char idx2, + unsigned char idx3, opc_handler_t *handler) +{ + if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) { + printf("*** ERROR: unable to join indirect table idx " + "[%02x-%02x]\n", idx1, idx2); + return -1; + } + if (register_ind_in_table(ind_table(ppc_opcodes[idx1]), idx2, idx3, + handler) < 0) { + printf("*** ERROR: unable to insert opcode " + "[%02x-%02x-%02x]\n", idx1, idx2, idx3); + return -1; + } + + return 0; +} + +static int register_trplind_insn(opc_handler_t **ppc_opcodes, + unsigned char idx1, unsigned char idx2, + unsigned char idx3, unsigned char idx4, + opc_handler_t *handler) +{ + opc_handler_t **table; + + if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) { + printf("*** ERROR: unable to join indirect table idx " + "[%02x-%02x]\n", idx1, idx2); + return -1; + } + table = ind_table(ppc_opcodes[idx1]); + if (register_ind_in_table(table, idx2, idx3, NULL) < 0) { + printf("*** ERROR: unable to join 2nd-level indirect table idx " + "[%02x-%02x-%02x]\n", idx1, idx2, idx3); + return -1; + } + table = ind_table(table[idx2]); + if (register_ind_in_table(table, idx3, idx4, handler) < 0) { + printf("*** ERROR: unable to insert opcode " + "[%02x-%02x-%02x-%02x]\n", idx1, idx2, idx3, idx4); + return -1; + } + return 0; +} +static int register_insn (opc_handler_t **ppc_opcodes, opcode_t *insn) +{ + if (insn->opc2 != 0xFF) { + if (insn->opc3 != 0xFF) { + if (insn->opc4 != 0xFF) { + if (register_trplind_insn(ppc_opcodes, insn->opc1, insn->opc2, + insn->opc3, insn->opc4, + &insn->handler) < 0) { + return -1; + } + } else { + if (register_dblind_insn(ppc_opcodes, insn->opc1, insn->opc2, + insn->opc3, &insn->handler) < 0) + return -1; + } + } else { + if (register_ind_insn(ppc_opcodes, insn->opc1, + insn->opc2, &insn->handler) < 0) + return -1; + } + } else { + if (register_direct_insn(ppc_opcodes, insn->opc1, &insn->handler) < 0) + return -1; + } + + return 0; +} + +static int test_opcode_table (opc_handler_t **table, int len) +{ + int i, count, tmp; + + for (i = 0, count = 0; i < len; i++) { + /* Consistency fixup */ + if (table[i] == NULL) + table[i] = &invalid_handler; + if (table[i] != &invalid_handler) { + if (is_indirect_opcode(table[i])) { + tmp = test_opcode_table(ind_table(table[i]), + PPC_CPU_INDIRECT_OPCODES_LEN); + if (tmp == 0) { + free(table[i]); + table[i] = &invalid_handler; + } else { + count++; + } + } else { + count++; + } + } + } + + return count; +} + +static void fix_opcode_tables (opc_handler_t **ppc_opcodes) +{ + if (test_opcode_table(ppc_opcodes, PPC_CPU_OPCODES_LEN) == 0) + printf("*** WARNING: no opcode defined !\n"); +} + +/*****************************************************************************/ +static void create_ppc_opcodes(PowerPCCPU *cpu, Error **errp) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + CPUPPCState *env = &cpu->env; + opcode_t *opc; + + fill_new_table(env->opcodes, PPC_CPU_OPCODES_LEN); + for (opc = opcodes; opc < &opcodes[ARRAY_SIZE(opcodes)]; opc++) { + if (((opc->handler.type & pcc->insns_flags) != 0) || + ((opc->handler.type2 & pcc->insns_flags2) != 0)) { + if (register_insn(env->opcodes, opc) < 0) { + error_setg(errp, "ERROR initializing PowerPC instruction " + "0x%02x 0x%02x 0x%02x", opc->opc1, opc->opc2, + opc->opc3); + return; + } + } + } + fix_opcode_tables(env->opcodes); + fflush(stdout); + fflush(stderr); +} + +#if defined(PPC_DUMP_CPU) +static void dump_ppc_insns (CPUPPCState *env) +{ + opc_handler_t **table, *handler; + const char *p, *q; + uint8_t opc1, opc2, opc3, opc4; + + printf("Instructions set:\n"); + /* opc1 is 6 bits long */ + for (opc1 = 0x00; opc1 < PPC_CPU_OPCODES_LEN; opc1++) { + table = env->opcodes; + handler = table[opc1]; + if (is_indirect_opcode(handler)) { + /* opc2 is 5 bits long */ + for (opc2 = 0; opc2 < PPC_CPU_INDIRECT_OPCODES_LEN; opc2++) { + table = env->opcodes; + handler = env->opcodes[opc1]; + table = ind_table(handler); + handler = table[opc2]; + if (is_indirect_opcode(handler)) { + table = ind_table(handler); + /* opc3 is 5 bits long */ + for (opc3 = 0; opc3 < PPC_CPU_INDIRECT_OPCODES_LEN; + opc3++) { + handler = table[opc3]; + if (is_indirect_opcode(handler)) { + table = ind_table(handler); + /* opc4 is 5 bits long */ + for (opc4 = 0; opc4 < PPC_CPU_INDIRECT_OPCODES_LEN; + opc4++) { + handler = table[opc4]; + if (handler->handler != &gen_invalid) { + printf("INSN: %02x %02x %02x %02x -- " + "(%02d %04d %02d) : %s\n", + opc1, opc2, opc3, opc4, + opc1, (opc3 << 5) | opc2, opc4, + handler->oname); + } + } + } else { + if (handler->handler != &gen_invalid) { + /* Special hack to properly dump SPE insns */ + p = strchr(handler->oname, '_'); + if (p == NULL) { + printf("INSN: %02x %02x %02x (%02d %04d) : " + "%s\n", + opc1, opc2, opc3, opc1, + (opc3 << 5) | opc2, + handler->oname); + } else { + q = "speundef"; + if ((p - handler->oname) != strlen(q) + || (memcmp(handler->oname, q, strlen(q)) + != 0)) { + /* First instruction */ + printf("INSN: %02x %02x %02x" + "(%02d %04d) : %.*s\n", + opc1, opc2 << 1, opc3, opc1, + (opc3 << 6) | (opc2 << 1), + (int)(p - handler->oname), + handler->oname); + } + if (strcmp(p + 1, q) != 0) { + /* Second instruction */ + printf("INSN: %02x %02x %02x " + "(%02d %04d) : %s\n", opc1, + (opc2 << 1) | 1, opc3, opc1, + (opc3 << 6) | (opc2 << 1) | 1, + p + 1); + } + } + } + } + } + } else { + if (handler->handler != &gen_invalid) { + printf("INSN: %02x %02x -- (%02d %04d) : %s\n", + opc1, opc2, opc1, opc2, handler->oname); + } + } + } + } else { + if (handler->handler != &gen_invalid) { + printf("INSN: %02x -- -- (%02d ----) : %s\n", + opc1, opc1, handler->oname); + } + } + } +} +#endif + +static bool avr_need_swap(CPUPPCState *env) +{ +#ifdef HOST_WORDS_BIGENDIAN + return msr_le; +#else + return !msr_le; +#endif +} + +static int gdb_get_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { + stfq_p(mem_buf, env->fpr[n]); + ppc_maybe_bswap_register(env, mem_buf, 8); + return 8; + } + if (n == 32) { + stl_p(mem_buf, env->fpscr); + ppc_maybe_bswap_register(env, mem_buf, 4); + return 4; + } + return 0; +} + +static int gdb_set_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { + ppc_maybe_bswap_register(env, mem_buf, 8); + env->fpr[n] = ldfq_p(mem_buf); + return 8; + } + if (n == 32) { + ppc_maybe_bswap_register(env, mem_buf, 4); + helper_store_fpscr(env, ldl_p(mem_buf), 0xffffffff); + return 4; + } + return 0; +} + +static int gdb_get_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { + if (!avr_need_swap(env)) { + stq_p(mem_buf, env->avr[n].u64[0]); + stq_p(mem_buf+8, env->avr[n].u64[1]); + } else { + stq_p(mem_buf, env->avr[n].u64[1]); + stq_p(mem_buf+8, env->avr[n].u64[0]); + } + ppc_maybe_bswap_register(env, mem_buf, 8); + ppc_maybe_bswap_register(env, mem_buf + 8, 8); + return 16; + } + if (n == 32) { + stl_p(mem_buf, env->vscr); + ppc_maybe_bswap_register(env, mem_buf, 4); + return 4; + } + if (n == 33) { + stl_p(mem_buf, (uint32_t)env->spr[SPR_VRSAVE]); + ppc_maybe_bswap_register(env, mem_buf, 4); + return 4; + } + return 0; +} + +static int gdb_set_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { + ppc_maybe_bswap_register(env, mem_buf, 8); + ppc_maybe_bswap_register(env, mem_buf + 8, 8); + if (!avr_need_swap(env)) { + env->avr[n].u64[0] = ldq_p(mem_buf); + env->avr[n].u64[1] = ldq_p(mem_buf+8); + } else { + env->avr[n].u64[1] = ldq_p(mem_buf); + env->avr[n].u64[0] = ldq_p(mem_buf+8); + } + return 16; + } + if (n == 32) { + ppc_maybe_bswap_register(env, mem_buf, 4); + env->vscr = ldl_p(mem_buf); + return 4; + } + if (n == 33) { + ppc_maybe_bswap_register(env, mem_buf, 4); + env->spr[SPR_VRSAVE] = (target_ulong)ldl_p(mem_buf); + return 4; + } + return 0; +} + +static int gdb_get_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { +#if defined(TARGET_PPC64) + stl_p(mem_buf, env->gpr[n] >> 32); + ppc_maybe_bswap_register(env, mem_buf, 4); +#else + stl_p(mem_buf, env->gprh[n]); +#endif + return 4; + } + if (n == 32) { + stq_p(mem_buf, env->spe_acc); + ppc_maybe_bswap_register(env, mem_buf, 8); + return 8; + } + if (n == 33) { + stl_p(mem_buf, env->spe_fscr); + ppc_maybe_bswap_register(env, mem_buf, 4); + return 4; + } + return 0; +} + +static int gdb_set_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { +#if defined(TARGET_PPC64) + target_ulong lo = (uint32_t)env->gpr[n]; + target_ulong hi; + + ppc_maybe_bswap_register(env, mem_buf, 4); + + hi = (target_ulong)ldl_p(mem_buf) << 32; + env->gpr[n] = lo | hi; +#else + env->gprh[n] = ldl_p(mem_buf); +#endif + return 4; + } + if (n == 32) { + ppc_maybe_bswap_register(env, mem_buf, 8); + env->spe_acc = ldq_p(mem_buf); + return 8; + } + if (n == 33) { + ppc_maybe_bswap_register(env, mem_buf, 4); + env->spe_fscr = ldl_p(mem_buf); + return 4; + } + return 0; +} + +static int gdb_get_vsx_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { + stq_p(mem_buf, env->vsr[n]); + ppc_maybe_bswap_register(env, mem_buf, 8); + return 8; + } + return 0; +} + +static int gdb_set_vsx_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { + ppc_maybe_bswap_register(env, mem_buf, 8); + env->vsr[n] = ldq_p(mem_buf); + return 8; + } + return 0; +} + +static int ppc_fixup_cpu(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + + /* TCG doesn't (yet) emulate some groups of instructions that + * are implemented on some otherwise supported CPUs (e.g. VSX + * and decimal floating point instructions on POWER7). We + * remove unsupported instruction groups from the cpu state's + * instruction masks and hope the guest can cope. For at + * least the pseries machine, the unavailability of these + * instructions can be advertised to the guest via the device + * tree. */ + if ((env->insns_flags & ~PPC_TCG_INSNS) + || (env->insns_flags2 & ~PPC_TCG_INSNS2)) { + fprintf(stderr, "Warning: Disabling some instructions which are not " + "emulated by TCG (0x%" PRIx64 ", 0x%" PRIx64 ")\n", + env->insns_flags & ~PPC_TCG_INSNS, + env->insns_flags2 & ~PPC_TCG_INSNS2); + } + env->insns_flags &= PPC_TCG_INSNS; + env->insns_flags2 &= PPC_TCG_INSNS2; + return 0; +} + +static inline bool ppc_cpu_is_valid(PowerPCCPUClass *pcc) +{ +#ifdef TARGET_PPCEMB + return pcc->mmu_model == POWERPC_MMU_BOOKE || + pcc->mmu_model == POWERPC_MMU_SOFT_4xx || + pcc->mmu_model == POWERPC_MMU_SOFT_4xx_Z; +#else + return true; +#endif +} + +static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) +{ + CPUState *cs = CPU(dev); + PowerPCCPU *cpu = POWERPC_CPU(dev); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + Error *local_err = NULL; +#if !defined(CONFIG_USER_ONLY) + int max_smt = kvmppc_smt_threads(); +#endif + +#if !defined(CONFIG_USER_ONLY) + if (smp_threads > max_smt) { + error_setg(errp, "Cannot support more than %d threads on PPC with %s", + max_smt, kvm_enabled() ? "KVM" : "TCG"); + return; + } + if (!is_power_of_2(smp_threads)) { + error_setg(errp, "Cannot support %d threads on PPC with %s, " + "threads count must be a power of 2.", + smp_threads, kvm_enabled() ? "KVM" : "TCG"); + return; + } +#endif + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + +#if !defined(CONFIG_USER_ONLY) + cpu->cpu_dt_id = (cs->cpu_index / smp_threads) * max_smt + + (cs->cpu_index % smp_threads); + + if (kvm_enabled() && !kvm_vcpu_id_is_valid(cpu->cpu_dt_id)) { + error_setg(errp, "Can't create CPU with id %d in KVM", cpu->cpu_dt_id); + error_append_hint(errp, "Adjust the number of cpus to %d " + "or try to raise the number of threads per core\n", + cpu->cpu_dt_id * smp_threads / max_smt); + return; + } +#endif + + if (tcg_enabled()) { + if (ppc_fixup_cpu(cpu) != 0) { + error_setg(errp, "Unable to emulate selected CPU with TCG"); + return; + } + } + +#if defined(TARGET_PPCEMB) + if (!ppc_cpu_is_valid(pcc)) { + error_setg(errp, "CPU does not possess a BookE or 4xx MMU. " + "Please use qemu-system-ppc or qemu-system-ppc64 instead " + "or choose another CPU model."); + return; + } +#endif + + create_ppc_opcodes(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + init_ppc_proc(cpu); + + if (pcc->insns_flags & PPC_FLOAT) { + gdb_register_coprocessor(cs, gdb_get_float_reg, gdb_set_float_reg, + 33, "power-fpu.xml", 0); + } + if (pcc->insns_flags & PPC_ALTIVEC) { + gdb_register_coprocessor(cs, gdb_get_avr_reg, gdb_set_avr_reg, + 34, "power-altivec.xml", 0); + } + if (pcc->insns_flags & PPC_SPE) { + gdb_register_coprocessor(cs, gdb_get_spe_reg, gdb_set_spe_reg, + 34, "power-spe.xml", 0); + } + if (pcc->insns_flags2 & PPC2_VSX) { + gdb_register_coprocessor(cs, gdb_get_vsx_reg, gdb_set_vsx_reg, + 32, "power-vsx.xml", 0); + } + + qemu_init_vcpu(cs); + + pcc->parent_realize(dev, errp); + +#if defined(PPC_DUMP_CPU) + { + CPUPPCState *env = &cpu->env; + const char *mmu_model, *excp_model, *bus_model; + switch (env->mmu_model) { + case POWERPC_MMU_32B: + mmu_model = "PowerPC 32"; + break; + case POWERPC_MMU_SOFT_6xx: + mmu_model = "PowerPC 6xx/7xx with software driven TLBs"; + break; + case POWERPC_MMU_SOFT_74xx: + mmu_model = "PowerPC 74xx with software driven TLBs"; + break; + case POWERPC_MMU_SOFT_4xx: + mmu_model = "PowerPC 4xx with software driven TLBs"; + break; + case POWERPC_MMU_SOFT_4xx_Z: + mmu_model = "PowerPC 4xx with software driven TLBs " + "and zones protections"; + break; + case POWERPC_MMU_REAL: + mmu_model = "PowerPC real mode only"; + break; + case POWERPC_MMU_MPC8xx: + mmu_model = "PowerPC MPC8xx"; + break; + case POWERPC_MMU_BOOKE: + mmu_model = "PowerPC BookE"; + break; + case POWERPC_MMU_BOOKE206: + mmu_model = "PowerPC BookE 2.06"; + break; + case POWERPC_MMU_601: + mmu_model = "PowerPC 601"; + break; +#if defined (TARGET_PPC64) + case POWERPC_MMU_64B: + mmu_model = "PowerPC 64"; + break; +#endif + default: + mmu_model = "Unknown or invalid"; + break; + } + switch (env->excp_model) { + case POWERPC_EXCP_STD: + excp_model = "PowerPC"; + break; + case POWERPC_EXCP_40x: + excp_model = "PowerPC 40x"; + break; + case POWERPC_EXCP_601: + excp_model = "PowerPC 601"; + break; + case POWERPC_EXCP_602: + excp_model = "PowerPC 602"; + break; + case POWERPC_EXCP_603: + excp_model = "PowerPC 603"; + break; + case POWERPC_EXCP_603E: + excp_model = "PowerPC 603e"; + break; + case POWERPC_EXCP_604: + excp_model = "PowerPC 604"; + break; + case POWERPC_EXCP_7x0: + excp_model = "PowerPC 740/750"; + break; + case POWERPC_EXCP_7x5: + excp_model = "PowerPC 745/755"; + break; + case POWERPC_EXCP_74xx: + excp_model = "PowerPC 74xx"; + break; + case POWERPC_EXCP_BOOKE: + excp_model = "PowerPC BookE"; + break; +#if defined (TARGET_PPC64) + case POWERPC_EXCP_970: + excp_model = "PowerPC 970"; + break; +#endif + default: + excp_model = "Unknown or invalid"; + break; + } + switch (env->bus_model) { + case PPC_FLAGS_INPUT_6xx: + bus_model = "PowerPC 6xx"; + break; + case PPC_FLAGS_INPUT_BookE: + bus_model = "PowerPC BookE"; + break; + case PPC_FLAGS_INPUT_405: + bus_model = "PowerPC 405"; + break; + case PPC_FLAGS_INPUT_401: + bus_model = "PowerPC 401/403"; + break; + case PPC_FLAGS_INPUT_RCPU: + bus_model = "RCPU / MPC8xx"; + break; +#if defined (TARGET_PPC64) + case PPC_FLAGS_INPUT_970: + bus_model = "PowerPC 970"; + break; +#endif + default: + bus_model = "Unknown or invalid"; + break; + } + printf("PowerPC %-12s : PVR %08x MSR %016" PRIx64 "\n" + " MMU model : %s\n", + object_class_get_name(OBJECT_CLASS(pcc)), + pcc->pvr, pcc->msr_mask, mmu_model); +#if !defined(CONFIG_USER_ONLY) + if (env->tlb.tlb6) { + printf(" %d %s TLB in %d ways\n", + env->nb_tlb, env->id_tlbs ? "splitted" : "merged", + env->nb_ways); + } +#endif + printf(" Exceptions model : %s\n" + " Bus model : %s\n", + excp_model, bus_model); + printf(" MSR features :\n"); + if (env->flags & POWERPC_FLAG_SPE) + printf(" signal processing engine enable" + "\n"); + else if (env->flags & POWERPC_FLAG_VRE) + printf(" vector processor enable\n"); + if (env->flags & POWERPC_FLAG_TGPR) + printf(" temporary GPRs\n"); + else if (env->flags & POWERPC_FLAG_CE) + printf(" critical input enable\n"); + if (env->flags & POWERPC_FLAG_SE) + printf(" single-step trace mode\n"); + else if (env->flags & POWERPC_FLAG_DWE) + printf(" debug wait enable\n"); + else if (env->flags & POWERPC_FLAG_UBLE) + printf(" user BTB lock enable\n"); + if (env->flags & POWERPC_FLAG_BE) + printf(" branch-step trace mode\n"); + else if (env->flags & POWERPC_FLAG_DE) + printf(" debug interrupt enable\n"); + if (env->flags & POWERPC_FLAG_PX) + printf(" inclusive protection\n"); + else if (env->flags & POWERPC_FLAG_PMM) + printf(" performance monitor mark\n"); + if (env->flags == POWERPC_FLAG_NONE) + printf(" none\n"); + printf(" Time-base/decrementer clock source: %s\n", + env->flags & POWERPC_FLAG_RTC_CLK ? "RTC clock" : "bus clock"); + dump_ppc_insns(env); + dump_ppc_sprs(env); + fflush(stdout); + } +#endif +} + +static void ppc_cpu_unrealizefn(DeviceState *dev, Error **errp) +{ + PowerPCCPU *cpu = POWERPC_CPU(dev); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + CPUPPCState *env = &cpu->env; + Error *local_err = NULL; + opc_handler_t **table, **table_2; + int i, j, k; + + pcc->parent_unrealize(dev, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + + for (i = 0; i < PPC_CPU_OPCODES_LEN; i++) { + if (env->opcodes[i] == &invalid_handler) { + continue; + } + if (is_indirect_opcode(env->opcodes[i])) { + table = ind_table(env->opcodes[i]); + for (j = 0; j < PPC_CPU_INDIRECT_OPCODES_LEN; j++) { + if (table[j] == &invalid_handler) { + continue; + } + if (is_indirect_opcode(table[j])) { + table_2 = ind_table(table[j]); + for (k = 0; k < PPC_CPU_INDIRECT_OPCODES_LEN; k++) { + if (table_2[k] != &invalid_handler && + is_indirect_opcode(table_2[k])) { + g_free((opc_handler_t *)((uintptr_t)table_2[k] & + ~PPC_INDIRECT)); + } + } + g_free((opc_handler_t *)((uintptr_t)table[j] & + ~PPC_INDIRECT)); + } + } + g_free((opc_handler_t *)((uintptr_t)env->opcodes[i] & + ~PPC_INDIRECT)); + } + } +} + +int ppc_get_compat_smt_threads(PowerPCCPU *cpu) +{ + CPUState *cs = CPU(cpu); + int ret = MIN(cs->nr_threads, kvmppc_smt_threads()); + + switch (cpu->cpu_version) { + case CPU_POWERPC_LOGICAL_2_05: + ret = MIN(ret, 2); + break; + case CPU_POWERPC_LOGICAL_2_06: + ret = MIN(ret, 4); + break; + case CPU_POWERPC_LOGICAL_2_07: + ret = MIN(ret, 8); + break; + } + + return ret; +} + +#ifdef TARGET_PPC64 +void ppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version, Error **errp) +{ + int ret = 0; + CPUPPCState *env = &cpu->env; + PowerPCCPUClass *host_pcc; + + cpu->cpu_version = cpu_version; + + switch (cpu_version) { + case CPU_POWERPC_LOGICAL_2_05: + env->spr[SPR_PCR] = PCR_TM_DIS | PCR_VSX_DIS | PCR_COMPAT_2_07 | + PCR_COMPAT_2_06 | PCR_COMPAT_2_05; + break; + case CPU_POWERPC_LOGICAL_2_06: + case CPU_POWERPC_LOGICAL_2_06_PLUS: + env->spr[SPR_PCR] = PCR_TM_DIS | PCR_COMPAT_2_07 | PCR_COMPAT_2_06; + break; + case CPU_POWERPC_LOGICAL_2_07: + env->spr[SPR_PCR] = PCR_COMPAT_2_07; + break; + default: + env->spr[SPR_PCR] = 0; + break; + } + + host_pcc = kvm_ppc_get_host_cpu_class(); + if (host_pcc) { + env->spr[SPR_PCR] &= host_pcc->pcr_mask; + } + + if (kvm_enabled()) { + ret = kvmppc_set_compat(cpu, cpu->cpu_version); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Unable to set CPU compatibility mode in KVM"); + } + } +} +#endif + +static gint ppc_cpu_compare_class_pvr(gconstpointer a, gconstpointer b) +{ + ObjectClass *oc = (ObjectClass *)a; + uint32_t pvr = *(uint32_t *)b; + PowerPCCPUClass *pcc = (PowerPCCPUClass *)a; + + /* -cpu host does a PVR lookup during construction */ + if (unlikely(strcmp(object_class_get_name(oc), + TYPE_HOST_POWERPC_CPU) == 0)) { + return -1; + } + + if (!ppc_cpu_is_valid(pcc)) { + return -1; + } + + return pcc->pvr == pvr ? 0 : -1; +} + +PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr) +{ + GSList *list, *item; + PowerPCCPUClass *pcc = NULL; + + list = object_class_get_list(TYPE_POWERPC_CPU, false); + item = g_slist_find_custom(list, &pvr, ppc_cpu_compare_class_pvr); + if (item != NULL) { + pcc = POWERPC_CPU_CLASS(item->data); + } + g_slist_free(list); + + return pcc; +} + +static gint ppc_cpu_compare_class_pvr_mask(gconstpointer a, gconstpointer b) +{ + ObjectClass *oc = (ObjectClass *)a; + uint32_t pvr = *(uint32_t *)b; + PowerPCCPUClass *pcc = (PowerPCCPUClass *)a; + + /* -cpu host does a PVR lookup during construction */ + if (unlikely(strcmp(object_class_get_name(oc), + TYPE_HOST_POWERPC_CPU) == 0)) { + return -1; + } + + if (!ppc_cpu_is_valid(pcc)) { + return -1; + } + + if (pcc->pvr_match(pcc, pvr)) { + return 0; + } + + return -1; +} + +PowerPCCPUClass *ppc_cpu_class_by_pvr_mask(uint32_t pvr) +{ + GSList *list, *item; + PowerPCCPUClass *pcc = NULL; + + list = object_class_get_list(TYPE_POWERPC_CPU, true); + item = g_slist_find_custom(list, &pvr, ppc_cpu_compare_class_pvr_mask); + if (item != NULL) { + pcc = POWERPC_CPU_CLASS(item->data); + } + g_slist_free(list); + + return pcc; +} + +static gint ppc_cpu_compare_class_name(gconstpointer a, gconstpointer b) +{ + ObjectClass *oc = (ObjectClass *)a; + const char *name = b; + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + if (strncasecmp(name, object_class_get_name(oc), strlen(name)) == 0 && + ppc_cpu_is_valid(pcc) && + strcmp(object_class_get_name(oc) + strlen(name), + "-" TYPE_POWERPC_CPU) == 0) { + return 0; + } + return -1; +} + + +static ObjectClass *ppc_cpu_class_by_name(const char *name); + +static ObjectClass *ppc_cpu_class_by_alias(PowerPCCPUAlias *alias) +{ + ObjectClass *invalid_class = (void*)ppc_cpu_class_by_alias; + + /* Cache target class lookups in the alias table */ + if (!alias->oc) { + alias->oc = ppc_cpu_class_by_name(alias->model); + if (!alias->oc) { + /* Fast check for non-existing aliases */ + alias->oc = invalid_class; + } + } + + if (alias->oc == invalid_class) { + return NULL; + } else { + return alias->oc; + } +} + +static ObjectClass *ppc_cpu_class_by_name(const char *name) +{ + GSList *list, *item; + ObjectClass *ret = NULL; + const char *p; + int i, len; + + /* Check if the given name is a PVR */ + len = strlen(name); + if (len == 10 && name[0] == '0' && name[1] == 'x') { + p = name + 2; + goto check_pvr; + } else if (len == 8) { + p = name; + check_pvr: + for (i = 0; i < 8; i++) { + if (!qemu_isxdigit(*p++)) + break; + } + if (i == 8) { + return OBJECT_CLASS(ppc_cpu_class_by_pvr(strtoul(name, NULL, 16))); + } + } + + list = object_class_get_list(TYPE_POWERPC_CPU, false); + item = g_slist_find_custom(list, name, ppc_cpu_compare_class_name); + if (item != NULL) { + ret = OBJECT_CLASS(item->data); + } + g_slist_free(list); + + if (ret) { + return ret; + } + + for (i = 0; ppc_cpu_aliases[i].alias != NULL; i++) { + if (strcmp(ppc_cpu_aliases[i].alias, name) == 0) { + return ppc_cpu_class_by_alias(&ppc_cpu_aliases[i]); + } + } + + return NULL; +} + +const char *ppc_cpu_lookup_alias(const char *alias) +{ + int ai; + + for (ai = 0; ppc_cpu_aliases[ai].alias != NULL; ai++) { + if (strcmp(ppc_cpu_aliases[ai].alias, alias) == 0) { + return ppc_cpu_aliases[ai].model; + } + } + + return NULL; +} + +PowerPCCPU *cpu_ppc_init(const char *cpu_model) +{ + return POWERPC_CPU(cpu_generic_init(TYPE_POWERPC_CPU, cpu_model)); +} + +/* Sort by PVR, ordering special case "host" last. */ +static gint ppc_cpu_list_compare(gconstpointer a, gconstpointer b) +{ + ObjectClass *oc_a = (ObjectClass *)a; + ObjectClass *oc_b = (ObjectClass *)b; + PowerPCCPUClass *pcc_a = POWERPC_CPU_CLASS(oc_a); + PowerPCCPUClass *pcc_b = POWERPC_CPU_CLASS(oc_b); + const char *name_a = object_class_get_name(oc_a); + const char *name_b = object_class_get_name(oc_b); + + if (strcmp(name_a, TYPE_HOST_POWERPC_CPU) == 0) { + return 1; + } else if (strcmp(name_b, TYPE_HOST_POWERPC_CPU) == 0) { + return -1; + } else { + /* Avoid an integer overflow during subtraction */ + if (pcc_a->pvr < pcc_b->pvr) { + return -1; + } else if (pcc_a->pvr > pcc_b->pvr) { + return 1; + } else { + return 0; + } + } +} + +static void ppc_cpu_list_entry(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CPUListState *s = user_data; + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + const char *typename = object_class_get_name(oc); + char *name; + int i; + + if (!ppc_cpu_is_valid(pcc)) { + return; + } + if (unlikely(strcmp(typename, TYPE_HOST_POWERPC_CPU) == 0)) { + return; + } + + name = g_strndup(typename, + strlen(typename) - strlen("-" TYPE_POWERPC_CPU)); + (*s->cpu_fprintf)(s->file, "PowerPC %-16s PVR %08x\n", + name, pcc->pvr); + for (i = 0; ppc_cpu_aliases[i].alias != NULL; i++) { + PowerPCCPUAlias *alias = &ppc_cpu_aliases[i]; + ObjectClass *alias_oc = ppc_cpu_class_by_alias(alias); + + if (alias_oc != oc) { + continue; + } + (*s->cpu_fprintf)(s->file, "PowerPC %-16s (alias for %s)\n", + alias->alias, name); + } + g_free(name); +} + +void ppc_cpu_list(FILE *f, fprintf_function cpu_fprintf) +{ + CPUListState s = { + .file = f, + .cpu_fprintf = cpu_fprintf, + }; + GSList *list; + + list = object_class_get_list(TYPE_POWERPC_CPU, false); + list = g_slist_sort(list, ppc_cpu_list_compare); + g_slist_foreach(list, ppc_cpu_list_entry, &s); + g_slist_free(list); + +#ifdef CONFIG_KVM + cpu_fprintf(f, "\n"); + cpu_fprintf(f, "PowerPC %-16s\n", "host"); +#endif +} + +static void ppc_cpu_defs_entry(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CpuDefinitionInfoList **first = user_data; + const char *typename; + CpuDefinitionInfoList *entry; + CpuDefinitionInfo *info; + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + if (!ppc_cpu_is_valid(pcc)) { + return; + } + + typename = object_class_get_name(oc); + info = g_malloc0(sizeof(*info)); + info->name = g_strndup(typename, + strlen(typename) - strlen("-" TYPE_POWERPC_CPU)); + + entry = g_malloc0(sizeof(*entry)); + entry->value = info; + entry->next = *first; + *first = entry; +} + +CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) +{ + CpuDefinitionInfoList *cpu_list = NULL; + GSList *list; + int i; + + list = object_class_get_list(TYPE_POWERPC_CPU, false); + g_slist_foreach(list, ppc_cpu_defs_entry, &cpu_list); + g_slist_free(list); + + for (i = 0; ppc_cpu_aliases[i].alias != NULL; i++) { + PowerPCCPUAlias *alias = &ppc_cpu_aliases[i]; + ObjectClass *oc; + CpuDefinitionInfoList *entry; + CpuDefinitionInfo *info; + + oc = ppc_cpu_class_by_alias(alias); + if (oc == NULL) { + continue; + } + + info = g_malloc0(sizeof(*info)); + info->name = g_strdup(alias->alias); + + entry = g_malloc0(sizeof(*entry)); + entry->value = info; + entry->next = cpu_list; + cpu_list = entry; + } + + return cpu_list; +} + +static void ppc_cpu_set_pc(CPUState *cs, vaddr value) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + + cpu->env.nip = value; +} + +static bool ppc_cpu_has_work(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + return msr_ee && (cs->interrupt_request & CPU_INTERRUPT_HARD); +} + +static void ppc_cpu_exec_enter(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + env->reserve_addr = -1; +} + +/* CPUClass::reset() */ +static void ppc_cpu_reset(CPUState *s) +{ + PowerPCCPU *cpu = POWERPC_CPU(s); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + CPUPPCState *env = &cpu->env; + target_ulong msr; + int i; + + pcc->parent_reset(s); + + msr = (target_ulong)0; + msr |= (target_ulong)MSR_HVB; + msr |= (target_ulong)0 << MSR_AP; /* TO BE CHECKED */ + msr |= (target_ulong)0 << MSR_SA; /* TO BE CHECKED */ + msr |= (target_ulong)1 << MSR_EP; +#if defined(DO_SINGLE_STEP) && 0 + /* Single step trace mode */ + msr |= (target_ulong)1 << MSR_SE; + msr |= (target_ulong)1 << MSR_BE; +#endif +#if defined(CONFIG_USER_ONLY) + msr |= (target_ulong)1 << MSR_FP; /* Allow floating point usage */ + msr |= (target_ulong)1 << MSR_VR; /* Allow altivec usage */ + msr |= (target_ulong)1 << MSR_VSX; /* Allow VSX usage */ + msr |= (target_ulong)1 << MSR_SPE; /* Allow SPE usage */ + msr |= (target_ulong)1 << MSR_PR; +#if defined(TARGET_PPC64) + msr |= (target_ulong)1 << MSR_TM; /* Transactional memory */ +#endif +#if !defined(TARGET_WORDS_BIGENDIAN) + msr |= (target_ulong)1 << MSR_LE; /* Little-endian user mode */ + if (!((env->msr_mask >> MSR_LE) & 1)) { + fprintf(stderr, "Selected CPU does not support little-endian.\n"); + exit(1); + } +#endif +#endif + +#if defined(TARGET_PPC64) + if (env->mmu_model & POWERPC_MMU_64) { + msr |= (1ULL << MSR_SF); + } +#endif + + hreg_store_msr(env, msr, 1); + +#if !defined(CONFIG_USER_ONLY) + env->nip = env->hreset_vector | env->excp_prefix; + if (env->mmu_model != POWERPC_MMU_REAL) { + ppc_tlb_invalidate_all(env); + } +#endif + + hreg_compute_hflags(env); + env->reserve_addr = (target_ulong)-1ULL; + /* Be sure no exception or interrupt is pending */ + env->pending_interrupts = 0; + s->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) + env->vpa_addr = 0; + env->slb_shadow_addr = 0; + env->slb_shadow_size = 0; + env->dtl_addr = 0; + env->dtl_size = 0; +#endif /* TARGET_PPC64 */ + + for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) { + ppc_spr_t *spr = &env->spr_cb[i]; + + if (!spr->name) { + continue; + } + env->spr[i] = spr->default_value; + } + + /* Flush all TLBs */ + tlb_flush(s, 1); +} + +#ifndef CONFIG_USER_ONLY +static bool ppc_cpu_is_big_endian(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + cpu_synchronize_state(cs); + + return !msr_le; +} +#endif + +static void ppc_cpu_initfn(Object *obj) +{ + CPUState *cs = CPU(obj); + PowerPCCPU *cpu = POWERPC_CPU(obj); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + CPUPPCState *env = &cpu->env; + + cs->env_ptr = env; + + env->msr_mask = pcc->msr_mask; + env->mmu_model = pcc->mmu_model; + env->excp_model = pcc->excp_model; + env->bus_model = pcc->bus_model; + env->insns_flags = pcc->insns_flags; + env->insns_flags2 = pcc->insns_flags2; + env->flags = pcc->flags; + env->bfd_mach = pcc->bfd_mach; + env->check_pow = pcc->check_pow; + + /* Mark HV mode as supported if the CPU has an MSR_HV bit + * in the msr_mask. The mask can later be cleared by PAPR + * mode but the hv mode support will remain, thus enforcing + * that we cannot use priv. instructions in guest in PAPR + * mode. For 970 we currently simply don't set HV in msr_mask + * thus simulating an "Apple mode" 970. If we ever want to + * support 970 HV mode, we'll have to add a processor attribute + * of some sort. + */ +#if !defined(CONFIG_USER_ONLY) + env->has_hv_mode = !!(env->msr_mask & MSR_HVB); +#endif + +#if defined(TARGET_PPC64) + if (pcc->sps) { + env->sps = *pcc->sps; + } else if (env->mmu_model & POWERPC_MMU_64) { + /* Use default sets of page sizes. We don't support MPSS */ + static const struct ppc_segment_page_sizes defsps_4k = { + .sps = { + { .page_shift = 12, /* 4K */ + .slb_enc = 0, + .enc = { { .page_shift = 12, .pte_enc = 0 } } + }, + { .page_shift = 24, /* 16M */ + .slb_enc = 0x100, + .enc = { { .page_shift = 24, .pte_enc = 0 } } + }, + }, + }; + static const struct ppc_segment_page_sizes defsps_64k = { + .sps = { + { .page_shift = 12, /* 4K */ + .slb_enc = 0, + .enc = { { .page_shift = 12, .pte_enc = 0 } } + }, + { .page_shift = 16, /* 64K */ + .slb_enc = 0x110, + .enc = { { .page_shift = 16, .pte_enc = 1 } } + }, + { .page_shift = 24, /* 16M */ + .slb_enc = 0x100, + .enc = { { .page_shift = 24, .pte_enc = 0 } } + }, + }, + }; + env->sps = (env->mmu_model & POWERPC_MMU_64K) ? defsps_64k : defsps_4k; + } +#endif /* defined(TARGET_PPC64) */ + + if (tcg_enabled()) { + ppc_translate_init(); + } +} + +static bool ppc_pvr_match_default(PowerPCCPUClass *pcc, uint32_t pvr) +{ + return pcc->pvr == pvr; +} + +static gchar *ppc_gdb_arch_name(CPUState *cs) +{ +#if defined(TARGET_PPC64) + return g_strdup("powerpc:common64"); +#else + return g_strdup("powerpc:common"); +#endif +} + +static Property ppc_cpu_properties[] = { + DEFINE_PROP_BOOL("pre-2.8-migration", PowerPCCPU, pre_2_8_migration, false), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ppc_cpu_class_init(ObjectClass *oc, void *data) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + pcc->parent_realize = dc->realize; + pcc->parent_unrealize = dc->unrealize; + pcc->pvr_match = ppc_pvr_match_default; + pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_always; + dc->realize = ppc_cpu_realizefn; + dc->unrealize = ppc_cpu_unrealizefn; + dc->props = ppc_cpu_properties; + + pcc->parent_reset = cc->reset; + cc->reset = ppc_cpu_reset; + + cc->class_by_name = ppc_cpu_class_by_name; + cc->has_work = ppc_cpu_has_work; + cc->do_interrupt = ppc_cpu_do_interrupt; + cc->cpu_exec_interrupt = ppc_cpu_exec_interrupt; + cc->dump_state = ppc_cpu_dump_state; + cc->dump_statistics = ppc_cpu_dump_statistics; + cc->set_pc = ppc_cpu_set_pc; + cc->gdb_read_register = ppc_cpu_gdb_read_register; + cc->gdb_write_register = ppc_cpu_gdb_write_register; +#ifdef CONFIG_USER_ONLY + cc->handle_mmu_fault = ppc_cpu_handle_mmu_fault; +#else + cc->get_phys_page_debug = ppc_cpu_get_phys_page_debug; + cc->vmsd = &vmstate_ppc_cpu; +#if defined(TARGET_PPC64) + cc->write_elf64_note = ppc64_cpu_write_elf64_note; +#endif +#endif + cc->cpu_exec_enter = ppc_cpu_exec_enter; + + cc->gdb_num_core_regs = 71; + +#ifdef USE_APPLE_GDB + cc->gdb_read_register = ppc_cpu_gdb_read_register_apple; + cc->gdb_write_register = ppc_cpu_gdb_write_register_apple; + cc->gdb_num_core_regs = 71 + 32; +#endif + + cc->gdb_arch_name = ppc_gdb_arch_name; +#if defined(TARGET_PPC64) + cc->gdb_core_xml_file = "power64-core.xml"; +#else + cc->gdb_core_xml_file = "power-core.xml"; +#endif +#ifndef CONFIG_USER_ONLY + cc->virtio_is_big_endian = ppc_cpu_is_big_endian; +#endif + + dc->fw_name = "PowerPC,UNKNOWN"; +} + +static const TypeInfo ppc_cpu_type_info = { + .name = TYPE_POWERPC_CPU, + .parent = TYPE_CPU, + .instance_size = sizeof(PowerPCCPU), + .instance_init = ppc_cpu_initfn, + .abstract = true, + .class_size = sizeof(PowerPCCPUClass), + .class_init = ppc_cpu_class_init, +}; + +static void ppc_cpu_register_types(void) +{ + type_register_static(&ppc_cpu_type_info); +} + +type_init(ppc_cpu_register_types) diff --git a/target/ppc/user_only_helper.c b/target/ppc/user_only_helper.c new file mode 100644 index 0000000000..6aff34713f --- /dev/null +++ b/target/ppc/user_only_helper.c @@ -0,0 +1,47 @@ +/* + * PowerPC MMU stub handling for user mode emulation + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * Copyright (c) 2013 David Gibson, IBM Corporation. + * + * 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 "qemu/osdep.h" +#include "cpu.h" + +int ppc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, + int mmu_idx) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + int exception, error_code; + + if (rw == 2) { + exception = POWERPC_EXCP_ISI; + error_code = 0x40000000; + } else { + exception = POWERPC_EXCP_DSI; + error_code = 0x40000000; + if (rw) { + error_code |= 0x02000000; + } + env->spr[SPR_DAR] = address; + env->spr[SPR_DSISR] = error_code; + } + cs->exception_index = exception; + env->error_code = error_code; + + return 1; +} |