ppc patch queue 2017-02-02

This obsoletes ppc-for-2.9-20170112, which had a MacOS build bug.
 
 This is a long overdue ppc pull request for qemu-2.9.  It's been a
 long time coming due to some holidays and inconveniently timed
 problems with testing.  So, there's a lot in here:
 
     * More POWER9 instruction implementations for TCG
     * The simpler parts of my CPU compatibility mode cleanup
         * This changes behaviour to prefer compatibility modes over
           "raW" mode for new machine type versions
     * New "40p" machine type which is essentially a modernized and
       cleaned up "prep".  The intention is that it will replace "prep"
       once it has some more testing and polish.
     * Add pseries-2.9 machine type
     * Implement H_SIGNAL_SYS_RESET hypercall
     * Consolidate the two alternate CPU init paths in pseries by
       making it always go through CPU core objects to initialize CPU
     * A number of bugfixes and cleanups
     * Stop the guest timebase when the guest is stopped under KVM.
       This makes the guest system clock also stop when paused, which
       matches the x86 behaviour.
     * Some preliminary cleanups leading towards implementation of the
       POWER9 MMU.
 
 There are also some changes not strictly related to ppc code, but for
 its benefit:
 
     * Limit the pxi-expander-bridge (PXB) device to x86 guests only
       (it's essentially a hack to work around historical x86
       limitations)
     * Some additions to the 128-bit math in host_utils, necessary for
       some of the new instructions.
     * Revise a number of qtests and enable them for ppc
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABCAAGBQJYko4AAAoJEGw4ysog2bOStEYQAIk0Pd6ifZzJUcTWQaR8+AZ7
 nTbzQyWtSHqSAiwBNsykJMFXV1liZVglf2e+VBsrVOwKoU50VOyVm5LspG2z1h8N
 Rxe4FGA2MA//2F3+9/AP8Oe3RdsClNCDaXAVuCFRP4xQWxqqwwasChDeS4Ph/cZq
 CXnlhKTpk9v5vSCsr64bUOSYh3RPumnQepiBgT82hOo7R+VaJ79AFbTeCYKkd0hY
 Sq8g3mg0zOX1ekNXPk1h8oZWqkoZGbqKiXgoy/evGXWURVzTSJO6VTyM65tdwWB7
 Zds77gYAYCIYKq+Iwv4iBCmo4KJofjKQcQepQUr+eGDv9syXebtp6fY0btnIS+DX
 uGzzaixZNms9r2+FAiIlKwIeQgQvl76lYEGmvBrbrgSOyA/7GAkOId0E0Ul6D5LW
 EJSwk9ZDbyE0JBEq6Bx+LClpwye+bpdScU26djQTTcWpFApIeJTyG9V6b1xwulVZ
 rw68ZvfMYxktkvhTbEtvk2O9YZI5eQStBJkmJXeOiOduiP93aiC82MM1Jp+82Q1E
 4qRVvCpGTwzF3GLFciUKAqmwfYxByo4G0/dwG8qw6WNEemLyXFHV5TkzLhgwl3kC
 gDGl5AdH4MXj8NRjuHcDiGXfePBCD578dmz4xo5ZLA2yBavxkRzM8QsEUmD8hf5w
 jhLgyKt0G2hNNtOnGOdG
 =vLVl
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-2.9-20170202' into staging

ppc patch queue 2017-02-02

This obsoletes ppc-for-2.9-20170112, which had a MacOS build bug.

This is a long overdue ppc pull request for qemu-2.9.  It's been a
long time coming due to some holidays and inconveniently timed
problems with testing.  So, there's a lot in here:

    * More POWER9 instruction implementations for TCG
    * The simpler parts of my CPU compatibility mode cleanup
        * This changes behaviour to prefer compatibility modes over
          "raW" mode for new machine type versions
    * New "40p" machine type which is essentially a modernized and
      cleaned up "prep".  The intention is that it will replace "prep"
      once it has some more testing and polish.
    * Add pseries-2.9 machine type
    * Implement H_SIGNAL_SYS_RESET hypercall
    * Consolidate the two alternate CPU init paths in pseries by
      making it always go through CPU core objects to initialize CPU
    * A number of bugfixes and cleanups
    * Stop the guest timebase when the guest is stopped under KVM.
      This makes the guest system clock also stop when paused, which
      matches the x86 behaviour.
    * Some preliminary cleanups leading towards implementation of the
      POWER9 MMU.

There are also some changes not strictly related to ppc code, but for
its benefit:

    * Limit the pxi-expander-bridge (PXB) device to x86 guests only
      (it's essentially a hack to work around historical x86
      limitations)
    * Some additions to the 128-bit math in host_utils, necessary for
      some of the new instructions.
    * Revise a number of qtests and enable them for ppc

# gpg: Signature made Thu 02 Feb 2017 01:40:16 GMT
# gpg:                using RSA key 0x6C38CACA20D9B392
# gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>"
# gpg:                 aka "David Gibson (Red Hat) <dgibson@redhat.com>"
# gpg:                 aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>"
# gpg:                 aka "David Gibson (kernel.org) <dwg@kernel.org>"
# Primary key fingerprint: 75F4 6586 AE61 A66C C44E  87DC 6C38 CACA 20D9 B392

* remotes/dgibson/tags/ppc-for-2.9-20170202: (107 commits)
  hw/ppc/pnv: Use error_report instead of hw_error if a ROM file can't be found
  ppc/kvm: Handle the "family" CPU via alias instead of registering new types
  target/ppc/mmu_hash64: Fix incorrect shift value in amr calculation
  target/ppc/mmu_hash64: Fix printing unsigned as signed int
  tcg/POWER9: NOOP the cp_abort instruction
  target/ppc/debug: Print LPCR register value if register exists
  target-ppc: Add xststdc[sp, dp, qp] instructions
  target-ppc: Add xvtstdc[sp,dp] instructions
  target-ppc: Add MMU model check for booke machines
  ppc: switch to constants within BUILD_BUG_ON
  target/ppc/cpu-models: Fix/remove bad CPU aliases
  target/ppc: Remove unused POWERPC_FAMILY(POWER)
  spapr: clock should count only if vm is running
  ppc: Remove unused function cpu_ppc601_rtc_init()
  target/ppc: Add pcr_supported to POWER9 cpu class definition
  powerpc/cpu-models: rename ISAv3.00 logical PVR definition
  target-ppc: Add xvcv[hpsp, sphp] instructions
  target-ppc: Add xsmulqp instruction
  target-ppc: Add xsdivqp instruction
  target-ppc: Add xscvsdqp and xscvudqp instructions
  ...

# Conflicts:
#	hw/pci-bridge/Makefile.objs

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2017-02-02 18:48:06 +00:00
commit 5459ef3bff
63 changed files with 3871 additions and 775 deletions

View File

@ -671,10 +671,13 @@ F: hw/misc/macio/
F: hw/intc/heathrow_pic.c
PReP
M: Hervé Poussineau <hpoussin@reactos.org>
L: qemu-devel@nongnu.org
L: qemu-ppc@nongnu.org
S: Odd Fixes
S: Maintained
F: hw/ppc/prep.c
F: hw/ppc/prep_systemio.c
F: hw/ppc/rs6000_mc.c
F: hw/pci-host/prep.[hc]
F: hw/isa/pc87312.[hc]
F: pc-bios/ppc_rom.bin

View File

@ -57,3 +57,4 @@ CONFIG_IOH3420=y
CONFIG_I82801B11=y
CONFIG_SMBIOS=y
CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
CONFIG_PXB=y

View File

@ -18,6 +18,7 @@ CONFIG_I82378=y
CONFIG_PC87312=y
CONFIG_MACIO=y
CONFIG_PCSPK=y
CONFIG_CS4231A=y
CONFIG_CUDA=y
CONFIG_ADB=y
CONFIG_MAC_NVRAM=y
@ -47,3 +48,4 @@ CONFIG_LIBDECNUMBER=y
# For PReP
CONFIG_MC146818RTC=y
CONFIG_ISA_TESTDEV=y
CONFIG_RS6000_MC=y

View File

@ -55,3 +55,4 @@ CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM))
CONFIG_MC146818RTC=y
CONFIG_ISA_TESTDEV=y
CONFIG_MEM_HOTPLUG=y
CONFIG_RS6000_MC=y

View File

@ -57,3 +57,4 @@ CONFIG_IOH3420=y
CONFIG_I82801B11=y
CONFIG_SMBIOS=y
CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
CONFIG_PXB=y

View File

@ -1653,11 +1653,11 @@ extract_tbr (unsigned long insn,
#define BBOYBI_MASK (BBOYCB_MASK | BI_MASK)
#define BBOATBI_MASK (BBOAT2CB_MASK | BI_MASK)
/* An Context form instruction. */
/* A Context form instruction. */
#define CTX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x7))
#define CTX_MASK CTX(0x3f, 0x7)
/* An User Context form instruction. */
/* A User Context form instruction. */
#define UCTX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x1f))
#define UCTX_MASK UCTX(0x3f, 0x1f)
@ -1710,19 +1710,19 @@ extract_tbr (unsigned long insn,
#define SC(op, sa, lk) (OP (op) | ((((unsigned long)(sa)) & 1) << 1) | ((lk) & 1))
#define SC_MASK (OP_MASK | (((unsigned long)0x3ff) << 16) | (((unsigned long)1) << 1) | 1)
/* An VX form instruction. */
/* A VX form instruction. */
#define VX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x7ff))
/* The mask for an VX form instruction. */
#define VX_MASK VX(0x3f, 0x7ff)
/* An VA form instruction. */
/* A VA form instruction. */
#define VXA(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x03f))
/* The mask for an VA form instruction. */
/* The mask for a VA form instruction. */
#define VXA_MASK VXA(0x3f, 0x3f)
/* An VXR form instruction. */
/* A VXR form instruction. */
#define VXR(op, xop, rc) (OP (op) | (((rc) & 1) << 10) | (((unsigned long)(xop)) & 0x3ff))
/* The mask for a VXR form instruction. */

View File

@ -185,7 +185,7 @@ float128 float128_default_nan(float_status *status)
r.high = LIT64(0x7FFF7FFFFFFFFFFF);
} else {
r.low = LIT64(0x0000000000000000);
#if defined(TARGET_S390X)
#if defined(TARGET_S390X) || defined(TARGET_PPC)
r.high = LIT64(0x7FFF800000000000);
#else
r.high = LIT64(0xFFFF800000000000);

View File

@ -143,8 +143,10 @@ static void mpc8xxx_gpio_write(void *opaque, hwaddr offset,
mpc8xxx_gpio_update(s);
}
static void mpc8xxx_gpio_reset(MPC8XXXGPIOState *s)
static void mpc8xxx_gpio_reset(DeviceState *dev)
{
MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev);
s->dir = 0;
s->odr = 0;
s->dat = 0;
@ -180,33 +182,33 @@ static const MemoryRegionOps mpc8xxx_gpio_ops = {
.endianness = DEVICE_BIG_ENDIAN,
};
static int mpc8xxx_gpio_initfn(SysBusDevice *sbd)
static void mpc8xxx_gpio_initfn(Object *obj)
{
DeviceState *dev = DEVICE(sbd);
MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev);
DeviceState *dev = DEVICE(obj);
MPC8XXXGPIOState *s = MPC8XXX_GPIO(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
memory_region_init_io(&s->iomem, OBJECT(s), &mpc8xxx_gpio_ops, s, "mpc8xxx_gpio", 0x1000);
memory_region_init_io(&s->iomem, obj, &mpc8xxx_gpio_ops,
s, "mpc8xxx_gpio", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
qdev_init_gpio_in(dev, mpc8xxx_gpio_set_irq, 32);
qdev_init_gpio_out(dev, s->out, 32);
mpc8xxx_gpio_reset(s);
return 0;
}
static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = mpc8xxx_gpio_initfn;
dc->vmsd = &vmstate_mpc8xxx_gpio;
dc->reset = mpc8xxx_gpio_reset;
}
static const TypeInfo mpc8xxx_gpio_info = {
.name = TYPE_MPC8XXX_GPIO,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(MPC8XXXGPIOState),
.instance_init = mpc8xxx_gpio_initfn,
.class_init = mpc8xxx_gpio_class_init,
};

View File

@ -1,6 +1,6 @@
common-obj-y += pci_bridge_dev.o
common-obj-y += pci_expander_bridge.o
common-obj-$(CONFIG_PCIE_PORT) += pcie_root_port.o gen_pcie_root_port.o
common-obj-$(CONFIG_PXB) += pci_expander_bridge.o
common-obj-$(CONFIG_XIO3130) += xio3130_upstream.o xio3130_downstream.o
common-obj-$(CONFIG_IOH3420) += ioh3420.o
common-obj-$(CONFIG_I82801B11) += i82801b11.o

View File

@ -16,6 +16,8 @@ obj-y += ppc405_boards.o ppc4xx_devs.o ppc405_uc.o ppc440_bamboo.o
obj-y += ppc4xx_pci.o
# PReP
obj-$(CONFIG_PREP) += prep.o
obj-$(CONFIG_PREP) += prep_systemio.o
obj-${CONFIG_RS6000_MC} += rs6000_mc.o
# OldWorld PowerMac
obj-$(CONFIG_MAC) += mac_oldworld.o
# NewWorld PowerMac

View File

@ -827,6 +827,12 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
env = &cpu->env;
cs = CPU(cpu);
if (env->mmu_model != POWERPC_MMU_BOOKE206) {
fprintf(stderr, "MMU model %i not supported by this machine.\n",
env->mmu_model);
exit(1);
}
if (!firstenv) {
firstenv = env;
}
@ -1049,27 +1055,18 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
boot_info->dt_size = dt_size;
}
static int e500_ccsr_initfn(SysBusDevice *dev)
static void e500_ccsr_initfn(Object *obj)
{
PPCE500CCSRState *ccsr;
ccsr = CCSR(dev);
memory_region_init(&ccsr->ccsr_space, OBJECT(ccsr), "e500-ccsr",
PPCE500CCSRState *ccsr = CCSR(obj);
memory_region_init(&ccsr->ccsr_space, obj, "e500-ccsr",
MPC8544_CCSRBAR_SIZE);
return 0;
}
static void e500_ccsr_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = e500_ccsr_initfn;
}
static const TypeInfo e500_ccsr_info = {
.name = TYPE_CCSR,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(PPCE500CCSRState),
.class_init = e500_ccsr_class_init,
.instance_init = e500_ccsr_initfn,
};
static void e500_register_types(void)

View File

@ -381,7 +381,7 @@ static void ppc_powernv_init(MachineState *machine)
fw_size = load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE);
if (fw_size < 0) {
hw_error("qemu: could not load OPAL '%s'\n", fw_filename);
error_report("qemu: could not load OPAL '%s'", fw_filename);
exit(1);
}
g_free(fw_filename);
@ -393,8 +393,8 @@ static void ppc_powernv_init(MachineState *machine)
kernel_size = load_image_targphys(machine->kernel_filename,
KERNEL_LOAD_ADDR, 0x2000000);
if (kernel_size < 0) {
hw_error("qemu: could not load kernel'%s'\n",
machine->kernel_filename);
error_report("qemu: could not load kernel'%s'",
machine->kernel_filename);
exit(1);
}
}

View File

@ -847,9 +847,8 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq)
cpu_ppc_store_purr(cpu, 0x0000000000000000ULL);
}
static void timebase_pre_save(void *opaque)
static void timebase_save(PPCTimebase *tb)
{
PPCTimebase *tb = opaque;
uint64_t ticks = cpu_get_host_ticks();
PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu);
@ -858,43 +857,30 @@ static void timebase_pre_save(void *opaque)
return;
}
/* not used anymore, we keep it for compatibility */
tb->time_of_the_day_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST);
/*
* tb_offset is only expected to be changed by migration so
* tb_offset is only expected to be changed by QEMU so
* there is no need to update it from KVM here
*/
tb->guest_timebase = ticks + first_ppc_cpu->env.tb_env->tb_offset;
}
static int timebase_post_load(void *opaque, int version_id)
static void timebase_load(PPCTimebase *tb)
{
PPCTimebase *tb_remote = opaque;
CPUState *cpu;
PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu);
int64_t tb_off_adj, tb_off, ns_diff;
int64_t migration_duration_ns, migration_duration_tb, guest_tb, host_ns;
int64_t tb_off_adj, tb_off;
unsigned long freq;
if (!first_ppc_cpu->env.tb_env) {
error_report("No timebase object");
return -1;
return;
}
freq = first_ppc_cpu->env.tb_env->tb_freq;
/*
* Calculate timebase on the destination side of migration.
* The destination timebase must be not less than the source timebase.
* We try to adjust timebase by downtime if host clocks are not
* too much out of sync (1 second for now).
*/
host_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST);
ns_diff = MAX(0, host_ns - tb_remote->time_of_the_day_ns);
migration_duration_ns = MIN(NANOSECONDS_PER_SECOND, ns_diff);
migration_duration_tb = muldiv64(freq, migration_duration_ns,
NANOSECONDS_PER_SECOND);
guest_tb = tb_remote->guest_timebase + MIN(0, migration_duration_tb);
tb_off_adj = guest_tb - cpu_get_host_ticks();
tb_off_adj = tb->guest_timebase - cpu_get_host_ticks();
tb_off = first_ppc_cpu->env.tb_env->tb_offset;
trace_ppc_tb_adjust(tb_off, tb_off_adj, tb_off_adj - tb_off,
@ -904,9 +890,44 @@ static int timebase_post_load(void *opaque, int version_id)
CPU_FOREACH(cpu) {
PowerPCCPU *pcpu = POWERPC_CPU(cpu);
pcpu->env.tb_env->tb_offset = tb_off_adj;
#if defined(CONFIG_KVM)
kvm_set_one_reg(cpu, KVM_REG_PPC_TB_OFFSET,
&pcpu->env.tb_env->tb_offset);
#endif
}
}
return 0;
void cpu_ppc_clock_vm_state_change(void *opaque, int running,
RunState state)
{
PPCTimebase *tb = opaque;
if (running) {
timebase_load(tb);
} else {
timebase_save(tb);
}
}
/*
* When migrating, read the clock just before migration,
* so that the guest clock counts during the events
* between:
*
* * vm_stop()
* *
* * pre_save()
*
* This reduces clock difference on migration from 5s
* to 0.1s (when max_downtime == 5s), because sending the
* final pages of memory (which happens between vm_stop()
* and pre_save()) takes max_downtime.
*/
static void timebase_pre_save(void *opaque)
{
PPCTimebase *tb = opaque;
timebase_save(tb);
}
const VMStateDescription vmstate_ppc_timebase = {
@ -915,7 +936,6 @@ const VMStateDescription vmstate_ppc_timebase = {
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.pre_save = timebase_pre_save,
.post_load = timebase_post_load,
.fields = (VMStateField []) {
VMSTATE_UINT64(guest_timebase, PPCTimebase),
VMSTATE_INT64(time_of_the_day_ns, PPCTimebase),
@ -950,13 +970,6 @@ clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq)
}
/* Specific helpers for POWER & PowerPC 601 RTC */
#if 0
static clk_setup_cb cpu_ppc601_rtc_init (CPUPPCState *env)
{
return cpu_ppc_tb_init(env, 7812500);
}
#endif
void cpu_ppc601_store_rtcu (CPUPPCState *env, uint32_t value)
{
_cpu_ppc_store_tbu(env, value);

View File

@ -193,6 +193,12 @@ static void bamboo_init(MachineState *machine)
}
env = &cpu->env;
if (env->mmu_model != POWERPC_MMU_BOOKE) {
fprintf(stderr, "MMU model %i not supported by this machine.\n",
env->mmu_model);
exit(1);
}
qemu_register_reset(main_cpu_reset, cpu);
ppc_booke_timers_init(cpu, 400000000, 0);
ppc_dcr_init(env, NULL, NULL);

View File

@ -198,8 +198,12 @@ static void booke_decr_cb(void *opaque)
booke_update_irq(cpu);
if (env->spr[SPR_BOOKE_TCR] & TCR_ARE) {
/* Auto Reload */
cpu_ppc_store_decr(env, env->spr[SPR_BOOKE_DECAR]);
/* Do not reload 0, it is already there. It would just trigger
* the timer again and lead to infinite loop */
if (env->spr[SPR_BOOKE_DECAR] != 0) {
/* Auto Reload */
cpu_ppc_store_decr(env, env->spr[SPR_BOOKE_DECAR]);
}
}
}

View File

@ -54,9 +54,9 @@ typedef struct SpinState {
SpinInfo spin[MAX_CPUS];
} SpinState;
static void spin_reset(void *opaque)
static void spin_reset(DeviceState *dev)
{
SpinState *s = opaque;
SpinState *s = E500_SPIN(dev);
int i;
for (i = 0; i < MAX_CPUS; i++) {
@ -174,30 +174,28 @@ static const MemoryRegionOps spin_rw_ops = {
.endianness = DEVICE_BIG_ENDIAN,
};
static int ppce500_spin_initfn(SysBusDevice *dev)
static void ppce500_spin_initfn(Object *obj)
{
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
SpinState *s = E500_SPIN(dev);
memory_region_init_io(&s->iomem, OBJECT(s), &spin_rw_ops, s,
memory_region_init_io(&s->iomem, obj, &spin_rw_ops, s,
"e500 spin pv device", sizeof(SpinInfo) * MAX_CPUS);
sysbus_init_mmio(dev, &s->iomem);
qemu_register_reset(spin_reset, s);
return 0;
}
static void ppce500_spin_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
k->init = ppce500_spin_initfn;
dc->reset = spin_reset;
}
static const TypeInfo ppce500_spin_info = {
.name = TYPE_E500_SPIN,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(SpinState),
.instance_init = ppce500_spin_initfn,
.class_init = ppce500_spin_class_init,
};

View File

@ -2,6 +2,7 @@
* QEMU PPC PREP hardware System Emulator
*
* Copyright (c) 2003-2007 Jocelyn Mayer
* Copyright (c) 2017 Hervé Poussineau
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -43,17 +44,21 @@
#include "hw/isa/pc87312.h"
#include "sysemu/block-backend.h"
#include "sysemu/arch_init.h"
#include "sysemu/kvm.h"
#include "sysemu/qtest.h"
#include "exec/address-spaces.h"
#include "trace.h"
#include "elf.h"
#include "qemu/cutils.h"
#include "kvm_ppc.h"
/* SMP is not enabled, for now */
#define MAX_CPUS 1
#define MAX_IDE_BUS 2
#define CFG_ADDR 0xf0000510
#define BIOS_SIZE (1024 * 1024)
#define BIOS_FILENAME "ppc_rom.bin"
#define KERNEL_LOAD_ADDR 0x01000000
@ -316,6 +321,12 @@ static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr)
#define NVRAM_SIZE 0x2000
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
Error **errp)
{
fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
}
static void ppc_prep_reset(void *opaque)
{
PowerPCCPU *cpu = opaque;
@ -339,13 +350,13 @@ static PortioList prep_port_list;
/* NVRAM helpers */
static inline uint32_t nvram_read(Nvram *nvram, uint32_t addr)
{
NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram);
NvramClass *k = NVRAM_GET_CLASS(nvram);
return (k->read)(nvram, addr);
}
static inline void nvram_write(Nvram *nvram, uint32_t addr, uint32_t val)
{
NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram);
NvramClass *k = NVRAM_GET_CLASS(nvram);
(k->write)(nvram, addr, val);
}
@ -677,4 +688,223 @@ static void prep_machine_init(MachineClass *mc)
mc->default_boot_order = "cad";
}
static int prep_set_cmos_checksum(DeviceState *dev, void *opaque)
{
uint16_t checksum = *(uint16_t *)opaque;
ISADevice *rtc;
if (object_dynamic_cast(OBJECT(dev), "mc146818rtc")) {
rtc = ISA_DEVICE(dev);
rtc_set_memory(rtc, 0x2e, checksum & 0xff);
rtc_set_memory(rtc, 0x3e, checksum & 0xff);
rtc_set_memory(rtc, 0x2f, checksum >> 8);
rtc_set_memory(rtc, 0x3f, checksum >> 8);
}
return 0;
}
static void ibm_40p_init(MachineState *machine)
{
CPUPPCState *env = NULL;
uint16_t cmos_checksum;
PowerPCCPU *cpu;
DeviceState *dev;
SysBusDevice *pcihost;
Nvram *m48t59 = NULL;
PCIBus *pci_bus;
ISABus *isa_bus;
void *fw_cfg;
int i;
uint32_t kernel_base = 0, initrd_base = 0;
long kernel_size = 0, initrd_size = 0;
char boot_device;
/* init CPU */
if (!machine->cpu_model) {
machine->cpu_model = "604";
}
cpu = cpu_ppc_init(machine->cpu_model);
if (!cpu) {
error_report("could not initialize CPU '%s'",
machine->cpu_model);
exit(1);
}
env = &cpu->env;
if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) {
error_report("only 6xx bus is supported on this machine");
exit(1);
}
if (env->flags & POWERPC_FLAG_RTC_CLK) {
/* POWER / PowerPC 601 RTC clock frequency is 7.8125 MHz */
cpu_ppc_tb_init(env, 7812500UL);
} else {
/* Set time-base frequency to 100 Mhz */
cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL);
}
qemu_register_reset(ppc_prep_reset, cpu);
/* PCI host */
dev = qdev_create(NULL, "raven-pcihost");
if (!bios_name) {
bios_name = BIOS_FILENAME;
}
qdev_prop_set_string(dev, "bios-name", bios_name);
qdev_prop_set_uint32(dev, "elf-machine", PPC_ELF_MACHINE);
pcihost = SYS_BUS_DEVICE(dev);
object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev), NULL);
qdev_init_nofail(dev);
pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci.0"));
if (!pci_bus) {
error_report("could not create PCI host controller");
exit(1);
}
/* PCI -> ISA bridge */
dev = DEVICE(pci_create_simple(pci_bus, PCI_DEVFN(11, 0), "i82378"));
qdev_connect_gpio_out(dev, 0,
cpu->env.irq_inputs[PPC6xx_INPUT_INT]);
sysbus_connect_irq(pcihost, 0, qdev_get_gpio_in(dev, 15));
sysbus_connect_irq(pcihost, 1, qdev_get_gpio_in(dev, 13));
sysbus_connect_irq(pcihost, 2, qdev_get_gpio_in(dev, 15));
sysbus_connect_irq(pcihost, 3, qdev_get_gpio_in(dev, 13));
isa_bus = ISA_BUS(qdev_get_child_bus(dev, "isa.0"));
/* Memory controller */
dev = DEVICE(isa_create(isa_bus, "rs6000-mc"));
qdev_prop_set_uint32(dev, "ram-size", machine->ram_size);
qdev_init_nofail(dev);
/* initialize CMOS checksums */
cmos_checksum = 0x6aa9;
qbus_walk_children(BUS(isa_bus), prep_set_cmos_checksum, NULL, NULL, NULL,
&cmos_checksum);
/* initialize audio subsystem */
audio_init();
/* add some more devices */
if (defaults_enabled()) {
isa_create_simple(isa_bus, "i8042");
m48t59 = NVRAM(isa_create_simple(isa_bus, "isa-m48t59"));
dev = DEVICE(isa_create(isa_bus, "cs4231a"));
qdev_prop_set_uint32(dev, "iobase", 0x830);
qdev_prop_set_uint32(dev, "irq", 10);
qdev_init_nofail(dev);
dev = DEVICE(isa_create(isa_bus, "pc87312"));
qdev_prop_set_uint32(dev, "config", 12);
qdev_init_nofail(dev);
dev = DEVICE(isa_create(isa_bus, "prep-systemio"));
qdev_prop_set_uint32(dev, "ibm-planar-id", 0xfc);
qdev_prop_set_uint32(dev, "equipment", 0xc0);
qdev_init_nofail(dev);
pci_create_simple(pci_bus, PCI_DEVFN(1, 0), "lsi53c810");
/* XXX: s3-trio at PCI_DEVFN(2, 0) */
pci_vga_init(pci_bus);
for (i = 0; i < nb_nics; i++) {
pci_nic_init_nofail(&nd_table[i], pci_bus, "pcnet",
i == 0 ? "3" : NULL);
}
}
/* Prepare firmware configuration for OpenBIOS */
fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2);
if (machine->kernel_filename) {
/* load kernel */
kernel_base = KERNEL_LOAD_ADDR;
kernel_size = load_image_targphys(machine->kernel_filename,
kernel_base,
machine->ram_size - kernel_base);
if (kernel_size < 0) {
error_report("could not load kernel '%s'",
machine->kernel_filename);
exit(1);
}
fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base);
fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
/* load initrd */
if (machine->initrd_filename) {
initrd_base = INITRD_LOAD_ADDR;
initrd_size = load_image_targphys(machine->initrd_filename,
initrd_base,
machine->ram_size - initrd_base);
if (initrd_size < 0) {
error_report("could not load initial ram disk '%s'",
machine->initrd_filename);
exit(1);
}
fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_base);
fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
}
if (machine->kernel_cmdline && *machine->kernel_cmdline) {
fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR);
pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE,
machine->kernel_cmdline);
fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA,
machine->kernel_cmdline);
fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
strlen(machine->kernel_cmdline) + 1);
}
boot_device = 'm';
} else {
boot_device = machine->boot_order[0];
}
fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus);
fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)machine->ram_size);
fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, ARCH_PREP);
fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_WIDTH, graphic_width);
fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height);
fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth);
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled());
if (kvm_enabled()) {
#ifdef CONFIG_KVM
uint8_t *hypercall;
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq());
hypercall = g_malloc(16);
kvmppc_get_hypercall(env, hypercall, 16);
fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16);
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid());
#endif
} else {
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, NANOSECONDS_PER_SECOND);
}
fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, boot_device);
qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
/* Prepare firmware configuration for Open Hack'Ware */
if (m48t59) {
PPC_NVRAM_set_params(m48t59, NVRAM_SIZE, "PREP", ram_size,
boot_device,
kernel_base, kernel_size,
machine->kernel_cmdline,
initrd_base, initrd_size,
/* XXX: need an option to load a NVRAM image */
0,
graphic_width, graphic_height, graphic_depth);
}
}
static void ibm_40p_machine_init(MachineClass *mc)
{
mc->desc = "IBM RS/6000 7020 (40p)",
mc->init = ibm_40p_init;
mc->max_cpus = 1;
mc->pci_allow_0_address = true;
mc->default_ram_size = 128 * M_BYTE;
mc->block_default_type = IF_SCSI;
mc->default_boot_order = "c";
}
DEFINE_MACHINE("40p", ibm_40p_machine_init)
DEFINE_MACHINE("prep", prep_machine_init)

303
hw/ppc/prep_systemio.c Normal file
View File

@ -0,0 +1,303 @@
/*
* QEMU PReP System I/O emulation
*
* Copyright (c) 2017 Hervé Poussineau
*
* 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 "hw/isa/isa.h"
#include "exec/address-spaces.h"
#include "qemu/error-report.h" /* for error_report() */
#include "sysemu/sysemu.h" /* for vm_stop() */
#include "cpu.h"
#include "trace.h"
#define TYPE_PREP_SYSTEMIO "prep-systemio"
#define PREP_SYSTEMIO(obj) \
OBJECT_CHECK(PrepSystemIoState, (obj), TYPE_PREP_SYSTEMIO)
/* Bit as defined in PowerPC Reference Plaform v1.1, sect. 6.1.5, p. 132 */
#define PREP_BIT(n) (1 << (7 - (n)))
typedef struct PrepSystemIoState {
ISADevice parent_obj;
MemoryRegion ppc_parity_mem;
qemu_irq non_contiguous_io_map_irq;
uint8_t sreset; /* 0x0092 */
uint8_t equipment; /* 0x080c */
uint8_t system_control; /* 0x081c */
uint8_t iomap_type; /* 0x0850 */
uint8_t ibm_planar_id; /* 0x0852 */
qemu_irq softreset_irq;
PortioList portio;
} PrepSystemIoState;
/* PORT 0092 -- Special Port 92 (Read/Write) */
enum {
PORT0092_SOFTRESET = PREP_BIT(7),
PORT0092_LE_MODE = PREP_BIT(6),
};
static void prep_port0092_write(void *opaque, uint32_t addr, uint32_t val)
{
PrepSystemIoState *s = opaque;
trace_prep_systemio_write(addr, val);
s->sreset = val & PORT0092_SOFTRESET;
qemu_set_irq(s->softreset_irq, s->sreset);
if ((val & PORT0092_LE_MODE) != 0) {
/* XXX Not supported yet */
error_report("little-endian mode not supported");
vm_stop(RUN_STATE_PAUSED);
} else {
/* Nothing to do */
}
}
static uint32_t prep_port0092_read(void *opaque, uint32_t addr)
{
PrepSystemIoState *s = opaque;
trace_prep_systemio_read(addr, s->sreset);
return s->sreset;
}
/* PORT 0808 -- Hardfile Light Register (Write Only) */
enum {
PORT0808_HARDFILE_LIGHT_ON = PREP_BIT(7),
};
static void prep_port0808_write(void *opaque, uint32_t addr, uint32_t val)
{
trace_prep_systemio_write(addr, val);
}
/* PORT 0810 -- Password Protect 1 Register (Write Only) */
/* reset by port 0x4D in the SIO */
static void prep_port0810_write(void *opaque, uint32_t addr, uint32_t val)
{
trace_prep_systemio_write(addr, val);
}
/* PORT 0812 -- Password Protect 2 Register (Write Only) */
/* reset by port 0x4D in the SIO */
static void prep_port0812_write(void *opaque, uint32_t addr, uint32_t val)
{
trace_prep_systemio_write(addr, val);
}
/* PORT 0814 -- L2 Invalidate Register (Write Only) */
static void prep_port0814_write(void *opaque, uint32_t addr, uint32_t val)
{
trace_prep_systemio_write(addr, val);
}
/* PORT 0818 -- Reserved for Keylock (Read Only) */
enum {
PORT0818_KEYLOCK_SIGNAL_HIGH = PREP_BIT(7),
};
static uint32_t prep_port0818_read(void *opaque, uint32_t addr)
{
uint32_t val = 0;
trace_prep_systemio_read(addr, val);
return val;
}
/* PORT 080C -- Equipment */
enum {
PORT080C_SCSIFUSE = PREP_BIT(1),
PORT080C_L2_COPYBACK = PREP_BIT(4),
PORT080C_L2_256 = PREP_BIT(5),
PORT080C_UPGRADE_CPU = PREP_BIT(6),
PORT080C_L2 = PREP_BIT(7),
};
static uint32_t prep_port080c_read(void *opaque, uint32_t addr)
{
PrepSystemIoState *s = opaque;
trace_prep_systemio_read(addr, s->equipment);
return s->equipment;
}
/* PORT 081C -- System Control Register (Read/Write) */
enum {
PORT081C_FLOPPY_MOTOR_INHIBIT = PREP_BIT(3),
PORT081C_MASK_TEA = PREP_BIT(2),
PORT081C_L2_UPDATE_INHIBIT = PREP_BIT(1),
PORT081C_L2_CACHEMISS_INHIBIT = PREP_BIT(0),
};
static void prep_port081c_write(void *opaque, uint32_t addr, uint32_t val)
{
static const uint8_t mask = PORT081C_FLOPPY_MOTOR_INHIBIT |
PORT081C_MASK_TEA |
PORT081C_L2_UPDATE_INHIBIT |
PORT081C_L2_CACHEMISS_INHIBIT;
PrepSystemIoState *s = opaque;
trace_prep_systemio_write(addr, val);
s->system_control = val & mask;
}
static uint32_t prep_port081c_read(void *opaque, uint32_t addr)
{
PrepSystemIoState *s = opaque;
trace_prep_systemio_read(addr, s->system_control);
return s->system_control;
}
/* System Board Identification */
static uint32_t prep_port0852_read(void *opaque, uint32_t addr)
{
PrepSystemIoState *s = opaque;
trace_prep_systemio_read(addr, s->ibm_planar_id);
return s->ibm_planar_id;
}
/* PORT 0850 -- I/O Map Type Register (Read/Write) */
enum {
PORT0850_IOMAP_NONCONTIGUOUS = PREP_BIT(7),
};
static uint32_t prep_port0850_read(void *opaque, uint32_t addr)
{
PrepSystemIoState *s = opaque;
trace_prep_systemio_read(addr, s->iomap_type);
return s->iomap_type;
}
static void prep_port0850_write(void *opaque, uint32_t addr, uint32_t val)
{
PrepSystemIoState *s = opaque;
trace_prep_systemio_write(addr, val);
qemu_set_irq(s->non_contiguous_io_map_irq,
val & PORT0850_IOMAP_NONCONTIGUOUS);
s->iomap_type = val & PORT0850_IOMAP_NONCONTIGUOUS;
}
static const MemoryRegionPortio ppc_io800_port_list[] = {
{ 0x092, 1, 1, .read = prep_port0092_read,
.write = prep_port0092_write, },
{ 0x808, 1, 1, .write = prep_port0808_write, },
{ 0x80c, 1, 1, .read = prep_port080c_read, },
{ 0x810, 1, 1, .write = prep_port0810_write, },
{ 0x812, 1, 1, .write = prep_port0812_write, },
{ 0x814, 1, 1, .write = prep_port0814_write, },
{ 0x818, 1, 1, .read = prep_port0818_read },
{ 0x81c, 1, 1, .read = prep_port081c_read,
.write = prep_port081c_write, },
{ 0x850, 1, 1, .read = prep_port0850_read,
.write = prep_port0850_write, },
{ 0x852, 1, 1, .read = prep_port0852_read, },
PORTIO_END_OF_LIST()
};
static uint64_t ppc_parity_error_readl(void *opaque, hwaddr addr,
unsigned int size)
{
uint32_t val = 0;
trace_prep_systemio_read((unsigned int)addr, val);
return val;
}
static const MemoryRegionOps ppc_parity_error_ops = {
.read = ppc_parity_error_readl,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
};
static void prep_systemio_realize(DeviceState *dev, Error **errp)
{
ISADevice *isa = ISA_DEVICE(dev);
PrepSystemIoState *s = PREP_SYSTEMIO(dev);
PowerPCCPU *cpu;
qdev_init_gpio_out(dev, &s->non_contiguous_io_map_irq, 1);
s->iomap_type = PORT0850_IOMAP_NONCONTIGUOUS;
qemu_set_irq(s->non_contiguous_io_map_irq,
s->iomap_type & PORT0850_IOMAP_NONCONTIGUOUS);
cpu = POWERPC_CPU(first_cpu);
s->softreset_irq = cpu->env.irq_inputs[PPC6xx_INPUT_HRESET];
isa_register_portio_list(isa, &s->portio, 0x0, ppc_io800_port_list, s,
"systemio800");
memory_region_init_io(&s->ppc_parity_mem, OBJECT(dev),
&ppc_parity_error_ops, s, "ppc-parity", 0x4);
memory_region_add_subregion(get_system_memory(), 0xbfffeff0,
&s->ppc_parity_mem);
}
static const VMStateDescription vmstate_prep_systemio = {
.name = "prep_systemio",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT8(sreset, PrepSystemIoState),
VMSTATE_UINT8(system_control, PrepSystemIoState),
VMSTATE_UINT8(iomap_type, PrepSystemIoState),
VMSTATE_END_OF_LIST()
},
};
static Property prep_systemio_properties[] = {
DEFINE_PROP_UINT8("ibm-planar-id", PrepSystemIoState, ibm_planar_id, 0),
DEFINE_PROP_UINT8("equipment", PrepSystemIoState, equipment, 0),
DEFINE_PROP_END_OF_LIST()
};
static void prep_systemio_class_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = prep_systemio_realize;
dc->vmsd = &vmstate_prep_systemio;
dc->props = prep_systemio_properties;
}
static TypeInfo prep_systemio800_info = {
.name = TYPE_PREP_SYSTEMIO,
.parent = TYPE_ISA_DEVICE,
.instance_size = sizeof(PrepSystemIoState),
.class_init = prep_systemio_class_initfn,
};
static void prep_systemio_register_types(void)
{
type_register_static(&prep_systemio800_info);
}
type_init(prep_systemio_register_types)

232
hw/ppc/rs6000_mc.c Normal file
View File

@ -0,0 +1,232 @@
/*
* QEMU RS/6000 memory controller
*
* Copyright (c) 2017 Hervé Poussineau
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) version 3 or any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "hw/isa/isa.h"
#include "exec/address-spaces.h"
#include "hw/boards.h"
#include "qapi/error.h"
#include "trace.h"
#define TYPE_RS6000MC "rs6000-mc"
#define RS6000MC_DEVICE(obj) \
OBJECT_CHECK(RS6000MCState, (obj), TYPE_RS6000MC)
typedef struct RS6000MCState {
ISADevice parent_obj;
/* see US patent 5,684,979 for details (expired 2001-11-04) */
uint32_t ram_size;
bool autoconfigure;
MemoryRegion simm[6];
unsigned int simm_size[6];
uint32_t end_address[8];
uint8_t port0820_index;
PortioList portio;
} RS6000MCState;
/* P0RT 0803 -- SIMM ID Register (32/8 MB) (Read Only) */
static uint32_t rs6000mc_port0803_read(void *opaque, uint32_t addr)
{
RS6000MCState *s = opaque;
uint32_t val = 0;
int socket;
/* (1 << socket) indicates 32 MB SIMM at given socket */
for (socket = 0; socket < 6; socket++) {
if (s->simm_size[socket] == 32) {
val |= (1 << socket);
}
}
trace_rs6000mc_id_read(addr, val);
return val;
}
/* PORT 0804 -- SIMM Presence Register (Read Only) */
static uint32_t rs6000mc_port0804_read(void *opaque, uint32_t addr)
{
RS6000MCState *s = opaque;
uint32_t val = 0xff;
int socket;
/* (1 << socket) indicates SIMM absence at given socket */
for (socket = 0; socket < 6; socket++) {
if (s->simm_size[socket]) {
val &= ~(1 << socket);
}
}
s->port0820_index = 0;
trace_rs6000mc_presence_read(addr, val);
return val;
}
/* Memory Controller Size Programming Register */
static uint32_t rs6000mc_port0820_read(void *opaque, uint32_t addr)
{
RS6000MCState *s = opaque;
uint32_t val = s->end_address[s->port0820_index] & 0x1f;
s->port0820_index = (s->port0820_index + 1) & 7;
trace_rs6000mc_size_read(addr, val);
return val;
}
static void rs6000mc_port0820_write(void *opaque, uint32_t addr, uint32_t val)
{
RS6000MCState *s = opaque;
uint8_t socket = val >> 5;
uint32_t end_address = val & 0x1f;
trace_rs6000mc_size_write(addr, val);
s->end_address[socket] = end_address;
if (socket > 0 && socket < 7) {
if (s->simm_size[socket - 1]) {
uint32_t size;
uint32_t start_address = 0;
if (socket > 1) {
start_address = s->end_address[socket - 1];
}
size = end_address - start_address;
memory_region_set_enabled(&s->simm[socket - 1], size != 0);
memory_region_set_address(&s->simm[socket - 1],
start_address * 8 * 1024 * 1024);
}
}
}
/* Read Memory Parity Error */
enum {
PORT0841_NO_ERROR_DETECTED = 0x01,
};
static uint32_t rs6000mc_port0841_read(void *opaque, uint32_t addr)
{
uint32_t val = PORT0841_NO_ERROR_DETECTED;
trace_rs6000mc_parity_read(addr, val);
return val;
}
static const MemoryRegionPortio rs6000mc_port_list[] = {
{ 0x803, 1, 1, .read = rs6000mc_port0803_read },
{ 0x804, 1, 1, .read = rs6000mc_port0804_read },
{ 0x820, 1, 1, .read = rs6000mc_port0820_read,
.write = rs6000mc_port0820_write, },
{ 0x841, 1, 1, .read = rs6000mc_port0841_read },
PORTIO_END_OF_LIST()
};
static void rs6000mc_realize(DeviceState *dev, Error **errp)
{
RS6000MCState *s = RS6000MC_DEVICE(dev);
int socket = 0;
unsigned int ram_size = s->ram_size / (1024 * 1024);
while (socket < 6) {
if (ram_size >= 64) {
s->simm_size[socket] = 32;
s->simm_size[socket + 1] = 32;
ram_size -= 64;
} else if (ram_size >= 16) {
s->simm_size[socket] = 8;
s->simm_size[socket + 1] = 8;
ram_size -= 16;
} else {
/* Not enough memory */
break;
}
socket += 2;
}
for (socket = 0; socket < 6; socket++) {
if (s->simm_size[socket]) {
char name[] = "simm.?";
name[5] = socket + '0';
memory_region_allocate_system_memory(&s->simm[socket], OBJECT(dev),
name, s->simm_size[socket]
* 1024 * 1024);
memory_region_add_subregion_overlap(get_system_memory(), 0,
&s->simm[socket], socket);
}
}
if (ram_size) {
/* unable to push all requested RAM in SIMMs */
error_setg(errp, "RAM size incompatible with this board. "
"Try again with something else, like %d MB",
s->ram_size / 1024 / 1024 - ram_size);
return;
}
if (s->autoconfigure) {
uint32_t start_address = 0;
for (socket = 0; socket < 6; socket++) {
if (s->simm_size[socket]) {
memory_region_set_enabled(&s->simm[socket], true);
memory_region_set_address(&s->simm[socket], start_address);
start_address += memory_region_size(&s->simm[socket]);
}
}
}
isa_register_portio_list(ISA_DEVICE(dev), &s->portio, 0x0,
rs6000mc_port_list, s, "rs6000mc");
}
static const VMStateDescription vmstate_rs6000mc = {
.name = "rs6000-mc",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT8(port0820_index, RS6000MCState),
VMSTATE_END_OF_LIST()
},
};
static Property rs6000mc_properties[] = {
DEFINE_PROP_UINT32("ram-size", RS6000MCState, ram_size, 0),
DEFINE_PROP_BOOL("auto-configure", RS6000MCState, autoconfigure, true),
DEFINE_PROP_END_OF_LIST()
};
static void rs6000mc_class_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = rs6000mc_realize;
dc->vmsd = &vmstate_rs6000mc;
dc->props = rs6000mc_properties;
}
static const TypeInfo rs6000mc_info = {
.name = TYPE_RS6000MC,
.parent = TYPE_ISA_DEVICE,
.instance_size = sizeof(RS6000MCState),
.class_init = rs6000mc_class_initfn,
};
static void rs6000mc_types(void)
{
type_register_static(&rs6000mc_info);
}
type_init(rs6000mc_types)

View File

@ -148,8 +148,8 @@ static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu,
uint32_t gservers_prop[smt_threads * 2];
int index = ppc_get_vcpu_dt_id(cpu);
if (cpu->cpu_version) {
ret = fdt_setprop_cell(fdt, offset, "cpu-version", cpu->cpu_version);
if (cpu->compat_pvr) {
ret = fdt_setprop_cell(fdt, offset, "cpu-version", cpu->compat_pvr);
if (ret < 0) {
return ret;
}
@ -206,6 +206,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr)
PowerPCCPU *cpu = POWERPC_CPU(cs);
DeviceClass *dc = DEVICE_GET_CLASS(cs);
int index = ppc_get_vcpu_dt_id(cpu);
int compat_smt = MIN(smp_threads, ppc_compat_max_threads(cpu));
if ((index % smt) != 0) {
continue;
@ -240,8 +241,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr)
return ret;
}
ret = spapr_fixup_cpu_smt_dt(fdt, offset, cpu,
ppc_get_compat_smt_threads(cpu));
ret = spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt);
if (ret < 0) {
return ret;
}
@ -407,6 +407,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
size_t page_sizes_prop_size;
uint32_t vcpus_per_socket = smp_threads * smp_cores;
uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)};
int compat_smt = MIN(smp_threads, ppc_compat_max_threads(cpu));
sPAPRDRConnector *drc;
sPAPRDRConnectorClass *drck;
int drc_index;
@ -494,8 +495,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
_FDT(spapr_fixup_cpu_numa_dt(fdt, offset, cs));
_FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu,
ppc_get_compat_smt_threads(cpu)));
_FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt));
}
static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr)
@ -685,7 +685,6 @@ out:
int spapr_h_cas_compose_response(sPAPRMachineState *spapr,
target_ulong addr, target_ulong size,
bool cpu_update,
sPAPROptionVector *ov5_updates)
{
void *fdt, *fdt_skel;
@ -704,9 +703,7 @@ int spapr_h_cas_compose_response(sPAPRMachineState *spapr,
g_free(fdt_skel);
/* Fixup cpu nodes */
if (cpu_update) {
_FDT((spapr_fixup_cpu_dt(fdt, spapr)));
}
_FDT((spapr_fixup_cpu_dt(fdt, spapr)));
if (spapr_dt_cas_updates(spapr, fdt, ov5_updates)) {
return -1;
@ -1008,7 +1005,8 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR;
}
static void emulate_spapr_hypercall(PowerPCCPU *cpu)
static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp,
PowerPCCPU *cpu)
{
CPUPPCState *env = &cpu->env;
@ -1753,11 +1751,80 @@ static void spapr_validate_node_memory(MachineState *machine, Error **errp)
}
}
static void spapr_init_cpus(sPAPRMachineState *spapr)
{
MachineState *machine = MACHINE(spapr);
MachineClass *mc = MACHINE_GET_CLASS(machine);
char *type = spapr_get_cpu_core_type(machine->cpu_model);
int smt = kvmppc_smt_threads();
int spapr_max_cores, spapr_cores;
int i;
if (!type) {
error_report("Unable to find sPAPR CPU Core definition");
exit(1);
}
if (mc->query_hotpluggable_cpus) {
if (smp_cpus % smp_threads) {
error_report("smp_cpus (%u) must be multiple of threads (%u)",
smp_cpus, smp_threads);
exit(1);
}
if (max_cpus % smp_threads) {
error_report("max_cpus (%u) must be multiple of threads (%u)",
max_cpus, smp_threads);
exit(1);
}
spapr_max_cores = max_cpus / smp_threads;
spapr_cores = smp_cpus / smp_threads;
} else {
if (max_cpus != smp_cpus) {
error_report("This machine version does not support CPU hotplug");
exit(1);
}
spapr_max_cores = QEMU_ALIGN_UP(smp_cpus, smp_threads) / smp_threads;
spapr_cores = spapr_max_cores;
}
spapr->cores = g_new0(Object *, spapr_max_cores);
for (i = 0; i < spapr_max_cores; i++) {
int core_id = i * smp_threads;
if (mc->query_hotpluggable_cpus) {
sPAPRDRConnector *drc =
spapr_dr_connector_new(OBJECT(spapr),
SPAPR_DR_CONNECTOR_TYPE_CPU,
(core_id / smp_threads) * smt);
qemu_register_reset(spapr_drc_reset, drc);
}
if (i < spapr_cores) {
Object *core = object_new(type);
int nr_threads = smp_threads;
/* Handle the partially filled core for older machine types */
if ((i + 1) * smp_threads >= smp_cpus) {
nr_threads = smp_cpus - i * smp_threads;
}
object_property_set_int(core, nr_threads, "nr-threads",
&error_fatal);
object_property_set_int(core, core_id, CPU_CORE_PROP_CORE_ID,
&error_fatal);
object_property_set_bool(core, true, "realized", &error_fatal);
}
}
g_free(type);
}
/* pSeries LPAR / sPAPR hardware init */
static void ppc_spapr_init(MachineState *machine)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(machine);
MachineClass *mc = MACHINE_GET_CLASS(machine);
sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine);
const char *kernel_filename = machine->kernel_filename;
const char *initrd_filename = machine->initrd_filename;
@ -1772,28 +1839,11 @@ static void ppc_spapr_init(MachineState *machine)
long load_limit, fw_size;
char *filename;
int smt = kvmppc_smt_threads();
int spapr_cores = smp_cpus / smp_threads;
int spapr_max_cores = max_cpus / smp_threads;
if (mc->query_hotpluggable_cpus) {
if (smp_cpus % smp_threads) {
error_report("smp_cpus (%u) must be multiple of threads (%u)",
smp_cpus, smp_threads);
exit(1);
}
if (max_cpus % smp_threads) {
error_report("max_cpus (%u) must be multiple of threads (%u)",
max_cpus, smp_threads);
exit(1);
}
}
msi_nonbroken = true;
QLIST_INIT(&spapr->phbs);
cpu_ppc_hypercall = emulate_spapr_hypercall;
/* Allocate RMA if necessary */
rma_alloc_size = kvmppc_alloc_rma(&rma);
@ -1866,44 +1916,7 @@ static void ppc_spapr_init(MachineState *machine)
ppc_cpu_parse_features(machine->cpu_model);
if (mc->query_hotpluggable_cpus) {
char *type = spapr_get_cpu_core_type(machine->cpu_model);
if (type == NULL) {
error_report("Unable to find sPAPR CPU Core definition");
exit(1);
}
spapr->cores = g_new0(Object *, spapr_max_cores);
for (i = 0; i < spapr_max_cores; i++) {
int core_id = i * smp_threads;
sPAPRDRConnector *drc =
spapr_dr_connector_new(OBJECT(spapr),
SPAPR_DR_CONNECTOR_TYPE_CPU,
(core_id / smp_threads) * smt);
qemu_register_reset(spapr_drc_reset, drc);
if (i < spapr_cores) {
Object *core = object_new(type);
object_property_set_int(core, smp_threads, "nr-threads",
&error_fatal);
object_property_set_int(core, core_id, CPU_CORE_PROP_CORE_ID,
&error_fatal);
object_property_set_bool(core, true, "realized", &error_fatal);
}
}
g_free(type);
} else {
for (i = 0; i < smp_cpus; i++) {
PowerPCCPU *cpu = cpu_ppc_init(machine->cpu_model);
if (cpu == NULL) {
error_report("Unable to find PowerPC CPU definition");
exit(1);
}
spapr_cpu_init(spapr, cpu, &error_fatal);
}
}
spapr_init_cpus(spapr);
if (kvm_enabled()) {
/* Enable H_LOGICAL_CI_* so SLOF can talk to in-kernel devices */
@ -2116,6 +2129,12 @@ static void ppc_spapr_init(MachineState *machine)
qemu_register_reset(spapr_ccs_reset_hook, spapr);
qemu_register_boot_set(spapr_boot_set, spapr);
/* to stop and start vmclock */
if (kvm_enabled()) {
qemu_add_vm_change_state_handler(cpu_ppc_clock_vm_state_change,
&spapr->tb);
}
}
static int spapr_kvm_type(const char *vm_type)
@ -2185,6 +2204,19 @@ static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus,
}
}
/*
* SLOF probes the USB devices, and if it recognizes that the device is a
* storage device, it changes its name to "storage" instead of "usb-host",
* and additionally adds a child node for the SCSI LUN, so the correct
* boot path in SLOF is something like .../storage@1/disk@xxx" instead.
*/
if (strcmp("usb-host", qdev_fw_name(dev)) == 0) {
USBDevice *usbdev = CAST(USBDevice, dev, TYPE_USB_DEVICE);
if (usb_host_dev_is_scsi_storage(usbdev)) {
return g_strdup_printf("storage@%s/disk", usbdev->port->path);
}
}
if (phb) {
/* Replace "pci" with "pci@800000020000000" */
return g_strdup_printf("pci@%"PRIX64, phb->buid);
@ -2252,7 +2284,7 @@ static void spapr_machine_finalizefn(Object *obj)
g_free(spapr->kvm_type);
}
static void ppc_cpu_do_nmi_on_cpu(CPUState *cs, run_on_cpu_data arg)
void spapr_do_system_reset_on_cpu(CPUState *cs, run_on_cpu_data arg)
{
cpu_synchronize_state(cs);
ppc_cpu_do_system_reset(cs);
@ -2263,7 +2295,7 @@ static void spapr_nmi(NMIState *n, int cpu_index, Error **errp)
CPUState *cs;
CPU_FOREACH(cs) {
async_run_on_cpu(cs, ppc_cpu_do_nmi_on_cpu, RUN_ON_CPU_NULL);
async_run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL);
}
}
@ -2668,6 +2700,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc);
NMIClass *nc = NMI_CLASS(oc);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_CLASS(oc);
mc->desc = "pSeries Logical Partition (PAPR compliant)";
@ -2699,6 +2732,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
fwc->get_dev_path = spapr_get_fw_dev_path;
nc->nmi_monitor_handler = spapr_nmi;
smc->phb_placement = spapr_phb_placement;
vhc->hypercall = emulate_spapr_hypercall;
}
static const TypeInfo spapr_machine_info = {
@ -2714,6 +2748,7 @@ static const TypeInfo spapr_machine_info = {
{ TYPE_FW_PATH_PROVIDER },
{ TYPE_NMI },
{ TYPE_HOTPLUG_HANDLER },
{ TYPE_PPC_VIRTUAL_HYPERVISOR },
{ }
},
};
@ -2747,18 +2782,37 @@ static const TypeInfo spapr_machine_info = {
type_init(spapr_machine_register_##suffix)
/*
* pseries-2.8
* pseries-2.9
*/
static void spapr_machine_2_8_instance_options(MachineState *machine)
static void spapr_machine_2_9_instance_options(MachineState *machine)
{
}
static void spapr_machine_2_8_class_options(MachineClass *mc)
static void spapr_machine_2_9_class_options(MachineClass *mc)
{
/* Defaults for the latest behaviour inherited from the base class */
}
DEFINE_SPAPR_MACHINE(2_8, "2.8", true);
DEFINE_SPAPR_MACHINE(2_9, "2.9", true);
/*
* pseries-2.8
*/
#define SPAPR_COMPAT_2_8 \
HW_COMPAT_2_8
static void spapr_machine_2_8_instance_options(MachineState *machine)
{
spapr_machine_2_9_instance_options(machine);
}
static void spapr_machine_2_8_class_options(MachineClass *mc)
{
spapr_machine_2_9_class_options(mc);
SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_8);
}
DEFINE_SPAPR_MACHINE(2_8, "2.8", false);
/*
* pseries-2.7

View File

@ -46,7 +46,8 @@ static void spapr_cpu_destroy(PowerPCCPU *cpu)
qemu_unregister_reset(spapr_cpu_reset, cpu);
}
void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp)
static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu,
Error **errp)
{
CPUPPCState *env = &cpu->env;
CPUState *cs = CPU(cpu);
@ -56,6 +57,7 @@ void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp)
cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ);
/* Enable PAPR mode in TCG or KVM */
cpu_ppc_set_vhyp(cpu, PPC_VIRTUAL_HYPERVISOR(spapr));
cpu_ppc_set_papr(cpu);
if (cpu->max_compat) {
@ -166,11 +168,11 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev));
MachineClass *mc = MACHINE_GET_CLASS(spapr);
sPAPRCPUCore *core = SPAPR_CPU_CORE(OBJECT(dev));
CPUCore *cc = CPU_CORE(dev);
CPUState *cs = CPU(core->threads);
sPAPRDRConnector *drc;
sPAPRDRConnectorClass *drck;
Error *local_err = NULL;
void *fdt = NULL;
int fdt_offset = 0;
@ -180,7 +182,7 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_CPU, index * smt);
spapr->cores[index] = OBJECT(dev);
g_assert(drc);
g_assert(drc || !mc->query_hotpluggable_cpus);
/*
* Setup CPU DT entries only for hotplugged CPUs. For boot time or
@ -190,13 +192,15 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
fdt = spapr_populate_hotplug_cpu_dt(cs, &fdt_offset, spapr);
}
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, &local_err);
if (local_err) {
g_free(fdt);
spapr->cores[index] = NULL;
error_propagate(errp, local_err);
return;
if (drc) {
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, &local_err);
if (local_err) {
g_free(fdt);
spapr->cores[index] = NULL;
error_propagate(errp, local_err);
return;
}
}
if (dev->hotplugged) {
@ -209,8 +213,11 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
/*
* Set the right DRC states for cold plugged CPU.
*/
drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_USABLE);
drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_UNISOLATED);
if (drc) {
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_USABLE);
drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_UNISOLATED);
}
}
}
@ -227,7 +234,7 @@ void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
char *base_core_type = spapr_get_cpu_core_type(machine->cpu_model);
const char *type = object_get_typename(OBJECT(dev));
if (!mc->query_hotpluggable_cpus) {
if (dev->hotplugged && !mc->query_hotpluggable_cpus) {
error_setg(&local_err, "CPU hotplug not supported for this machine");
goto out;
}
@ -237,11 +244,6 @@ void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
goto out;
}
if (cc->nr_threads != smp_threads) {
error_setg(&local_err, "threads must be %d", smp_threads);
goto out;
}
if (cc->core_id % smp_threads) {
error_setg(&local_err, "invalid core id %d", cc->core_id);
goto out;

View File

@ -881,126 +881,103 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return ret;
}
typedef struct {
uint32_t cpu_version;
Error *err;
} SetCompatState;
#define H_SIGNAL_SYS_RESET_ALL -1
#define H_SIGNAL_SYS_RESET_ALLBUTSELF -2
static void do_set_compat(CPUState *cs, run_on_cpu_data arg)
static target_ulong h_signal_sys_reset(PowerPCCPU *cpu,
sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);
SetCompatState *s = arg.host_ptr;
target_long target = args[0];
CPUState *cs;
cpu_synchronize_state(cs);
ppc_set_compat(cpu, s->cpu_version, &s->err);
}
#define get_compat_level(cpuver) ( \
((cpuver) == CPU_POWERPC_LOGICAL_2_05) ? 2050 : \
((cpuver) == CPU_POWERPC_LOGICAL_2_06) ? 2060 : \
((cpuver) == CPU_POWERPC_LOGICAL_2_06_PLUS) ? 2061 : \
((cpuver) == CPU_POWERPC_LOGICAL_2_07) ? 2070 : 0)
static void cas_handle_compat_cpu(PowerPCCPUClass *pcc, uint32_t pvr,
unsigned max_lvl, unsigned *compat_lvl,
unsigned *cpu_version)
{
unsigned lvl = get_compat_level(pvr);
bool is205, is206, is207;
if (!lvl) {
return;
}
/* If it is a logical PVR, try to determine the highest level */
is205 = (pcc->pcr_supported & PCR_COMPAT_2_05) &&
(lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_05));
is206 = (pcc->pcr_supported & PCR_COMPAT_2_06) &&
((lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06)) ||
(lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06_PLUS)));
is207 = (pcc->pcr_supported & PCR_COMPAT_2_07) &&
(lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_07));
if (is205 || is206 || is207) {
if (!max_lvl) {
/* User did not set the level, choose the highest */
if (*compat_lvl <= lvl) {
*compat_lvl = lvl;
*cpu_version = pvr;
}
} else if (max_lvl >= lvl) {
/* User chose the level, don't set higher than this */
*compat_lvl = lvl;
*cpu_version = pvr;
if (target < 0) {
/* Broadcast */
if (target < H_SIGNAL_SYS_RESET_ALLBUTSELF) {
return H_PARAMETER;
}
CPU_FOREACH(cs) {
PowerPCCPU *c = POWERPC_CPU(cs);
if (target == H_SIGNAL_SYS_RESET_ALLBUTSELF) {
if (c == cpu) {
continue;
}
}
run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL);
}
return H_SUCCESS;
} else {
/* Unicast */
CPU_FOREACH(cs) {
if (cpu->cpu_dt_id == target) {
run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL);
return H_SUCCESS;
}
}
return H_PARAMETER;
}
}
static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
sPAPRMachineState *spapr,
target_ulong opcode,
target_ulong *args)
{
target_ulong list = ppc64_phys_to_real(args[0]);
target_ulong ov_table;
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu_);
CPUState *cs;
bool cpu_match = false, cpu_update = true;
unsigned old_cpu_version = cpu_->cpu_version;
unsigned compat_lvl = 0, cpu_version = 0;
unsigned max_lvl = get_compat_level(cpu_->max_compat);
int counter;
bool explicit_match = false; /* Matched the CPU's real PVR */
uint32_t max_compat = cpu->max_compat;
uint32_t best_compat = 0;
int i;
sPAPROptionVector *ov5_guest, *ov5_cas_old, *ov5_updates;
/* Parse PVR list */
for (counter = 0; counter < 512; ++counter) {
/*
* We scan the supplied table of PVRs looking for two things
* 1. Is our real CPU PVR in the list?
* 2. What's the "best" listed logical PVR
*/
for (i = 0; i < 512; ++i) {
uint32_t pvr, pvr_mask;
pvr_mask = ldl_be_phys(&address_space_memory, list);
list += 4;
pvr = ldl_be_phys(&address_space_memory, list);
list += 4;
pvr = ldl_be_phys(&address_space_memory, list + 4);
list += 8;
trace_spapr_cas_pvr_try(pvr);
if (!max_lvl &&
((cpu_->env.spr[SPR_PVR] & pvr_mask) == (pvr & pvr_mask))) {
cpu_match = true;
cpu_version = 0;
} else if (pvr == cpu_->cpu_version) {
cpu_match = true;
cpu_version = cpu_->cpu_version;
} else if (!cpu_match) {
cas_handle_compat_cpu(pcc, pvr, max_lvl, &compat_lvl, &cpu_version);
}
/* Terminator record */
if (~pvr_mask & pvr) {
break;
break; /* Terminator record */
}
}
/* Parsing finished */
trace_spapr_cas_pvr(cpu_->cpu_version, cpu_match,
cpu_version, pcc->pcr_mask);
/* Update CPUs */
if (old_cpu_version != cpu_version) {
CPU_FOREACH(cs) {
SetCompatState s = {
.cpu_version = cpu_version,
.err = NULL,
};
run_on_cpu(cs, do_set_compat, RUN_ON_CPU_HOST_PTR(&s));
if (s.err) {
error_report_err(s.err);
return H_HARDWARE;
if ((cpu->env.spr[SPR_PVR] & pvr_mask) == (pvr & pvr_mask)) {
explicit_match = true;
} else {
if (ppc_check_compat(cpu, pvr, best_compat, max_compat)) {
best_compat = pvr;
}
}
}
if (!cpu_version) {
cpu_update = false;
if ((best_compat == 0) && (!explicit_match || max_compat)) {
/* We couldn't find a suitable compatibility mode, and either
* the guest doesn't support "raw" mode for this CPU, or raw
* mode is disabled because a maximum compat mode is set */
return H_HARDWARE;
}
/* Parsing finished */
trace_spapr_cas_pvr(cpu->compat_pvr, explicit_match, best_compat);
/* Update CPUs */
if (cpu->compat_pvr != best_compat) {
Error *local_err = NULL;
ppc_set_compat_all(best_compat, &local_err);
if (local_err) {
error_report_err(local_err);
return H_HARDWARE;
}
}
/* For the future use: here @ov_table points to the first option vector */
@ -1028,7 +1005,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
if (!spapr->cas_reboot) {
spapr->cas_reboot =
(spapr_h_cas_compose_response(spapr, args[1], args[2], cpu_update,
(spapr_h_cas_compose_response(spapr, args[1], args[2],
ov5_updates) != 0);
}
spapr_ovec_cleanup(ov5_updates);
@ -1101,6 +1078,7 @@ static void hypercall_register_types(void)
/* hcall-splpar */
spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa);
spapr_register_hypercall(H_CEDE, h_cede);
spapr_register_hypercall(H_SIGNAL_SYS_RESET, h_signal_sys_reset);
/* processor register resource access h-calls */
spapr_register_hypercall(H_SET_SPRG0, h_set_sprg0);

View File

@ -538,21 +538,11 @@ VIOsPAPRBus *spapr_vio_bus_init(void)
return bus;
}
/* Represents sPAPR hcall VIO devices */
static int spapr_vio_bridge_init(SysBusDevice *dev)
{
/* nothing */
return 0;
}
static void spapr_vio_bridge_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
dc->fw_name = "vdevice";
k->init = spapr_vio_bridge_init;
}
static const TypeInfo spapr_vio_bridge_info = {

View File

@ -15,7 +15,7 @@ spapr_cas_continue(unsigned long n) "Copy changes to the guest: %ld bytes"
# hw/ppc/spapr_hcall.c
spapr_cas_pvr_try(uint32_t pvr) "%x"
spapr_cas_pvr(uint32_t cur_pvr, bool cpu_match, uint32_t new_pvr, uint64_t pcr) "current=%x, cpu_match=%u, new=%x, compat flags=%"PRIx64
spapr_cas_pvr(uint32_t cur_pvr, bool explicit_match, uint32_t new_pvr) "current=%x, explicit_match=%u, new=%x"
# hw/ppc/spapr_iommu.c
spapr_iommu_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tce=0x%"PRIx64" ret=%"PRId64
@ -74,3 +74,14 @@ ppc_tb_adjust(uint64_t offs1, uint64_t offs2, int64_t diff, int64_t seconds) "ad
# hw/ppc/prep.c
prep_io_800_writeb(uint32_t addr, uint32_t val) "0x%08" PRIx32 " => 0x%02" PRIx32
prep_io_800_readb(uint32_t addr, uint32_t retval) "0x%08" PRIx32 " <= 0x%02" PRIx32
# hw/ppc/prep_systemio.c
prep_systemio_read(uint32_t addr, uint32_t val) "read addr=%x val=%x"
prep_systemio_write(uint32_t addr, uint32_t val) "write addr=%x val=%x"
# hw/ppc/rs6000_mc.c
rs6000mc_id_read(uint32_t addr, uint32_t val) "read addr=%x val=%x"
rs6000mc_presence_read(uint32_t addr, uint32_t val) "read addr=%x val=%x"
rs6000mc_size_read(uint32_t addr, uint32_t val) "read addr=%x val=%x"
rs6000mc_size_write(uint32_t addr, uint32_t val) "write addr=%x val=%x"
rs6000mc_parity_read(uint32_t addr, uint32_t val) "read addr=%x val=%x"

View File

@ -221,6 +221,13 @@ static void virtex_init(MachineState *machine)
cpu = ppc440_init_xilinx(&ram_size, 1, machine->cpu_model, 400000000);
env = &cpu->env;
if (env->mmu_model != POWERPC_MMU_BOOKE) {
fprintf(stderr, "MMU model %i not supported by this machine.\n",
env->mmu_model);
exit(1);
}
qemu_register_reset(main_cpu_reset, cpu);
memory_region_allocate_system_memory(phys_ram, NULL, "ram", ram_size);

View File

@ -1707,6 +1707,35 @@ static void usb_host_auto_check(void *unused)
timer_mod(usb_auto_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 2000);
}
/**
* Check whether USB host device has a USB mass storage SCSI interface
*/
bool usb_host_dev_is_scsi_storage(USBDevice *ud)
{
USBHostDevice *uhd = USB_HOST_DEVICE(ud);
struct libusb_config_descriptor *conf;
const struct libusb_interface_descriptor *intf;
bool is_scsi_storage = false;
int i;
if (!uhd || libusb_get_active_config_descriptor(uhd->dev, &conf) != 0) {
return false;
}
for (i = 0; i < conf->bNumInterfaces; i++) {
intf = &conf->interface[i].altsetting[ud->altsetting[i]];
if (intf->bInterfaceClass == LIBUSB_CLASS_MASS_STORAGE &&
intf->bInterfaceSubClass == 6) { /* 6 means SCSI */
is_scsi_storage = true;
break;
}
}
libusb_free_config_descriptor(conf);
return is_scsi_storage;
}
void hmp_info_usbhost(Monitor *mon, const QDict *qdict)
{
libusb_device **devs = NULL;

View File

@ -46,3 +46,8 @@ USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
{
return NULL;
}
bool usb_host_dev_is_scsi_storage(USBDevice *ud)
{
return false;
}

View File

@ -356,6 +356,26 @@ static inline int float16_is_any_nan(float16 a)
return ((float16_val(a) & ~0x8000) > 0x7c00);
}
static inline int float16_is_neg(float16 a)
{
return float16_val(a) >> 15;
}
static inline int float16_is_infinity(float16 a)
{
return (float16_val(a) & 0x7fff) == 0x7c00;
}
static inline int float16_is_zero(float16 a)
{
return (float16_val(a) & 0x7fff) == 0;
}
static inline int float16_is_zero_or_denormal(float16 a)
{
return (float16_val(a) & 0x7c00) == 0;
}
/*----------------------------------------------------------------------------
| The pattern for a default generated half-precision NaN.
*----------------------------------------------------------------------------*/

View File

@ -347,7 +347,8 @@ struct sPAPRMachineState {
#define H_XIRR_X 0x2FC
#define H_RANDOM 0x300
#define H_SET_MODE 0x31C
#define MAX_HCALL_OPCODE H_SET_MODE
#define H_SIGNAL_SYS_RESET 0x380
#define MAX_HCALL_OPCODE H_SIGNAL_SYS_RESET
/* The hcalls above are standardized in PAPR and implemented by pHyp
* as well.
@ -589,7 +590,6 @@ void spapr_events_init(sPAPRMachineState *sm);
void spapr_dt_events(sPAPRMachineState *sm, void *fdt);
int spapr_h_cas_compose_response(sPAPRMachineState *sm,
target_ulong addr, target_ulong size,
bool cpu_update,
sPAPROptionVector *ov5_updates);
sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn);
void spapr_tce_table_enable(sPAPRTCETable *tcet,
@ -614,7 +614,6 @@ void spapr_hotplug_req_add_by_count_indexed(sPAPRDRConnectorType drc_type,
uint32_t count, uint32_t index);
void spapr_hotplug_req_remove_by_count_indexed(sPAPRDRConnectorType drc_type,
uint32_t count, uint32_t index);
void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp);
void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset,
sPAPRMachineState *spapr);
@ -662,4 +661,6 @@ int spapr_rng_populate_dt(void *fdt);
#define SPAPR_LMB_FLAGS_DRC_INVALID 0x00000020
#define SPAPR_LMB_FLAGS_RESERVED 0x00000080
void spapr_do_system_reset_on_cpu(CPUState *cs, run_on_cpu_data arg);
#endif /* HW_SPAPR_H */

View File

@ -471,6 +471,7 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p);
/* usb-linux.c */
USBDevice *usb_host_device_open(USBBus *bus, const char *devname);
void hmp_info_usbhost(Monitor *mon, const QDict *qdict);
bool usb_host_dev_is_scsi_storage(USBDevice *usbdev);
/* usb ports of the VM */

View File

@ -513,4 +513,31 @@ static inline uint64_t pow2ceil(uint64_t value)
return 1ULL << (64 - nlz);
}
/**
* urshift - 128-bit Unsigned Right Shift.
* @plow: in/out - lower 64-bit integer.
* @phigh: in/out - higher 64-bit integer.
* @shift: in - bytes to shift, between 0 and 127.
*
* Result is zero-extended and stored in plow/phigh, which are
* input/output variables. Shift values outside the range will
* be mod to 128. In other words, the caller is responsible to
* verify/assert both the shift range and plow/phigh pointers.
*/
void urshift(uint64_t *plow, uint64_t *phigh, int32_t shift);
/**
* ulshift - 128-bit Unsigned Left Shift.
* @plow: in/out - lower 64-bit integer.
* @phigh: in/out - higher 64-bit integer.
* @shift: in - bytes to shift, between 0 and 127.
* @overflow: out - true if any 1-bit is shifted out.
*
* Result is zero-extended and stored in plow/phigh, which are
* input/output variables. Shift values outside the range will
* be mod to 128. In other words, the caller is responsible to
* verify/assert both the shift range and plow/phigh pointers.
*/
void ulshift(uint64_t *plow, uint64_t *phigh, int32_t shift, bool *overflow);
#endif

View File

@ -2,7 +2,7 @@ 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
obj-$(TARGET_PPC64) += mmu-hash64.o arch_dump.o compat.o
endif
obj-$(CONFIG_KVM) += kvm.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o

185
target/ppc/compat.c Normal file
View File

@ -0,0 +1,185 @@
/*
* PowerPC CPU initialization for qemu.
*
* Copyright 2016, David Gibson, Red Hat Inc. <dgibson@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "sysemu/hw_accel.h"
#include "sysemu/kvm.h"
#include "kvm_ppc.h"
#include "sysemu/cpus.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "cpu-models.h"
typedef struct {
uint32_t pvr;
uint64_t pcr;
uint64_t pcr_level;
int max_threads;
} CompatInfo;
static const CompatInfo compat_table[] = {
/*
* Ordered from oldest to newest - the code relies on this
*/
{ /* POWER6, ISA2.05 */
.pvr = CPU_POWERPC_LOGICAL_2_05,
.pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05
| PCR_TM_DIS | PCR_VSX_DIS,
.pcr_level = PCR_COMPAT_2_05,
.max_threads = 2,
},
{ /* POWER7, ISA2.06 */
.pvr = CPU_POWERPC_LOGICAL_2_06,
.pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS,
.pcr_level = PCR_COMPAT_2_06,
.max_threads = 4,
},
{
.pvr = CPU_POWERPC_LOGICAL_2_06_PLUS,
.pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS,
.pcr_level = PCR_COMPAT_2_06,
.max_threads = 4,
},
{ /* POWER8, ISA2.07 */
.pvr = CPU_POWERPC_LOGICAL_2_07,
.pcr = PCR_COMPAT_2_07,
.pcr_level = PCR_COMPAT_2_07,
.max_threads = 8,
},
};
static const CompatInfo *compat_by_pvr(uint32_t pvr)
{
int i;
for (i = 0; i < ARRAY_SIZE(compat_table); i++) {
if (compat_table[i].pvr == pvr) {
return &compat_table[i];
}
}
return NULL;
}
bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr,
uint32_t min_compat_pvr, uint32_t max_compat_pvr)
{
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
const CompatInfo *compat = compat_by_pvr(compat_pvr);
const CompatInfo *min = compat_by_pvr(min_compat_pvr);
const CompatInfo *max = compat_by_pvr(max_compat_pvr);
#if !defined(CONFIG_USER_ONLY)
g_assert(cpu->vhyp);
#endif
g_assert(!min_compat_pvr || min);
g_assert(!max_compat_pvr || max);
if (!compat) {
/* Not a recognized logical PVR */
return false;
}
if ((min && (compat < min)) || (max && (compat > max))) {
/* Outside specified range */
return false;
}
if (!(pcc->pcr_supported & compat->pcr_level)) {
/* Not supported by this CPU */
return false;
}
return true;
}
void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp)
{
const CompatInfo *compat = compat_by_pvr(compat_pvr);
CPUPPCState *env = &cpu->env;
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
uint64_t pcr;
if (!compat_pvr) {
pcr = 0;
} else if (!compat) {
error_setg(errp, "Unknown compatibility PVR 0x%08"PRIx32, compat_pvr);
return;
} else if (!ppc_check_compat(cpu, compat_pvr, 0, 0)) {
error_setg(errp, "Compatibility PVR 0x%08"PRIx32" not valid for CPU",
compat_pvr);
return;
} else {
pcr = compat->pcr;
}
cpu_synchronize_state(CPU(cpu));
cpu->compat_pvr = compat_pvr;
env->spr[SPR_PCR] = pcr & pcc->pcr_mask;
if (kvm_enabled()) {
int ret = kvmppc_set_compat(cpu, cpu->compat_pvr);
if (ret < 0) {
error_setg_errno(errp, -ret,
"Unable to set CPU compatibility mode in KVM");
}
}
}
typedef struct {
uint32_t compat_pvr;
Error *err;
} SetCompatState;
static void do_set_compat(CPUState *cs, run_on_cpu_data arg)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);
SetCompatState *s = arg.host_ptr;
ppc_set_compat(cpu, s->compat_pvr, &s->err);
}
void ppc_set_compat_all(uint32_t compat_pvr, Error **errp)
{
CPUState *cs;
CPU_FOREACH(cs) {
SetCompatState s = {
.compat_pvr = compat_pvr,
.err = NULL,
};
run_on_cpu(cs, do_set_compat, RUN_ON_CPU_HOST_PTR(&s));
if (s.err) {
error_propagate(errp, s.err);
return;
}
}
}
int ppc_compat_max_threads(PowerPCCPU *cpu)
{
const CompatInfo *compat = compat_by_pvr(cpu->compat_pvr);
int n_threads = CPU(cpu)->nr_threads;
if (cpu->compat_pvr) {
g_assert(compat);
n_threads = MIN(n_threads, compat->max_threads);
}
return n_threads;
}

View File

@ -1375,19 +1375,15 @@ PowerPCCPUAlias ppc_cpu_aliases[] = {
{ "7445", "7445_v3.2" },
{ "7455", "7455_v3.2" },
{ "Apollo6", "7455" },
{ "7447", "7447_v1.2" },
{ "7447", "7447_v1.1" },
{ "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" },
@ -1399,21 +1395,7 @@ PowerPCCPUAlias ppc_cpu_aliases[] = {
{ "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)

View File

@ -601,7 +601,7 @@ enum {
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,
CPU_POWERPC_LOGICAL_3_00 = 0x0F000005,
};
/* System version register (used on MPC 8xxx) */

View File

@ -214,6 +214,9 @@ extern const struct VMStateDescription vmstate_ppc_timebase;
.flags = VMS_STRUCT, \
.offset = vmstate_offset_value(_state, _field, PPCTimebase), \
}
void cpu_ppc_clock_vm_state_change(void *opaque, int running,
RunState state);
#endif
#endif

View File

@ -21,6 +21,7 @@
#define PPC_CPU_H
#include "qemu-common.h"
#include "qemu/int128.h"
//#define PPC_EMULATE_32BITS_HYPV
@ -262,6 +263,7 @@ union ppc_avr_t {
#ifdef CONFIG_INT128
__uint128_t u128;
#endif
Int128 s128;
};
#if !defined(CONFIG_USER_ONLY)
@ -1148,12 +1150,15 @@ do { \
env->wdt_period[3] = (d_); \
} while (0)
typedef struct PPCVirtualHypervisor PPCVirtualHypervisor;
typedef struct PPCVirtualHypervisorClass PPCVirtualHypervisorClass;
/**
* 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
* @compat_pvr: Current logical PVR, zero if in "raw" mode
*
* A PowerPC CPU.
*/
@ -1165,7 +1170,8 @@ struct PowerPCCPU {
CPUPPCState env;
int cpu_dt_id;
uint32_t max_compat;
uint32_t cpu_version;
uint32_t compat_pvr;
PPCVirtualHypervisor *vhyp;
/* Fields related to migration compatibility hacks */
bool pre_2_8_migration;
@ -1187,6 +1193,25 @@ static inline PowerPCCPU *ppc_env_get_cpu(CPUPPCState *env)
PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr);
PowerPCCPUClass *ppc_cpu_class_by_pvr_mask(uint32_t pvr);
struct PPCVirtualHypervisor {
Object parent;
};
struct PPCVirtualHypervisorClass {
InterfaceClass parent;
void (*hypercall)(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu);
};
#define TYPE_PPC_VIRTUAL_HYPERVISOR "ppc-virtual-hypervisor"
#define PPC_VIRTUAL_HYPERVISOR(obj) \
OBJECT_CHECK(PPCVirtualHypervisor, (obj), TYPE_PPC_VIRTUAL_HYPERVISOR)
#define PPC_VIRTUAL_HYPERVISOR_CLASS(klass) \
OBJECT_CLASS_CHECK(PPCVirtualHypervisorClass, (klass), \
TYPE_PPC_VIRTUAL_HYPERVISOR)
#define PPC_VIRTUAL_HYPERVISOR_GET_CLASS(obj) \
OBJECT_GET_CLASS(PPCVirtualHypervisorClass, (obj), \
TYPE_PPC_VIRTUAL_HYPERVISOR)
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,
@ -1225,9 +1250,7 @@ void ppc_store_sdr1 (CPUPPCState *env, target_ulong value);
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 */
@ -1259,6 +1282,7 @@ 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_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp);
void cpu_ppc_set_papr(PowerPCCPU *cpu);
#endif
#endif
@ -1297,18 +1321,34 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch)
return ifetch ? env->immu_idx : env->dmmu_idx;
}
/* Compatibility modes */
#if defined(TARGET_PPC64)
bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr,
uint32_t min_compat_pvr, uint32_t max_compat_pvr);
void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp);
#if !defined(CONFIG_USER_ONLY)
void ppc_set_compat_all(uint32_t compat_pvr, Error **errp);
#endif
int ppc_compat_max_threads(PowerPCCPU *cpu);
#endif /* defined(TARGET_PPC64) */
#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)
#define CRF_LT_BIT 3
#define CRF_GT_BIT 2
#define CRF_EQ_BIT 1
#define CRF_SO_BIT 0
#define CRF_LT (1 << CRF_LT_BIT)
#define CRF_GT (1 << CRF_GT_BIT)
#define CRF_EQ (1 << CRF_EQ_BIT)
#define CRF_SO (1 << CRF_SO_BIT)
/* For SPE extensions */
#define CRF_CH (1 << CRF_LT_BIT)
#define CRF_CL (1 << CRF_GT_BIT)
#define CRF_CH_OR_CL (1 << CRF_EQ_BIT)
#define CRF_CH_AND_CL (1 << CRF_SO_BIT)
/* XER definitions */
#define XER_SO 31
@ -2250,6 +2290,7 @@ enum {
PCR_COMPAT_2_05 = 1ull << (63-62),
PCR_COMPAT_2_06 = 1ull << (63-61),
PCR_COMPAT_2_07 = 1ull << (63-60),
PCR_COMPAT_3_00 = 1ull << (63-59),
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) */
@ -2428,8 +2469,6 @@ static inline bool lsw_reg_in_range(int start, int nregs, int rx)
(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);
/**

View File

@ -34,11 +34,6 @@
# define LOG_EXCP(...) do { } while (0)
#endif
/*****************************************************************************/
/* PowerPC Hypercall emulation */
void (*cpu_ppc_hypercall)(PowerPCCPU *);
/*****************************************************************************/
/* Exception processing */
#if defined(CONFIG_USER_ONLY)
@ -318,8 +313,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
env->nip += 4;
/* "PAPR mode" built-in hypercall emulation */
if ((lev == 1) && cpu_ppc_hypercall) {
cpu_ppc_hypercall(cpu);
if ((lev == 1) && cpu->vhyp) {
PPCVirtualHypervisorClass *vhc =
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
vhc->hypercall(cpu->vhyp, cpu);
return;
}
if (lev == 1) {

View File

@ -20,9 +20,20 @@
#include "cpu.h"
#include "exec/helper-proto.h"
#include "exec/exec-all.h"
#include "internal.h"
static inline float128 float128_snan_to_qnan(float128 x)
{
float128 r;
r.high = x.high | 0x0000800000000000;
r.low = x.low;
return r;
}
#define float64_snan_to_qnan(x) ((x) | 0x0008000000000000ULL)
#define float32_snan_to_qnan(x) ((x) | 0x00400000)
#define float16_snan_to_qnan(x) ((x) | 0x0200)
/*****************************************************************************/
/* Floating point operations helpers */
@ -46,15 +57,6 @@ uint32_t helper_float64_to_float32(CPUPPCState *env, uint64_t arg)
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;
@ -65,57 +67,61 @@ 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;
#define COMPUTE_FPRF(tp) \
void helper_compute_fprf_##tp(CPUPPCState *env, tp arg) \
{ \
int isneg; \
int fprf; \
\
isneg = tp##_is_neg(arg); \
if (unlikely(tp##_is_any_nan(arg))) { \
if (tp##_is_signaling_nan(arg, &env->fp_status)) { \
/* Signaling NaN: flags are undefined */ \
fprf = 0x00; \
} else { \
/* Quiet NaN */ \
fprf = 0x11; \
} \
} else if (unlikely(tp##_is_infinity(arg))) { \
/* +/- infinity */ \
if (isneg) { \
fprf = 0x09; \
} else { \
fprf = 0x05; \
} \
} else { \
if (tp##_is_zero(arg)) { \
/* +/- zero */ \
if (isneg) { \
fprf = 0x12; \
} else { \
fprf = 0x02; \
} \
} else { \
if (tp##_is_zero_or_denormal(arg)) { \
/* 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; \
}
COMPUTE_FPRF(float16)
COMPUTE_FPRF(float32)
COMPUTE_FPRF(float64)
COMPUTE_FPRF(float128)
/* Floating-point invalid operations exception */
static inline __attribute__((__always_inline__))
uint64_t float_invalid_op_excp(CPUPPCState *env, int op, int set_fpcc)
@ -1776,53 +1782,6 @@ uint32_t helper_efdcmpeq(CPUPPCState *env, uint64_t op1, uint64_t op2)
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
@ -1865,7 +1824,7 @@ void helper_##name(CPUPPCState *env, uint32_t opcode) \
} \
\
if (sfprf) { \
helper_compute_fprf(env, xt.fld); \
helper_compute_fprf_float64(env, xt.fld); \
} \
} \
putVSR(xT(opcode), &xt, env); \
@ -1881,6 +1840,41 @@ 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)
void helper_xsaddqp(CPUPPCState *env, uint32_t opcode)
{
ppc_vsr_t xt, xa, xb;
float_status tstat;
getVSR(rA(opcode) + 32, &xa, env);
getVSR(rB(opcode) + 32, &xb, env);
getVSR(rD(opcode) + 32, &xt, env);
helper_reset_fpstatus(env);
if (unlikely(Rc(opcode) != 0)) {
/* TODO: Support xsadddpo after round-to-odd is implemented */
abort();
}
tstat = env->fp_status;
set_float_exception_flags(0, &tstat);
xt.f128 = float128_add(xa.f128, xb.f128, &tstat);
env->fp_status.float_exception_flags |= tstat.float_exception_flags;
if (unlikely(tstat.float_exception_flags & float_flag_invalid)) {
if (float128_is_infinity(xa.f128) && float128_is_infinity(xb.f128)) {
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1);
} else if (float128_is_signaling_nan(xa.f128, &tstat) ||
float128_is_signaling_nan(xb.f128, &tstat)) {
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
}
}
helper_compute_fprf_float128(env, xt.f128);
putVSR(rD(opcode) + 32, &xt, env);
float_check_status(env);
}
/* VSX_MUL - VSX floating point multiply
* op - instruction mnemonic
* nels - number of elements (1, 2 or 4)
@ -1920,7 +1914,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \
} \
\
if (sfprf) { \
helper_compute_fprf(env, xt.fld); \
helper_compute_fprf_float64(env, xt.fld); \
} \
} \
\
@ -1933,6 +1927,41 @@ 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)
void helper_xsmulqp(CPUPPCState *env, uint32_t opcode)
{
ppc_vsr_t xt, xa, xb;
getVSR(rA(opcode) + 32, &xa, env);
getVSR(rB(opcode) + 32, &xb, env);
getVSR(rD(opcode) + 32, &xt, env);
if (unlikely(Rc(opcode) != 0)) {
/* TODO: Support xsmulpo after round-to-odd is implemented */
abort();
}
helper_reset_fpstatus(env);
float_status tstat = env->fp_status;
set_float_exception_flags(0, &tstat);
xt.f128 = float128_mul(xa.f128, xb.f128, &tstat);
env->fp_status.float_exception_flags |= tstat.float_exception_flags;
if (unlikely(tstat.float_exception_flags & float_flag_invalid)) {
if ((float128_is_infinity(xa.f128) && float128_is_zero(xb.f128)) ||
(float128_is_infinity(xb.f128) && float128_is_zero(xa.f128))) {
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1);
} else if (float128_is_signaling_nan(xa.f128, &tstat) ||
float128_is_signaling_nan(xb.f128, &tstat)) {
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
}
}
helper_compute_fprf_float128(env, xt.f128);
putVSR(rD(opcode) + 32, &xt, env);
float_check_status(env);
}
/* VSX_DIV - VSX floating point divide
* op - instruction mnemonic
* nels - number of elements (1, 2 or 4)
@ -1974,7 +2003,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \
} \
\
if (sfprf) { \
helper_compute_fprf(env, xt.fld); \
helper_compute_fprf_float64(env, xt.fld); \
} \
} \
\
@ -1987,6 +2016,42 @@ 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)
void helper_xsdivqp(CPUPPCState *env, uint32_t opcode)
{
ppc_vsr_t xt, xa, xb;
getVSR(rA(opcode) + 32, &xa, env);
getVSR(rB(opcode) + 32, &xb, env);
getVSR(rD(opcode) + 32, &xt, env);
if (unlikely(Rc(opcode) != 0)) {
/* TODO: Support xsdivqpo after round-to-odd is implemented */
abort();
}
helper_reset_fpstatus(env);
float_status tstat = env->fp_status;
set_float_exception_flags(0, &tstat);
xt.f128 = float128_div(xa.f128, xb.f128, &tstat);
env->fp_status.float_exception_flags |= tstat.float_exception_flags;
if (unlikely(tstat.float_exception_flags & float_flag_invalid)) {
if (float128_is_infinity(xa.f128) && float128_is_infinity(xb.f128)) {
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI, 1);
} else if (float128_is_zero(xa.f128) &&
float128_is_zero(xb.f128)) {
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ, 1);
} else if (float128_is_signaling_nan(xa.f128, &tstat) ||
float128_is_signaling_nan(xb.f128, &tstat)) {
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
}
}
helper_compute_fprf_float128(env, xt.f128);
putVSR(rD(opcode) + 32, &xt, env);
float_check_status(env);
}
/* VSX_RE - VSX floating point reciprocal estimate
* op - instruction mnemonic
* nels - number of elements (1, 2 or 4)
@ -2015,7 +2080,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \
} \
\
if (sfprf) { \
helper_compute_fprf(env, xt.fld); \
helper_compute_fprf_float64(env, xt.fld); \
} \
} \
\
@ -2064,7 +2129,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \
} \
\
if (sfprf) { \
helper_compute_fprf(env, xt.fld); \
helper_compute_fprf_float64(env, xt.fld); \
} \
} \
\
@ -2114,7 +2179,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \
} \
\
if (sfprf) { \
helper_compute_fprf(env, xt.fld); \
helper_compute_fprf_float64(env, xt.fld); \
} \
} \
\
@ -2314,7 +2379,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \
} \
\
if (sfprf) { \
helper_compute_fprf(env, xt_out.fld); \
helper_compute_fprf_float64(env, xt_out.fld); \
} \
} \
putVSR(xT(opcode), &xt_out, env); \
@ -2414,34 +2479,108 @@ VSX_SCALAR_CMP_DP(xscmpgedp, le, 1, 1)
VSX_SCALAR_CMP_DP(xscmpgtdp, lt, 1, 1)
VSX_SCALAR_CMP_DP(xscmpnedp, eq, 0, 0)
void helper_xscmpexpdp(CPUPPCState *env, uint32_t opcode)
{
ppc_vsr_t xa, xb;
int64_t exp_a, exp_b;
uint32_t cc;
getVSR(xA(opcode), &xa, env);
getVSR(xB(opcode), &xb, env);
exp_a = extract64(xa.VsrD(0), 52, 11);
exp_b = extract64(xb.VsrD(0), 52, 11);
if (unlikely(float64_is_any_nan(xa.VsrD(0)) ||
float64_is_any_nan(xb.VsrD(0)))) {
cc = CRF_SO;
} else {
if (exp_a < exp_b) {
cc = CRF_LT;
} else if (exp_a > exp_b) {
cc = CRF_GT;
} else {
cc = CRF_EQ;
}
}
env->fpscr &= ~(0x0F << FPSCR_FPRF);
env->fpscr |= cc << FPSCR_FPRF;
env->crf[BF(opcode)] = cc;
helper_float_check_status(env);
}
void helper_xscmpexpqp(CPUPPCState *env, uint32_t opcode)
{
ppc_vsr_t xa, xb;
int64_t exp_a, exp_b;
uint32_t cc;
getVSR(rA(opcode) + 32, &xa, env);
getVSR(rB(opcode) + 32, &xb, env);
exp_a = extract64(xa.VsrD(0), 48, 15);
exp_b = extract64(xb.VsrD(0), 48, 15);
if (unlikely(float128_is_any_nan(xa.f128) ||
float128_is_any_nan(xb.f128))) {
cc = CRF_SO;
} else {
if (exp_a < exp_b) {
cc = CRF_LT;
} else if (exp_a > exp_b) {
cc = CRF_GT;
} else {
cc = CRF_EQ;
}
}
env->fpscr &= ~(0x0F << FPSCR_FPRF);
env->fpscr |= cc << FPSCR_FPRF;
env->crf[BF(opcode)] = cc;
helper_float_check_status(env);
}
#define VSX_SCALAR_CMP(op, ordered) \
void helper_##op(CPUPPCState *env, uint32_t opcode) \
{ \
ppc_vsr_t xa, xb; \
uint32_t cc = 0; \
bool vxsnan_flag = false, vxvc_flag = false; \
\
helper_reset_fpstatus(env); \
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 (float64_is_signaling_nan(xa.VsrD(0), &env->fp_status) || \
float64_is_signaling_nan(xb.VsrD(0), &env->fp_status)) { \
vxsnan_flag = true; \
cc = CRF_SO; \
if (fpscr_ve == 0 && ordered) { \
vxvc_flag = true; \
} \
} else if (float64_is_quiet_nan(xa.VsrD(0), &env->fp_status) || \
float64_is_quiet_nan(xb.VsrD(0), &env->fp_status)) { \
cc = CRF_SO; \
if (ordered) { \
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \
vxvc_flag = true; \
} \
cc = 1; \
} \
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); \
} \
\
if (float64_lt(xa.VsrD(0), xb.VsrD(0), &env->fp_status)) { \
cc |= CRF_LT; \
} else if (!float64_le(xa.VsrD(0), xb.VsrD(0), &env->fp_status)) { \
cc |= CRF_GT; \
} 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; \
} \
cc |= CRF_EQ; \
} \
\
env->fpscr &= ~(0x0F << FPSCR_FPRF); \
@ -2454,6 +2593,56 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \
VSX_SCALAR_CMP(xscmpodp, 1)
VSX_SCALAR_CMP(xscmpudp, 0)
#define VSX_SCALAR_CMPQ(op, ordered) \
void helper_##op(CPUPPCState *env, uint32_t opcode) \
{ \
ppc_vsr_t xa, xb; \
uint32_t cc = 0; \
bool vxsnan_flag = false, vxvc_flag = false; \
\
helper_reset_fpstatus(env); \
getVSR(rA(opcode) + 32, &xa, env); \
getVSR(rB(opcode) + 32, &xb, env); \
\
if (float128_is_signaling_nan(xa.f128, &env->fp_status) || \
float128_is_signaling_nan(xb.f128, &env->fp_status)) { \
vxsnan_flag = true; \
cc = CRF_SO; \
if (fpscr_ve == 0 && ordered) { \
vxvc_flag = true; \
} \
} else if (float128_is_quiet_nan(xa.f128, &env->fp_status) || \
float128_is_quiet_nan(xb.f128, &env->fp_status)) { \
cc = CRF_SO; \
if (ordered) { \
vxvc_flag = true; \
} \
} \
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); \
} \
\
if (float128_lt(xa.f128, xb.f128, &env->fp_status)) { \
cc |= CRF_LT; \
} else if (!float128_le(xa.f128, xb.f128, &env->fp_status)) { \
cc |= CRF_GT; \
} else { \
cc |= CRF_EQ; \
} \
\
env->fpscr &= ~(0x0F << FPSCR_FPRF); \
env->fpscr |= cc << FPSCR_FPRF; \
env->crf[BF(opcode)] = cc; \
\
float_check_status(env); \
}
VSX_SCALAR_CMPQ(xscmpoqp, 1)
VSX_SCALAR_CMPQ(xscmpuqp, 0)
/* VSX_MAX_MIN - VSX floating point maximum/minimum
* name - instruction mnemonic
* op - operation (max or min)
@ -2576,8 +2765,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \
xt.tfld = ttp##_snan_to_qnan(xt.tfld); \
} \
if (sfprf) { \
helper_compute_fprf(env, ttp##_to_float64(xt.tfld, \
&env->fp_status)); \
helper_compute_fprf_##ttp(env, xt.tfld); \
} \
} \
\
@ -2590,6 +2778,110 @@ 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)
/* VSX_CVT_FP_TO_FP_VECTOR - 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_VECTOR(op, nels, stp, ttp, sfld, tfld, sfprf) \
void helper_##op(CPUPPCState *env, uint32_t opcode) \
{ \
ppc_vsr_t xt, xb; \
int i; \
\
getVSR(rB(opcode) + 32, &xb, env); \
getVSR(rD(opcode) + 32, &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_##ttp(env, xt.tfld); \
} \
} \
\
putVSR(rD(opcode) + 32, &xt, env); \
float_check_status(env); \
}
VSX_CVT_FP_TO_FP_VECTOR(xscvdpqp, 1, float64, float128, VsrD(0), f128, 1)
/* VSX_CVT_FP_TO_FP_HP - VSX floating point/floating point conversion
* involving one half precision value
* op - instruction mnemonic
* nels - number of elements (1, 2 or 4)
* stp - source type
* ttp - target type
* sfld - source vsr_t field
* tfld - target vsr_t field
* sfprf - set FPRF
*/
#define VSX_CVT_FP_TO_FP_HP(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); \
memset(&xt, 0, sizeof(xt)); \
\
for (i = 0; i < nels; i++) { \
xt.tfld = stp##_to_##ttp(xb.sfld, 1, &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_##ttp(env, xt.tfld); \
} \
} \
\
putVSR(xT(opcode), &xt, env); \
float_check_status(env); \
}
VSX_CVT_FP_TO_FP_HP(xscvdphp, 1, float64, float16, VsrD(0), VsrH(3), 1)
VSX_CVT_FP_TO_FP_HP(xscvhpdp, 1, float16, float64, VsrH(3), VsrD(0), 1)
VSX_CVT_FP_TO_FP_HP(xvcvsphp, 4, float32, float16, VsrW(i), VsrH(2 * i + 1), 0)
VSX_CVT_FP_TO_FP_HP(xvcvhpsp, 4, float16, float32, VsrH(2 * i + 1), VsrW(i), 0)
/*
* xscvqpdp isn't using VSX_CVT_FP_TO_FP() because xscvqpdpo will be
* added to this later.
*/
void helper_xscvqpdp(CPUPPCState *env, uint32_t opcode)
{
ppc_vsr_t xt, xb;
getVSR(rB(opcode) + 32, &xb, env);
memset(&xt, 0, sizeof(xt));
if (unlikely(Rc(opcode) != 0)) {
/* TODO: Support xscvqpdpo after round-to-odd is implemented */
abort();
}
xt.VsrD(0) = float128_to_float64(xb.f128, &env->fp_status);
if (unlikely(float128_is_signaling_nan(xb.f128,
&env->fp_status))) {
float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0);
xt.VsrD(0) = float64_snan_to_qnan(xt.VsrD(0));
}
helper_compute_fprf_float64(env, xt.VsrD(0));
putVSR(rD(opcode) + 32, &xt, env);
float_check_status(env);
}
uint64_t helper_xscvdpspn(CPUPPCState *env, uint64_t xb)
{
float_status tstat = env->fp_status;
@ -2662,6 +2954,46 @@ 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_FP_TO_INT_VECTOR - VSX floating point to integer conversion
* op - instruction mnemonic
* 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_VECTOR(op, stp, ttp, sfld, tfld, rnan) \
void helper_##op(CPUPPCState *env, uint32_t opcode) \
{ \
ppc_vsr_t xt, xb; \
\
getVSR(rB(opcode) + 32, &xb, env); \
memset(&xt, 0, sizeof(xt)); \
\
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(rD(opcode) + 32, &xt, env); \
float_check_status(env); \
}
VSX_CVT_FP_TO_INT_VECTOR(xscvqpsdz, float128, int64, f128, VsrD(0), \
0x8000000000000000ULL)
VSX_CVT_FP_TO_INT_VECTOR(xscvqpswz, float128, int32, f128, VsrD(0), \
0xffffffff80000000ULL)
/* VSX_CVT_INT_TO_FP - VSX integer to floating point conversion
* op - instruction mnemonic
* nels - number of elements (1, 2 or 4)
@ -2687,7 +3019,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \
xt.tfld = helper_frsp(env, xt.tfld); \
} \
if (sfprf) { \
helper_compute_fprf(env, xt.tfld); \
helper_compute_fprf_float64(env, xt.tfld); \
} \
} \
\
@ -2708,6 +3040,31 @@ 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)
/* VSX_CVT_INT_TO_FP_VECTOR - VSX integer to floating point conversion
* op - instruction mnemonic
* stp - source type (int32, uint32, int64 or uint64)
* ttp - target type (float32 or float64)
* sfld - source vsr_t field
* tfld - target vsr_t field
*/
#define VSX_CVT_INT_TO_FP_VECTOR(op, stp, ttp, sfld, tfld) \
void helper_##op(CPUPPCState *env, uint32_t opcode) \
{ \
ppc_vsr_t xt, xb; \
\
getVSR(rB(opcode) + 32, &xb, env); \
getVSR(rD(opcode) + 32, &xt, env); \
\
xt.tfld = stp##_to_##ttp(xb.sfld, &env->fp_status); \
helper_compute_fprf_##ttp(env, xt.tfld); \
\
putVSR(xT(opcode) + 32, &xt, env); \
float_check_status(env); \
}
VSX_CVT_INT_TO_FP_VECTOR(xscvsdqp, int64, float128, VsrD(0), f128)
VSX_CVT_INT_TO_FP_VECTOR(xscvudqp, uint64, float128, VsrD(0), f128)
/* For "use current rounding mode", define a value that will not be one of
* the existing rounding model enums.
*/
@ -2743,7 +3100,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \
xt.fld = tp##_round_to_int(xb.fld, &env->fp_status); \
} \
if (sfprf) { \
helper_compute_fprf(env, xt.fld); \
helper_compute_fprf_float64(env, xt.fld); \
} \
} \
\
@ -2783,7 +3140,140 @@ uint64_t helper_xsrsp(CPUPPCState *env, uint64_t xb)
uint64_t xt = helper_frsp(env, xb);
helper_compute_fprf(env, xt);
helper_compute_fprf_float64(env, xt);
float_check_status(env);
return xt;
}
#define VSX_XXPERM(op, indexed) \
void helper_##op(CPUPPCState *env, uint32_t opcode) \
{ \
ppc_vsr_t xt, xa, pcv, xto; \
int i, idx; \
\
getVSR(xA(opcode), &xa, env); \
getVSR(xT(opcode), &xt, env); \
getVSR(xB(opcode), &pcv, env); \
\
for (i = 0; i < 16; i++) { \
idx = pcv.VsrB(i) & 0x1F; \
if (indexed) { \
idx = 31 - idx; \
} \
xto.VsrB(i) = (idx <= 15) ? xa.VsrB(idx) : xt.VsrB(idx - 16); \
} \
putVSR(xT(opcode), &xto, env); \
}
VSX_XXPERM(xxperm, 0)
VSX_XXPERM(xxpermr, 1)
void helper_xvxsigsp(CPUPPCState *env, uint32_t opcode)
{
ppc_vsr_t xt, xb;
uint32_t exp, i, fraction;
getVSR(xB(opcode), &xb, env);
memset(&xt, 0, sizeof(xt));
for (i = 0; i < 4; i++) {
exp = (xb.VsrW(i) >> 23) & 0xFF;
fraction = xb.VsrW(i) & 0x7FFFFF;
if (exp != 0 && exp != 255) {
xt.VsrW(i) = fraction | 0x00800000;
} else {
xt.VsrW(i) = fraction;
}
}
putVSR(xT(opcode), &xt, env);
}
/* VSX_TEST_DC - VSX floating point test data class
* op - instruction mnemonic
* nels - number of elements (1, 2 or 4)
* xbn - VSR register number
* tp - type (float32 or float64)
* fld - vsr_t field (VsrD(*) or VsrW(*))
* tfld - target vsr_t field (VsrD(*) or VsrW(*))
* fld_max - target field max
* scrf - set result in CR and FPCC
*/
#define VSX_TEST_DC(op, nels, xbn, tp, fld, tfld, fld_max, scrf) \
void helper_##op(CPUPPCState *env, uint32_t opcode) \
{ \
ppc_vsr_t xt, xb; \
uint32_t i, sign, dcmx; \
uint32_t cc, match = 0; \
\
getVSR(xbn, &xb, env); \
if (!scrf) { \
memset(&xt, 0, sizeof(xt)); \
dcmx = DCMX_XV(opcode); \
} else { \
dcmx = DCMX(opcode); \
} \
\
for (i = 0; i < nels; i++) { \
sign = tp##_is_neg(xb.fld); \
if (tp##_is_any_nan(xb.fld)) { \
match = extract32(dcmx, 6, 1); \
} else if (tp##_is_infinity(xb.fld)) { \
match = extract32(dcmx, 4 + !sign, 1); \
} else if (tp##_is_zero(xb.fld)) { \
match = extract32(dcmx, 2 + !sign, 1); \
} else if (tp##_is_zero_or_denormal(xb.fld)) { \
match = extract32(dcmx, 0 + !sign, 1); \
} \
\
if (scrf) { \
cc = sign << CRF_LT_BIT | match << CRF_EQ_BIT; \
env->fpscr &= ~(0x0F << FPSCR_FPRF); \
env->fpscr |= cc << FPSCR_FPRF; \
env->crf[BF(opcode)] = cc; \
} else { \
xt.tfld = match ? fld_max : 0; \
} \
match = 0; \
} \
if (!scrf) { \
putVSR(xT(opcode), &xt, env); \
} \
}
VSX_TEST_DC(xvtstdcdp, 2, xB(opcode), float64, VsrD(i), VsrD(i), UINT64_MAX, 0)
VSX_TEST_DC(xvtstdcsp, 4, xB(opcode), float32, VsrW(i), VsrW(i), UINT32_MAX, 0)
VSX_TEST_DC(xststdcdp, 1, xB(opcode), float64, VsrD(0), VsrD(0), 0, 1)
VSX_TEST_DC(xststdcqp, 1, (rB(opcode) + 32), float128, f128, VsrD(0), 0, 1)
void helper_xststdcsp(CPUPPCState *env, uint32_t opcode)
{
ppc_vsr_t xb;
uint32_t dcmx, sign, exp;
uint32_t cc, match = 0, not_sp = 0;
getVSR(xB(opcode), &xb, env);
dcmx = DCMX(opcode);
exp = (xb.VsrD(0) >> 52) & 0x7FF;
sign = float64_is_neg(xb.VsrD(0));
if (float64_is_any_nan(xb.VsrD(0))) {
match = extract32(dcmx, 6, 1);
} else if (float64_is_infinity(xb.VsrD(0))) {
match = extract32(dcmx, 4 + !sign, 1);
} else if (float64_is_zero(xb.VsrD(0))) {
match = extract32(dcmx, 2 + !sign, 1);
} else if (float64_is_zero_or_denormal(xb.VsrD(0)) ||
(exp > 0 && exp < 0x381)) {
match = extract32(dcmx, 0 + !sign, 1);
}
not_sp = !float64_eq(xb.VsrD(0),
float32_to_float64(
float64_to_float32(xb.VsrD(0), &env->fp_status),
&env->fp_status), &env->fp_status);
cc = sign << CRF_LT_BIT | match << CRF_EQ_BIT | not_sp << CRF_SO_BIT;
env->fpscr &= ~(0x0F << FPSCR_FPRF);
env->fpscr |= cc << FPSCR_FPRF;
env->crf[BF(opcode)] = cc;
}

View File

@ -56,7 +56,7 @@ 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_2(compute_fprf_float64, 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)
@ -312,6 +312,12 @@ 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)
#if defined(TARGET_PPC64)
DEF_HELPER_4(lxvl, void, env, tl, tl, tl)
DEF_HELPER_4(lxvll, void, env, tl, tl, tl)
DEF_HELPER_4(stxvl, void, env, tl, tl, tl)
DEF_HELPER_4(stxvll, void, env, tl, tl, tl)
#endif
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)
@ -361,6 +367,12 @@ 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(vextublx, tl, tl, avr)
DEF_HELPER_2(vextuhlx, tl, tl, avr)
DEF_HELPER_2(vextuwlx, tl, tl, avr)
DEF_HELPER_2(vextubrx, tl, tl, avr)
DEF_HELPER_2(vextuhrx, tl, tl, avr)
DEF_HELPER_2(vextuwrx, tl, tl, avr)
DEF_HELPER_2(vsbox, void, avr, avr)
DEF_HELPER_3(vcipher, void, avr, avr, avr)
@ -377,11 +389,23 @@ 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_3(bcdcfsq, i32, avr, avr, i32)
DEF_HELPER_3(bcdctsq, i32, avr, avr, i32)
DEF_HELPER_4(bcdcpsgn, i32, avr, avr, avr, i32)
DEF_HELPER_3(bcdsetsgn, i32, avr, avr, i32)
DEF_HELPER_4(bcds, i32, avr, avr, avr, i32)
DEF_HELPER_4(bcdus, i32, avr, avr, avr, i32)
DEF_HELPER_4(bcdsr, i32, avr, avr, avr, i32)
DEF_HELPER_4(bcdtrunc, i32, avr, avr, avr, i32)
DEF_HELPER_4(bcdutrunc, i32, avr, avr, avr, i32)
DEF_HELPER_2(xsadddp, void, env, i32)
DEF_HELPER_2(xsaddqp, void, env, i32)
DEF_HELPER_2(xssubdp, void, env, i32)
DEF_HELPER_2(xsmuldp, void, env, i32)
DEF_HELPER_2(xsmulqp, void, env, i32)
DEF_HELPER_2(xsdivdp, void, env, i32)
DEF_HELPER_2(xsdivqp, void, env, i32)
DEF_HELPER_2(xsredp, void, env, i32)
DEF_HELPER_2(xssqrtdp, void, env, i32)
DEF_HELPER_2(xsrsqrtedp, void, env, i32)
@ -399,12 +423,23 @@ 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(xscmpexpdp, void, env, i32)
DEF_HELPER_2(xscmpexpqp, void, env, i32)
DEF_HELPER_2(xscmpodp, void, env, i32)
DEF_HELPER_2(xscmpudp, void, env, i32)
DEF_HELPER_2(xscmpoqp, void, env, i32)
DEF_HELPER_2(xscmpuqp, void, env, i32)
DEF_HELPER_2(xsmaxdp, void, env, i32)
DEF_HELPER_2(xsmindp, void, env, i32)
DEF_HELPER_2(xscvdphp, void, env, i32)
DEF_HELPER_2(xscvdpqp, void, env, i32)
DEF_HELPER_2(xscvdpsp, void, env, i32)
DEF_HELPER_2(xscvdpspn, i64, env, i64)
DEF_HELPER_2(xscvqpdp, void, env, i32)
DEF_HELPER_2(xscvqpsdz, void, env, i32)
DEF_HELPER_2(xscvqpswz, void, env, i32)
DEF_HELPER_2(xscvhpdp, void, env, i32)
DEF_HELPER_2(xscvsdqp, void, env, i32)
DEF_HELPER_2(xscvspdp, void, env, i32)
DEF_HELPER_2(xscvspdpn, i64, env, i64)
DEF_HELPER_2(xscvdpsxds, void, env, i32)
@ -414,7 +449,11 @@ 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(xscvudqp, void, env, i32)
DEF_HELPER_2(xscvuxddp, void, env, i32)
DEF_HELPER_2(xststdcsp, void, env, i32)
DEF_HELPER_2(xststdcdp, void, env, i32)
DEF_HELPER_2(xststdcqp, void, env, i32)
DEF_HELPER_2(xsrdpi, void, env, i32)
DEF_HELPER_2(xsrdpic, void, env, i32)
DEF_HELPER_2(xsrdpim, void, env, i32)
@ -500,6 +539,8 @@ 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(xvcvsphp, void, env, i32)
DEF_HELPER_2(xvcvhpsp, void, env, i32)
DEF_HELPER_2(xvcvspsxds, void, env, i32)
DEF_HELPER_2(xvcvspsxws, void, env, i32)
DEF_HELPER_2(xvcvspuxds, void, env, i32)
@ -508,11 +549,18 @@ 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(xvtstdcsp, void, env, i32)
DEF_HELPER_2(xvtstdcdp, 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(xxperm, void, env, i32)
DEF_HELPER_2(xxpermr, void, env, i32)
DEF_HELPER_4(xxextractuw, void, env, tl, tl, i32)
DEF_HELPER_4(xxinsertw, void, env, tl, tl, i32)
DEF_HELPER_2(xvxsigsp, void, env, i32)
DEF_HELPER_2(efscfsi, i32, env, i32)
DEF_HELPER_2(efscfui, i32, env, i32)

View File

@ -157,7 +157,7 @@ uint64_t helper_divde(CPUPPCState *env, uint64_t rau, uint64_t rbu, uint32_t oe)
uint32_t helper_cmpeqb(target_ulong ra, target_ulong rb)
{
return hasvalue(rb, ra) ? 1 << CRF_GT : 0;
return hasvalue(rb, ra) ? CRF_GT : 0;
}
#undef pattern
@ -1773,6 +1773,42 @@ void helper_vlogefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b)
}
}
#if defined(HOST_WORDS_BIGENDIAN)
#define VEXTU_X_DO(name, size, left) \
target_ulong glue(helper_, name)(target_ulong a, ppc_avr_t *b) \
{ \
int index; \
if (left) { \
index = (a & 0xf) * 8; \
} else { \
index = ((15 - (a & 0xf) + 1) * 8) - size; \
} \
return int128_getlo(int128_rshift(b->s128, index)) & \
MAKE_64BIT_MASK(0, size); \
}
#else
#define VEXTU_X_DO(name, size, left) \
target_ulong glue(helper_, name)(target_ulong a, ppc_avr_t *b) \
{ \
int index; \
if (left) { \
index = ((15 - (a & 0xf) + 1) * 8) - size; \
} else { \
index = (a & 0xf) * 8; \
} \
return int128_getlo(int128_rshift(b->s128, index)) & \
MAKE_64BIT_MASK(0, size); \
}
#endif
VEXTU_X_DO(vextublx, 8, 1)
VEXTU_X_DO(vextuhlx, 16, 1)
VEXTU_X_DO(vextuwlx, 32, 1)
VEXTU_X_DO(vextubrx, 8, 0)
VEXTU_X_DO(vextuhrx, 16, 0)
VEXTU_X_DO(vextuwrx, 32, 0)
#undef VEXTU_X_DO
/* 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. */
@ -1965,6 +2001,57 @@ VEXTRACT(uw, u32)
VEXTRACT(d, u64)
#undef VEXTRACT
void helper_xxextractuw(CPUPPCState *env, target_ulong xtn,
target_ulong xbn, uint32_t index)
{
ppc_vsr_t xt, xb;
size_t es = sizeof(uint32_t);
uint32_t ext_index;
int i;
getVSR(xbn, &xb, env);
memset(&xt, 0, sizeof(xt));
#if defined(HOST_WORDS_BIGENDIAN)
ext_index = index;
for (i = 0; i < es; i++, ext_index++) {
xt.u8[8 - es + i] = xb.u8[ext_index % 16];
}
#else
ext_index = 15 - index;
for (i = es - 1; i >= 0; i--, ext_index--) {
xt.u8[8 + i] = xb.u8[ext_index % 16];
}
#endif
putVSR(xtn, &xt, env);
}
void helper_xxinsertw(CPUPPCState *env, target_ulong xtn,
target_ulong xbn, uint32_t index)
{
ppc_vsr_t xt, xb;
size_t es = sizeof(uint32_t);
int ins_index, i = 0;
getVSR(xbn, &xb, env);
getVSR(xtn, &xt, env);
#if defined(HOST_WORDS_BIGENDIAN)
ins_index = index;
for (i = 0; i < es && ins_index < 16; i++, ins_index++) {
xt.u8[ins_index] = xb.u8[8 - es + i];
}
#else
ins_index = 15 - index;
for (i = es - 1; i >= 0 && ins_index >= 0; i--, ins_index--) {
xt.u8[ins_index] = xb.u8[8 + i];
}
#endif
putVSR(xtn, &xt, env);
}
#define VEXT_SIGNED(name, element, mask, cast, recast) \
void helper_##name(ppc_avr_t *r, ppc_avr_t *b) \
{ \
@ -2464,9 +2551,9 @@ void helper_vsubecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
#define NATIONAL_NEG 0x2D
#if defined(HOST_WORDS_BIGENDIAN)
#define BCD_DIG_BYTE(n) (15 - (n/2))
#define BCD_DIG_BYTE(n) (15 - ((n) / 2))
#else
#define BCD_DIG_BYTE(n) (n/2)
#define BCD_DIG_BYTE(n) ((n) / 2)
#endif
static int bcd_get_sgn(ppc_avr_t *bcd)
@ -2528,12 +2615,30 @@ static void bcd_put_digit(ppc_avr_t *bcd, uint8_t digit, int n)
}
}
static bool bcd_is_valid(ppc_avr_t *bcd)
{
int i;
int invalid = 0;
if (bcd_get_sgn(bcd) == 0) {
return false;
}
for (i = 1; i < 32; i++) {
bcd_get_digit(bcd, i, &invalid);
if (unlikely(invalid)) {
return false;
}
}
return true;
}
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;
return CRF_EQ;
} else {
return (bcd_get_sgn(bcd) == 1) ? 1 << CRF_GT : 1 << CRF_LT;
return (bcd_get_sgn(bcd) == 1) ? CRF_GT : CRF_LT;
}
}
@ -2645,25 +2750,25 @@ uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
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;
cr = (sgna > 0) ? CRF_GT : 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;
cr = (sgna > 0) ? CRF_GT : 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;
cr = (sgnb > 0) ? CRF_GT : CRF_LT;
}
}
if (unlikely(invalid)) {
result.u64[HI_IDX] = result.u64[LO_IDX] = -1;
cr = 1 << CRF_SO;
cr = CRF_SO;
} else if (overflow) {
cr |= 1 << CRF_SO;
cr |= CRF_SO;
} else if (zero) {
cr = 1 << CRF_EQ;
cr = CRF_EQ;
}
*r = result;
@ -2713,7 +2818,7 @@ uint32_t helper_bcdcfn(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
cr = bcd_cmp_zero(&ret);
if (unlikely(invalid)) {
cr = 1 << CRF_SO;
cr = CRF_SO;
}
*r = ret;
@ -2743,11 +2848,11 @@ uint32_t helper_bcdctn(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
cr = bcd_cmp_zero(b);
if (ox_flag) {
cr |= 1 << CRF_SO;
cr |= CRF_SO;
}
if (unlikely(invalid)) {
cr = 1 << CRF_SO;
cr = CRF_SO;
}
*r = ret;
@ -2771,7 +2876,7 @@ uint32_t helper_bcdcfz(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
}
for (i = 0; i < 16; i++) {
zone_digit = (i * 2) ? b->u8[BCD_DIG_BYTE(i * 2)] >> 4 : zone_lead;
zone_digit = i ? 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;
@ -2791,7 +2896,7 @@ uint32_t helper_bcdcfz(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
cr = bcd_cmp_zero(&ret);
if (unlikely(invalid)) {
cr = 1 << CRF_SO;
cr = CRF_SO;
}
*r = ret;
@ -2830,11 +2935,11 @@ uint32_t helper_bcdctz(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
cr = bcd_cmp_zero(b);
if (ox_flag) {
cr |= 1 << CRF_SO;
cr |= CRF_SO;
}
if (unlikely(invalid)) {
cr = 1 << CRF_SO;
cr = CRF_SO;
}
*r = ret;
@ -2842,6 +2947,338 @@ uint32_t helper_bcdctz(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
return cr;
}
uint32_t helper_bcdcfsq(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
{
int i;
int cr = 0;
uint64_t lo_value;
uint64_t hi_value;
ppc_avr_t ret = { .u64 = { 0, 0 } };
if (b->s64[HI_IDX] < 0) {
lo_value = -b->s64[LO_IDX];
hi_value = ~b->u64[HI_IDX] + !lo_value;
bcd_put_digit(&ret, 0xD, 0);
} else {
lo_value = b->u64[LO_IDX];
hi_value = b->u64[HI_IDX];
bcd_put_digit(&ret, bcd_preferred_sgn(0, ps), 0);
}
if (divu128(&lo_value, &hi_value, 1000000000000000ULL) ||
lo_value > 9999999999999999ULL) {
cr = CRF_SO;
}
for (i = 1; i < 16; hi_value /= 10, i++) {
bcd_put_digit(&ret, hi_value % 10, i);
}
for (; i < 32; lo_value /= 10, i++) {
bcd_put_digit(&ret, lo_value % 10, i);
}
cr |= bcd_cmp_zero(&ret);
*r = ret;
return cr;
}
uint32_t helper_bcdctsq(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
{
uint8_t i;
int cr;
uint64_t carry;
uint64_t unused;
uint64_t lo_value;
uint64_t hi_value = 0;
int sgnb = bcd_get_sgn(b);
int invalid = (sgnb == 0);
lo_value = bcd_get_digit(b, 31, &invalid);
for (i = 30; i > 0; i--) {
mulu64(&lo_value, &carry, lo_value, 10ULL);
mulu64(&hi_value, &unused, hi_value, 10ULL);
lo_value += bcd_get_digit(b, i, &invalid);
hi_value += carry;
if (unlikely(invalid)) {
break;
}
}
if (sgnb == -1) {
r->s64[LO_IDX] = -lo_value;
r->s64[HI_IDX] = ~hi_value + !r->s64[LO_IDX];
} else {
r->s64[LO_IDX] = lo_value;
r->s64[HI_IDX] = hi_value;
}
cr = bcd_cmp_zero(b);
if (unlikely(invalid)) {
cr = CRF_SO;
}
return cr;
}
uint32_t helper_bcdcpsgn(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
{
int i;
int invalid = 0;
if (bcd_get_sgn(a) == 0 || bcd_get_sgn(b) == 0) {
return CRF_SO;
}
*r = *a;
bcd_put_digit(r, b->u8[BCD_DIG_BYTE(0)] & 0xF, 0);
for (i = 1; i < 32; i++) {
bcd_get_digit(a, i, &invalid);
bcd_get_digit(b, i, &invalid);
if (unlikely(invalid)) {
return CRF_SO;
}
}
return bcd_cmp_zero(r);
}
uint32_t helper_bcdsetsgn(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps)
{
int sgnb = bcd_get_sgn(b);
*r = *b;
bcd_put_digit(r, bcd_preferred_sgn(sgnb, ps), 0);
if (bcd_is_valid(b) == false) {
return CRF_SO;
}
return bcd_cmp_zero(r);
}
uint32_t helper_bcds(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
{
int cr;
#if defined(HOST_WORDS_BIGENDIAN)
int i = a->s8[7];
#else
int i = a->s8[8];
#endif
bool ox_flag = false;
int sgnb = bcd_get_sgn(b);
ppc_avr_t ret = *b;
ret.u64[LO_IDX] &= ~0xf;
if (bcd_is_valid(b) == false) {
return CRF_SO;
}
if (unlikely(i > 31)) {
i = 31;
} else if (unlikely(i < -31)) {
i = -31;
}
if (i > 0) {
ulshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], i * 4, &ox_flag);
} else {
urshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], -i * 4);
}
bcd_put_digit(&ret, bcd_preferred_sgn(sgnb, ps), 0);
*r = ret;
cr = bcd_cmp_zero(r);
if (ox_flag) {
cr |= CRF_SO;
}
return cr;
}
uint32_t helper_bcdus(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
{
int cr;
int i;
int invalid = 0;
bool ox_flag = false;
ppc_avr_t ret = *b;
for (i = 0; i < 32; i++) {
bcd_get_digit(b, i, &invalid);
if (unlikely(invalid)) {
return CRF_SO;
}
}
#if defined(HOST_WORDS_BIGENDIAN)
i = a->s8[7];
#else
i = a->s8[8];
#endif
if (i >= 32) {
ox_flag = true;
ret.u64[LO_IDX] = ret.u64[HI_IDX] = 0;
} else if (i <= -32) {
ret.u64[LO_IDX] = ret.u64[HI_IDX] = 0;
} else if (i > 0) {
ulshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], i * 4, &ox_flag);
} else {
urshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], -i * 4);
}
*r = ret;
cr = bcd_cmp_zero(r);
if (ox_flag) {
cr |= CRF_SO;
}
return cr;
}
uint32_t helper_bcdsr(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
{
int cr;
int unused = 0;
int invalid = 0;
bool ox_flag = false;
int sgnb = bcd_get_sgn(b);
ppc_avr_t ret = *b;
ret.u64[LO_IDX] &= ~0xf;
#if defined(HOST_WORDS_BIGENDIAN)
int i = a->s8[7];
ppc_avr_t bcd_one = { .u64 = { 0, 0x10 } };
#else
int i = a->s8[8];
ppc_avr_t bcd_one = { .u64 = { 0x10, 0 } };
#endif
if (bcd_is_valid(b) == false) {
return CRF_SO;
}
if (unlikely(i > 31)) {
i = 31;
} else if (unlikely(i < -31)) {
i = -31;
}
if (i > 0) {
ulshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], i * 4, &ox_flag);
} else {
urshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], -i * 4);
if (bcd_get_digit(&ret, 0, &invalid) >= 5) {
bcd_add_mag(&ret, &ret, &bcd_one, &invalid, &unused);
}
}
bcd_put_digit(&ret, bcd_preferred_sgn(sgnb, ps), 0);
cr = bcd_cmp_zero(&ret);
if (ox_flag) {
cr |= CRF_SO;
}
*r = ret;
return cr;
}
uint32_t helper_bcdtrunc(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
{
uint64_t mask;
uint32_t ox_flag = 0;
#if defined(HOST_WORDS_BIGENDIAN)
int i = a->s16[3] + 1;
#else
int i = a->s16[4] + 1;
#endif
ppc_avr_t ret = *b;
if (bcd_is_valid(b) == false) {
return CRF_SO;
}
if (i > 16 && i < 32) {
mask = (uint64_t)-1 >> (128 - i * 4);
if (ret.u64[HI_IDX] & ~mask) {
ox_flag = CRF_SO;
}
ret.u64[HI_IDX] &= mask;
} else if (i >= 0 && i <= 16) {
mask = (uint64_t)-1 >> (64 - i * 4);
if (ret.u64[HI_IDX] || (ret.u64[LO_IDX] & ~mask)) {
ox_flag = CRF_SO;
}
ret.u64[LO_IDX] &= mask;
ret.u64[HI_IDX] = 0;
}
bcd_put_digit(&ret, bcd_preferred_sgn(bcd_get_sgn(b), ps), 0);
*r = ret;
return bcd_cmp_zero(&ret) | ox_flag;
}
uint32_t helper_bcdutrunc(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
{
int i;
uint64_t mask;
uint32_t ox_flag = 0;
int invalid = 0;
ppc_avr_t ret = *b;
for (i = 0; i < 32; i++) {
bcd_get_digit(b, i, &invalid);
if (unlikely(invalid)) {
return CRF_SO;
}
}
#if defined(HOST_WORDS_BIGENDIAN)
i = a->s16[3];
#else
i = a->s16[4];
#endif
if (i > 16 && i < 33) {
mask = (uint64_t)-1 >> (128 - i * 4);
if (ret.u64[HI_IDX] & ~mask) {
ox_flag = CRF_SO;
}
ret.u64[HI_IDX] &= mask;
} else if (i > 0 && i <= 16) {
mask = (uint64_t)-1 >> (64 - i * 4);
if (ret.u64[HI_IDX] || (ret.u64[LO_IDX] & ~mask)) {
ox_flag = CRF_SO;
}
ret.u64[LO_IDX] &= mask;
ret.u64[HI_IDX] = 0;
} else if (i == 0) {
if (ret.u64[HI_IDX] || ret.u64[LO_IDX]) {
ox_flag = CRF_SO;
}
ret.u64[HI_IDX] = ret.u64[LO_IDX] = 0;
}
*r = ret;
if (r->u64[HI_IDX] == 0 && r->u64[LO_IDX] == 0) {
return ox_flag | CRF_EQ;
}
return ox_flag | CRF_GT;
}
void helper_vsbox(ppc_avr_t *r, ppc_avr_t *a)
{
int i;

View File

@ -47,4 +47,206 @@ FUNC_MASK(MASK, target_ulong, 32, UINT32_MAX);
FUNC_MASK(mask_u32, uint32_t, 32, UINT32_MAX);
FUNC_MASK(mask_u64, uint64_t, 64, UINT64_MAX);
/*****************************************************************************/
/*** 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_SPLIT_3(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(BF, 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_SPLIT_3(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(DQxT, 3, 1, 21, 5);
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);
EXTRACT_HELPER(DCMX, 16, 7);
EXTRACT_HELPER_SPLIT_3(DCMX_XV, 5, 16, 0, 1, 2, 5, 1, 6, 6);
typedef union _ppc_vsr_t {
uint8_t u8[16];
uint16_t u16[8];
uint32_t u32[4];
uint64_t u64[2];
float32 f32[4];
float64 f64[2];
float128 f128;
Int128 s128;
} ppc_vsr_t;
#if defined(HOST_WORDS_BIGENDIAN)
#define VsrB(i) u8[i]
#define VsrH(i) u16[i]
#define VsrW(i) u32[i]
#define VsrD(i) u64[i]
#else
#define VsrB(i) u8[15 - (i)]
#define VsrH(i) u16[7 - (i)]
#define VsrW(i) u32[3 - (i)]
#define VsrD(i) u64[1 - (i)]
#endif
static inline 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 inline 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];
}
}
void helper_compute_fprf_float16(CPUPPCState *env, float16 arg);
void helper_compute_fprf_float32(CPUPPCState *env, float32 arg);
void helper_compute_fprf_float128(CPUPPCState *env, float128 arg);
#endif /* PPC_INTERNAL_H */

View File

@ -24,6 +24,7 @@
#include "qemu-common.h"
#include "qemu/error-report.h"
#include "cpu.h"
#include "cpu-models.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
#include "sysemu/hw_accel.h"
@ -2108,9 +2109,9 @@ void kvmppc_set_papr(PowerPCCPU *cpu)
cap_papr = 1;
}
int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version)
int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr)
{
return kvm_set_one_reg(CPU(cpu), KVM_REG_PPC_ARCH_COMPAT, &cpu_version);
return kvm_set_one_reg(CPU(cpu), KVM_REG_PPC_ARCH_COMPAT, &compat_pvr);
}
void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy)
@ -2412,6 +2413,7 @@ static int kvm_ppc_register_host_cpu_type(void)
};
PowerPCCPUClass *pvr_pcc;
DeviceClass *dc;
int i;
pvr_pcc = kvm_ppc_get_host_cpu_class();
if (pvr_pcc == NULL) {
@ -2420,13 +2422,6 @@ static int kvm_ppc_register_host_cpu_type(void)
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,
@ -2436,14 +2431,29 @@ static int kvm_ppc_register_host_cpu_type(void)
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
/*
* Update generic CPU family class alias (e.g. on a POWER8NVL host,
* we want "POWER8" to be a "family" alias that points to the current
* host CPU type, too)
*/
dc = DEVICE_CLASS(ppc_cpu_get_family_class(pvr_pcc));
for (i = 0; ppc_cpu_aliases[i].alias != NULL; i++) {
if (strcmp(ppc_cpu_aliases[i].alias, dc->desc) == 0) {
ObjectClass *oc = OBJECT_CLASS(pvr_pcc);
char *suffix;
ppc_cpu_aliases[i].model = g_strdup(object_class_get_name(oc));
suffix = strstr(ppc_cpu_aliases[i].model, "-"TYPE_POWERPC_CPU);
if (suffix) {
*suffix = 0;
}
ppc_cpu_aliases[i].oc = oc;
break;
}
}
return 0;
}

View File

@ -26,7 +26,7 @@ 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);
int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr);
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);
@ -123,7 +123,7 @@ static inline void kvmppc_set_papr(PowerPCCPU *cpu)
{
}
static inline int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version)
static inline int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr)
{
return 0;
}

View File

@ -24,6 +24,7 @@
#include "helper_regs.h"
#include "exec/cpu_ldst.h"
#include "internal.h"
//#define DEBUG_OP
@ -284,6 +285,71 @@ STVE(stvewx, cpu_stl_data_ra, bswap32, u32)
#undef I
#undef LVE
#ifdef TARGET_PPC64
#define GET_NB(rb) ((rb >> 56) & 0xFF)
#define VSX_LXVL(name, lj) \
void helper_##name(CPUPPCState *env, target_ulong addr, \
target_ulong xt_num, target_ulong rb) \
{ \
int i; \
ppc_vsr_t xt; \
uint64_t nb = GET_NB(rb); \
\
xt.s128 = int128_zero(); \
if (nb) { \
nb = (nb >= 16) ? 16 : nb; \
if (msr_le && !lj) { \
for (i = 16; i > 16 - nb; i--) { \
xt.VsrB(i - 1) = cpu_ldub_data_ra(env, addr, GETPC()); \
addr = addr_add(env, addr, 1); \
} \
} else { \
for (i = 0; i < nb; i++) { \
xt.VsrB(i) = cpu_ldub_data_ra(env, addr, GETPC()); \
addr = addr_add(env, addr, 1); \
} \
} \
} \
putVSR(xt_num, &xt, env); \
}
VSX_LXVL(lxvl, 0)
VSX_LXVL(lxvll, 1)
#undef VSX_LXVL
#define VSX_STXVL(name, lj) \
void helper_##name(CPUPPCState *env, target_ulong addr, \
target_ulong xt_num, target_ulong rb) \
{ \
int i; \
ppc_vsr_t xt; \
target_ulong nb = GET_NB(rb); \
\
if (!nb) { \
return; \
} \
getVSR(xt_num, &xt, env); \
nb = (nb >= 16) ? 16 : nb; \
if (msr_le && !lj) { \
for (i = 16; i > 16 - nb; i--) { \
cpu_stb_data_ra(env, addr, xt.VsrB(i - 1), GETPC()); \
addr = addr_add(env, addr, 1); \
} \
} else { \
for (i = 0; i < nb; i++) { \
cpu_stb_data_ra(env, addr, xt.VsrB(i), GETPC()); \
addr = addr_add(env, addr, 1); \
} \
} \
}
VSX_STXVL(stxvl, 0)
VSX_STXVL(stxvll, 1)
#undef VSX_STXVL
#undef GET_NB
#endif /* TARGET_PPC64 */
#undef HI_IDX
#undef LO_IDX

View File

@ -181,8 +181,8 @@ int ppc_store_slb(PowerPCCPU *cpu, target_ulong slot,
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,
LOG_SLB("%s: " TARGET_FMT_lu " " TARGET_FMT_lx " - " TARGET_FMT_lx
" => %016" PRIx64 " %016" PRIx64 "\n", __func__, slot, esid, vsid,
slb->esid, slb->vsid);
return 0;

View File

@ -85,7 +85,7 @@ void ppc_hash64_update_rmls(CPUPPCState *env);
#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) | \
#define HPTE64_R_KEY(x) ((((x) & HPTE64_R_KEY_HI) >> 57) | \
(((x) & HPTE64_R_KEY_LO) >> 9))
#define HPTE64_V_1TB_SEG 0x4000000000000000ULL

View File

@ -422,157 +422,6 @@ typedef struct opcode_t {
#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 */
@ -763,17 +612,17 @@ static inline void gen_op_cmp(TCGv arg0, TCGv arg1, int s, int crf)
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_shli_i32(t1, t1, CRF_LT_BIT);
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_shli_i32(t1, t1, CRF_GT_BIT);
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_shli_i32(t1, t1, CRF_EQ_BIT);
tcg_gen_or_i32(cpu_crf[crf], cpu_crf[crf], t1);
tcg_temp_free(t0);
@ -899,7 +748,7 @@ static void gen_cmprb(DisasContext *ctx)
tcg_gen_and_i32(src2lo, src2lo, src2hi);
tcg_gen_or_i32(crf, crf, src2lo);
}
tcg_gen_shli_i32(crf, crf, CRF_GT);
tcg_gen_shli_i32(crf, crf, CRF_GT_BIT);
tcg_temp_free_i32(src1);
tcg_temp_free_i32(src2);
tcg_temp_free_i32(src2lo);
@ -3148,7 +2997,7 @@ static void gen_conditional_store(DisasContext *ctx, TCGv EA,
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_ori_i32(cpu_crf[0], cpu_crf[0], 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);
@ -3242,7 +3091,7 @@ static void gen_stqcx_(DisasContext *ctx)
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_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ);
if (unlikely(ctx->le_mode)) {
gpr1 = cpu_gpr[reg + 1];
@ -3323,6 +3172,11 @@ static void gen_nap(DisasContext *ctx)
#endif /* defined(CONFIG_USER_ONLY) */
}
static void gen_stop(DisasContext *ctx)
{
gen_nap(ctx);
}
static void gen_sleep(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
@ -4423,7 +4277,7 @@ static void gen_slbfee_(DisasContext *ctx)
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_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ);
tcg_gen_br(l2);
gen_set_label(l1);
tcg_gen_movi_tl(cpu_gpr[rS(ctx->opcode)], 0);
@ -6166,6 +6020,10 @@ GEN_TM_NOOP(tabortwci);
GEN_TM_NOOP(tabortdc);
GEN_TM_NOOP(tabortdci);
GEN_TM_NOOP(tsr);
static inline void gen_cp_abort(DisasContext *ctx)
{
// Do Nothing
}
static void gen_tcheck(DisasContext *ctx)
{
@ -6223,6 +6081,67 @@ GEN_TM_PRIV_NOOP(trechkpt);
#include "translate/spe-impl.inc.c"
/* Handles lfdp, lxsd, lxssp */
static void gen_dform39(DisasContext *ctx)
{
switch (ctx->opcode & 0x3) {
case 0: /* lfdp */
if (ctx->insns_flags2 & PPC2_ISA205) {
return gen_lfdp(ctx);
}
break;
case 2: /* lxsd */
if (ctx->insns_flags2 & PPC2_ISA300) {
return gen_lxsd(ctx);
}
break;
case 3: /* lxssp */
if (ctx->insns_flags2 & PPC2_ISA300) {
return gen_lxssp(ctx);
}
break;
}
return gen_invalid(ctx);
}
/* handles stfdp, lxv, stxsd, stxssp lxvx */
static void gen_dform3D(DisasContext *ctx)
{
if ((ctx->opcode & 3) == 1) { /* DQ-FORM */
switch (ctx->opcode & 0x7) {
case 1: /* lxv */
if (ctx->insns_flags2 & PPC2_ISA300) {
return gen_lxv(ctx);
}
break;
case 5: /* stxv */
if (ctx->insns_flags2 & PPC2_ISA300) {
return gen_stxv(ctx);
}
break;
}
} else { /* DS-FORM */
switch (ctx->opcode & 0x3) {
case 0: /* stfdp */
if (ctx->insns_flags2 & PPC2_ISA205) {
return gen_stfdp(ctx);
}
break;
case 2: /* stxsd */
if (ctx->insns_flags2 & PPC2_ISA300) {
return gen_stxsd(ctx);
}
break;
case 3: /* stxssp */
if (ctx->insns_flags2 & PPC2_ISA300) {
return gen_stxssp(ctx);
}
break;
}
}
return gen_invalid(ctx);
}
static opcode_t opcodes[] = {
GEN_HANDLER(invalid, 0x00, 0x00, 0x00, 0xFFFFFFFF, PPC_NONE),
GEN_HANDLER(cmp, 0x1F, 0x00, 0x00, 0x00400000, PPC_INTEGER),
@ -6255,6 +6174,7 @@ 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_E(cp_abort, 0x1F, 0x06, 0x1A, 0x03FFF801, 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),
@ -6295,6 +6215,10 @@ 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
/* handles lfdp, lxsd, lxssp */
GEN_HANDLER_E(dform39, 0x39, 0xFF, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA205),
/* handles stfdp, lxv, stxsd, stxssp, stxv */
GEN_HANDLER_E(dform3D, 0x3D, 0xFF, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA205),
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),
@ -6326,6 +6250,7 @@ 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(stop, 0x13, 0x12, 0x0b, 0x03FFF801, PPC_NONE, PPC2_ISA300),
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),
@ -6910,6 +6835,9 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
}
#endif
if (env->spr_cb[SPR_LPCR].name)
cpu_fprintf(f, " LPCR " TARGET_FMT_lx "\n", env->spr[SPR_LPCR]);
switch (env->mmu_model) {
case POWERPC_MMU_32B:
case POWERPC_MMU_601:

View File

@ -9,9 +9,9 @@ static inline void gen_reset_fpstatus(void)
gen_helper_reset_fpstatus(cpu_env);
}
static inline void gen_compute_fprf(TCGv_i64 arg)
static inline void gen_compute_fprf_float64(TCGv_i64 arg)
{
gen_helper_compute_fprf(cpu_env, arg);
gen_helper_compute_fprf_float64(cpu_env, arg);
gen_helper_float_check_status(cpu_env);
}
@ -47,7 +47,7 @@ static void gen_f##name(DisasContext *ctx) \
cpu_fpr[rD(ctx->opcode)]); \
} \
if (set_fprf) { \
gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \
gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); \
} \
if (unlikely(Rc(ctx->opcode) != 0)) { \
gen_set_cr1_from_fpscr(ctx); \
@ -74,7 +74,7 @@ static void gen_f##name(DisasContext *ctx) \
cpu_fpr[rD(ctx->opcode)]); \
} \
if (set_fprf) { \
gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \
gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); \
} \
if (unlikely(Rc(ctx->opcode) != 0)) { \
gen_set_cr1_from_fpscr(ctx); \
@ -100,7 +100,7 @@ static void gen_f##name(DisasContext *ctx) \
cpu_fpr[rD(ctx->opcode)]); \
} \
if (set_fprf) { \
gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \
gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); \
} \
if (unlikely(Rc(ctx->opcode) != 0)) { \
gen_set_cr1_from_fpscr(ctx); \
@ -121,7 +121,7 @@ static void gen_f##name(DisasContext *ctx) \
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)]); \
gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); \
} \
if (unlikely(Rc(ctx->opcode) != 0)) { \
gen_set_cr1_from_fpscr(ctx); \
@ -139,7 +139,7 @@ static void gen_f##name(DisasContext *ctx) \
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)]); \
gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); \
} \
if (unlikely(Rc(ctx->opcode) != 0)) { \
gen_set_cr1_from_fpscr(ctx); \
@ -174,7 +174,7 @@ static void gen_frsqrtes(DisasContext *ctx)
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)]);
gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]);
if (unlikely(Rc(ctx->opcode) != 0)) {
gen_set_cr1_from_fpscr(ctx);
}
@ -196,7 +196,7 @@ static void gen_fsqrt(DisasContext *ctx)
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)]);
gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]);
if (unlikely(Rc(ctx->opcode) != 0)) {
gen_set_cr1_from_fpscr(ctx);
}
@ -213,7 +213,7 @@ static void gen_fsqrts(DisasContext *ctx)
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)]);
gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]);
if (unlikely(Rc(ctx->opcode) != 0)) {
gen_set_cr1_from_fpscr(ctx);
}

View File

@ -68,7 +68,6 @@ 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) \
@ -88,7 +87,6 @@ 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),

View File

@ -340,6 +340,19 @@ static void glue(gen_, name0##_##name1)(DisasContext *ctx) \
} \
}
#define GEN_VXFORM_HETRO(name, opc2, opc3) \
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)], cpu_gpr[rA(ctx->opcode)], rb); \
tcg_temp_free_ptr(rb); \
}
GEN_VXFORM(vaddubm, 0, 0);
GEN_VXFORM_DUAL_EXT(vaddubm, PPC_ALTIVEC, PPC_NONE, 0, \
vmul10cuq, PPC_NONE, PPC2_ISA300, 0x0000F800)
@ -525,6 +538,16 @@ GEN_VXFORM_ENV(vaddfp, 5, 0);
GEN_VXFORM_ENV(vsubfp, 5, 1);
GEN_VXFORM_ENV(vmaxfp, 5, 16);
GEN_VXFORM_ENV(vminfp, 5, 17);
GEN_VXFORM_HETRO(vextublx, 6, 24)
GEN_VXFORM_HETRO(vextuhlx, 6, 25)
GEN_VXFORM_HETRO(vextuwlx, 6, 26)
GEN_VXFORM_DUAL(vmrgow, PPC_NONE, PPC2_ALTIVEC_207,
vextuwlx, PPC_NONE, PPC2_ISA300)
GEN_VXFORM_HETRO(vextubrx, 6, 28)
GEN_VXFORM_HETRO(vextuhrx, 6, 29)
GEN_VXFORM_HETRO(vextuwrx, 6, 30)
GEN_VXFORM_DUAL(vmrgew, PPC_NONE, PPC2_ALTIVEC_207, \
vextuwrx, PPC_NONE, PPC2_ISA300)
#define GEN_VXRFORM1(opname, name, str, opc2, opc3) \
static void glue(gen_, name)(DisasContext *ctx) \
@ -989,10 +1012,25 @@ GEN_BCD2(bcdcfn)
GEN_BCD2(bcdctn)
GEN_BCD2(bcdcfz)
GEN_BCD2(bcdctz)
GEN_BCD2(bcdcfsq)
GEN_BCD2(bcdctsq)
GEN_BCD2(bcdsetsgn)
GEN_BCD(bcdcpsgn);
GEN_BCD(bcds);
GEN_BCD(bcdus);
GEN_BCD(bcdsr);
GEN_BCD(bcdtrunc);
GEN_BCD(bcdutrunc);
static void gen_xpnd04_1(DisasContext *ctx)
{
switch (opc4(ctx->opcode)) {
case 0:
gen_bcdctsq(ctx);
break;
case 2:
gen_bcdcfsq(ctx);
break;
case 4:
gen_bcdctz(ctx);
break;
@ -1005,6 +1043,9 @@ static void gen_xpnd04_1(DisasContext *ctx)
case 7:
gen_bcdcfn(ctx);
break;
case 31:
gen_bcdsetsgn(ctx);
break;
default:
gen_invalid(ctx);
break;
@ -1014,6 +1055,12 @@ static void gen_xpnd04_1(DisasContext *ctx)
static void gen_xpnd04_2(DisasContext *ctx)
{
switch (opc4(ctx->opcode)) {
case 0:
gen_bcdctsq(ctx);
break;
case 2:
gen_bcdcfsq(ctx);
break;
case 4:
gen_bcdctz(ctx);
break;
@ -1023,12 +1070,16 @@ static void gen_xpnd04_2(DisasContext *ctx)
case 7:
gen_bcdcfn(ctx);
break;
case 31:
gen_bcdsetsgn(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, \
@ -1042,6 +1093,19 @@ 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)
GEN_VXFORM_DUAL(vaddshs, PPC_ALTIVEC, PPC_NONE, \
bcdcpsgn, PPC_NONE, PPC2_ISA300)
GEN_VXFORM_DUAL(vsubudm, PPC2_ALTIVEC_207, PPC_NONE, \
bcds, PPC_NONE, PPC2_ISA300)
GEN_VXFORM_DUAL(vsubuwm, PPC_ALTIVEC, PPC_NONE, \
bcdus, PPC_NONE, PPC2_ISA300)
GEN_VXFORM_DUAL(vsubsbs, PPC_ALTIVEC, PPC_NONE, \
bcdtrunc, PPC_NONE, PPC2_ISA300)
GEN_VXFORM_DUAL(vsubuqm, PPC2_ALTIVEC_207, PPC_NONE, \
bcdtrunc, PPC_NONE, PPC2_ISA300)
GEN_VXFORM_DUAL(vsubcuq, PPC2_ALTIVEC_207, PPC_NONE, \
bcdutrunc, PPC_NONE, PPC2_ISA300)
static void gen_vsbox(DisasContext *ctx)
{

View File

@ -61,8 +61,9 @@ 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_DUAL(vsubuwm, bcdus, 0, 18, PPC_ALTIVEC, PPC2_ISA300),
GEN_VXFORM_DUAL(vsubudm, bcds, 0, 19, PPC2_ALTIVEC_207, PPC2_ISA300),
GEN_VXFORM_300(bcds, 0, 27),
GEN_VXFORM(vmaxub, 1, 0),
GEN_VXFORM(vmaxuh, 1, 1),
GEN_VXFORM(vmaxuw, 1, 2),
@ -91,8 +92,12 @@ 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_300(vextublx, 6, 24),
GEN_VXFORM_300(vextuhlx, 6, 25),
GEN_VXFORM_DUAL(vmrgow, vextuwlx, 6, 26, PPC_NONE, PPC2_ALTIVEC_207),
GEN_VXFORM_300(vextubrx, 6, 28),
GEN_VXFORM_300(vextuhrx, 6, 29),
GEN_VXFORM_DUAL(vmrgew, vextuwrx, 6, 30, PPC_NONE, PPC2_ALTIVEC_207),
GEN_VXFORM(vmuloub, 4, 0),
GEN_VXFORM(vmulouh, 4, 1),
GEN_VXFORM_DUAL(vmulouw, vmuluwm, 4, 2, PPC_ALTIVEC, PPC_NONE),
@ -127,23 +132,25 @@ 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_300(bcdsr, 0, 23),
GEN_VXFORM_300(bcdsr, 0, 31),
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_DUAL(vaddshs, bcdcpsgn, 0, 13, PPC_ALTIVEC, PPC_NONE),
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_DUAL(vsubsbs, bcdtrunc, 0, 28, PPC_NONE, PPC2_ISA300),
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(vsubuqm, bcdtrunc, 0, 20, PPC2_ALTIVEC_207, PPC2_ISA300),
GEN_VXFORM_DUAL(vsubcuq, bcdutrunc, 0, 21, PPC2_ALTIVEC_207, PPC2_ISA300),
GEN_VXFORM_DUAL(vsubeuqm, vsubecuq, 31, 0xFF, PPC_NONE, PPC2_ALTIVEC_207),
GEN_VXFORM(vrlb, 2, 0),
GEN_VXFORM(vrlh, 2, 1),

View File

@ -190,6 +190,109 @@ static void gen_lxvb16x(DisasContext *ctx)
tcg_temp_free(EA);
}
#define VSX_VECTOR_LOAD_STORE(name, op, indexed) \
static void gen_##name(DisasContext *ctx) \
{ \
int xt; \
TCGv EA; \
TCGv_i64 xth, xtl; \
\
if (indexed) { \
xt = xT(ctx->opcode); \
} else { \
xt = DQxT(ctx->opcode); \
} \
xth = cpu_vsrh(xt); \
xtl = cpu_vsrl(xt); \
\
if (xt < 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; \
} \
} \
gen_set_access_type(ctx, ACCESS_INT); \
EA = tcg_temp_new(); \
if (indexed) { \
gen_addr_reg_index(ctx, EA); \
} else { \
gen_addr_imm_index(ctx, EA, 0x0F); \
} \
if (ctx->le_mode) { \
tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_LEQ); \
tcg_gen_addi_tl(EA, EA, 8); \
tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_LEQ); \
} else { \
tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_BEQ); \
tcg_gen_addi_tl(EA, EA, 8); \
tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_BEQ); \
} \
tcg_temp_free(EA); \
}
VSX_VECTOR_LOAD_STORE(lxv, ld_i64, 0)
VSX_VECTOR_LOAD_STORE(stxv, st_i64, 0)
VSX_VECTOR_LOAD_STORE(lxvx, ld_i64, 1)
VSX_VECTOR_LOAD_STORE(stxvx, st_i64, 1)
#ifdef TARGET_PPC64
#define VSX_VECTOR_LOAD_STORE_LENGTH(name) \
static void gen_##name(DisasContext *ctx) \
{ \
TCGv EA, xt; \
\
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; \
} \
} \
EA = tcg_temp_new(); \
xt = tcg_const_tl(xT(ctx->opcode)); \
gen_set_access_type(ctx, ACCESS_INT); \
gen_addr_register(ctx, EA); \
gen_helper_##name(cpu_env, EA, xt, cpu_gpr[rB(ctx->opcode)]); \
tcg_temp_free(EA); \
tcg_temp_free(xt); \
}
VSX_VECTOR_LOAD_STORE_LENGTH(lxvl)
VSX_VECTOR_LOAD_STORE_LENGTH(lxvll)
VSX_VECTOR_LOAD_STORE_LENGTH(stxvl)
VSX_VECTOR_LOAD_STORE_LENGTH(stxvll)
#endif
#define VSX_LOAD_SCALAR_DS(name, operation) \
static void gen_##name(DisasContext *ctx) \
{ \
TCGv EA; \
TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32); \
\
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_imm_index(ctx, EA, 0x03); \
gen_qemu_##operation(ctx, xth, EA); \
/* NOTE: cpu_vsrl is undefined */ \
tcg_temp_free(EA); \
}
VSX_LOAD_SCALAR_DS(lxsd, ld64_i64)
VSX_LOAD_SCALAR_DS(lxssp, ld32fs)
#define VSX_STORE_SCALAR(name, operation) \
static void gen_##name(DisasContext *ctx) \
{ \
@ -311,6 +414,27 @@ static void gen_stxvb16x(DisasContext *ctx)
tcg_temp_free(EA);
}
#define VSX_STORE_SCALAR_DS(name, operation) \
static void gen_##name(DisasContext *ctx) \
{ \
TCGv EA; \
TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32); \
\
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_imm_index(ctx, EA, 0x03); \
gen_qemu_##operation(ctx, xth, EA); \
/* NOTE: cpu_vsrl is undefined */ \
tcg_temp_free(EA); \
}
VSX_LOAD_SCALAR_DS(stxsd, st64_i64)
VSX_LOAD_SCALAR_DS(stxssp, st32fs)
#define MV_VSRW(name, tcgop1, tcgop2, target, source) \
static void gen_##name(DisasContext *ctx) \
{ \
@ -517,6 +641,55 @@ 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_SCALAR_MOVE_QP(name, op, sgn_mask) \
static void glue(gen_, name)(DisasContext *ctx) \
{ \
int xa; \
int xt = rD(ctx->opcode) + 32; \
int xb = rB(ctx->opcode) + 32; \
TCGv_i64 xah, 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)); \
tcg_gen_mov_i64(xbl, cpu_vsrl(xb)); \
tcg_gen_movi_i64(sgm, sgn_mask); \
switch (op) { \
case OP_ABS: \
tcg_gen_andc_i64(xbh, xbh, sgm); \
break; \
case OP_NABS: \
tcg_gen_or_i64(xbh, xbh, sgm); \
break; \
case OP_NEG: \
tcg_gen_xor_i64(xbh, xbh, sgm); \
break; \
case OP_CPSGN: \
xah = tcg_temp_new_i64(); \
xa = rA(ctx->opcode) + 32; \
tcg_gen_and_i64(xah, cpu_vsrh(xa), sgm); \
tcg_gen_andc_i64(xbh, xbh, sgm); \
tcg_gen_or_i64(xbh, xbh, xah); \
tcg_temp_free_i64(xah); \
break; \
} \
tcg_gen_mov_i64(cpu_vsrh(xt), xbh); \
tcg_gen_mov_i64(cpu_vsrl(xt), xbl); \
tcg_temp_free_i64(xbl); \
tcg_temp_free_i64(xbh); \
tcg_temp_free_i64(sgm); \
}
VSX_SCALAR_MOVE_QP(xsabsqp, OP_ABS, SGN_MASK_DP)
VSX_SCALAR_MOVE_QP(xsnabsqp, OP_NABS, SGN_MASK_DP)
VSX_SCALAR_MOVE_QP(xsnegqp, OP_NEG, SGN_MASK_DP)
VSX_SCALAR_MOVE_QP(xscpsgnqp, OP_CPSGN, SGN_MASK_DP)
#define VSX_VECTOR_MOVE(name, op, sgn_mask) \
static void glue(gen_, name)(DisasContext * ctx) \
{ \
@ -604,9 +777,12 @@ static void gen_##name(DisasContext * ctx) \
}
GEN_VSX_HELPER_2(xsadddp, 0x00, 0x04, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xsaddqp, 0x04, 0x00, 0, PPC2_ISA300)
GEN_VSX_HELPER_2(xssubdp, 0x00, 0x05, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xsmuldp, 0x00, 0x06, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xsmulqp, 0x04, 0x01, 0, PPC2_ISA300)
GEN_VSX_HELPER_2(xsdivdp, 0x00, 0x07, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xsdivqp, 0x04, 0x11, 0, PPC2_ISA300)
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)
@ -624,12 +800,23 @@ 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(xscmpexpdp, 0x0C, 0x07, 0, PPC2_ISA300)
GEN_VSX_HELPER_2(xscmpexpqp, 0x04, 0x05, 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(xscmpoqp, 0x04, 0x04, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xscmpuqp, 0x04, 0x14, 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(xscvdphp, 0x16, 0x15, 0x11, PPC2_ISA300)
GEN_VSX_HELPER_2(xscvdpsp, 0x12, 0x10, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xscvdpqp, 0x04, 0x1A, 0x16, PPC2_ISA300)
GEN_VSX_HELPER_XT_XB_ENV(xscvdpspn, 0x16, 0x10, 0, PPC2_VSX207)
GEN_VSX_HELPER_2(xscvqpdp, 0x04, 0x1A, 0x14, PPC2_ISA300)
GEN_VSX_HELPER_2(xscvqpsdz, 0x04, 0x1A, 0x19, PPC2_ISA300)
GEN_VSX_HELPER_2(xscvqpswz, 0x04, 0x1A, 0x09, PPC2_ISA300)
GEN_VSX_HELPER_2(xscvhpdp, 0x16, 0x15, 0x10, PPC2_ISA300)
GEN_VSX_HELPER_2(xscvsdqp, 0x04, 0x1A, 0x0A, PPC2_ISA300)
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)
@ -637,6 +824,7 @@ 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(xscvudqp, 0x04, 0x1A, 0x02, PPC2_ISA300)
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)
@ -662,6 +850,9 @@ 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(xststdcsp, 0x14, 0x12, 0, PPC2_ISA300)
GEN_VSX_HELPER_2(xststdcdp, 0x14, 0x16, 0, PPC2_ISA300)
GEN_VSX_HELPER_2(xststdcqp, 0x04, 0x16, 0, PPC2_ISA300)
GEN_VSX_HELPER_2(xvadddp, 0x00, 0x0C, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xvsubdp, 0x00, 0x0D, 0, PPC2_VSX)
@ -725,6 +916,8 @@ 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(xvcvhpsp, 0x16, 0x1D, 0x18, PPC2_ISA300)
GEN_VSX_HELPER_2(xvcvsphp, 0x16, 0x1D, 0x19, PPC2_ISA300)
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)
@ -738,6 +931,10 @@ 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)
GEN_VSX_HELPER_2(xvtstdcsp, 0x14, 0x1A, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xvtstdcdp, 0x14, 0x1E, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xxperm, 0x08, 0x03, 0, PPC2_ISA300)
GEN_VSX_HELPER_2(xxpermr, 0x08, 0x07, 0, PPC2_ISA300)
static void gen_xxbrd(DisasContext *ctx)
{
@ -1001,6 +1198,293 @@ static void gen_xxsldwi(DisasContext *ctx)
tcg_temp_free_i64(xtl);
}
#define VSX_EXTRACT_INSERT(name) \
static void gen_##name(DisasContext *ctx) \
{ \
TCGv xt, xb; \
TCGv_i32 t0 = tcg_temp_new_i32(); \
uint8_t uimm = UIMM4(ctx->opcode); \
\
if (unlikely(!ctx->vsx_enabled)) { \
gen_exception(ctx, POWERPC_EXCP_VSXU); \
return; \
} \
xt = tcg_const_tl(xT(ctx->opcode)); \
xb = tcg_const_tl(xB(ctx->opcode)); \
/* uimm > 15 out of bound and for \
* uimm > 12 handle as per hardware in helper \
*/ \
if (uimm > 15) { \
tcg_gen_movi_i64(cpu_vsrh(xT(ctx->opcode)), 0); \
tcg_gen_movi_i64(cpu_vsrl(xT(ctx->opcode)), 0); \
return; \
} \
tcg_gen_movi_i32(t0, uimm); \
gen_helper_##name(cpu_env, xt, xb, t0); \
tcg_temp_free(xb); \
tcg_temp_free(xt); \
tcg_temp_free_i32(t0); \
}
VSX_EXTRACT_INSERT(xxextractuw)
VSX_EXTRACT_INSERT(xxinsertw)
#ifdef TARGET_PPC64
static void gen_xsxexpdp(DisasContext *ctx)
{
TCGv rt = cpu_gpr[rD(ctx->opcode)];
if (unlikely(!ctx->vsx_enabled)) {
gen_exception(ctx, POWERPC_EXCP_VSXU);
return;
}
tcg_gen_shri_i64(rt, cpu_vsrh(xB(ctx->opcode)), 52);
tcg_gen_andi_i64(rt, rt, 0x7FF);
}
static void gen_xsxexpqp(DisasContext *ctx)
{
TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32);
TCGv_i64 xtl = cpu_vsrl(rD(ctx->opcode) + 32);
TCGv_i64 xbh = cpu_vsrh(rB(ctx->opcode) + 32);
if (unlikely(!ctx->vsx_enabled)) {
gen_exception(ctx, POWERPC_EXCP_VSXU);
return;
}
tcg_gen_shri_i64(xth, xbh, 48);
tcg_gen_andi_i64(xth, xth, 0x7FFF);
tcg_gen_movi_i64(xtl, 0);
}
static void gen_xsiexpdp(DisasContext *ctx)
{
TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode));
TCGv ra = cpu_gpr[rA(ctx->opcode)];
TCGv rb = cpu_gpr[rB(ctx->opcode)];
TCGv_i64 t0;
if (unlikely(!ctx->vsx_enabled)) {
gen_exception(ctx, POWERPC_EXCP_VSXU);
return;
}
t0 = tcg_temp_new_i64();
tcg_gen_andi_i64(xth, ra, 0x800FFFFFFFFFFFFF);
tcg_gen_andi_i64(t0, rb, 0x7FF);
tcg_gen_shli_i64(t0, t0, 52);
tcg_gen_or_i64(xth, xth, t0);
/* dword[1] is undefined */
tcg_temp_free_i64(t0);
}
static void gen_xsiexpqp(DisasContext *ctx)
{
TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32);
TCGv_i64 xtl = cpu_vsrl(rD(ctx->opcode) + 32);
TCGv_i64 xah = cpu_vsrh(rA(ctx->opcode) + 32);
TCGv_i64 xal = cpu_vsrl(rA(ctx->opcode) + 32);
TCGv_i64 xbh = cpu_vsrh(rB(ctx->opcode) + 32);
TCGv_i64 t0;
if (unlikely(!ctx->vsx_enabled)) {
gen_exception(ctx, POWERPC_EXCP_VSXU);
return;
}
t0 = tcg_temp_new_i64();
tcg_gen_andi_i64(xth, xah, 0x8000FFFFFFFFFFFF);
tcg_gen_andi_i64(t0, xbh, 0x7FFF);
tcg_gen_shli_i64(t0, t0, 48);
tcg_gen_or_i64(xth, xth, t0);
tcg_gen_mov_i64(xtl, xal);
tcg_temp_free_i64(t0);
}
static void gen_xsxsigdp(DisasContext *ctx)
{
TCGv rt = cpu_gpr[rD(ctx->opcode)];
TCGv_i64 t0, zr, nan, exp;
if (unlikely(!ctx->vsx_enabled)) {
gen_exception(ctx, POWERPC_EXCP_VSXU);
return;
}
exp = tcg_temp_new_i64();
t0 = tcg_temp_new_i64();
zr = tcg_const_i64(0);
nan = tcg_const_i64(2047);
tcg_gen_shri_i64(exp, cpu_vsrh(xB(ctx->opcode)), 52);
tcg_gen_andi_i64(exp, exp, 0x7FF);
tcg_gen_movi_i64(t0, 0x0010000000000000);
tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0);
tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0);
tcg_gen_andi_i64(rt, cpu_vsrh(xB(ctx->opcode)), 0x000FFFFFFFFFFFFF);
tcg_gen_or_i64(rt, rt, t0);
tcg_temp_free_i64(t0);
tcg_temp_free_i64(exp);
tcg_temp_free_i64(zr);
tcg_temp_free_i64(nan);
}
static void gen_xsxsigqp(DisasContext *ctx)
{
TCGv_i64 t0, zr, nan, exp;
TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32);
TCGv_i64 xtl = cpu_vsrl(rD(ctx->opcode) + 32);
if (unlikely(!ctx->vsx_enabled)) {
gen_exception(ctx, POWERPC_EXCP_VSXU);
return;
}
exp = tcg_temp_new_i64();
t0 = tcg_temp_new_i64();
zr = tcg_const_i64(0);
nan = tcg_const_i64(32767);
tcg_gen_shri_i64(exp, cpu_vsrh(rB(ctx->opcode) + 32), 48);
tcg_gen_andi_i64(exp, exp, 0x7FFF);
tcg_gen_movi_i64(t0, 0x0001000000000000);
tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0);
tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0);
tcg_gen_andi_i64(xth, cpu_vsrh(rB(ctx->opcode) + 32), 0x0000FFFFFFFFFFFF);
tcg_gen_or_i64(xth, xth, t0);
tcg_gen_mov_i64(xtl, cpu_vsrl(rB(ctx->opcode) + 32));
tcg_temp_free_i64(t0);
tcg_temp_free_i64(exp);
tcg_temp_free_i64(zr);
tcg_temp_free_i64(nan);
}
#endif
static void gen_xviexpsp(DisasContext *ctx)
{
TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode));
TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode));
TCGv_i64 xah = cpu_vsrh(xA(ctx->opcode));
TCGv_i64 xal = cpu_vsrl(xA(ctx->opcode));
TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode));
TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode));
TCGv_i64 t0;
if (unlikely(!ctx->vsx_enabled)) {
gen_exception(ctx, POWERPC_EXCP_VSXU);
return;
}
t0 = tcg_temp_new_i64();
tcg_gen_andi_i64(xth, xah, 0x807FFFFF807FFFFF);
tcg_gen_andi_i64(t0, xbh, 0xFF000000FF);
tcg_gen_shli_i64(t0, t0, 23);
tcg_gen_or_i64(xth, xth, t0);
tcg_gen_andi_i64(xtl, xal, 0x807FFFFF807FFFFF);
tcg_gen_andi_i64(t0, xbl, 0xFF000000FF);
tcg_gen_shli_i64(t0, t0, 23);
tcg_gen_or_i64(xtl, xtl, t0);
tcg_temp_free_i64(t0);
}
static void gen_xviexpdp(DisasContext *ctx)
{
TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode));
TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode));
TCGv_i64 xah = cpu_vsrh(xA(ctx->opcode));
TCGv_i64 xal = cpu_vsrl(xA(ctx->opcode));
TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode));
TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode));
TCGv_i64 t0;
if (unlikely(!ctx->vsx_enabled)) {
gen_exception(ctx, POWERPC_EXCP_VSXU);
return;
}
t0 = tcg_temp_new_i64();
tcg_gen_andi_i64(xth, xah, 0x800FFFFFFFFFFFFF);
tcg_gen_andi_i64(t0, xbh, 0x7FF);
tcg_gen_shli_i64(t0, t0, 52);
tcg_gen_or_i64(xth, xth, t0);
tcg_gen_andi_i64(xtl, xal, 0x800FFFFFFFFFFFFF);
tcg_gen_andi_i64(t0, xbl, 0x7FF);
tcg_gen_shli_i64(t0, t0, 52);
tcg_gen_or_i64(xtl, xtl, t0);
tcg_temp_free_i64(t0);
}
static void gen_xvxexpsp(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_shri_i64(xth, xbh, 23);
tcg_gen_andi_i64(xth, xth, 0xFF000000FF);
tcg_gen_shri_i64(xtl, xbl, 23);
tcg_gen_andi_i64(xtl, xtl, 0xFF000000FF);
}
static void gen_xvxexpdp(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_shri_i64(xth, xbh, 52);
tcg_gen_andi_i64(xth, xth, 0x7FF);
tcg_gen_shri_i64(xtl, xbl, 52);
tcg_gen_andi_i64(xtl, xtl, 0x7FF);
}
GEN_VSX_HELPER_2(xvxsigsp, 0x00, 0x04, 0, PPC2_ISA300)
static void gen_xvxsigdp(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, zr, nan, exp;
if (unlikely(!ctx->vsx_enabled)) {
gen_exception(ctx, POWERPC_EXCP_VSXU);
return;
}
exp = tcg_temp_new_i64();
t0 = tcg_temp_new_i64();
zr = tcg_const_i64(0);
nan = tcg_const_i64(2047);
tcg_gen_shri_i64(exp, xbh, 52);
tcg_gen_andi_i64(exp, exp, 0x7FF);
tcg_gen_movi_i64(t0, 0x0010000000000000);
tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0);
tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0);
tcg_gen_andi_i64(xth, xbh, 0x000FFFFFFFFFFFFF);
tcg_gen_or_i64(xth, xth, t0);
tcg_gen_shri_i64(exp, xbl, 52);
tcg_gen_andi_i64(exp, exp, 0x7FF);
tcg_gen_movi_i64(t0, 0x0010000000000000);
tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0);
tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0);
tcg_gen_andi_i64(xtl, xbl, 0x000FFFFFFFFFFFFF);
tcg_gen_or_i64(xtl, xtl, t0);
tcg_temp_free_i64(t0);
tcg_temp_free_i64(exp);
tcg_temp_free_i64(zr);
tcg_temp_free_i64(nan);
}
#undef GEN_XX2FORM
#undef GEN_XX3FORM
#undef GEN_XX2IFORM

View File

@ -9,6 +9,11 @@ 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(lxvx, 0x1F, 0x0C, 0x08, 0x00000040, PPC_NONE, PPC2_ISA300),
#if defined(TARGET_PPC64)
GEN_HANDLER_E(lxvl, 0x1F, 0x0D, 0x08, 0, PPC_NONE, PPC2_ISA300),
GEN_HANDLER_E(lxvll, 0x1F, 0x0D, 0x09, 0, PPC_NONE, PPC2_ISA300),
#endif
GEN_HANDLER_E(stxsdx, 0x1F, 0xC, 0x16, 0, PPC_NONE, PPC2_VSX),
GEN_HANDLER_E(stxsibx, 0x1F, 0xD, 0x1C, 0, PPC_NONE, PPC2_ISA300),
@ -19,6 +24,11 @@ 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(stxvx, 0x1F, 0x0C, 0x0C, 0, PPC_NONE, PPC2_ISA300),
#if defined(TARGET_PPC64)
GEN_HANDLER_E(stxvl, 0x1F, 0x0D, 0x0C, 0, PPC_NONE, PPC2_ISA300),
GEN_HANDLER_E(stxvll, 0x1F, 0x0D, 0x0D, 0, PPC_NONE, PPC2_ISA300),
#endif
GEN_HANDLER_E(mfvsrwz, 0x1F, 0x13, 0x03, 0x0000F800, PPC_NONE, PPC2_VSX207),
GEN_HANDLER_E(mtvsrwa, 0x1F, 0x13, 0x06, 0x0000F800, PPC_NONE, PPC2_VSX207),
@ -39,6 +49,10 @@ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, 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_EXT(name, opc2, opc3, fl2) \
GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0x00100000, PPC_NONE, fl2), \
GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0x00100000, 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)
@ -83,11 +97,54 @@ 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)
#define GEN_VSX_XFORM_300(name, opc2, opc3, inval) \
GEN_HANDLER_E(name, 0x3F, opc2, opc3, inval, PPC_NONE, PPC2_ISA300)
#define GEN_VSX_XFORM_300_EO(name, opc2, opc3, opc4, inval) \
GEN_HANDLER_E_2(name, 0x3F, opc2, opc3, opc4, inval, PPC_NONE, PPC2_ISA300)
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_VSX_XFORM_300_EO(xsabsqp, 0x04, 0x19, 0x00, 0x00000001),
GEN_VSX_XFORM_300_EO(xsnabsqp, 0x04, 0x19, 0x08, 0x00000001),
GEN_VSX_XFORM_300_EO(xsnegqp, 0x04, 0x19, 0x10, 0x00000001),
GEN_VSX_XFORM_300(xscpsgnqp, 0x04, 0x03, 0x00000001),
GEN_VSX_XFORM_300_EO(xscvdpqp, 0x04, 0x1A, 0x16, 0x00000001),
GEN_VSX_XFORM_300_EO(xscvqpdp, 0x04, 0x1A, 0x14, 0x0),
GEN_VSX_XFORM_300_EO(xscvqpsdz, 0x04, 0x1A, 0x19, 0x00000001),
GEN_VSX_XFORM_300_EO(xscvqpswz, 0x04, 0x1A, 0x09, 0x00000001),
#ifdef TARGET_PPC64
GEN_XX2FORM_EO(xsxexpdp, 0x16, 0x15, 0x00, PPC2_ISA300),
GEN_VSX_XFORM_300_EO(xsxexpqp, 0x04, 0x19, 0x02, 0x00000001),
GEN_XX2FORM_EO(xsxsigdp, 0x16, 0x15, 0x01, PPC2_ISA300),
GEN_VSX_XFORM_300_EO(xsxsigqp, 0x04, 0x19, 0x12, 0x00000001),
GEN_HANDLER_E(xsiexpdp, 0x3C, 0x16, 0x1C, 0, PPC_NONE, PPC2_ISA300),
GEN_VSX_XFORM_300(xsiexpqp, 0x4, 0x1B, 0x00000001),
#endif
GEN_XX2FORM(xststdcdp, 0x14, 0x16, PPC2_ISA300),
GEN_XX2FORM(xststdcsp, 0x14, 0x12, PPC2_ISA300),
GEN_VSX_XFORM_300(xststdcqp, 0x04, 0x16, 0x00000001),
GEN_XX3FORM(xviexpsp, 0x00, 0x1B, PPC2_ISA300),
GEN_XX3FORM(xviexpdp, 0x00, 0x1F, PPC2_ISA300),
GEN_XX2FORM_EO(xvxexpdp, 0x16, 0x1D, 0x00, PPC2_ISA300),
GEN_XX2FORM_EO(xvxsigdp, 0x16, 0x1D, 0x01, PPC2_ISA300),
GEN_XX2FORM_EO(xvxexpsp, 0x16, 0x1D, 0x08, PPC2_ISA300),
GEN_XX2FORM_EO(xvxsigsp, 0x16, 0x1D, 0x09, PPC2_ISA300),
/* DCMX = bit[25] << 6 | bit[29] << 5 | bit[11:15] */
#define GEN_XX2FORM_DCMX(name, opc2, opc3, fl2) \
GEN_XX3FORM(name, opc2, opc3 | 0, fl2), \
GEN_XX3FORM(name, opc2, opc3 | 1, fl2)
GEN_XX2FORM_DCMX(xvtstdcdp, 0x14, 0x1E, PPC2_ISA300),
GEN_XX2FORM_DCMX(xvtstdcsp, 0x14, 0x1A, PPC2_ISA300),
GEN_XX2FORM(xvabsdp, 0x12, 0x1D, PPC2_VSX),
GEN_XX2FORM(xvnabsdp, 0x12, 0x1E, PPC2_VSX),
GEN_XX2FORM(xvnegdp, 0x12, 0x1F, PPC2_VSX),
@ -98,8 +155,10 @@ GEN_XX2FORM(xvnegsp, 0x12, 0x1B, PPC2_VSX),
GEN_XX3FORM(xvcpsgnsp, 0x00, 0x1A, PPC2_VSX),
GEN_XX3FORM(xsadddp, 0x00, 0x04, PPC2_VSX),
GEN_VSX_XFORM_300(xsaddqp, 0x04, 0x00, 0x0),
GEN_XX3FORM(xssubdp, 0x00, 0x05, PPC2_VSX),
GEN_XX3FORM(xsmuldp, 0x00, 0x06, PPC2_VSX),
GEN_VSX_XFORM_300(xsmulqp, 0x04, 0x01, 0x0),
GEN_XX3FORM(xsdivdp, 0x00, 0x07, PPC2_VSX),
GEN_XX2FORM(xsredp, 0x14, 0x05, PPC2_VSX),
GEN_XX2FORM(xssqrtdp, 0x16, 0x04, PPC2_VSX),
@ -118,12 +177,19 @@ 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_XX3FORM(xscmpexpdp, 0x0C, 0x07, PPC2_ISA300),
GEN_VSX_XFORM_300(xscmpexpqp, 0x04, 0x05, 0x00600001),
GEN_XX2IFORM(xscmpodp, 0x0C, 0x05, PPC2_VSX),
GEN_XX2IFORM(xscmpudp, 0x0C, 0x04, PPC2_VSX),
GEN_VSX_XFORM_300(xscmpoqp, 0x04, 0x04, 0x00600001),
GEN_VSX_XFORM_300(xscmpuqp, 0x04, 0x14, 0x00600001),
GEN_XX3FORM(xsmaxdp, 0x00, 0x14, PPC2_VSX),
GEN_XX3FORM(xsmindp, 0x00, 0x15, PPC2_VSX),
GEN_XX2FORM_EO(xscvdphp, 0x16, 0x15, 0x11, PPC2_ISA300),
GEN_XX2FORM(xscvdpsp, 0x12, 0x10, PPC2_VSX),
GEN_XX2FORM(xscvdpspn, 0x16, 0x10, PPC2_VSX207),
GEN_XX2FORM_EO(xscvhpdp, 0x16, 0x15, 0x10, PPC2_ISA300),
GEN_VSX_XFORM_300_EO(xscvsdqp, 0x04, 0x1A, 0x0A, 0x00000001),
GEN_XX2FORM(xscvspdp, 0x12, 0x14, PPC2_VSX),
GEN_XX2FORM(xscvspdpn, 0x16, 0x14, PPC2_VSX207),
GEN_XX2FORM(xscvdpsxds, 0x10, 0x15, PPC2_VSX),
@ -131,6 +197,7 @@ 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_VSX_XFORM_300_EO(xscvudqp, 0x04, 0x1A, 0x02, 0x00000001),
GEN_XX2FORM(xscvuxddp, 0x10, 0x16, PPC2_VSX),
GEN_XX2FORM(xsrdpi, 0x12, 0x04, PPC2_VSX),
GEN_XX2FORM(xsrdpic, 0x16, 0x06, PPC2_VSX),
@ -142,6 +209,7 @@ 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_VSX_XFORM_300(xsdivqp, 0x04, 0x11, 0x0),
GEN_XX2FORM(xsresp, 0x14, 0x01, PPC2_VSX207),
GEN_XX2FORM(xsrsp, 0x12, 0x11, PPC2_VSX207),
GEN_XX2FORM(xssqrtsp, 0x16, 0x00, PPC2_VSX207),
@ -235,6 +303,8 @@ 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(xvcvhpsp, 0x16, 0x1D, 0x18, PPC2_ISA300),
GEN_XX2FORM_EO(xvcvsphp, 0x16, 0x1D, 0x19, PPC2_ISA300),
GEN_XX2FORM_EO(xxbrq, 0x16, 0x1D, 0x1F, PPC2_ISA300),
#define VSX_LOGICAL(name, opc2, opc3, fl2) \
@ -250,9 +320,13 @@ 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_XX3FORM(xxperm, 0x08, 0x03, PPC2_ISA300),
GEN_XX3FORM(xxpermr, 0x08, 0x07, PPC2_ISA300),
GEN_XX2FORM(xxspltw, 0x08, 0x0A, PPC2_VSX),
GEN_XX1FORM(xxspltib, 0x08, 0x0B, PPC2_ISA300),
GEN_XX3FORM_DM(xxsldwi, 0x08, 0x00),
GEN_XX2FORM_EXT(xxextractuw, 0x0A, 0x0A, PPC2_ISA300),
GEN_XX2FORM_EXT(xxinsertw, 0x0A, 0x0B, PPC2_ISA300),
#define GEN_XXSEL_ROW(opc3) \
GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x18, opc3, 0, PPC_NONE, PPC2_VSX), \

View File

@ -5217,28 +5217,6 @@ POWERPC_FAMILY(e5500)(ObjectClass *oc, void *data)
/* 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)
@ -8797,6 +8775,8 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
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->pcr_supported = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 |
PCR_COMPAT_2_05;
pcc->init_proc = init_proc_POWER9;
pcc->check_pow = check_pow_nocheck;
pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
@ -8857,6 +8837,11 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
#if !defined(CONFIG_USER_ONLY)
void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp)
{
cpu->vhyp = vhyp;
}
void cpu_ppc_set_papr(PowerPCCPU *cpu)
{
CPUPPCState *env = &cpu->env;
@ -9947,67 +9932,6 @@ static void ppc_cpu_unrealizefn(DeviceState *dev, Error **errp)
}
}
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;
@ -10591,9 +10515,16 @@ static const TypeInfo ppc_cpu_type_info = {
.class_init = ppc_cpu_class_init,
};
static const TypeInfo ppc_vhyp_type_info = {
.name = TYPE_PPC_VIRTUAL_HYPERVISOR,
.parent = TYPE_INTERFACE,
.class_size = sizeof(PPCVirtualHypervisorClass),
};
static void ppc_cpu_register_types(void)
{
type_register_static(&ppc_cpu_type_info);
type_register_static(&ppc_vhyp_type_info);
}
type_init(ppc_cpu_register_types)

1
tests/.gitignore vendored
View File

@ -69,6 +69,7 @@ test-qmp-marshal.c
test-qobject-output-visitor
test-rcu-list
test-replication
test-shift128
test-string-input-visitor
test-string-output-visitor
test-thread-pool

View File

@ -65,6 +65,8 @@ check-unit-$(CONFIG_POSIX) += tests/test-vmstate$(EXESUF)
endif
check-unit-y += tests/test-cutils$(EXESUF)
gcov-files-test-cutils-y += util/cutils.c
check-unit-y += tests/test-shift128$(EXESUF)
gcov-files-test-shift128-y = util/host-utils.c
check-unit-y += tests/test-mul64$(EXESUF)
gcov-files-test-mul64-y = util/host-utils.c
check-unit-y += tests/test-int128$(EXESUF)
@ -285,6 +287,11 @@ gcov-files-ppc64-y += hw/usb/hcd-uhci.c
check-qtest-ppc64-y += tests/usb-hcd-xhci-test$(EXESUF)
gcov-files-ppc64-y += hw/usb/hcd-xhci.c
check-qtest-ppc64-y += $(check-qtest-virtio-y)
check-qtest-ppc64-y += tests/test-netfilter$(EXESUF)
check-qtest-ppc64-y += tests/test-filter-mirror$(EXESUF)
check-qtest-ppc64-y += tests/test-filter-redirector$(EXESUF)
check-qtest-ppc64-y += tests/display-vga-test$(EXESUF)
check-qtest-ppc64-$(CONFIG_EVENTFD) += tests/ivshmem-test$(EXESUF)
check-qtest-sh4-y = tests/endianness-test$(EXESUF)
@ -482,7 +489,7 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \
tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \
tests/test-opts-visitor.o tests/test-qmp-event.o \
tests/rcutorture.o tests/test-rcu-list.o \
tests/test-qdist.o \
tests/test-qdist.o tests/test-shift128.o \
tests/test-qht.o tests/qht-bench.o tests/test-qht-par.o \
tests/atomic_add-bench.o
@ -592,6 +599,7 @@ tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marsh
tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y)
tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y)
tests/test-shift128$(EXESUF): tests/test-shift128.o $(test-util-obj-y)
tests/test-mul64$(EXESUF): tests/test-mul64.o $(test-util-obj-y)
tests/test-bitops$(EXESUF): tests/test-bitops.o $(test-util-obj-y)
tests/test-bitcnt$(EXESUF): tests/test-bitcnt.o $(test-util-obj-y)
@ -714,7 +722,7 @@ tests/test-netfilter$(EXESUF): tests/test-netfilter.o $(qtest-obj-y)
tests/test-filter-mirror$(EXESUF): tests/test-filter-mirror.o $(qtest-obj-y)
tests/test-filter-redirector$(EXESUF): tests/test-filter-redirector.o $(qtest-obj-y)
tests/test-x86-cpuid-compat$(EXESUF): tests/test-x86-cpuid-compat.o $(qtest-obj-y)
tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y)
tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) $(libqos-spapr-obj-y)
tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o contrib/libvhost-user/libvhost-user.o $(test-util-obj-y)
tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y)
tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o

View File

@ -50,9 +50,14 @@ static void pci_virtio_vga(void)
int main(int argc, char **argv)
{
const char *arch = qtest_get_arch();
g_test_init(&argc, &argv, NULL);
qtest_add_func("/display/pci/cirrus", pci_cirrus);
if (strcmp(arch, "alpha") == 0 || strcmp(arch, "i386") == 0 ||
strcmp(arch, "mips") == 0 || strcmp(arch, "x86_64") == 0) {
qtest_add_func("/display/pci/cirrus", pci_cirrus);
}
qtest_add_func("/display/pci/stdvga", pci_stdvga);
qtest_add_func("/display/pci/secondary", pci_secondary);
qtest_add_func("/display/pci/multihead", pci_multihead);

View File

@ -11,7 +11,8 @@
#include "qemu/osdep.h"
#include <glib/gstdio.h>
#include "contrib/ivshmem-server/ivshmem-server.h"
#include "libqos/pci-pc.h"
#include "libqos/libqos-pc.h"
#include "libqos/libqos-spapr.h"
#include "libqtest.h"
#include "qemu-common.h"
@ -40,9 +41,8 @@ static QPCIDevice *get_device(QPCIBus *pcibus)
}
typedef struct _IVState {
QTestState *qtest;
QOSState *qs;
QPCIBar reg_bar, mem_bar;
QPCIBus *pcibus;
QPCIDevice *dev;
} IVState;
@ -74,7 +74,7 @@ static inline unsigned in_reg(IVState *s, enum Reg reg)
QTestState *qtest = global_qtest;
unsigned res;
global_qtest = s->qtest;
global_qtest = s->qs->qts;
res = qpci_io_readl(s->dev, s->reg_bar, reg);
g_test_message("*%s -> %x\n", name, res);
global_qtest = qtest;
@ -87,7 +87,7 @@ static inline void out_reg(IVState *s, enum Reg reg, unsigned v)
const char *name = reg2str(reg);
QTestState *qtest = global_qtest;
global_qtest = s->qtest;
global_qtest = s->qs->qts;
g_test_message("%x -> *%s\n", v, name);
qpci_io_writel(s->dev, s->reg_bar, reg, v);
global_qtest = qtest;
@ -97,7 +97,7 @@ static inline void read_mem(IVState *s, uint64_t off, void *buf, size_t len)
{
QTestState *qtest = global_qtest;
global_qtest = s->qtest;
global_qtest = s->qs->qts;
qpci_memread(s->dev, s->mem_bar, off, buf, len);
global_qtest = qtest;
}
@ -107,7 +107,7 @@ static inline void write_mem(IVState *s, uint64_t off,
{
QTestState *qtest = global_qtest;
global_qtest = s->qtest;
global_qtest = s->qs->qts;
qpci_memwrite(s->dev, s->mem_bar, off, buf, len);
global_qtest = qtest;
}
@ -115,17 +115,23 @@ static inline void write_mem(IVState *s, uint64_t off,
static void cleanup_vm(IVState *s)
{
g_free(s->dev);
qpci_free_pc(s->pcibus);
qtest_quit(s->qtest);
qtest_shutdown(s->qs);
}
static void setup_vm_cmd(IVState *s, const char *cmd, bool msix)
{
uint64_t barsize;
const char *arch = qtest_get_arch();
s->qtest = qtest_start(cmd);
s->pcibus = qpci_init_pc(NULL);
s->dev = get_device(s->pcibus);
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
s->qs = qtest_pc_boot(cmd);
} else if (strcmp(arch, "ppc64") == 0) {
s->qs = qtest_spapr_boot(cmd);
} else {
g_printerr("ivshmem-test tests are only available on x86 or ppc64\n");
exit(EXIT_FAILURE);
}
s->dev = get_device(s->qs->pcibus);
s->reg_bar = qpci_iomap(s->dev, 0, &barsize);
g_assert_cmpuint(barsize, ==, 256);
@ -347,7 +353,7 @@ static void test_ivshmem_server(bool msi)
g_assert_cmpint(vm1, !=, vm2);
/* check number of MSI-X vectors */
global_qtest = s1->qtest;
global_qtest = s1->qs->qts;
if (msi) {
ret = qpci_msix_table_size(s1->dev);
g_assert_cmpuint(ret, ==, nvectors);
@ -370,7 +376,7 @@ static void test_ivshmem_server(bool msi)
g_assert_cmpuint(ret, !=, 0);
/* ping vm1 -> vm2 on vector 1 */
global_qtest = s2->qtest;
global_qtest = s2->qs->qts;
if (msi) {
ret = qpci_msix_pending(s2->dev, 1);
g_assert_cmpuint(ret, ==, 0);
@ -412,6 +418,7 @@ static void test_ivshmem_server_irq(void)
static void test_ivshmem_hotplug(void)
{
const char *arch = qtest_get_arch();
gchar *opts;
qtest_start("");
@ -419,7 +426,9 @@ static void test_ivshmem_hotplug(void)
opts = g_strdup_printf("'shm': '%s', 'size': '1M'", tmpshm);
qpci_plug_device_test("ivshmem", "iv1", PCI_SLOT_HP, opts);
qpci_unplug_acpi_device_test("iv1", PCI_SLOT_HP);
if (strcmp(arch, "ppc64") != 0) {
qpci_unplug_acpi_device_test("iv1", PCI_SLOT_HP);
}
qtest_end();
g_free(opts);
@ -491,6 +500,7 @@ static gchar *mktempshm(int size, int *fd)
int main(int argc, char **argv)
{
int ret, fd;
const char *arch = qtest_get_arch();
gchar dir[] = "/tmp/ivshmem-test.XXXXXX";
#if !GLIB_CHECK_VERSION(2, 31, 0)
@ -521,8 +531,10 @@ int main(int argc, char **argv)
qtest_add_func("/ivshmem/memdev", test_ivshmem_memdev);
if (g_test_slow()) {
qtest_add_func("/ivshmem/pair", test_ivshmem_pair);
qtest_add_func("/ivshmem/server-msi", test_ivshmem_server_msi);
qtest_add_func("/ivshmem/server-irq", test_ivshmem_server_irq);
if (strcmp(arch, "ppc64") != 0) {
qtest_add_func("/ivshmem/server-msi", test_ivshmem_server_msi);
qtest_add_func("/ivshmem/server-irq", test_ivshmem_server_irq);
}
}
ret = g_test_run();

View File

@ -193,8 +193,8 @@ QPCIBus *qpci_init_spapr(QGuestAllocator *alloc)
ret->pio.size = SPAPR_PCI_IO_WIN_SIZE;
/* 32-bit portion of the MMIO window is at PCI address 2..4 GiB */
ret->mmio32_cpu_base = SPAPR_PCI_BASE + SPAPR_PCI_MMIO32_WIN_SIZE;
ret->mmio32.pci_base = 0x80000000; /* 2 GiB */
ret->mmio32_cpu_base = SPAPR_PCI_BASE;
ret->mmio32.pci_base = SPAPR_PCI_MMIO32_WIN_SIZE;
ret->mmio32.size = SPAPR_PCI_MMIO32_WIN_SIZE;
ret->bus.pio_alloc_ptr = 0xc000;

139
tests/test-shift128.c Normal file
View File

@ -0,0 +1,139 @@
/*
* Test unsigned left and right shift
*
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
* See the COPYING.LIB file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qemu/host-utils.h"
typedef struct {
uint64_t low;
uint64_t high;
uint64_t rlow;
uint64_t rhigh;
int32_t shift;
bool overflow;
} test_data;
static const test_data test_ltable[] = {
{ 0x4C7ULL, 0x0ULL, 0x00000000000004C7ULL,
0x0000000000000000ULL, 0, false },
{ 0x001ULL, 0x0ULL, 0x0000000000000002ULL,
0x0000000000000000ULL, 1, false },
{ 0x001ULL, 0x0ULL, 0x0000000000000004ULL,
0x0000000000000000ULL, 2, false },
{ 0x001ULL, 0x0ULL, 0x0000000000000010ULL,
0x0000000000000000ULL, 4, false },
{ 0x001ULL, 0x0ULL, 0x0000000000000100ULL,
0x0000000000000000ULL, 8, false },
{ 0x001ULL, 0x0ULL, 0x0000000000010000ULL,
0x0000000000000000ULL, 16, false },
{ 0x001ULL, 0x0ULL, 0x0000000080000000ULL,
0x0000000000000000ULL, 31, false },
{ 0x001ULL, 0x0ULL, 0x0000200000000000ULL,
0x0000000000000000ULL, 45, false },
{ 0x001ULL, 0x0ULL, 0x1000000000000000ULL,
0x0000000000000000ULL, 60, false },
{ 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
0x0000000000000001ULL, 64, false },
{ 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
0x0000000000010000ULL, 80, false },
{ 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
0x8000000000000000ULL, 127, false },
{ 0x000ULL, 0x1ULL, 0x0000000000000000ULL,
0x0000000000000000ULL, 64, true },
{ 0x008ULL, 0x0ULL, 0x0000000000000000ULL,
0x0000000000000008ULL, 64, false },
{ 0x008ULL, 0x0ULL, 0x0000000000000000ULL,
0x8000000000000000ULL, 124, false },
{ 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
0x4000000000000000ULL, 126, false },
{ 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
0x8000000000000000ULL, 127, false },
{ 0x001ULL, 0x0ULL, 0x0000000000000001ULL,
0x0000000000000000ULL, 128, false },
{ 0x000ULL, 0x0ULL, 0x0000000000000000ULL,
0x0000000000000000ULL, 200, false },
{ 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
0x0000000000000100ULL, 200, false },
{ 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
0x8000000000000000ULL, -1, false },
{ 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
0x8000000000000000ULL, INT32_MAX, false },
{ 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
0x4000000000000000ULL, -2, false },
{ 0x001ULL, 0x0ULL, 0x0000000000000000ULL,
0x4000000000000000ULL, INT32_MAX - 1, false },
{ 0x8888888888888888ULL, 0x9999999999999999ULL,
0x8000000000000000ULL, 0x9888888888888888ULL, 60, true },
{ 0x8888888888888888ULL, 0x9999999999999999ULL,
0x0000000000000000ULL, 0x8888888888888888ULL, 64, true },
};
static const test_data test_rtable[] = {
{ 0x00000000000004C7ULL, 0x0ULL, 0x00000000000004C7ULL, 0x0ULL, 0, false },
{ 0x0800000000000000ULL, 0x0ULL, 0x0400000000000000ULL, 0x0ULL, 1, false },
{ 0x0800000000000000ULL, 0x0ULL, 0x0200000000000000ULL, 0x0ULL, 2, false },
{ 0x0800000000000000ULL, 0x0ULL, 0x0008000000000000ULL, 0x0ULL, 8, false },
{ 0x0800000000000000ULL, 0x0ULL, 0x0000080000000000ULL, 0x0ULL, 16, false },
{ 0x0800000000000000ULL, 0x0ULL, 0x0000000008000000ULL, 0x0ULL, 32, false },
{ 0x8000000000000000ULL, 0x0ULL, 0x0000000000000001ULL, 0x0ULL, 63, false },
{ 0x8000000000000000ULL, 0x0ULL, 0x0000000000000000ULL, 0x0ULL, 64, false },
{ 0x0000000000000000ULL, 0x8000000000000000ULL,
0x0000000000000000ULL, 0x8000000000000000ULL, 128, false },
{ 0x0000000000000000ULL, 0x8000000000000000ULL,
0x0080000000000000ULL, 0x0000000000000000ULL, 200, false },
{ 0x0000000000000000ULL, 0x0000000000000000ULL,
0x0000000000000000ULL, 0x0000000000000000ULL, 200, false },
{ 0x0000000000000000ULL, 0x8000000000000000ULL,
0x0000000000000000ULL, 0x0000000000000080ULL, -200, false },
{ 0x8000000000000000ULL, 0x8000000000000000ULL,
0x0000000080000000ULL, 0x0000000080000000ULL, 32, false },
{ 0x0800000000000000ULL, 0x0800000000000000ULL,
0x0800000000000000ULL, 0x0000000000000000ULL, 64, false },
{ 0x0800000000000000ULL, 0x0800000000000000ULL,
0x0008000000000000ULL, 0x0000000000000000ULL, 72, false },
{ 0x8000000000000000ULL, 0x8000000000000000ULL,
0x0000000000000001ULL, 0x0000000000000000ULL, 127, false },
{ 0x0000000000000000ULL, 0x8000000000000000ULL,
0x0000000000000001ULL, 0x0000000000000000ULL, -1, false },
{ 0x0000000000000000ULL, 0x8000000000000000ULL,
0x0000000000000002ULL, 0x0000000000000000ULL, -2, false },
};
static void test_lshift(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(test_ltable); ++i) {
bool overflow = false;
test_data tmp = test_ltable[i];
ulshift(&tmp.low, &tmp.high, tmp.shift, &overflow);
g_assert_cmpuint(tmp.low, ==, tmp.rlow);
g_assert_cmpuint(tmp.high, ==, tmp.rhigh);
g_assert_cmpuint(tmp.overflow, ==, overflow);
}
}
static void test_rshift(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(test_rtable); ++i) {
test_data tmp = test_rtable[i];
urshift(&tmp.low, &tmp.high, tmp.shift);
g_assert_cmpuint(tmp.low, ==, tmp.rlow);
g_assert_cmpuint(tmp.high, ==, tmp.rhigh);
}
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/host-utils/test_lshift", test_lshift);
g_test_add_func("/host-utils/test_rshift", test_rshift);
return g_test_run();
}

View File

@ -12,7 +12,7 @@ util-obj-$(CONFIG_POSIX) += memfd.o
util-obj-$(CONFIG_WIN32) += oslib-win32.o
util-obj-$(CONFIG_WIN32) += qemu-thread-win32.o
util-obj-y += envlist.o path.o module.o
util-obj-$(call lnot,$(CONFIG_INT128)) += host-utils.o
util-obj-y += host-utils.o
util-obj-y += bitmap.o bitops.o hbitmap.o
util-obj-y += fifo8.o
util-obj-y += acl.o

View File

@ -26,6 +26,7 @@
#include "qemu/osdep.h"
#include "qemu/host-utils.h"
#ifndef CONFIG_INT128
/* Long integer helpers */
static inline void mul64(uint64_t *plow, uint64_t *phigh,
uint64_t a, uint64_t b)
@ -158,4 +159,69 @@ int divs128(int64_t *plow, int64_t *phigh, int64_t divisor)
return overflow;
}
#endif
/**
* urshift - 128-bit Unsigned Right Shift.
* @plow: in/out - lower 64-bit integer.
* @phigh: in/out - higher 64-bit integer.
* @shift: in - bytes to shift, between 0 and 127.
*
* Result is zero-extended and stored in plow/phigh, which are
* input/output variables. Shift values outside the range will
* be mod to 128. In other words, the caller is responsible to
* verify/assert both the shift range and plow/phigh pointers.
*/
void urshift(uint64_t *plow, uint64_t *phigh, int32_t shift)
{
shift &= 127;
if (shift == 0) {
return;
}
uint64_t h = *phigh >> (shift & 63);
if (shift >= 64) {
*plow = h;
*phigh = 0;
} else {
*plow = (*plow >> (shift & 63)) | (*phigh << (64 - (shift & 63)));
*phigh = h;
}
}
/**
* ulshift - 128-bit Unsigned Left Shift.
* @plow: in/out - lower 64-bit integer.
* @phigh: in/out - higher 64-bit integer.
* @shift: in - bytes to shift, between 0 and 127.
* @overflow: out - true if any 1-bit is shifted out.
*
* Result is zero-extended and stored in plow/phigh, which are
* input/output variables. Shift values outside the range will
* be mod to 128. In other words, the caller is responsible to
* verify/assert both the shift range and plow/phigh pointers.
*/
void ulshift(uint64_t *plow, uint64_t *phigh, int32_t shift, bool *overflow)
{
uint64_t low = *plow;
uint64_t high = *phigh;
shift &= 127;
if (shift == 0) {
return;
}
/* check if any bit will be shifted out */
urshift(&low, &high, 128 - shift);
if (low | high) {
*overflow = true;
}
if (shift >= 64) {
*phigh = *plow << (shift & 63);
*plow = 0;
} else {
*phigh = (*plow >> (64 - (shift & 63))) | (*phigh << (shift & 63));
*plow = *plow << shift;
}
}