mirror of https://github.com/xemu-project/xemu.git
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:
commit
5459ef3bff
|
@ -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
|
||||
|
|
|
@ -57,3 +57,4 @@ CONFIG_IOH3420=y
|
|||
CONFIG_I82801B11=y
|
||||
CONFIG_SMBIOS=y
|
||||
CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
|
||||
CONFIG_PXB=y
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -57,3 +57,4 @@ CONFIG_IOH3420=y
|
|||
CONFIG_I82801B11=y
|
||||
CONFIG_SMBIOS=y
|
||||
CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
|
||||
CONFIG_PXB=y
|
||||
|
|
12
disas/ppc.c
12
disas/ppc.c
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
73
hw/ppc/ppc.c
73
hw/ppc/ppc.c
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
234
hw/ppc/prep.c
234
hw/ppc/prep.c
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
200
hw/ppc/spapr.c
200
hw/ppc/spapr.c
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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), \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue