ppc patch queue for 2022-10-29:

This queue has the second part of the ppc4xx_sdram cleanups, doorbell
 instructions for POWER8,  new pflash handling for the e500 machine and a
 Radix MMU regression fix.
 
 It also has a lot of performance optimizations in the PowerPC emulation
 done by the researchers of the Eldorado institute. Between using gvec
 for VMX/VSX instructions, a full rework of the interrupt model and PMU
 optimizations, they managed to drastically speed up the emulation of
 powernv8/9/10 machines.  Here's an example with avocado tests:
 
 - with master:
 
 tests/avocado/boot_linux_console.py:BootLinuxConsole.test_ppc_powernv8:
 PASS (38.89 s)
 tests/avocado/boot_linux_console.py:BootLinuxConsole.test_ppc_powernv9:
 PASS (43.89 s)
 
 - with this queue applied:
 
 tests/avocado/boot_linux_console.py:BootLinuxConsole.test_ppc_powernv8:
 PASS (21.23 s)
 tests/avocado/boot_linux_console.py:BootLinuxConsole.test_ppc_powernv9:
 PASS (22.58 s)
 
 Other ppc machines, like pseries, also had a noticeable performance
 boost.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQQX6/+ZI9AYAK8oOBk82cqW3gMxZAUCY10J/gAKCRA82cqW3gMx
 ZAbjAPwKNbE1wE2POJbMALBQAM5MewwLMV/UKGjE6jA7HAbb/AEA9e3o11FoUmSJ
 rZkmTvMzBQZ81mMGRlS0cnqbrr4ADgc=
 =gnKY
 -----END PGP SIGNATURE-----

Merge tag 'pull-ppc-20221029' of https://gitlab.com/danielhb/qemu into staging

ppc patch queue for 2022-10-29:

This queue has the second part of the ppc4xx_sdram cleanups, doorbell
instructions for POWER8,  new pflash handling for the e500 machine and a
Radix MMU regression fix.

It also has a lot of performance optimizations in the PowerPC emulation
done by the researchers of the Eldorado institute. Between using gvec
for VMX/VSX instructions, a full rework of the interrupt model and PMU
optimizations, they managed to drastically speed up the emulation of
powernv8/9/10 machines.  Here's an example with avocado tests:

- with master:

tests/avocado/boot_linux_console.py:BootLinuxConsole.test_ppc_powernv8:
PASS (38.89 s)
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_ppc_powernv9:
PASS (43.89 s)

- with this queue applied:

tests/avocado/boot_linux_console.py:BootLinuxConsole.test_ppc_powernv8:
PASS (21.23 s)
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_ppc_powernv9:
PASS (22.58 s)

Other ppc machines, like pseries, also had a noticeable performance
boost.

# -----BEGIN PGP SIGNATURE-----
#
# iHUEABYKAB0WIQQX6/+ZI9AYAK8oOBk82cqW3gMxZAUCY10J/gAKCRA82cqW3gMx
# ZAbjAPwKNbE1wE2POJbMALBQAM5MewwLMV/UKGjE6jA7HAbb/AEA9e3o11FoUmSJ
# rZkmTvMzBQZ81mMGRlS0cnqbrr4ADgc=
# =gnKY
# -----END PGP SIGNATURE-----
# gpg: Signature made Sat 29 Oct 2022 07:09:50 EDT
# gpg:                using EDDSA key 17EBFF9923D01800AF2838193CD9CA96DE033164
# gpg: Good signature from "Daniel Henrique Barboza <danielhb413@gmail.com>" [unknown]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 17EB FF99 23D0 1800 AF28  3819 3CD9 CA96 DE03 3164

* tag 'pull-ppc-20221029' of https://gitlab.com/danielhb/qemu: (63 commits)
  target/ppc: Fix regression in Radix MMU
  hw/ppc/e500: Implement pflash handling
  hw/sd/sdhci: Rename ESDHC_* defines to USDHC_*
  hw/sd/sdhci-internal: Unexport ESDHC defines
  hw/block/pflash_cfi0{1, 2}: Error out if device length isn't a power of two
  docs/system/ppc/ppce500: Use qemu-system-ppc64 across the board(s)
  target/ppc: Increment PMC5 with inline insns
  target/ppc: Add new PMC HFLAGS
  ppc4xx_sdram: Add errp parameter to ppc4xx_sdram_banks()
  ppc4xx_sdram: Convert DDR SDRAM controller to new bank handling
  ppc4xx_sdram: Generalise bank setup
  ppc4xx_sdram: Rename local state variable for brevity
  ppc4xx_sdram: Use hwaddr for memory bank size
  ppc4xx_sdram: Move ppc4xx_sdram_banks() to ppc4xx_sdram.c
  ppc4xx_devs.c: Move DDR SDRAM controller model to ppc4xx_sdram.c
  ppc440_uc.c: Move DDR2 SDRAM controller model to ppc4xx_sdram.c
  target/ppc: move the p*_interrupt_powersave methods to excp_helper.c
  target/ppc: unify cpu->has_work based on cs->interrupt_request
  target/ppc: introduce ppc_maybe_interrupt
  target/ppc: remove ppc_store_lpcr from CONFIG_USER_ONLY builds
  ...

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2022-10-31 06:28:43 -04:00
commit 179938097d
36 changed files with 2736 additions and 1615 deletions

View File

@ -113,7 +113,7 @@ To boot the 32-bit Linux kernel:
.. code-block:: bash
$ qemu-system-ppc{64|32} -M ppce500 -cpu e500mc -smp 4 -m 2G \
$ qemu-system-ppc64 -M ppce500 -cpu e500mc -smp 4 -m 2G \
-display none -serial stdio \
-kernel vmlinux \
-initrd /path/to/rootfs.cpio \
@ -154,10 +154,10 @@ interface at PCI address 0.1.0, but we can switch that to an e1000 NIC by:
.. code-block:: bash
$ qemu-system-ppc -M ppce500 -smp 4 -m 2G \
-display none -serial stdio \
-bios u-boot \
-nic tap,ifname=tap0,script=no,downscript=no,model=e1000
$ qemu-system-ppc64 -M ppce500 -smp 4 -m 2G \
-display none -serial stdio \
-bios u-boot \
-nic tap,ifname=tap0,script=no,downscript=no,model=e1000
The QEMU ``ppce500`` machine can also dynamically instantiate an eTSEC device
if “-device eTSEC” is given to QEMU:
@ -165,3 +165,18 @@ if “-device eTSEC” is given to QEMU:
.. code-block:: bash
-netdev tap,ifname=tap0,script=no,downscript=no,id=net0 -device eTSEC,netdev=net0
Root file system on flash drive
-------------------------------
Rather than using a root file system on ram disk, it is possible to have it on
CFI flash. Given an ext2 image whose size must be a power of two, it can be used
as follows:
.. code-block:: bash
$ qemu-system-ppc64 -M ppce500 -cpu e500mc -smp 4 -m 2G \
-display none -serial stdio \
-kernel vmlinux \
-drive if=pflash,file=/path/to/rootfs.ext2,format=raw \
-append "rootwait root=/dev/mtdblock0"

View File

@ -690,7 +690,7 @@ static const MemoryRegionOps pflash_cfi01_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void pflash_cfi01_fill_cfi_table(PFlashCFI01 *pfl)
static void pflash_cfi01_fill_cfi_table(PFlashCFI01 *pfl, Error **errp)
{
uint64_t blocks_per_device, sector_len_per_device, device_len;
int num_devices;
@ -708,6 +708,10 @@ static void pflash_cfi01_fill_cfi_table(PFlashCFI01 *pfl)
sector_len_per_device = pfl->sector_len / num_devices;
}
device_len = sector_len_per_device * blocks_per_device;
if (!is_power_of_2(device_len)) {
error_setg(errp, "Device size must be a power of two.");
return;
}
/* Hardcoded CFI table */
/* Standard "QRY" string */
@ -865,7 +869,7 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
*/
pfl->cmd = 0x00;
pfl->status = 0x80; /* WSM ready */
pflash_cfi01_fill_cfi_table(pfl);
pflash_cfi01_fill_cfi_table(pfl, errp);
}
static void pflash_cfi01_system_reset(DeviceState *dev)

View File

@ -880,6 +880,11 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
return;
}
if (!is_power_of_2(pfl->chip_len)) {
error_setg(errp, "Device size must be a power of two.");
return;
}
memory_region_init_rom_device(&pfl->orig_mem, OBJECT(pfl),
&pflash_cfi02_ops, pfl, pfl->name,
pfl->chip_len, errp);

View File

@ -126,6 +126,7 @@ config E500
select ETSEC
select GPIO_MPC8XXX
select OPENPIC
select PFLASH_CFI01
select PLATFORM_BUS
select PPCE500_PCI
select SERIAL

View File

@ -23,8 +23,10 @@
#include "e500-ccsr.h"
#include "net/net.h"
#include "qemu/config-file.h"
#include "hw/block/flash.h"
#include "hw/char/serial.h"
#include "hw/pci/pci.h"
#include "sysemu/block-backend-io.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
#include "sysemu/reset.h"
@ -267,6 +269,31 @@ static void sysbus_device_create_devtree(SysBusDevice *sbdev, void *opaque)
}
}
static void create_devtree_flash(SysBusDevice *sbdev,
PlatformDevtreeData *data)
{
g_autofree char *name = NULL;
uint64_t num_blocks = object_property_get_uint(OBJECT(sbdev),
"num-blocks",
&error_fatal);
uint64_t sector_length = object_property_get_uint(OBJECT(sbdev),
"sector-length",
&error_fatal);
uint64_t bank_width = object_property_get_uint(OBJECT(sbdev),
"width",
&error_fatal);
hwaddr flashbase = 0;
hwaddr flashsize = num_blocks * sector_length;
void *fdt = data->fdt;
name = g_strdup_printf("%s/nor@%" PRIx64, data->node, flashbase);
qemu_fdt_add_subnode(fdt, name);
qemu_fdt_setprop_string(fdt, name, "compatible", "cfi-flash");
qemu_fdt_setprop_sized_cells(fdt, name, "reg",
1, flashbase, 1, flashsize);
qemu_fdt_setprop_cell(fdt, name, "bank-width", bank_width);
}
static void platform_bus_create_devtree(PPCE500MachineState *pms,
void *fdt, const char *mpic)
{
@ -276,6 +303,8 @@ static void platform_bus_create_devtree(PPCE500MachineState *pms,
uint64_t addr = pmc->platform_bus_base;
uint64_t size = pmc->platform_bus_size;
int irq_start = pmc->platform_bus_first_irq;
SysBusDevice *sbdev;
bool ambiguous;
/* Create a /platform node that we can put all devices into */
@ -302,6 +331,13 @@ static void platform_bus_create_devtree(PPCE500MachineState *pms,
/* Loop through all dynamic sysbus devices and create nodes for them */
foreach_dynamic_sysbus_device(sysbus_device_create_devtree, &data);
sbdev = SYS_BUS_DEVICE(object_resolve_path_type("", TYPE_PFLASH_CFI01,
&ambiguous));
if (sbdev) {
assert(!ambiguous);
create_devtree_flash(sbdev, &data);
}
g_free(node);
}
@ -856,6 +892,7 @@ void ppce500_init(MachineState *machine)
unsigned int pci_irq_nrs[PCI_NUM_PINS] = {1, 2, 3, 4};
IrqLines *irqs;
DeviceState *dev, *mpicdev;
DriveInfo *dinfo;
CPUPPCState *firstenv = NULL;
MemoryRegion *ccsr_addr_space;
SysBusDevice *s;
@ -1024,6 +1061,48 @@ void ppce500_init(MachineState *machine)
pmc->platform_bus_base,
&pms->pbus_dev->mmio);
dinfo = drive_get(IF_PFLASH, 0, 0);
if (dinfo) {
BlockBackend *blk = blk_by_legacy_dinfo(dinfo);
BlockDriverState *bs = blk_bs(blk);
uint64_t mmio_size = memory_region_size(&pms->pbus_dev->mmio);
uint64_t size = bdrv_getlength(bs);
uint32_t sector_len = 64 * KiB;
if (!is_power_of_2(size)) {
error_report("Size of pflash file must be a power of two.");
exit(1);
}
if (size > mmio_size) {
error_report("Size of pflash file must not be bigger than %" PRIu64
" bytes.", mmio_size);
exit(1);
}
if (!QEMU_IS_ALIGNED(size, sector_len)) {
error_report("Size of pflash file must be a multiple of %" PRIu32
".", sector_len);
exit(1);
}
dev = qdev_new(TYPE_PFLASH_CFI01);
qdev_prop_set_drive(dev, "drive", blk);
qdev_prop_set_uint32(dev, "num-blocks", size / sector_len);
qdev_prop_set_uint64(dev, "sector-length", sector_len);
qdev_prop_set_uint8(dev, "width", 2);
qdev_prop_set_bit(dev, "big-endian", true);
qdev_prop_set_uint16(dev, "id0", 0x89);
qdev_prop_set_uint16(dev, "id1", 0x18);
qdev_prop_set_uint16(dev, "id2", 0x0000);
qdev_prop_set_uint16(dev, "id3", 0x0);
qdev_prop_set_string(dev, "name", "e500.flash");
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
memory_region_add_subregion(&pms->pbus_dev->mmio, 0,
pflash_cfi01_get_memory(PFLASH_CFI01(dev)));
}
/*
* Smart firmware defaults ahead!
*

View File

@ -59,8 +59,9 @@ ppc_ss.add(when: 'CONFIG_PPC440', if_true: files(
'ppc440_bamboo.c',
'ppc440_pcix.c', 'ppc440_uc.c'))
ppc_ss.add(when: 'CONFIG_PPC4XX', if_true: files(
'ppc4xx_devs.c',
'ppc4xx_pci.c',
'ppc4xx_devs.c'))
'ppc4xx_sdram.c'))
ppc_ss.add(when: 'CONFIG_SAM460EX', if_true: files('sam460ex.c'))
# PReP
ppc_ss.add(when: 'CONFIG_PREP', if_true: files('prep.c'))

View File

@ -58,6 +58,7 @@ static void pnv_core_cpu_reset(PnvCore *pc, PowerPCCPU *cpu)
env->msr |= MSR_HVB; /* Hypervisor mode */
env->spr[SPR_HRMOR] = pc->hrmor;
hreg_compute_hflags(env);
ppc_maybe_interrupt(env);
pcc->intc_reset(pc->chip, cpu);
}

View File

@ -40,9 +40,8 @@
static void cpu_ppc_tb_stop (CPUPPCState *env);
static void cpu_ppc_tb_start (CPUPPCState *env);
void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
void ppc_set_irq(PowerPCCPU *cpu, int irq, int level)
{
CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
unsigned int old_pending;
bool locked = false;
@ -56,21 +55,17 @@ void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
old_pending = env->pending_interrupts;
if (level) {
env->pending_interrupts |= 1 << n_IRQ;
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
env->pending_interrupts |= irq;
} else {
env->pending_interrupts &= ~(1 << n_IRQ);
if (env->pending_interrupts == 0) {
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
}
env->pending_interrupts &= ~irq;
}
if (old_pending != env->pending_interrupts) {
kvmppc_set_interrupt(cpu, n_IRQ, level);
ppc_maybe_interrupt(env);
kvmppc_set_interrupt(cpu, irq, level);
}
trace_ppc_irq_set_exit(env, n_IRQ, level, env->pending_interrupts,
trace_ppc_irq_set_exit(env, irq, level, env->pending_interrupts,
CPU(cpu)->interrupt_request);
if (locked) {

View File

@ -10,21 +10,14 @@
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "hw/irq.h"
#include "exec/memory.h"
#include "cpu.h"
#include "hw/ppc/ppc4xx.h"
#include "hw/qdev-properties.h"
#include "hw/pci/pci.h"
#include "sysemu/block-backend.h"
#include "sysemu/reset.h"
#include "ppc440.h"
#include "qom/object.h"
#include "trace.h"
/*****************************************************************************/
/* L2 Cache as SRAM */
@ -478,331 +471,6 @@ void ppc4xx_sdr_init(CPUPPCState *env)
sdr, &dcr_read_sdr, &dcr_write_sdr);
}
/*****************************************************************************/
/* SDRAM controller */
enum {
SDRAM0_CFGADDR = 0x10,
SDRAM0_CFGDATA,
SDRAM_R0BAS = 0x40,
SDRAM_R1BAS,
SDRAM_R2BAS,
SDRAM_R3BAS,
SDRAM_CONF1HB = 0x45,
SDRAM_PLBADDULL = 0x4a,
SDRAM_CONF1LL = 0x4b,
SDRAM_CONFPATHB = 0x4f,
SDRAM_PLBADDUHB = 0x50,
};
static uint32_t sdram_ddr2_bcr(hwaddr ram_base, hwaddr ram_size)
{
uint32_t bcr;
switch (ram_size) {
case 8 * MiB:
bcr = 0xffc0;
break;
case 16 * MiB:
bcr = 0xff80;
break;
case 32 * MiB:
bcr = 0xff00;
break;
case 64 * MiB:
bcr = 0xfe00;
break;
case 128 * MiB:
bcr = 0xfc00;
break;
case 256 * MiB:
bcr = 0xf800;
break;
case 512 * MiB:
bcr = 0xf000;
break;
case 1 * GiB:
bcr = 0xe000;
break;
case 2 * GiB:
bcr = 0xc000;
break;
case 4 * GiB:
bcr = 0x8000;
break;
default:
error_report("invalid RAM size " TARGET_FMT_plx, ram_size);
return 0;
}
bcr |= ram_base >> 2 & 0xffe00000;
bcr |= 1;
return bcr;
}
static inline hwaddr sdram_ddr2_base(uint32_t bcr)
{
return (bcr & 0xffe00000) << 2;
}
static uint64_t sdram_ddr2_size(uint32_t bcr)
{
uint64_t size;
int sh;
sh = 1024 - ((bcr >> 6) & 0x3ff);
size = 8 * MiB * sh;
return size;
}
static void sdram_bank_map(Ppc4xxSdramBank *bank)
{
memory_region_init(&bank->container, NULL, "sdram-container", bank->size);
memory_region_add_subregion(&bank->container, 0, &bank->ram);
memory_region_add_subregion(get_system_memory(), bank->base,
&bank->container);
}
static void sdram_bank_unmap(Ppc4xxSdramBank *bank)
{
memory_region_del_subregion(get_system_memory(), &bank->container);
memory_region_del_subregion(&bank->container, &bank->ram);
object_unparent(OBJECT(&bank->container));
}
static void sdram_ddr2_set_bcr(Ppc4xxSdramDdr2State *sdram, int i,
uint32_t bcr, int enabled)
{
if (sdram->bank[i].bcr & 1) {
/* First unmap RAM if enabled */
trace_ppc4xx_sdram_unmap(sdram_ddr2_base(sdram->bank[i].bcr),
sdram_ddr2_size(sdram->bank[i].bcr));
sdram_bank_unmap(&sdram->bank[i]);
}
sdram->bank[i].bcr = bcr & 0xffe0ffc1;
if (enabled && (bcr & 1)) {
trace_ppc4xx_sdram_map(sdram_ddr2_base(bcr), sdram_ddr2_size(bcr));
sdram_bank_map(&sdram->bank[i]);
}
}
static void sdram_ddr2_map_bcr(Ppc4xxSdramDdr2State *sdram)
{
int i;
for (i = 0; i < sdram->nbanks; i++) {
if (sdram->bank[i].size) {
sdram_ddr2_set_bcr(sdram, i,
sdram_ddr2_bcr(sdram->bank[i].base,
sdram->bank[i].size), 1);
} else {
sdram_ddr2_set_bcr(sdram, i, 0, 0);
}
}
}
static void sdram_ddr2_unmap_bcr(Ppc4xxSdramDdr2State *sdram)
{
int i;
for (i = 0; i < sdram->nbanks; i++) {
if (sdram->bank[i].size) {
sdram_ddr2_set_bcr(sdram, i, sdram->bank[i].bcr & ~1, 0);
}
}
}
static uint32_t sdram_ddr2_dcr_read(void *opaque, int dcrn)
{
Ppc4xxSdramDdr2State *sdram = opaque;
uint32_t ret = 0;
switch (dcrn) {
case SDRAM_R0BAS:
case SDRAM_R1BAS:
case SDRAM_R2BAS:
case SDRAM_R3BAS:
if (sdram->bank[dcrn - SDRAM_R0BAS].size) {
ret = sdram_ddr2_bcr(sdram->bank[dcrn - SDRAM_R0BAS].base,
sdram->bank[dcrn - SDRAM_R0BAS].size);
}
break;
case SDRAM_CONF1HB:
case SDRAM_CONF1LL:
case SDRAM_CONFPATHB:
case SDRAM_PLBADDULL:
case SDRAM_PLBADDUHB:
break;
case SDRAM0_CFGADDR:
ret = sdram->addr;
break;
case SDRAM0_CFGDATA:
switch (sdram->addr) {
case 0x14: /* SDRAM_MCSTAT (405EX) */
case 0x1F:
ret = 0x80000000;
break;
case 0x21: /* SDRAM_MCOPT2 */
ret = sdram->mcopt2;
break;
case 0x40: /* SDRAM_MB0CF */
ret = 0x00008001;
break;
case 0x7A: /* SDRAM_DLCR */
ret = 0x02000000;
break;
case 0xE1: /* SDR0_DDR0 */
ret = SDR0_DDR0_DDRM_ENCODE(1) | SDR0_DDR0_DDRM_DDR1;
break;
default:
break;
}
break;
default:
break;
}
return ret;
}
#define SDRAM_DDR2_MCOPT2_DCEN BIT(27)
static void sdram_ddr2_dcr_write(void *opaque, int dcrn, uint32_t val)
{
Ppc4xxSdramDdr2State *sdram = opaque;
switch (dcrn) {
case SDRAM_R0BAS:
case SDRAM_R1BAS:
case SDRAM_R2BAS:
case SDRAM_R3BAS:
case SDRAM_CONF1HB:
case SDRAM_CONF1LL:
case SDRAM_CONFPATHB:
case SDRAM_PLBADDULL:
case SDRAM_PLBADDUHB:
break;
case SDRAM0_CFGADDR:
sdram->addr = val;
break;
case SDRAM0_CFGDATA:
switch (sdram->addr) {
case 0x00: /* B0CR */
break;
case 0x21: /* SDRAM_MCOPT2 */
if (!(sdram->mcopt2 & SDRAM_DDR2_MCOPT2_DCEN) &&
(val & SDRAM_DDR2_MCOPT2_DCEN)) {
trace_ppc4xx_sdram_enable("enable");
/* validate all RAM mappings */
sdram_ddr2_map_bcr(sdram);
sdram->mcopt2 |= SDRAM_DDR2_MCOPT2_DCEN;
} else if ((sdram->mcopt2 & SDRAM_DDR2_MCOPT2_DCEN) &&
!(val & SDRAM_DDR2_MCOPT2_DCEN)) {
trace_ppc4xx_sdram_enable("disable");
/* invalidate all RAM mappings */
sdram_ddr2_unmap_bcr(sdram);
sdram->mcopt2 &= ~SDRAM_DDR2_MCOPT2_DCEN;
}
break;
default:
break;
}
break;
default:
break;
}
}
static void ppc4xx_sdram_ddr2_reset(DeviceState *dev)
{
Ppc4xxSdramDdr2State *sdram = PPC4xx_SDRAM_DDR2(dev);
sdram->addr = 0;
sdram->mcopt2 = 0;
}
static void ppc4xx_sdram_ddr2_realize(DeviceState *dev, Error **errp)
{
Ppc4xxSdramDdr2State *s = PPC4xx_SDRAM_DDR2(dev);
Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev);
/*
* SoC also has 4 GiB but that causes problem with 32 bit
* builds (4*GiB overflows the 32 bit ram_addr_t).
*/
const ram_addr_t valid_bank_sizes[] = {
2 * GiB, 1 * GiB, 512 * MiB, 256 * MiB, 128 * MiB,
64 * MiB, 32 * MiB, 16 * MiB, 8 * MiB, 0
};
if (s->nbanks < 1 || s->nbanks > 4) {
error_setg(errp, "Invalid number of RAM banks");
return;
}
if (!s->dram_mr) {
error_setg(errp, "Missing dram memory region");
return;
}
ppc4xx_sdram_banks(s->dram_mr, s->nbanks, s->bank, valid_bank_sizes);
ppc4xx_dcr_register(dcr, SDRAM0_CFGADDR,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM0_CFGDATA,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_R0BAS,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_R1BAS,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_R2BAS,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_R3BAS,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_CONF1HB,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_PLBADDULL,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_CONF1LL,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_CONFPATHB,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_PLBADDUHB,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
}
static Property ppc4xx_sdram_ddr2_props[] = {
DEFINE_PROP_LINK("dram", Ppc4xxSdramDdr2State, dram_mr, TYPE_MEMORY_REGION,
MemoryRegion *),
DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdr2State, nbanks, 4),
DEFINE_PROP_END_OF_LIST(),
};
static void ppc4xx_sdram_ddr2_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = ppc4xx_sdram_ddr2_realize;
dc->reset = ppc4xx_sdram_ddr2_reset;
/* Reason: only works as function of a ppc4xx SoC */
dc->user_creatable = false;
device_class_set_props(dc, ppc4xx_sdram_ddr2_props);
}
void ppc4xx_sdram_ddr2_enable(Ppc4xxSdramDdr2State *s)
{
sdram_ddr2_dcr_write(s, SDRAM0_CFGADDR, 0x21);
sdram_ddr2_dcr_write(s, SDRAM0_CFGDATA, 0x08000000);
}
static const TypeInfo ppc4xx_types[] = {
{
.name = TYPE_PPC4xx_SDRAM_DDR2,
.parent = TYPE_PPC4xx_DCR_DEVICE,
.instance_size = sizeof(Ppc4xxSdramDdr2State),
.class_init = ppc4xx_sdram_ddr2_class_init,
}
};
DEFINE_TYPES(ppc4xx_types)
/*****************************************************************************/
/* PLB to AHB bridge */
enum {

View File

@ -23,419 +23,10 @@
*/
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "sysemu/reset.h"
#include "cpu.h"
#include "hw/irq.h"
#include "hw/ppc/ppc.h"
#include "hw/ppc/ppc4xx.h"
#include "hw/qdev-properties.h"
#include "qemu/log.h"
#include "exec/address-spaces.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "trace.h"
/*****************************************************************************/
/* SDRAM controller */
enum {
SDRAM0_CFGADDR = 0x010,
SDRAM0_CFGDATA = 0x011,
};
/*
* XXX: TOFIX: some patches have made this code become inconsistent:
* there are type inconsistencies, mixing hwaddr, target_ulong
* and uint32_t
*/
static uint32_t sdram_ddr_bcr(hwaddr ram_base, hwaddr ram_size)
{
uint32_t bcr;
switch (ram_size) {
case 4 * MiB:
bcr = 0;
break;
case 8 * MiB:
bcr = 0x20000;
break;
case 16 * MiB:
bcr = 0x40000;
break;
case 32 * MiB:
bcr = 0x60000;
break;
case 64 * MiB:
bcr = 0x80000;
break;
case 128 * MiB:
bcr = 0xA0000;
break;
case 256 * MiB:
bcr = 0xC0000;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid RAM size 0x%" HWADDR_PRIx "\n", __func__,
ram_size);
return 0;
}
bcr |= ram_base & 0xFF800000;
bcr |= 1;
return bcr;
}
static inline hwaddr sdram_ddr_base(uint32_t bcr)
{
return bcr & 0xFF800000;
}
static target_ulong sdram_ddr_size(uint32_t bcr)
{
target_ulong size;
int sh;
sh = (bcr >> 17) & 0x7;
if (sh == 7) {
size = -1;
} else {
size = (4 * MiB) << sh;
}
return size;
}
static void sdram_ddr_set_bcr(Ppc4xxSdramDdrState *sdram, int i,
uint32_t bcr, int enabled)
{
if (sdram->bank[i].bcr & 1) {
/* Unmap RAM */
trace_ppc4xx_sdram_unmap(sdram_ddr_base(sdram->bank[i].bcr),
sdram_ddr_size(sdram->bank[i].bcr));
memory_region_del_subregion(get_system_memory(),
&sdram->bank[i].container);
memory_region_del_subregion(&sdram->bank[i].container,
&sdram->bank[i].ram);
object_unparent(OBJECT(&sdram->bank[i].container));
}
sdram->bank[i].bcr = bcr & 0xFFDEE001;
if (enabled && (bcr & 1)) {
trace_ppc4xx_sdram_map(sdram_ddr_base(bcr), sdram_ddr_size(bcr));
memory_region_init(&sdram->bank[i].container, NULL, "sdram-container",
sdram_ddr_size(bcr));
memory_region_add_subregion(&sdram->bank[i].container, 0,
&sdram->bank[i].ram);
memory_region_add_subregion(get_system_memory(),
sdram_ddr_base(bcr),
&sdram->bank[i].container);
}
}
static void sdram_ddr_map_bcr(Ppc4xxSdramDdrState *sdram)
{
int i;
for (i = 0; i < sdram->nbanks; i++) {
if (sdram->bank[i].size != 0) {
sdram_ddr_set_bcr(sdram, i, sdram_ddr_bcr(sdram->bank[i].base,
sdram->bank[i].size), 1);
} else {
sdram_ddr_set_bcr(sdram, i, 0, 0);
}
}
}
static void sdram_ddr_unmap_bcr(Ppc4xxSdramDdrState *sdram)
{
int i;
for (i = 0; i < sdram->nbanks; i++) {
trace_ppc4xx_sdram_unmap(sdram_ddr_base(sdram->bank[i].bcr),
sdram_ddr_size(sdram->bank[i].bcr));
memory_region_del_subregion(get_system_memory(),
&sdram->bank[i].ram);
}
}
static uint32_t sdram_ddr_dcr_read(void *opaque, int dcrn)
{
Ppc4xxSdramDdrState *sdram = opaque;
uint32_t ret;
switch (dcrn) {
case SDRAM0_CFGADDR:
ret = sdram->addr;
break;
case SDRAM0_CFGDATA:
switch (sdram->addr) {
case 0x00: /* SDRAM_BESR0 */
ret = sdram->besr0;
break;
case 0x08: /* SDRAM_BESR1 */
ret = sdram->besr1;
break;
case 0x10: /* SDRAM_BEAR */
ret = sdram->bear;
break;
case 0x20: /* SDRAM_CFG */
ret = sdram->cfg;
break;
case 0x24: /* SDRAM_STATUS */
ret = sdram->status;
break;
case 0x30: /* SDRAM_RTR */
ret = sdram->rtr;
break;
case 0x34: /* SDRAM_PMIT */
ret = sdram->pmit;
break;
case 0x40: /* SDRAM_B0CR */
ret = sdram->bank[0].bcr;
break;
case 0x44: /* SDRAM_B1CR */
ret = sdram->bank[1].bcr;
break;
case 0x48: /* SDRAM_B2CR */
ret = sdram->bank[2].bcr;
break;
case 0x4C: /* SDRAM_B3CR */
ret = sdram->bank[3].bcr;
break;
case 0x80: /* SDRAM_TR */
ret = -1; /* ? */
break;
case 0x94: /* SDRAM_ECCCFG */
ret = sdram->ecccfg;
break;
case 0x98: /* SDRAM_ECCESR */
ret = sdram->eccesr;
break;
default: /* Error */
ret = -1;
break;
}
break;
default:
/* Avoid gcc warning */
ret = 0;
break;
}
return ret;
}
static void sdram_ddr_dcr_write(void *opaque, int dcrn, uint32_t val)
{
Ppc4xxSdramDdrState *sdram = opaque;
switch (dcrn) {
case SDRAM0_CFGADDR:
sdram->addr = val;
break;
case SDRAM0_CFGDATA:
switch (sdram->addr) {
case 0x00: /* SDRAM_BESR0 */
sdram->besr0 &= ~val;
break;
case 0x08: /* SDRAM_BESR1 */
sdram->besr1 &= ~val;
break;
case 0x10: /* SDRAM_BEAR */
sdram->bear = val;
break;
case 0x20: /* SDRAM_CFG */
val &= 0xFFE00000;
if (!(sdram->cfg & 0x80000000) && (val & 0x80000000)) {
trace_ppc4xx_sdram_enable("enable");
/* validate all RAM mappings */
sdram_ddr_map_bcr(sdram);
sdram->status &= ~0x80000000;
} else if ((sdram->cfg & 0x80000000) && !(val & 0x80000000)) {
trace_ppc4xx_sdram_enable("disable");
/* invalidate all RAM mappings */
sdram_ddr_unmap_bcr(sdram);
sdram->status |= 0x80000000;
}
if (!(sdram->cfg & 0x40000000) && (val & 0x40000000)) {
sdram->status |= 0x40000000;
} else if ((sdram->cfg & 0x40000000) && !(val & 0x40000000)) {
sdram->status &= ~0x40000000;
}
sdram->cfg = val;
break;
case 0x24: /* SDRAM_STATUS */
/* Read-only register */
break;
case 0x30: /* SDRAM_RTR */
sdram->rtr = val & 0x3FF80000;
break;
case 0x34: /* SDRAM_PMIT */
sdram->pmit = (val & 0xF8000000) | 0x07C00000;
break;
case 0x40: /* SDRAM_B0CR */
sdram_ddr_set_bcr(sdram, 0, val, sdram->cfg & 0x80000000);
break;
case 0x44: /* SDRAM_B1CR */
sdram_ddr_set_bcr(sdram, 1, val, sdram->cfg & 0x80000000);
break;
case 0x48: /* SDRAM_B2CR */
sdram_ddr_set_bcr(sdram, 2, val, sdram->cfg & 0x80000000);
break;
case 0x4C: /* SDRAM_B3CR */
sdram_ddr_set_bcr(sdram, 3, val, sdram->cfg & 0x80000000);
break;
case 0x80: /* SDRAM_TR */
sdram->tr = val & 0x018FC01F;
break;
case 0x94: /* SDRAM_ECCCFG */
sdram->ecccfg = val & 0x00F00000;
break;
case 0x98: /* SDRAM_ECCESR */
val &= 0xFFF0F000;
if (sdram->eccesr == 0 && val != 0) {
qemu_irq_raise(sdram->irq);
} else if (sdram->eccesr != 0 && val == 0) {
qemu_irq_lower(sdram->irq);
}
sdram->eccesr = val;
break;
default: /* Error */
break;
}
break;
}
}
static void ppc4xx_sdram_ddr_reset(DeviceState *dev)
{
Ppc4xxSdramDdrState *sdram = PPC4xx_SDRAM_DDR(dev);
sdram->addr = 0;
sdram->bear = 0;
sdram->besr0 = 0; /* No error */
sdram->besr1 = 0; /* No error */
sdram->cfg = 0;
sdram->ecccfg = 0; /* No ECC */
sdram->eccesr = 0; /* No error */
sdram->pmit = 0x07C00000;
sdram->rtr = 0x05F00000;
sdram->tr = 0x00854009;
/* We pre-initialize RAM banks */
sdram->status = 0;
sdram->cfg = 0x00800000;
}
static void ppc4xx_sdram_ddr_realize(DeviceState *dev, Error **errp)
{
Ppc4xxSdramDdrState *s = PPC4xx_SDRAM_DDR(dev);
Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev);
const ram_addr_t valid_bank_sizes[] = {
256 * MiB, 128 * MiB, 64 * MiB, 32 * MiB, 16 * MiB, 8 * MiB, 4 * MiB, 0
};
if (s->nbanks < 1 || s->nbanks > 4) {
error_setg(errp, "Invalid number of RAM banks");
return;
}
if (!s->dram_mr) {
error_setg(errp, "Missing dram memory region");
return;
}
ppc4xx_sdram_banks(s->dram_mr, s->nbanks, s->bank, valid_bank_sizes);
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
ppc4xx_dcr_register(dcr, SDRAM0_CFGADDR,
s, &sdram_ddr_dcr_read, &sdram_ddr_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM0_CFGDATA,
s, &sdram_ddr_dcr_read, &sdram_ddr_dcr_write);
}
static Property ppc4xx_sdram_ddr_props[] = {
DEFINE_PROP_LINK("dram", Ppc4xxSdramDdrState, dram_mr, TYPE_MEMORY_REGION,
MemoryRegion *),
DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdrState, nbanks, 4),
DEFINE_PROP_END_OF_LIST(),
};
static void ppc4xx_sdram_ddr_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = ppc4xx_sdram_ddr_realize;
dc->reset = ppc4xx_sdram_ddr_reset;
/* Reason: only works as function of a ppc4xx SoC */
dc->user_creatable = false;
device_class_set_props(dc, ppc4xx_sdram_ddr_props);
}
void ppc4xx_sdram_ddr_enable(Ppc4xxSdramDdrState *s)
{
sdram_ddr_dcr_write(s, SDRAM0_CFGADDR, 0x20);
sdram_ddr_dcr_write(s, SDRAM0_CFGDATA, 0x80000000);
}
/*
* Split RAM between SDRAM banks.
*
* sdram_bank_sizes[] must be in descending order, that is sizes[i] > sizes[i+1]
* and must be 0-terminated.
*
* The 4xx SDRAM controller supports a small number of banks, and each bank
* must be one of a small set of sizes. The number of banks and the supported
* sizes varies by SoC.
*/
void ppc4xx_sdram_banks(MemoryRegion *ram, int nr_banks,
Ppc4xxSdramBank ram_banks[],
const ram_addr_t sdram_bank_sizes[])
{
ram_addr_t size_left = memory_region_size(ram);
ram_addr_t base = 0;
ram_addr_t bank_size;
int i;
int j;
for (i = 0; i < nr_banks; i++) {
for (j = 0; sdram_bank_sizes[j] != 0; j++) {
bank_size = sdram_bank_sizes[j];
if (bank_size <= size_left) {
char name[32];
ram_banks[i].base = base;
ram_banks[i].size = bank_size;
base += bank_size;
size_left -= bank_size;
snprintf(name, sizeof(name), "ppc4xx.sdram%d", i);
memory_region_init_alias(&ram_banks[i].ram, NULL, name, ram,
ram_banks[i].base, ram_banks[i].size);
break;
}
}
if (!size_left) {
/* No need to use the remaining banks. */
break;
}
}
if (size_left) {
ram_addr_t used_size = memory_region_size(ram) - size_left;
GString *s = g_string_new(NULL);
for (i = 0; sdram_bank_sizes[i]; i++) {
g_string_append_printf(s, "%" PRIi64 "%s",
sdram_bank_sizes[i] / MiB,
sdram_bank_sizes[i + 1] ? ", " : "");
}
error_report("at most %d bank%s of %s MiB each supported",
nr_banks, nr_banks == 1 ? "" : "s", s->str);
error_printf("Possible valid RAM size: %" PRIi64 " MiB\n",
used_size ? used_size / MiB : sdram_bank_sizes[i - 1] / MiB);
g_string_free(s, true);
exit(EXIT_FAILURE);
}
}
/*****************************************************************************/
/* MAL */
@ -963,11 +554,6 @@ static void ppc4xx_dcr_class_init(ObjectClass *oc, void *data)
static const TypeInfo ppc4xx_types[] = {
{
.name = TYPE_PPC4xx_SDRAM_DDR,
.parent = TYPE_PPC4xx_DCR_DEVICE,
.instance_size = sizeof(Ppc4xxSdramDdrState),
.class_init = ppc4xx_sdram_ddr_class_init,
}, {
.name = TYPE_PPC4xx_MAL,
.parent = TYPE_PPC4xx_DCR_DEVICE,
.instance_size = sizeof(Ppc4xxMalState),

757
hw/ppc/ppc4xx_sdram.c Normal file
View File

@ -0,0 +1,757 @@
/*
* QEMU PowerPC 4xx embedded processors SDRAM controller emulation
*
* DDR SDRAM controller:
* Copyright (c) 2007 Jocelyn Mayer
*
* 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.
*
* DDR2 SDRAM controller:
* Copyright (c) 2012 François Revol
* Copyright (c) 2016-2019 BALATON Zoltan
*
* This work is licensed under the GNU GPL license version 2 or later.
*/
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "qapi/error.h"
#include "qemu/log.h"
#include "exec/address-spaces.h" /* get_system_memory() */
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "hw/ppc/ppc4xx.h"
#include "trace.h"
/*****************************************************************************/
/* Shared functions */
/*
* Split RAM between SDRAM banks.
*
* sdram_bank_sizes[] must be in descending order, that is sizes[i] > sizes[i+1]
* and must be 0-terminated.
*
* The 4xx SDRAM controller supports a small number of banks, and each bank
* must be one of a small set of sizes. The number of banks and the supported
* sizes varies by SoC.
*/
static bool ppc4xx_sdram_banks(MemoryRegion *ram, int nr_banks,
Ppc4xxSdramBank ram_banks[],
const ram_addr_t sdram_bank_sizes[],
Error **errp)
{
ERRP_GUARD();
ram_addr_t size_left = memory_region_size(ram);
ram_addr_t base = 0;
ram_addr_t bank_size;
int i;
int j;
for (i = 0; i < nr_banks; i++) {
for (j = 0; sdram_bank_sizes[j] != 0; j++) {
bank_size = sdram_bank_sizes[j];
if (bank_size <= size_left) {
char name[32];
ram_banks[i].base = base;
ram_banks[i].size = bank_size;
base += bank_size;
size_left -= bank_size;
snprintf(name, sizeof(name), "ppc4xx.sdram%d", i);
memory_region_init_alias(&ram_banks[i].ram, NULL, name, ram,
ram_banks[i].base, ram_banks[i].size);
break;
}
}
if (!size_left) {
/* No need to use the remaining banks. */
break;
}
}
if (size_left) {
ram_addr_t used_size = memory_region_size(ram) - size_left;
GString *s = g_string_new(NULL);
for (i = 0; sdram_bank_sizes[i]; i++) {
g_string_append_printf(s, "%" PRIi64 "%s",
sdram_bank_sizes[i] / MiB,
sdram_bank_sizes[i + 1] ? ", " : "");
}
error_setg(errp, "Invalid SDRAM banks");
error_append_hint(errp, "at most %d bank%s of %s MiB each supported\n",
nr_banks, nr_banks == 1 ? "" : "s", s->str);
error_append_hint(errp, "Possible valid RAM size: %" PRIi64 " MiB\n",
used_size ? used_size / MiB : sdram_bank_sizes[i - 1] / MiB);
g_string_free(s, true);
return false;
}
return true;
}
static void sdram_bank_map(Ppc4xxSdramBank *bank)
{
trace_ppc4xx_sdram_map(bank->base, bank->size);
memory_region_init(&bank->container, NULL, "sdram-container", bank->size);
memory_region_add_subregion(&bank->container, 0, &bank->ram);
memory_region_add_subregion(get_system_memory(), bank->base,
&bank->container);
}
static void sdram_bank_unmap(Ppc4xxSdramBank *bank)
{
trace_ppc4xx_sdram_unmap(bank->base, bank->size);
memory_region_del_subregion(get_system_memory(), &bank->container);
memory_region_del_subregion(&bank->container, &bank->ram);
object_unparent(OBJECT(&bank->container));
}
static void sdram_bank_set_bcr(Ppc4xxSdramBank *bank, uint32_t bcr,
hwaddr base, hwaddr size, int enabled)
{
if (memory_region_is_mapped(&bank->container)) {
sdram_bank_unmap(bank);
}
bank->bcr = bcr;
bank->base = base;
bank->size = size;
if (enabled && (bcr & 1)) {
sdram_bank_map(bank);
}
}
enum {
SDRAM0_CFGADDR = 0x010,
SDRAM0_CFGDATA = 0x011,
};
/*****************************************************************************/
/* DDR SDRAM controller */
#define SDRAM_DDR_BCR_MASK 0xFFDEE001
static uint32_t sdram_ddr_bcr(hwaddr ram_base, hwaddr ram_size)
{
uint32_t bcr;
switch (ram_size) {
case 4 * MiB:
bcr = 0;
break;
case 8 * MiB:
bcr = 0x20000;
break;
case 16 * MiB:
bcr = 0x40000;
break;
case 32 * MiB:
bcr = 0x60000;
break;
case 64 * MiB:
bcr = 0x80000;
break;
case 128 * MiB:
bcr = 0xA0000;
break;
case 256 * MiB:
bcr = 0xC0000;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid RAM size 0x%" HWADDR_PRIx "\n", __func__,
ram_size);
return 0;
}
bcr |= ram_base & 0xFF800000;
bcr |= 1;
return bcr;
}
static inline hwaddr sdram_ddr_base(uint32_t bcr)
{
return bcr & 0xFF800000;
}
static hwaddr sdram_ddr_size(uint32_t bcr)
{
hwaddr size;
int sh;
sh = (bcr >> 17) & 0x7;
if (sh == 7) {
size = -1;
} else {
size = (4 * MiB) << sh;
}
return size;
}
static uint32_t sdram_ddr_dcr_read(void *opaque, int dcrn)
{
Ppc4xxSdramDdrState *s = opaque;
uint32_t ret;
switch (dcrn) {
case SDRAM0_CFGADDR:
ret = s->addr;
break;
case SDRAM0_CFGDATA:
switch (s->addr) {
case 0x00: /* SDRAM_BESR0 */
ret = s->besr0;
break;
case 0x08: /* SDRAM_BESR1 */
ret = s->besr1;
break;
case 0x10: /* SDRAM_BEAR */
ret = s->bear;
break;
case 0x20: /* SDRAM_CFG */
ret = s->cfg;
break;
case 0x24: /* SDRAM_STATUS */
ret = s->status;
break;
case 0x30: /* SDRAM_RTR */
ret = s->rtr;
break;
case 0x34: /* SDRAM_PMIT */
ret = s->pmit;
break;
case 0x40: /* SDRAM_B0CR */
ret = s->bank[0].bcr;
break;
case 0x44: /* SDRAM_B1CR */
ret = s->bank[1].bcr;
break;
case 0x48: /* SDRAM_B2CR */
ret = s->bank[2].bcr;
break;
case 0x4C: /* SDRAM_B3CR */
ret = s->bank[3].bcr;
break;
case 0x80: /* SDRAM_TR */
ret = -1; /* ? */
break;
case 0x94: /* SDRAM_ECCCFG */
ret = s->ecccfg;
break;
case 0x98: /* SDRAM_ECCESR */
ret = s->eccesr;
break;
default: /* Error */
ret = -1;
break;
}
break;
default:
/* Avoid gcc warning */
ret = 0;
break;
}
return ret;
}
static void sdram_ddr_dcr_write(void *opaque, int dcrn, uint32_t val)
{
Ppc4xxSdramDdrState *s = opaque;
int i;
switch (dcrn) {
case SDRAM0_CFGADDR:
s->addr = val;
break;
case SDRAM0_CFGDATA:
switch (s->addr) {
case 0x00: /* SDRAM_BESR0 */
s->besr0 &= ~val;
break;
case 0x08: /* SDRAM_BESR1 */
s->besr1 &= ~val;
break;
case 0x10: /* SDRAM_BEAR */
s->bear = val;
break;
case 0x20: /* SDRAM_CFG */
val &= 0xFFE00000;
if (!(s->cfg & 0x80000000) && (val & 0x80000000)) {
trace_ppc4xx_sdram_enable("enable");
/* validate all RAM mappings */
for (i = 0; i < s->nbanks; i++) {
if (s->bank[i].size) {
sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr,
s->bank[i].base, s->bank[i].size,
1);
}
}
s->status &= ~0x80000000;
} else if ((s->cfg & 0x80000000) && !(val & 0x80000000)) {
trace_ppc4xx_sdram_enable("disable");
/* invalidate all RAM mappings */
for (i = 0; i < s->nbanks; i++) {
if (s->bank[i].size) {
sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr,
s->bank[i].base, s->bank[i].size,
0);
}
}
s->status |= 0x80000000;
}
if (!(s->cfg & 0x40000000) && (val & 0x40000000)) {
s->status |= 0x40000000;
} else if ((s->cfg & 0x40000000) && !(val & 0x40000000)) {
s->status &= ~0x40000000;
}
s->cfg = val;
break;
case 0x24: /* SDRAM_STATUS */
/* Read-only register */
break;
case 0x30: /* SDRAM_RTR */
s->rtr = val & 0x3FF80000;
break;
case 0x34: /* SDRAM_PMIT */
s->pmit = (val & 0xF8000000) | 0x07C00000;
break;
case 0x40: /* SDRAM_B0CR */
case 0x44: /* SDRAM_B1CR */
case 0x48: /* SDRAM_B2CR */
case 0x4C: /* SDRAM_B3CR */
i = (s->addr - 0x40) / 4;
val &= SDRAM_DDR_BCR_MASK;
if (s->bank[i].size) {
sdram_bank_set_bcr(&s->bank[i], val,
sdram_ddr_base(val), sdram_ddr_size(val),
s->cfg & 0x80000000);
}
break;
case 0x80: /* SDRAM_TR */
s->tr = val & 0x018FC01F;
break;
case 0x94: /* SDRAM_ECCCFG */
s->ecccfg = val & 0x00F00000;
break;
case 0x98: /* SDRAM_ECCESR */
val &= 0xFFF0F000;
if (s->eccesr == 0 && val != 0) {
qemu_irq_raise(s->irq);
} else if (s->eccesr != 0 && val == 0) {
qemu_irq_lower(s->irq);
}
s->eccesr = val;
break;
default: /* Error */
break;
}
break;
}
}
static void ppc4xx_sdram_ddr_reset(DeviceState *dev)
{
Ppc4xxSdramDdrState *s = PPC4xx_SDRAM_DDR(dev);
s->addr = 0;
s->bear = 0;
s->besr0 = 0; /* No error */
s->besr1 = 0; /* No error */
s->cfg = 0;
s->ecccfg = 0; /* No ECC */
s->eccesr = 0; /* No error */
s->pmit = 0x07C00000;
s->rtr = 0x05F00000;
s->tr = 0x00854009;
/* We pre-initialize RAM banks */
s->status = 0;
s->cfg = 0x00800000;
}
static void ppc4xx_sdram_ddr_realize(DeviceState *dev, Error **errp)
{
Ppc4xxSdramDdrState *s = PPC4xx_SDRAM_DDR(dev);
Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev);
const ram_addr_t valid_bank_sizes[] = {
256 * MiB, 128 * MiB, 64 * MiB, 32 * MiB, 16 * MiB, 8 * MiB, 4 * MiB, 0
};
int i;
if (s->nbanks < 1 || s->nbanks > 4) {
error_setg(errp, "Invalid number of RAM banks");
return;
}
if (!s->dram_mr) {
error_setg(errp, "Missing dram memory region");
return;
}
if (!ppc4xx_sdram_banks(s->dram_mr, s->nbanks, s->bank,
valid_bank_sizes, errp)) {
return;
}
for (i = 0; i < s->nbanks; i++) {
if (s->bank[i].size) {
s->bank[i].bcr = sdram_ddr_bcr(s->bank[i].base, s->bank[i].size);
sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr,
s->bank[i].base, s->bank[i].size, 0);
} else {
sdram_bank_set_bcr(&s->bank[i], 0, 0, 0, 0);
}
trace_ppc4xx_sdram_init(sdram_ddr_base(s->bank[i].bcr),
sdram_ddr_size(s->bank[i].bcr),
s->bank[i].bcr);
}
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
ppc4xx_dcr_register(dcr, SDRAM0_CFGADDR,
s, &sdram_ddr_dcr_read, &sdram_ddr_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM0_CFGDATA,
s, &sdram_ddr_dcr_read, &sdram_ddr_dcr_write);
}
static Property ppc4xx_sdram_ddr_props[] = {
DEFINE_PROP_LINK("dram", Ppc4xxSdramDdrState, dram_mr, TYPE_MEMORY_REGION,
MemoryRegion *),
DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdrState, nbanks, 4),
DEFINE_PROP_END_OF_LIST(),
};
static void ppc4xx_sdram_ddr_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = ppc4xx_sdram_ddr_realize;
dc->reset = ppc4xx_sdram_ddr_reset;
/* Reason: only works as function of a ppc4xx SoC */
dc->user_creatable = false;
device_class_set_props(dc, ppc4xx_sdram_ddr_props);
}
void ppc4xx_sdram_ddr_enable(Ppc4xxSdramDdrState *s)
{
sdram_ddr_dcr_write(s, SDRAM0_CFGADDR, 0x20);
sdram_ddr_dcr_write(s, SDRAM0_CFGDATA, 0x80000000);
}
/*****************************************************************************/
/* DDR2 SDRAM controller */
#define SDRAM_DDR2_BCR_MASK 0xffe0ffc1
enum {
SDRAM_R0BAS = 0x40,
SDRAM_R1BAS,
SDRAM_R2BAS,
SDRAM_R3BAS,
SDRAM_CONF1HB = 0x45,
SDRAM_PLBADDULL = 0x4a,
SDRAM_CONF1LL = 0x4b,
SDRAM_CONFPATHB = 0x4f,
SDRAM_PLBADDUHB = 0x50,
};
static uint32_t sdram_ddr2_bcr(hwaddr ram_base, hwaddr ram_size)
{
uint32_t bcr;
switch (ram_size) {
case 8 * MiB:
bcr = 0xffc0;
break;
case 16 * MiB:
bcr = 0xff80;
break;
case 32 * MiB:
bcr = 0xff00;
break;
case 64 * MiB:
bcr = 0xfe00;
break;
case 128 * MiB:
bcr = 0xfc00;
break;
case 256 * MiB:
bcr = 0xf800;
break;
case 512 * MiB:
bcr = 0xf000;
break;
case 1 * GiB:
bcr = 0xe000;
break;
case 2 * GiB:
bcr = 0xc000;
break;
case 4 * GiB:
bcr = 0x8000;
break;
default:
error_report("invalid RAM size " TARGET_FMT_plx, ram_size);
return 0;
}
bcr |= ram_base >> 2 & 0xffe00000;
bcr |= 1;
return bcr;
}
static inline hwaddr sdram_ddr2_base(uint32_t bcr)
{
return (bcr & 0xffe00000) << 2;
}
static hwaddr sdram_ddr2_size(uint32_t bcr)
{
hwaddr size;
int sh;
sh = 1024 - ((bcr >> 6) & 0x3ff);
size = 8 * MiB * sh;
return size;
}
static uint32_t sdram_ddr2_dcr_read(void *opaque, int dcrn)
{
Ppc4xxSdramDdr2State *s = opaque;
uint32_t ret = 0;
switch (dcrn) {
case SDRAM_R0BAS:
case SDRAM_R1BAS:
case SDRAM_R2BAS:
case SDRAM_R3BAS:
if (s->bank[dcrn - SDRAM_R0BAS].size) {
ret = sdram_ddr2_bcr(s->bank[dcrn - SDRAM_R0BAS].base,
s->bank[dcrn - SDRAM_R0BAS].size);
}
break;
case SDRAM_CONF1HB:
case SDRAM_CONF1LL:
case SDRAM_CONFPATHB:
case SDRAM_PLBADDULL:
case SDRAM_PLBADDUHB:
break;
case SDRAM0_CFGADDR:
ret = s->addr;
break;
case SDRAM0_CFGDATA:
switch (s->addr) {
case 0x14: /* SDRAM_MCSTAT (405EX) */
case 0x1F:
ret = 0x80000000;
break;
case 0x21: /* SDRAM_MCOPT2 */
ret = s->mcopt2;
break;
case 0x40: /* SDRAM_MB0CF */
ret = 0x00008001;
break;
case 0x7A: /* SDRAM_DLCR */
ret = 0x02000000;
break;
case 0xE1: /* SDR0_DDR0 */
ret = SDR0_DDR0_DDRM_ENCODE(1) | SDR0_DDR0_DDRM_DDR1;
break;
default:
break;
}
break;
default:
break;
}
return ret;
}
#define SDRAM_DDR2_MCOPT2_DCEN BIT(27)
static void sdram_ddr2_dcr_write(void *opaque, int dcrn, uint32_t val)
{
Ppc4xxSdramDdr2State *s = opaque;
int i;
switch (dcrn) {
case SDRAM_R0BAS:
case SDRAM_R1BAS:
case SDRAM_R2BAS:
case SDRAM_R3BAS:
case SDRAM_CONF1HB:
case SDRAM_CONF1LL:
case SDRAM_CONFPATHB:
case SDRAM_PLBADDULL:
case SDRAM_PLBADDUHB:
break;
case SDRAM0_CFGADDR:
s->addr = val;
break;
case SDRAM0_CFGDATA:
switch (s->addr) {
case 0x00: /* B0CR */
break;
case 0x21: /* SDRAM_MCOPT2 */
if (!(s->mcopt2 & SDRAM_DDR2_MCOPT2_DCEN) &&
(val & SDRAM_DDR2_MCOPT2_DCEN)) {
trace_ppc4xx_sdram_enable("enable");
/* validate all RAM mappings */
for (i = 0; i < s->nbanks; i++) {
if (s->bank[i].size) {
sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr,
s->bank[i].base, s->bank[i].size,
1);
}
}
s->mcopt2 |= SDRAM_DDR2_MCOPT2_DCEN;
} else if ((s->mcopt2 & SDRAM_DDR2_MCOPT2_DCEN) &&
!(val & SDRAM_DDR2_MCOPT2_DCEN)) {
trace_ppc4xx_sdram_enable("disable");
/* invalidate all RAM mappings */
for (i = 0; i < s->nbanks; i++) {
if (s->bank[i].size) {
sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr,
s->bank[i].base, s->bank[i].size,
0);
}
}
s->mcopt2 &= ~SDRAM_DDR2_MCOPT2_DCEN;
}
break;
default:
break;
}
break;
default:
break;
}
}
static void ppc4xx_sdram_ddr2_reset(DeviceState *dev)
{
Ppc4xxSdramDdr2State *s = PPC4xx_SDRAM_DDR2(dev);
s->addr = 0;
s->mcopt2 = 0;
}
static void ppc4xx_sdram_ddr2_realize(DeviceState *dev, Error **errp)
{
Ppc4xxSdramDdr2State *s = PPC4xx_SDRAM_DDR2(dev);
Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev);
/*
* SoC also has 4 GiB but that causes problem with 32 bit
* builds (4*GiB overflows the 32 bit ram_addr_t).
*/
const ram_addr_t valid_bank_sizes[] = {
2 * GiB, 1 * GiB, 512 * MiB, 256 * MiB, 128 * MiB,
64 * MiB, 32 * MiB, 16 * MiB, 8 * MiB, 0
};
int i;
if (s->nbanks < 1 || s->nbanks > 4) {
error_setg(errp, "Invalid number of RAM banks");
return;
}
if (!s->dram_mr) {
error_setg(errp, "Missing dram memory region");
return;
}
if (!ppc4xx_sdram_banks(s->dram_mr, s->nbanks, s->bank,
valid_bank_sizes, errp)) {
return;
}
for (i = 0; i < s->nbanks; i++) {
if (s->bank[i].size) {
s->bank[i].bcr = sdram_ddr2_bcr(s->bank[i].base, s->bank[i].size);
s->bank[i].bcr &= SDRAM_DDR2_BCR_MASK;
sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr,
s->bank[i].base, s->bank[i].size, 0);
} else {
sdram_bank_set_bcr(&s->bank[i], 0, 0, 0, 0);
}
trace_ppc4xx_sdram_init(sdram_ddr2_base(s->bank[i].bcr),
sdram_ddr2_size(s->bank[i].bcr),
s->bank[i].bcr);
}
ppc4xx_dcr_register(dcr, SDRAM0_CFGADDR,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM0_CFGDATA,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_R0BAS,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_R1BAS,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_R2BAS,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_R3BAS,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_CONF1HB,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_PLBADDULL,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_CONF1LL,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_CONFPATHB,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
ppc4xx_dcr_register(dcr, SDRAM_PLBADDUHB,
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
}
static Property ppc4xx_sdram_ddr2_props[] = {
DEFINE_PROP_LINK("dram", Ppc4xxSdramDdr2State, dram_mr, TYPE_MEMORY_REGION,
MemoryRegion *),
DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdr2State, nbanks, 4),
DEFINE_PROP_END_OF_LIST(),
};
static void ppc4xx_sdram_ddr2_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = ppc4xx_sdram_ddr2_realize;
dc->reset = ppc4xx_sdram_ddr2_reset;
/* Reason: only works as function of a ppc4xx SoC */
dc->user_creatable = false;
device_class_set_props(dc, ppc4xx_sdram_ddr2_props);
}
void ppc4xx_sdram_ddr2_enable(Ppc4xxSdramDdr2State *s)
{
sdram_ddr2_dcr_write(s, SDRAM0_CFGADDR, 0x21);
sdram_ddr2_dcr_write(s, SDRAM0_CFGDATA, 0x08000000);
}
static const TypeInfo ppc4xx_sdram_types[] = {
{
.name = TYPE_PPC4xx_SDRAM_DDR,
.parent = TYPE_PPC4xx_DCR_DEVICE,
.instance_size = sizeof(Ppc4xxSdramDdrState),
.class_init = ppc4xx_sdram_ddr_class_init,
}, {
.name = TYPE_PPC4xx_SDRAM_DDR2,
.parent = TYPE_PPC4xx_DCR_DEVICE,
.instance_size = sizeof(Ppc4xxSdramDdr2State),
.class_init = ppc4xx_sdram_ddr2_class_init,
}
};
DEFINE_TYPES(ppc4xx_sdram_types)

View File

@ -490,6 +490,7 @@ static target_ulong h_cede(PowerPCCPU *cpu, SpaprMachineState *spapr,
env->msr |= (1ULL << MSR_EE);
hreg_compute_hflags(env);
ppc_maybe_interrupt(env);
if (spapr_cpu->prod) {
spapr_cpu->prod = false;
@ -500,6 +501,7 @@ static target_ulong h_cede(PowerPCCPU *cpu, SpaprMachineState *spapr,
cs->halted = 1;
cs->exception_index = EXCP_HLT;
cs->exit_request = 1;
ppc_maybe_interrupt(env);
}
return H_SUCCESS;
@ -521,6 +523,7 @@ static target_ulong h_confer_self(PowerPCCPU *cpu)
cs->halted = 1;
cs->exception_index = EXCP_HALTED;
cs->exit_request = 1;
ppc_maybe_interrupt(&cpu->env);
return H_SUCCESS;
}
@ -633,6 +636,7 @@ static target_ulong h_prod(PowerPCCPU *cpu, SpaprMachineState *spapr,
spapr_cpu = spapr_cpu_state(tcpu);
spapr_cpu->prod = true;
cs->halted = 0;
ppc_maybe_interrupt(&cpu->env);
qemu_cpu_kick(cs);
return H_SUCCESS;
@ -1669,6 +1673,7 @@ static target_ulong h_enter_nested(PowerPCCPU *cpu,
spapr_cpu->in_nested = true;
hreg_compute_hflags(env);
ppc_maybe_interrupt(env);
tlb_flush(cs);
env->reserve_addr = -1; /* Reset the reservation */
@ -1810,6 +1815,7 @@ out_restore_l1:
spapr_cpu->in_nested = false;
hreg_compute_hflags(env);
ppc_maybe_interrupt(env);
tlb_flush(cs);
env->reserve_addr = -1; /* Reset the reservation */

View File

@ -214,9 +214,9 @@ static void rtas_stop_self(PowerPCCPU *cpu, SpaprMachineState *spapr,
* guest.
* For the same reason, set PSSCR_EC.
*/
ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm);
env->spr[SPR_PSSCR] |= PSSCR_EC;
cs->halted = 1;
ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm);
kvmppc_set_reg_ppc_online(cpu, 0);
qemu_cpu_kick(cs);
}

View File

@ -127,7 +127,7 @@ ppc40x_set_tb_clk(uint32_t value) "new frequency %" PRIu32
ppc40x_timers_init(uint32_t value) "frequency %" PRIu32
ppc_irq_set(void *env, uint32_t pin, uint32_t level) "env [%p] pin %d level %d"
ppc_irq_set_exit(void *env, uint32_t n_IRQ, uint32_t level, uint32_t pending, uint32_t request) "env [%p] n_IRQ %d level %d => pending 0x%08" PRIx32 " req 0x%08" PRIx32
ppc_irq_set_exit(void *env, uint32_t irq, uint32_t level, uint32_t pending, uint32_t request) "env [%p] irq 0x%05" PRIx32 " level %d => pending 0x%08" PRIx32 " req 0x%08" PRIx32
ppc_irq_set_state(const char *name, uint32_t level) "\"%s\" level %d"
ppc_irq_reset(const char *name) "%s"
ppc_irq_cpu(const char *action) "%s"
@ -179,3 +179,4 @@ ppc405ep_clocks_setup(const char *trace) "%s"
ppc4xx_sdram_enable(const char *trace) "%s SDRAM controller"
ppc4xx_sdram_unmap(uint64_t addr, uint64_t size) "Unmap RAM area 0x%" PRIx64 " size 0x%" PRIx64
ppc4xx_sdram_map(uint64_t addr, uint64_t size) "Map RAM area 0x%" PRIx64 " size 0x%" PRIx64
ppc4xx_sdram_init(uint64_t base, uint64_t size, uint32_t bcr) "Init RAM area 0x%" PRIx64 " size 0x%" PRIx64 " bcr 0x%x"

View File

@ -288,26 +288,6 @@ enum {
extern const VMStateDescription sdhci_vmstate;
#define ESDHC_MIX_CTRL 0x48
#define ESDHC_VENDOR_SPEC 0xc0
#define ESDHC_IMX_FRC_SDCLK_ON (1 << 8)
#define ESDHC_DLL_CTRL 0x60
#define ESDHC_TUNING_CTRL 0xcc
#define ESDHC_TUNE_CTRL_STATUS 0x68
#define ESDHC_WTMK_LVL 0x44
/* Undocumented register used by guests working around erratum ERR004536 */
#define ESDHC_UNDOCUMENTED_REG27 0x6c
#define ESDHC_CTRL_4BITBUS (0x1 << 1)
#define ESDHC_CTRL_8BITBUS (0x2 << 1)
#define ESDHC_PRNSTS_SDSTB (1 << 3)
/*
* Default SD/MMC host controller features information, which will be
* presented in CAPABILITIES register of generic SD host controller at reset.

View File

@ -1577,6 +1577,25 @@ static const TypeInfo sdhci_bus_info = {
/* --- qdev i.MX eSDHC --- */
#define USDHC_MIX_CTRL 0x48
#define USDHC_VENDOR_SPEC 0xc0
#define USDHC_IMX_FRC_SDCLK_ON (1 << 8)
#define USDHC_DLL_CTRL 0x60
#define USDHC_TUNING_CTRL 0xcc
#define USDHC_TUNE_CTRL_STATUS 0x68
#define USDHC_WTMK_LVL 0x44
/* Undocumented register used by guests working around erratum ERR004536 */
#define USDHC_UNDOCUMENTED_REG27 0x6c
#define USDHC_CTRL_4BITBUS (0x1 << 1)
#define USDHC_CTRL_8BITBUS (0x2 << 1)
#define USDHC_PRNSTS_SDSTB (1 << 3)
static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
{
SDHCIState *s = SYSBUS_SDHCI(opaque);
@ -1596,11 +1615,11 @@ static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
hostctl1 = SDHC_DMA_TYPE(s->hostctl1) << (8 - 3);
if (s->hostctl1 & SDHC_CTRL_8BITBUS) {
hostctl1 |= ESDHC_CTRL_8BITBUS;
hostctl1 |= USDHC_CTRL_8BITBUS;
}
if (s->hostctl1 & SDHC_CTRL_4BITBUS) {
hostctl1 |= ESDHC_CTRL_4BITBUS;
hostctl1 |= USDHC_CTRL_4BITBUS;
}
ret = hostctl1;
@ -1611,21 +1630,21 @@ static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
case SDHC_PRNSTS:
/* Add SDSTB (SD Clock Stable) bit to PRNSTS */
ret = sdhci_read(opaque, offset, size) & ~ESDHC_PRNSTS_SDSTB;
ret = sdhci_read(opaque, offset, size) & ~USDHC_PRNSTS_SDSTB;
if (s->clkcon & SDHC_CLOCK_INT_STABLE) {
ret |= ESDHC_PRNSTS_SDSTB;
ret |= USDHC_PRNSTS_SDSTB;
}
break;
case ESDHC_VENDOR_SPEC:
case USDHC_VENDOR_SPEC:
ret = s->vendor_spec;
break;
case ESDHC_DLL_CTRL:
case ESDHC_TUNE_CTRL_STATUS:
case ESDHC_UNDOCUMENTED_REG27:
case ESDHC_TUNING_CTRL:
case ESDHC_MIX_CTRL:
case ESDHC_WTMK_LVL:
case USDHC_DLL_CTRL:
case USDHC_TUNE_CTRL_STATUS:
case USDHC_UNDOCUMENTED_REG27:
case USDHC_TUNING_CTRL:
case USDHC_MIX_CTRL:
case USDHC_WTMK_LVL:
ret = 0;
break;
}
@ -1641,18 +1660,18 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
uint32_t value = (uint32_t)val;
switch (offset) {
case ESDHC_DLL_CTRL:
case ESDHC_TUNE_CTRL_STATUS:
case ESDHC_UNDOCUMENTED_REG27:
case ESDHC_TUNING_CTRL:
case ESDHC_WTMK_LVL:
case USDHC_DLL_CTRL:
case USDHC_TUNE_CTRL_STATUS:
case USDHC_UNDOCUMENTED_REG27:
case USDHC_TUNING_CTRL:
case USDHC_WTMK_LVL:
break;
case ESDHC_VENDOR_SPEC:
case USDHC_VENDOR_SPEC:
s->vendor_spec = value;
switch (s->vendor) {
case SDHCI_VENDOR_IMX:
if (value & ESDHC_IMX_FRC_SDCLK_ON) {
if (value & USDHC_IMX_FRC_SDCLK_ON) {
s->prnsts &= ~SDHC_IMX_CLOCK_GATE_OFF;
} else {
s->prnsts |= SDHC_IMX_CLOCK_GATE_OFF;
@ -1721,12 +1740,12 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
* Second, split "Data Transfer Width" from bits 2 and 1 in to
* bits 5 and 1
*/
if (value & ESDHC_CTRL_8BITBUS) {
if (value & USDHC_CTRL_8BITBUS) {
hostctl1 |= SDHC_CTRL_8BITBUS;
}
if (value & ESDHC_CTRL_4BITBUS) {
hostctl1 |= ESDHC_CTRL_4BITBUS;
if (value & USDHC_CTRL_4BITBUS) {
hostctl1 |= USDHC_CTRL_4BITBUS;
}
/*
@ -1749,7 +1768,7 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
sdhci_write(opaque, offset, value, size);
break;
case ESDHC_MIX_CTRL:
case USDHC_MIX_CTRL:
/*
* So, when SD/MMC stack in Linux tries to write to "Transfer
* Mode Register", ESDHC i.MX quirk code will translate it

View File

@ -29,18 +29,6 @@
#include "exec/memory.h"
#include "hw/sysbus.h"
typedef struct {
MemoryRegion ram;
MemoryRegion container; /* used for clipping */
hwaddr base;
hwaddr size;
uint32_t bcr;
} Ppc4xxSdramBank;
void ppc4xx_sdram_banks(MemoryRegion *ram, int nr_banks,
Ppc4xxSdramBank ram_banks[],
const ram_addr_t sdram_bank_sizes[]);
#define TYPE_PPC4xx_PCI_HOST_BRIDGE "ppc4xx-pcihost"
/*
@ -111,6 +99,14 @@ struct Ppc4xxEbcState {
};
/* SDRAM DDR controller */
typedef struct {
MemoryRegion ram;
MemoryRegion container; /* used for clipping */
hwaddr base;
hwaddr size;
uint32_t bcr;
} Ppc4xxSdramBank;
#define SDR0_DDR0_DDRM_ENCODE(n) ((((unsigned long)(n)) & 0x03) << 29)
#define SDR0_DDR0_DDRM_DDR1 0x20000000
#define SDR0_DDR0_DDRM_DDR2 0x40000000

View File

@ -73,6 +73,7 @@ void ppc_store_msr(CPUPPCState *env, target_ulong value)
hreg_store_msr(env, value, 0);
}
#if !defined(CONFIG_USER_ONLY)
void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val)
{
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
@ -81,7 +82,10 @@ void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val)
env->spr[SPR_LPCR] = val & pcc->lpcr_mask;
/* The gtse bit affects hflags */
hreg_compute_hflags(env);
ppc_maybe_interrupt(env);
}
#endif
static inline void fpscr_set_rounding_mode(CPUPPCState *env)
{

View File

@ -696,7 +696,9 @@ enum {
HFLAGS_PR = 14, /* MSR_PR */
HFLAGS_PMCC0 = 15, /* MMCR0 PMCC bit 0 */
HFLAGS_PMCC1 = 16, /* MMCR0 PMCC bit 1 */
HFLAGS_INSN_CNT = 17, /* PMU instruction count enabled */
HFLAGS_PMCJCE = 17, /* MMCR0 PMCjCE bit */
HFLAGS_PMC_OTHER = 18, /* PMC other than PMC5-6 is enabled */
HFLAGS_INSN_CNT = 19, /* PMU instruction count enabled */
HFLAGS_VSX = 23, /* MSR_VSX if cpu has VSX */
HFLAGS_VR = 25, /* MSR_VR if cpu has VRE */
@ -1358,6 +1360,7 @@ int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
int ppc32_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs,
int cpuid, DumpState *s);
#ifndef CONFIG_USER_ONLY
void ppc_maybe_interrupt(CPUPPCState *env);
void ppc_cpu_do_interrupt(CPUState *cpu);
bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req);
void ppc_cpu_do_system_reset(CPUState *cs);
@ -1370,9 +1373,9 @@ void ppc_translate_init(void);
#if !defined(CONFIG_USER_ONLY)
void ppc_store_sdr1(CPUPPCState *env, target_ulong value);
void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val);
#endif /* !defined(CONFIG_USER_ONLY) */
void ppc_store_msr(CPUPPCState *env, target_ulong value);
void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val);
void ppc_cpu_list(void);
@ -2416,27 +2419,27 @@ enum {
/* Hardware exceptions definitions */
enum {
/* External hardware exception sources */
PPC_INTERRUPT_RESET = 0, /* Reset exception */
PPC_INTERRUPT_WAKEUP, /* Wakeup exception */
PPC_INTERRUPT_MCK, /* Machine check exception */
PPC_INTERRUPT_EXT, /* External interrupt */
PPC_INTERRUPT_SMI, /* System management interrupt */
PPC_INTERRUPT_CEXT, /* Critical external interrupt */
PPC_INTERRUPT_DEBUG, /* External debug exception */
PPC_INTERRUPT_THERM, /* Thermal exception */
PPC_INTERRUPT_RESET = 0x00001, /* Reset exception */
PPC_INTERRUPT_WAKEUP = 0x00002, /* Wakeup exception */
PPC_INTERRUPT_MCK = 0x00004, /* Machine check exception */
PPC_INTERRUPT_EXT = 0x00008, /* External interrupt */
PPC_INTERRUPT_SMI = 0x00010, /* System management interrupt */
PPC_INTERRUPT_CEXT = 0x00020, /* Critical external interrupt */
PPC_INTERRUPT_DEBUG = 0x00040, /* External debug exception */
PPC_INTERRUPT_THERM = 0x00080, /* Thermal exception */
/* Internal hardware exception sources */
PPC_INTERRUPT_DECR, /* Decrementer exception */
PPC_INTERRUPT_HDECR, /* Hypervisor decrementer exception */
PPC_INTERRUPT_PIT, /* Programmable interval timer interrupt */
PPC_INTERRUPT_FIT, /* Fixed interval timer interrupt */
PPC_INTERRUPT_WDT, /* Watchdog timer interrupt */
PPC_INTERRUPT_CDOORBELL, /* Critical doorbell interrupt */
PPC_INTERRUPT_DOORBELL, /* Doorbell interrupt */
PPC_INTERRUPT_PERFM, /* Performance monitor interrupt */
PPC_INTERRUPT_HMI, /* Hypervisor Maintenance interrupt */
PPC_INTERRUPT_HDOORBELL, /* Hypervisor Doorbell interrupt */
PPC_INTERRUPT_HVIRT, /* Hypervisor virtualization interrupt */
PPC_INTERRUPT_EBB, /* Event-based Branch exception */
PPC_INTERRUPT_DECR = 0x00100, /* Decrementer exception */
PPC_INTERRUPT_HDECR = 0x00200, /* Hypervisor decrementer exception */
PPC_INTERRUPT_PIT = 0x00400, /* Programmable interval timer int. */
PPC_INTERRUPT_FIT = 0x00800, /* Fixed interval timer interrupt */
PPC_INTERRUPT_WDT = 0x01000, /* Watchdog timer interrupt */
PPC_INTERRUPT_CDOORBELL = 0x02000, /* Critical doorbell interrupt */
PPC_INTERRUPT_DOORBELL = 0x04000, /* Doorbell interrupt */
PPC_INTERRUPT_PERFM = 0x08000, /* Performance monitor interrupt */
PPC_INTERRUPT_HMI = 0x10000, /* Hypervisor Maintenance interrupt */
PPC_INTERRUPT_HDOORBELL = 0x20000, /* Hypervisor Doorbell interrupt */
PPC_INTERRUPT_HVIRT = 0x40000, /* Hypervisor virtualization interrupt */
PPC_INTERRUPT_EBB = 0x80000, /* Event-based Branch exception */
};
/* Processor Compatibility mask (PCR) */

View File

@ -5960,46 +5960,10 @@ static bool ppc_pvr_match_power7(PowerPCCPUClass *pcc, uint32_t pvr, bool best)
return true;
}
static bool cpu_has_work_POWER7(CPUState *cs)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);
CPUPPCState *env = &cpu->env;
if (cs->halted) {
if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
return false;
}
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
(env->spr[SPR_LPCR] & LPCR_P7_PECE0)) {
return true;
}
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
(env->spr[SPR_LPCR] & LPCR_P7_PECE1)) {
return true;
}
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK)) &&
(env->spr[SPR_LPCR] & LPCR_P7_PECE2)) {
return true;
}
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HMI)) &&
(env->spr[SPR_LPCR] & LPCR_P7_PECE2)) {
return true;
}
if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
return true;
}
return false;
} else {
return FIELD_EX64(env->msr, MSR, EE) &&
(cs->interrupt_request & CPU_INTERRUPT_HARD);
}
}
POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
CPUClass *cc = CPU_CLASS(oc);
dc->fw_name = "PowerPC,POWER7";
dc->desc = "POWER7";
@ -6008,7 +5972,6 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
pcc->pcr_supported = PCR_COMPAT_2_06 | PCR_COMPAT_2_05;
pcc->init_proc = init_proc_POWER7;
pcc->check_pow = check_pow_nocheck;
cc->has_work = cpu_has_work_POWER7;
pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
@ -6133,54 +6096,10 @@ static bool ppc_pvr_match_power8(PowerPCCPUClass *pcc, uint32_t pvr, bool best)
return true;
}
static bool cpu_has_work_POWER8(CPUState *cs)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);
CPUPPCState *env = &cpu->env;
if (cs->halted) {
if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
return false;
}
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
(env->spr[SPR_LPCR] & LPCR_P8_PECE2)) {
return true;
}
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
(env->spr[SPR_LPCR] & LPCR_P8_PECE3)) {
return true;
}
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK)) &&
(env->spr[SPR_LPCR] & LPCR_P8_PECE4)) {
return true;
}
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HMI)) &&
(env->spr[SPR_LPCR] & LPCR_P8_PECE4)) {
return true;
}
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DOORBELL)) &&
(env->spr[SPR_LPCR] & LPCR_P8_PECE0)) {
return true;
}
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HDOORBELL)) &&
(env->spr[SPR_LPCR] & LPCR_P8_PECE1)) {
return true;
}
if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
return true;
}
return false;
} else {
return FIELD_EX64(env->msr, MSR, EE) &&
(cs->interrupt_request & CPU_INTERRUPT_HARD);
}
}
POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
CPUClass *cc = CPU_CLASS(oc);
dc->fw_name = "PowerPC,POWER8";
dc->desc = "POWER8";
@ -6189,7 +6108,6 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
pcc->pcr_supported = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05;
pcc->init_proc = init_proc_POWER8;
pcc->check_pow = check_pow_nocheck;
cc->has_work = cpu_has_work_POWER8;
pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
@ -6351,71 +6269,10 @@ static bool ppc_pvr_match_power9(PowerPCCPUClass *pcc, uint32_t pvr, bool best)
return false;
}
static bool cpu_has_work_POWER9(CPUState *cs)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);
CPUPPCState *env = &cpu->env;
if (cs->halted) {
uint64_t psscr = env->spr[SPR_PSSCR];
if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
return false;
}
/* If EC is clear, just return true on any pending interrupt */
if (!(psscr & PSSCR_EC)) {
return true;
}
/* External Exception */
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
(env->spr[SPR_LPCR] & LPCR_EEE)) {
bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC);
if (!heic || !FIELD_EX64_HV(env->msr) ||
FIELD_EX64(env->msr, MSR, PR)) {
return true;
}
}
/* Decrementer Exception */
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
(env->spr[SPR_LPCR] & LPCR_DEE)) {
return true;
}
/* Machine Check or Hypervisor Maintenance Exception */
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK |
1u << PPC_INTERRUPT_HMI)) && (env->spr[SPR_LPCR] & LPCR_OEE)) {
return true;
}
/* Privileged Doorbell Exception */
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DOORBELL)) &&
(env->spr[SPR_LPCR] & LPCR_PDEE)) {
return true;
}
/* Hypervisor Doorbell Exception */
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HDOORBELL)) &&
(env->spr[SPR_LPCR] & LPCR_HDEE)) {
return true;
}
/* Hypervisor virtualization exception */
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HVIRT)) &&
(env->spr[SPR_LPCR] & LPCR_HVEE)) {
return true;
}
if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
return true;
}
return false;
} else {
return FIELD_EX64(env->msr, MSR, EE) &&
(cs->interrupt_request & CPU_INTERRUPT_HARD);
}
}
POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
CPUClass *cc = CPU_CLASS(oc);
dc->fw_name = "PowerPC,POWER9";
dc->desc = "POWER9";
@ -6425,7 +6282,6 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
PCR_COMPAT_2_05;
pcc->init_proc = init_proc_POWER9;
pcc->check_pow = check_pow_nocheck;
cc->has_work = cpu_has_work_POWER9;
pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
@ -6584,71 +6440,10 @@ static bool ppc_pvr_match_power10(PowerPCCPUClass *pcc, uint32_t pvr, bool best)
return false;
}
static bool cpu_has_work_POWER10(CPUState *cs)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);
CPUPPCState *env = &cpu->env;
if (cs->halted) {
uint64_t psscr = env->spr[SPR_PSSCR];
if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
return false;
}
/* If EC is clear, just return true on any pending interrupt */
if (!(psscr & PSSCR_EC)) {
return true;
}
/* External Exception */
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
(env->spr[SPR_LPCR] & LPCR_EEE)) {
bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC);
if (!heic || !FIELD_EX64_HV(env->msr) ||
FIELD_EX64(env->msr, MSR, PR)) {
return true;
}
}
/* Decrementer Exception */
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
(env->spr[SPR_LPCR] & LPCR_DEE)) {
return true;
}
/* Machine Check or Hypervisor Maintenance Exception */
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK |
1u << PPC_INTERRUPT_HMI)) && (env->spr[SPR_LPCR] & LPCR_OEE)) {
return true;
}
/* Privileged Doorbell Exception */
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DOORBELL)) &&
(env->spr[SPR_LPCR] & LPCR_PDEE)) {
return true;
}
/* Hypervisor Doorbell Exception */
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HDOORBELL)) &&
(env->spr[SPR_LPCR] & LPCR_HDEE)) {
return true;
}
/* Hypervisor virtualization exception */
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HVIRT)) &&
(env->spr[SPR_LPCR] & LPCR_HVEE)) {
return true;
}
if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
return true;
}
return false;
} else {
return FIELD_EX64(env->msr, MSR, EE) &&
(cs->interrupt_request & CPU_INTERRUPT_HARD);
}
}
POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
CPUClass *cc = CPU_CLASS(oc);
dc->fw_name = "PowerPC,POWER10";
dc->desc = "POWER10";
@ -6659,7 +6454,6 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data)
PCR_COMPAT_2_06 | PCR_COMPAT_2_05;
pcc->init_proc = init_proc_POWER10;
pcc->check_pow = check_pow_nocheck;
cc->has_work = cpu_has_work_POWER10;
pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
@ -7232,11 +7026,7 @@ static void ppc_restore_state_to_opc(CPUState *cs,
static bool ppc_cpu_has_work(CPUState *cs)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);
CPUPPCState *env = &cpu->env;
return FIELD_EX64(env->msr, MSR, EE) &&
(cs->interrupt_request & CPU_INTERRUPT_HARD);
return cs->interrupt_request & CPU_INTERRUPT_HARD;
}
static void ppc_cpu_reset(DeviceState *dev)

File diff suppressed because it is too large Load Diff

View File

@ -3241,93 +3241,82 @@ void helper_XVXSIGSP(ppc_vsr_t *xt, ppc_vsr_t *xb)
*xt = t;
}
/*
* 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) \
#define VSX_TSTDC(tp) \
static int32_t tp##_tstdc(tp b, uint32_t dcmx) \
{ \
ppc_vsr_t *xt = &env->vsr[xT(opcode)]; \
ppc_vsr_t *xb = &env->vsr[xbn]; \
ppc_vsr_t t = { }; \
uint32_t i, sign, dcmx; \
uint32_t cc, match = 0; \
\
if (!scrf) { \
dcmx = DCMX_XV(opcode); \
} else { \
t = *xt; \
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 &= ~FP_FPCC; \
env->fpscr |= cc << FPSCR_FPCC; \
env->crf[BF(opcode)] = cc; \
} else { \
t.tfld = match ? fld_max : 0; \
} \
match = 0; \
} \
if (!scrf) { \
*xt = t; \
uint32_t match = 0; \
uint32_t sign = tp##_is_neg(b); \
if (tp##_is_any_nan(b)) { \
match = extract32(dcmx, 6, 1); \
} else if (tp##_is_infinity(b)) { \
match = extract32(dcmx, 4 + !sign, 1); \
} else if (tp##_is_zero(b)) { \
match = extract32(dcmx, 2 + !sign, 1); \
} else if (tp##_is_zero_or_denormal(b)) { \
match = extract32(dcmx, 0 + !sign, 1); \
} \
return (match != 0); \
}
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)
VSX_TSTDC(float32)
VSX_TSTDC(float64)
VSX_TSTDC(float128)
#undef VSX_TSTDC
void helper_xststdcsp(CPUPPCState *env, uint32_t opcode, ppc_vsr_t *xb)
void helper_XVTSTDCDP(ppc_vsr_t *t, ppc_vsr_t *b, uint64_t dcmx, uint32_t v)
{
uint32_t dcmx, sign, exp;
uint32_t cc, match = 0, not_sp = 0;
float64 arg = xb->VsrD(0);
float64 arg_sp;
int i;
for (i = 0; i < 2; i++) {
t->s64[i] = (int64_t)-float64_tstdc(b->f64[i], dcmx);
}
}
dcmx = DCMX(opcode);
exp = (arg >> 52) & 0x7FF;
sign = float64_is_neg(arg);
void helper_XVTSTDCSP(ppc_vsr_t *t, ppc_vsr_t *b, uint64_t dcmx, uint32_t v)
{
int i;
for (i = 0; i < 4; i++) {
t->s32[i] = (int32_t)-float32_tstdc(b->f32[i], dcmx);
}
}
if (float64_is_any_nan(arg)) {
match = extract32(dcmx, 6, 1);
} else if (float64_is_infinity(arg)) {
match = extract32(dcmx, 4 + !sign, 1);
} else if (float64_is_zero(arg)) {
match = extract32(dcmx, 2 + !sign, 1);
} else if (float64_is_zero_or_denormal(arg) || (exp > 0 && exp < 0x381)) {
match = extract32(dcmx, 0 + !sign, 1);
static bool not_SP_value(float64 val)
{
return val != helper_todouble(helper_tosingle(val));
}
/*
* VSX_XS_TSTDC - VSX Scalar Test Data Class
* NAME - instruction name
* FLD - vsr_t field (VsrD(0) or f128)
* TP - type (float64 or float128)
*/
#define VSX_XS_TSTDC(NAME, FLD, TP) \
void helper_##NAME(CPUPPCState *env, uint32_t bf, \
uint32_t dcmx, ppc_vsr_t *b) \
{ \
uint32_t cc, match, sign = TP##_is_neg(b->FLD); \
match = TP##_tstdc(b->FLD, dcmx); \
cc = sign << CRF_LT_BIT | match << CRF_EQ_BIT; \
env->fpscr &= ~FP_FPCC; \
env->fpscr |= cc << FPSCR_FPCC; \
env->crf[bf] = cc; \
}
arg_sp = helper_todouble(helper_tosingle(arg));
not_sp = arg != arg_sp;
VSX_XS_TSTDC(XSTSTDCDP, VsrD(0), float64)
VSX_XS_TSTDC(XSTSTDCQP, f128, float128)
#undef VSX_XS_TSTDC
void helper_XSTSTDCSP(CPUPPCState *env, uint32_t bf,
uint32_t dcmx, ppc_vsr_t *b)
{
uint32_t cc, match, sign = float64_is_neg(b->VsrD(0));
uint32_t exp = (b->VsrD(0) >> 52) & 0x7FF;
int not_sp = (int)not_SP_value(b->VsrD(0));
match = float64_tstdc(b->VsrD(0), dcmx) || (exp > 0 && exp < 0x381);
cc = sign << CRF_LT_BIT | match << CRF_EQ_BIT | not_sp << CRF_SO_BIT;
env->fpscr &= ~FP_FPCC;
env->fpscr |= cc << FPSCR_FPCC;
env->crf[BF(opcode)] = cc;
env->crf[bf] = cc;
}
void helper_xsrqpi(CPUPPCState *env, uint32_t opcode,

View File

@ -10,6 +10,7 @@ DEF_HELPER_4(HASHSTP, void, env, tl, tl, tl)
DEF_HELPER_4(HASHCHKP, void, env, tl, tl, tl)
#if !defined(CONFIG_USER_ONLY)
DEF_HELPER_2(store_msr, void, env, tl)
DEF_HELPER_1(ppc_maybe_interrupt, void, env)
DEF_HELPER_1(rfi, void, env)
DEF_HELPER_1(40x_rfci, void, env)
DEF_HELPER_1(rfci, void, env)
@ -29,6 +30,7 @@ DEF_HELPER_2(store_mmcr1, void, env, tl)
DEF_HELPER_3(store_pmc, void, env, i32, i64)
DEF_HELPER_2(read_pmc, tl, env, i32)
DEF_HELPER_2(insns_inc, void, env, i32)
DEF_HELPER_1(handle_pmc5_overflow, void, env)
#endif
DEF_HELPER_1(check_tlb_flush_local, void, env)
DEF_HELPER_1(check_tlb_flush_global, void, env)
@ -143,15 +145,15 @@ DEF_HELPER_FLAGS_1(ftsqrt, TCG_CALL_NO_RWG_SE, i32, i64)
#define dh_ctype_acc ppc_acc_t *
#define dh_typecode_acc dh_typecode_ptr
DEF_HELPER_FLAGS_3(vavgub, TCG_CALL_NO_RWG, void, avr, avr, avr)
DEF_HELPER_FLAGS_3(vavguh, TCG_CALL_NO_RWG, void, avr, avr, avr)
DEF_HELPER_FLAGS_3(vavguw, TCG_CALL_NO_RWG, void, avr, avr, avr)
DEF_HELPER_FLAGS_3(vabsdub, TCG_CALL_NO_RWG, void, avr, avr, avr)
DEF_HELPER_FLAGS_3(vabsduh, TCG_CALL_NO_RWG, void, avr, avr, avr)
DEF_HELPER_FLAGS_3(vabsduw, TCG_CALL_NO_RWG, void, avr, avr, avr)
DEF_HELPER_FLAGS_3(vavgsb, TCG_CALL_NO_RWG, void, avr, avr, avr)
DEF_HELPER_FLAGS_3(vavgsh, TCG_CALL_NO_RWG, void, avr, avr, avr)
DEF_HELPER_FLAGS_3(vavgsw, TCG_CALL_NO_RWG, void, avr, avr, avr)
DEF_HELPER_FLAGS_4(VAVGUB, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
DEF_HELPER_FLAGS_4(VAVGUH, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
DEF_HELPER_FLAGS_4(VAVGUW, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
DEF_HELPER_FLAGS_4(VABSDUB, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
DEF_HELPER_FLAGS_4(VABSDUH, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
DEF_HELPER_FLAGS_4(VABSDUW, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
DEF_HELPER_FLAGS_4(VAVGSB, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
DEF_HELPER_FLAGS_4(VAVGSH, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
DEF_HELPER_FLAGS_4(VAVGSW, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
DEF_HELPER_4(vcmpeqfp, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgefp, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgtfp, void, env, avr, avr, avr)
@ -193,11 +195,7 @@ DEF_HELPER_FLAGS_3(vslo, TCG_CALL_NO_RWG, void, avr, avr, avr)
DEF_HELPER_FLAGS_3(vsro, TCG_CALL_NO_RWG, void, avr, avr, avr)
DEF_HELPER_FLAGS_3(vsrv, TCG_CALL_NO_RWG, void, avr, avr, avr)
DEF_HELPER_FLAGS_3(vslv, TCG_CALL_NO_RWG, void, avr, avr, avr)
DEF_HELPER_FLAGS_3(vaddcuw, TCG_CALL_NO_RWG, void, avr, avr, avr)
DEF_HELPER_FLAGS_2(vprtybw, TCG_CALL_NO_RWG, void, avr, avr)
DEF_HELPER_FLAGS_2(vprtybd, TCG_CALL_NO_RWG, void, avr, avr)
DEF_HELPER_FLAGS_2(vprtybq, TCG_CALL_NO_RWG, void, avr, avr)
DEF_HELPER_FLAGS_3(vsubcuw, TCG_CALL_NO_RWG, void, avr, avr, avr)
DEF_HELPER_FLAGS_3(VPRTYBQ, TCG_CALL_NO_RWG, void, avr, avr, i32)
DEF_HELPER_FLAGS_5(vaddsbs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32)
DEF_HELPER_FLAGS_5(vaddshs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32)
DEF_HELPER_FLAGS_5(vaddsws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32)
@ -231,8 +229,6 @@ DEF_HELPER_FLAGS_2(VSTRIBL, TCG_CALL_NO_RWG, i32, avr, avr)
DEF_HELPER_FLAGS_2(VSTRIBR, TCG_CALL_NO_RWG, i32, avr, avr)
DEF_HELPER_FLAGS_2(VSTRIHL, TCG_CALL_NO_RWG, i32, avr, avr)
DEF_HELPER_FLAGS_2(VSTRIHR, TCG_CALL_NO_RWG, i32, avr, avr)
DEF_HELPER_FLAGS_2(vnegw, TCG_CALL_NO_RWG, void, avr, avr)
DEF_HELPER_FLAGS_2(vnegd, TCG_CALL_NO_RWG, void, avr, avr)
DEF_HELPER_FLAGS_2(vupkhpx, TCG_CALL_NO_RWG, void, avr, avr)
DEF_HELPER_FLAGS_2(vupklpx, TCG_CALL_NO_RWG, void, avr, avr)
DEF_HELPER_FLAGS_2(vupkhsb, TCG_CALL_NO_RWG, void, avr, avr)
@ -258,13 +254,13 @@ DEF_HELPER_4(vpkuhum, void, env, avr, avr, avr)
DEF_HELPER_4(vpkuwum, void, env, avr, avr, avr)
DEF_HELPER_4(vpkudum, void, env, avr, avr, avr)
DEF_HELPER_FLAGS_3(vpkpx, TCG_CALL_NO_RWG, void, avr, avr, avr)
DEF_HELPER_5(vmhaddshs, void, env, avr, avr, avr, avr)
DEF_HELPER_5(vmhraddshs, void, env, avr, avr, avr, avr)
DEF_HELPER_5(VMHADDSHS, void, env, avr, avr, avr, avr)
DEF_HELPER_5(VMHRADDSHS, void, env, avr, avr, avr, avr)
DEF_HELPER_FLAGS_4(VMSUMUHM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr)
DEF_HELPER_5(VMSUMUHS, void, env, avr, avr, avr, avr)
DEF_HELPER_FLAGS_4(VMSUMSHM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr)
DEF_HELPER_5(VMSUMSHS, void, env, avr, avr, avr, avr)
DEF_HELPER_FLAGS_4(vmladduhm, TCG_CALL_NO_RWG, void, avr, avr, avr, avr)
DEF_HELPER_FLAGS_5(VMLADDUHM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32)
DEF_HELPER_FLAGS_2(mtvscr, TCG_CALL_NO_RWG, void, env, i32)
DEF_HELPER_FLAGS_1(mfvscr, TCG_CALL_NO_RWG, i32, env)
DEF_HELPER_3(lvebx, void, env, avr, tl)
@ -423,9 +419,9 @@ DEF_HELPER_3(xscvuxdsp, void, env, vsr, vsr)
DEF_HELPER_3(xscvsxdsp, void, env, vsr, vsr)
DEF_HELPER_4(xscvudqp, void, env, i32, vsr, vsr)
DEF_HELPER_3(xscvuxddp, void, env, vsr, vsr)
DEF_HELPER_3(xststdcsp, void, env, i32, vsr)
DEF_HELPER_2(xststdcdp, void, env, i32)
DEF_HELPER_2(xststdcqp, void, env, i32)
DEF_HELPER_4(XSTSTDCSP, void, env, i32, i32, vsr)
DEF_HELPER_4(XSTSTDCDP, void, env, i32, i32, vsr)
DEF_HELPER_4(XSTSTDCQP, void, env, i32, i32, vsr)
DEF_HELPER_3(xsrdpi, void, env, vsr, vsr)
DEF_HELPER_3(xsrdpic, void, env, vsr, vsr)
DEF_HELPER_3(xsrdpim, void, env, vsr, vsr)
@ -523,8 +519,8 @@ DEF_HELPER_3(xvcvsxdsp, void, env, vsr, vsr)
DEF_HELPER_3(xvcvuxdsp, void, env, vsr, vsr)
DEF_HELPER_3(xvcvsxwsp, void, env, vsr, vsr)
DEF_HELPER_3(xvcvuxwsp, void, env, vsr, vsr)
DEF_HELPER_2(xvtstdcsp, void, env, i32)
DEF_HELPER_2(xvtstdcdp, void, env, i32)
DEF_HELPER_FLAGS_4(XVTSTDCSP, TCG_CALL_NO_RWG, void, vsr, vsr, i64, i32)
DEF_HELPER_FLAGS_4(XVTSTDCDP, TCG_CALL_NO_RWG, void, vsr, vsr, i64, i32)
DEF_HELPER_3(xvrspi, void, env, vsr, vsr)
DEF_HELPER_3(xvrspic, void, env, vsr, vsr)
DEF_HELPER_3(xvrspim, void, env, vsr, vsr)

View File

@ -109,6 +109,9 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env)
if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC1) {
hflags |= 1 << HFLAGS_PMCC1;
}
if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE) {
hflags |= 1 << HFLAGS_PMCJCE;
}
#ifndef CONFIG_USER_ONLY
if (!env->has_hv_mode || (msr & (1ull << MSR_HV))) {
@ -119,6 +122,9 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env)
if (env->pmc_ins_cnt) {
hflags |= 1 << HFLAGS_INSN_CNT;
}
if (env->pmc_ins_cnt & 0x1e) {
hflags |= 1 << HFLAGS_PMC_OTHER;
}
#endif
/*
@ -260,6 +266,8 @@ int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv)
env->msr = value;
hreg_compute_hflags(env);
#if !defined(CONFIG_USER_ONLY)
ppc_maybe_interrupt(env);
if (unlikely(FIELD_EX64(env->msr, MSR, POW))) {
if (!env->pending_interrupts && (*env->check_pow)(env)) {
cs->halted = 1;

View File

@ -199,6 +199,12 @@
@XX2_uim4 ...... ..... . uim:4 ..... ......... .. &XX2_uim xt=%xx_xt xb=%xx_xb
%xx_uim7 6:1 2:1 16:5
@XX2_uim7 ...... ..... ..... ..... .... . ... . .. &XX2_uim xt=%xx_xt xb=%xx_xb uim=%xx_uim7
&XX2_bf_uim bf xb uim
@XX2_bf_uim ...... bf:3 uim:7 ..... ......... . . &XX2_bf_uim
&XX2_bf_xb bf xb
@XX2_bf_xb ...... bf:3 .. ..... ..... ......... . . &XX2_bf_xb xb=%xx_xb
@ -519,6 +525,21 @@ VCMPNEZW 000100 ..... ..... ..... . 0110000111 @VC
VCMPSQ 000100 ... -- ..... ..... 00101000001 @VX_bf
VCMPUQ 000100 ... -- ..... ..... 00100000001 @VX_bf
## Vector Integer Average Instructions
VAVGSB 000100 ..... ..... ..... 10100000010 @VX
VAVGSH 000100 ..... ..... ..... 10101000010 @VX
VAVGSW 000100 ..... ..... ..... 10110000010 @VX
VAVGUB 000100 ..... ..... ..... 10000000010 @VX
VAVGUH 000100 ..... ..... ..... 10001000010 @VX
VAVGUW 000100 ..... ..... ..... 10010000010 @VX
## Vector Integer Absolute Difference Instructions
VABSDUB 000100 ..... ..... ..... 10000000011 @VX
VABSDUH 000100 ..... ..... ..... 10001000011 @VX
VABSDUW 000100 ..... ..... ..... 10010000011 @VX
## Vector Bit Manipulation Instruction
VGNB 000100 ..... -- ... ..... 10011001100 @VX_n
@ -529,6 +550,10 @@ VCTZDM 000100 ..... ..... ..... 11111000100 @VX
VPDEPD 000100 ..... ..... ..... 10111001101 @VX
VPEXTD 000100 ..... ..... ..... 10110001101 @VX
VPRTYBD 000100 ..... 01001 ..... 11000000010 @VX_tb
VPRTYBQ 000100 ..... 01010 ..... 11000000010 @VX_tb
VPRTYBW 000100 ..... 01000 ..... 11000000010 @VX_tb
## Vector Permute and Formatting Instruction
VEXTDUBVLX 000100 ..... ..... ..... ..... 011000 @VA
@ -608,12 +633,14 @@ VRLQNM 000100 ..... ..... ..... 00101000101 @VX
## Vector Integer Arithmetic Instructions
VADDCUW 000100 ..... ..... ..... 00110000000 @VX
VADDCUQ 000100 ..... ..... ..... 00101000000 @VX
VADDUQM 000100 ..... ..... ..... 00100000000 @VX
VADDEUQM 000100 ..... ..... ..... ..... 111100 @VA
VADDECUQ 000100 ..... ..... ..... ..... 111101 @VA
VSUBCUW 000100 ..... ..... ..... 10110000000 @VX
VSUBCUQ 000100 ..... ..... ..... 10101000000 @VX
VSUBUQM 000100 ..... ..... ..... 10100000000 @VX
@ -627,6 +654,9 @@ VEXTSH2D 000100 ..... 11001 ..... 11000000010 @VX_tb
VEXTSW2D 000100 ..... 11010 ..... 11000000010 @VX_tb
VEXTSD2Q 000100 ..... 11011 ..... 11000000010 @VX_tb
VNEGD 000100 ..... 00111 ..... 11000000010 @VX_tb
VNEGW 000100 ..... 00110 ..... 11000000010 @VX_tb
## Vector Mask Manipulation Instructions
MTVSRBM 000100 ..... 10000 ..... 11001000010 @VX_tb
@ -693,6 +723,10 @@ VMSUMUHS 000100 ..... ..... ..... ..... 100111 @VA
VMSUMCUD 000100 ..... ..... ..... ..... 010111 @VA
VMSUMUDM 000100 ..... ..... ..... ..... 100011 @VA
VMLADDUHM 000100 ..... ..... ..... ..... 100010 @VA
VMHADDSHS 000100 ..... ..... ..... ..... 100000 @VA
VMHRADDSHS 000100 ..... ..... ..... ..... 100001 @VA
## Vector String Instructions
VSTRIBL 000100 ..... 00000 ..... . 0000001101 @VX_tb_rc
@ -726,6 +760,17 @@ STXVRHX 011111 ..... ..... ..... 0010101101 . @X_TSX
STXVRWX 011111 ..... ..... ..... 0011001101 . @X_TSX
STXVRDX 011111 ..... ..... ..... 0011101101 . @X_TSX
## VSX Vector Binary Floating-Point Sign Manipulation Instructions
XVABSDP 111100 ..... 00000 ..... 111011001 .. @XX2
XVABSSP 111100 ..... 00000 ..... 110011001 .. @XX2
XVNABSDP 111100 ..... 00000 ..... 111101001 .. @XX2
XVNABSSP 111100 ..... 00000 ..... 110101001 .. @XX2
XVNEGDP 111100 ..... 00000 ..... 111111001 .. @XX2
XVNEGSP 111100 ..... 00000 ..... 110111001 .. @XX2
XVCPSGNDP 111100 ..... ..... ..... 11110000 ... @XX3
XVCPSGNSP 111100 ..... ..... ..... 11010000 ... @XX3
## VSX Scalar Multiply-Add Instructions
XSMADDADP 111100 ..... ..... ..... 00100001 . . . @XX3
@ -809,6 +854,11 @@ XSCVSPDPN 111100 ..... ----- ..... 101001011 .. @XX2
## VSX Binary Floating-Point Math Support Instructions
XVXSIGSP 111100 ..... 01001 ..... 111011011 .. @XX2
XVTSTDCDP 111100 ..... ..... ..... 1111 . 101 ... @XX2_uim7
XVTSTDCSP 111100 ..... ..... ..... 1101 . 101 ... @XX2_uim7
XSTSTDCSP 111100 ... ....... ..... 100101010 . - @XX2_bf_uim xb=%xx_xb
XSTSTDCDP 111100 ... ....... ..... 101101010 . - @XX2_bf_uim xb=%xx_xb
XSTSTDCQP 111111 ... ....... xb:5 1011000100 - @XX2_bf_uim
## VSX Vector Test Least-Significant Bit by Byte Instruction
@ -908,3 +958,11 @@ SLBSYNC 011111 ----- ----- ----- 0101010010 -
TLBIE 011111 ..... - .. . . ..... 0100110010 - @X_tlbie
TLBIEL 011111 ..... - .. . . ..... 0100010010 - @X_tlbie
# Processor Control Instructions
MSGCLR 011111 ----- ----- ..... 0011101110 - @X_rb
MSGSND 011111 ----- ----- ..... 0011001110 - @X_rb
MSGCLRP 011111 ----- ----- ..... 0010101110 - @X_rb
MSGSNDP 011111 ----- ----- ..... 0010001110 - @X_rb
MSGSYNC 011111 ----- ----- ----- 1101110110 -

View File

@ -492,40 +492,8 @@ static inline void set_vscr_sat(CPUPPCState *env)
env->vscr_sat.u32[0] = 1;
}
void helper_vaddcuw(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
{
int i;
for (i = 0; i < ARRAY_SIZE(r->u32); i++) {
r->u32[i] = ~a->u32[i] < b->u32[i];
}
}
/* vprtybw */
void helper_vprtybw(ppc_avr_t *r, ppc_avr_t *b)
{
int i;
for (i = 0; i < ARRAY_SIZE(r->u32); i++) {
uint64_t res = b->u32[i] ^ (b->u32[i] >> 16);
res ^= res >> 8;
r->u32[i] = res & 1;
}
}
/* vprtybd */
void helper_vprtybd(ppc_avr_t *r, ppc_avr_t *b)
{
int i;
for (i = 0; i < ARRAY_SIZE(r->u64); i++) {
uint64_t res = b->u64[i] ^ (b->u64[i] >> 32);
res ^= res >> 16;
res ^= res >> 8;
r->u64[i] = res & 1;
}
}
/* vprtybq */
void helper_vprtybq(ppc_avr_t *r, ppc_avr_t *b)
void helper_VPRTYBQ(ppc_avr_t *r, ppc_avr_t *b, uint32_t v)
{
uint64_t res = b->u64[0] ^ b->u64[1];
res ^= res >> 32;
@ -602,29 +570,27 @@ VARITHSAT_UNSIGNED(w, u32, uint64_t, cvtsduw)
#undef VARITHSAT_SIGNED
#undef VARITHSAT_UNSIGNED
#define VAVG_DO(name, element, etype) \
void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
{ \
int i; \
\
for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
etype x = (etype)a->element[i] + (etype)b->element[i] + 1; \
r->element[i] = x >> 1; \
} \
#define VAVG(name, element, etype) \
void helper_##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t v)\
{ \
int i; \
\
for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
etype x = (etype)a->element[i] + (etype)b->element[i] + 1; \
r->element[i] = x >> 1; \
} \
}
#define VAVG(type, signed_element, signed_type, unsigned_element, \
unsigned_type) \
VAVG_DO(avgs##type, signed_element, signed_type) \
VAVG_DO(avgu##type, unsigned_element, unsigned_type)
VAVG(b, s8, int16_t, u8, uint16_t)
VAVG(h, s16, int32_t, u16, uint32_t)
VAVG(w, s32, int64_t, u32, uint64_t)
#undef VAVG_DO
VAVG(VAVGSB, s8, int16_t)
VAVG(VAVGUB, u8, uint16_t)
VAVG(VAVGSH, s16, int32_t)
VAVG(VAVGUH, u16, uint32_t)
VAVG(VAVGSW, s32, int64_t)
VAVG(VAVGUW, u32, uint64_t)
#undef VAVG
#define VABSDU_DO(name, element) \
void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
#define VABSDU(name, element) \
void helper_##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t v)\
{ \
int i; \
\
@ -640,12 +606,9 @@ void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
* name - instruction mnemonic suffix (b: byte, h: halfword, w: word)
* element - element type to access from vector
*/
#define VABSDU(type, element) \
VABSDU_DO(absdu##type, element)
VABSDU(b, u8)
VABSDU(h, u16)
VABSDU(w, u32)
#undef VABSDU_DO
VABSDU(VABSDUB, u8)
VABSDU(VABSDUH, u16)
VABSDU(VABSDUW, u32)
#undef VABSDU
#define VCF(suffix, cvt, element) \
@ -939,7 +902,7 @@ target_ulong helper_vctzlsbb(ppc_avr_t *r)
return count;
}
void helper_vmhaddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a,
void helper_VMHADDSHS(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a,
ppc_avr_t *b, ppc_avr_t *c)
{
int sat = 0;
@ -957,7 +920,7 @@ void helper_vmhaddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a,
}
}
void helper_vmhraddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a,
void helper_VMHRADDSHS(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a,
ppc_avr_t *b, ppc_avr_t *c)
{
int sat = 0;
@ -974,7 +937,8 @@ void helper_vmhraddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a,
}
}
void helper_vmladduhm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
void helper_VMLADDUHM(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c,
uint32_t v)
{
int i;
@ -1936,18 +1900,6 @@ XXBLEND(W, 32)
XXBLEND(D, 64)
#undef XXBLEND
#define VNEG(name, element) \
void helper_##name(ppc_avr_t *r, ppc_avr_t *b) \
{ \
int i; \
for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
r->element[i] = -b->element[i]; \
} \
}
VNEG(vnegw, s32)
VNEG(vnegd, s64)
#undef VNEG
void helper_vsro(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
{
int sh = (b->VsrB(0xf) >> 3) & 0xf;
@ -1961,15 +1913,6 @@ void helper_vsro(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
#endif
}
void helper_vsubcuw(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
{
int i;
for (i = 0; i < ARRAY_SIZE(r->u32); i++) {
r->u32[i] = a->u32[i] >= b->u32[i];
}
}
void helper_vsumsws(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
{
int64_t t;

View File

@ -25,6 +25,7 @@
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "mmu-book3s-v3.h"
#include "hw/ppc/ppc.h"
#include "helper_regs.h"
@ -163,7 +164,7 @@ target_ulong helper_load_dpdes(CPUPPCState *env)
helper_hfscr_facility_check(env, HFSCR_MSGP, "load DPDES", HFSCR_IC_MSGP);
/* TODO: TCG supports only one thread */
if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) {
if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) {
dpdes = 1;
}
@ -173,7 +174,6 @@ target_ulong helper_load_dpdes(CPUPPCState *env)
void helper_store_dpdes(CPUPPCState *env, target_ulong val)
{
PowerPCCPU *cpu = env_archcpu(env);
CPUState *cs = CPU(cpu);
helper_hfscr_facility_check(env, HFSCR_MSGP, "store DPDES", HFSCR_IC_MSGP);
@ -184,12 +184,7 @@ void helper_store_dpdes(CPUPPCState *env, target_ulong val)
return;
}
if (val & 0x1) {
env->pending_interrupts |= 1 << PPC_INTERRUPT_DOORBELL;
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
} else {
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL);
}
ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & 0x1);
}
#endif /* defined(TARGET_PPC64) */

View File

@ -238,6 +238,8 @@ static void ppc_radix64_set_rc(PowerPCCPU *cpu, MMUAccessType access_type,
static bool ppc_radix64_is_valid_level(int level, int psize, uint64_t nls)
{
bool ret;
/*
* Check if this is a valid level, according to POWER9 and POWER10
* Processor User's Manuals, sections 4.10.4.1 and 5.10.6.1, respectively:
@ -249,16 +251,25 @@ static bool ppc_radix64_is_valid_level(int level, int psize, uint64_t nls)
*/
switch (level) {
case 0: /* Root Page Dir */
return psize == 52 && nls == 13;
ret = psize == 52 && nls == 13;
break;
case 1:
case 2:
return nls == 9;
ret = nls == 9;
break;
case 3:
return nls == 9 || nls == 5;
ret = nls == 9 || nls == 5;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "invalid radix level: %d\n", level);
return false;
ret = false;
}
if (unlikely(!ret)) {
qemu_log_mask(LOG_GUEST_ERROR, "invalid radix configuration: "
"level %d size %d nls %"PRIu64"\n",
level, psize, nls);
}
return ret;
}
static int ppc_radix64_next_level(AddressSpace *as, vaddr eaddr,
@ -519,11 +530,13 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu,
if (!ppc_radix64_is_valid_level(level++, *g_page_size, nls)) {
fault_cause |= DSISR_R_BADCONFIG;
return 1;
ret = 1;
} else {
ret = ppc_radix64_next_level(cs->as, eaddr & R_EADDR_MASK,
&h_raddr, &nls, g_page_size,
&pte, &fault_cause);
}
ret = ppc_radix64_next_level(cs->as, eaddr & R_EADDR_MASK, &h_raddr,
&nls, g_page_size, &pte, &fault_cause);
if (ret) {
/* No valid pte */
if (guest_visible) {

View File

@ -22,8 +22,6 @@
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
#define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL
static bool pmc_has_overflow_enabled(CPUPPCState *env, int sprn)
{
if (sprn == SPR_POWER_PMC1) {
@ -88,49 +86,47 @@ static bool pmu_increment_insns(CPUPPCState *env, uint32_t num_insns)
bool overflow_triggered = false;
target_ulong tmp;
if (unlikely(ins_cnt & 0x1e)) {
if (ins_cnt & (1 << 1)) {
tmp = env->spr[SPR_POWER_PMC1];
tmp += num_insns;
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMC1CE)) {
tmp = PMC_COUNTER_NEGATIVE_VAL;
overflow_triggered = true;
}
env->spr[SPR_POWER_PMC1] = tmp;
if (ins_cnt & (1 << 1)) {
tmp = env->spr[SPR_POWER_PMC1];
tmp += num_insns;
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMC1CE)) {
tmp = PMC_COUNTER_NEGATIVE_VAL;
overflow_triggered = true;
}
env->spr[SPR_POWER_PMC1] = tmp;
}
if (ins_cnt & (1 << 2)) {
tmp = env->spr[SPR_POWER_PMC2];
if (ins_cnt & (1 << 2)) {
tmp = env->spr[SPR_POWER_PMC2];
tmp += num_insns;
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
tmp = PMC_COUNTER_NEGATIVE_VAL;
overflow_triggered = true;
}
env->spr[SPR_POWER_PMC2] = tmp;
}
if (ins_cnt & (1 << 3)) {
tmp = env->spr[SPR_POWER_PMC3];
tmp += num_insns;
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
tmp = PMC_COUNTER_NEGATIVE_VAL;
overflow_triggered = true;
}
env->spr[SPR_POWER_PMC3] = tmp;
}
if (ins_cnt & (1 << 4)) {
target_ulong mmcr1 = env->spr[SPR_POWER_MMCR1];
int sel = extract64(mmcr1, MMCR1_PMC4EVT_EXTR, MMCR1_EVT_SIZE);
if (sel == 0x02 || (env->spr[SPR_CTRL] & CTRL_RUN)) {
tmp = env->spr[SPR_POWER_PMC4];
tmp += num_insns;
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
tmp = PMC_COUNTER_NEGATIVE_VAL;
overflow_triggered = true;
}
env->spr[SPR_POWER_PMC2] = tmp;
}
if (ins_cnt & (1 << 3)) {
tmp = env->spr[SPR_POWER_PMC3];
tmp += num_insns;
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
tmp = PMC_COUNTER_NEGATIVE_VAL;
overflow_triggered = true;
}
env->spr[SPR_POWER_PMC3] = tmp;
}
if (ins_cnt & (1 << 4)) {
target_ulong mmcr1 = env->spr[SPR_POWER_MMCR1];
int sel = extract64(mmcr1, MMCR1_PMC4EVT_EXTR, MMCR1_EVT_SIZE);
if (sel == 0x02 || (env->spr[SPR_CTRL] & CTRL_RUN)) {
tmp = env->spr[SPR_POWER_PMC4];
tmp += num_insns;
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
tmp = PMC_COUNTER_NEGATIVE_VAL;
overflow_triggered = true;
}
env->spr[SPR_POWER_PMC4] = tmp;
}
env->spr[SPR_POWER_PMC4] = tmp;
}
}
@ -310,6 +306,12 @@ static void fire_PMC_interrupt(PowerPCCPU *cpu)
raise_ebb_perfm_exception(env);
}
void helper_handle_pmc5_overflow(CPUPPCState *env)
{
env->spr[SPR_POWER_PMC5] = PMC_COUNTER_NEGATIVE_VAL;
fire_PMC_interrupt(env_archcpu(env));
}
/* This helper assumes that the PMC is running. */
void helper_insns_inc(CPUPPCState *env, uint32_t num_insns)
{

View File

@ -14,6 +14,9 @@
#define POWER8_PMU_H
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
#define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL
void cpu_ppc_pmu_init(CPUPPCState *env);
void pmu_update_summaries(CPUPPCState *env);
#else

View File

@ -36,6 +36,7 @@
#include "exec/log.h"
#include "qemu/atomic128.h"
#include "spr_common.h"
#include "power8-pmu.h"
#include "qemu/qemu-print.h"
#include "qapi/error.h"
@ -177,6 +178,8 @@ struct DisasContext {
bool hr;
bool mmcr0_pmcc0;
bool mmcr0_pmcc1;
bool mmcr0_pmcjce;
bool pmc_other;
bool pmu_insn_cnt;
ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
int singlestep_enabled;
@ -305,6 +308,14 @@ static void gen_icount_io_start(DisasContext *ctx)
}
}
#if !defined(CONFIG_USER_ONLY)
static void gen_ppc_maybe_interrupt(DisasContext *ctx)
{
gen_icount_io_start(ctx);
gen_helper_ppc_maybe_interrupt(cpu_env);
}
#endif
/*
* Tells the caller what is the appropriate exception to generate and prepares
* SPR registers for this exception.
@ -4261,6 +4272,9 @@ static void pmu_count_insns(DisasContext *ctx)
}
#if !defined(CONFIG_USER_ONLY)
TCGLabel *l;
TCGv t0;
/*
* The PMU insns_inc() helper stops the internal PMU timer if a
* counter overflows happens. In that case, if the guest is
@ -4269,8 +4283,26 @@ static void pmu_count_insns(DisasContext *ctx)
*/
gen_icount_io_start(ctx);
gen_helper_insns_inc(cpu_env, tcg_constant_i32(ctx->base.num_insns));
#else
/* Avoid helper calls when only PMC5-6 are enabled. */
if (!ctx->pmc_other) {
l = gen_new_label();
t0 = tcg_temp_new();
gen_load_spr(t0, SPR_POWER_PMC5);
tcg_gen_addi_tl(t0, t0, ctx->base.num_insns);
gen_store_spr(SPR_POWER_PMC5, t0);
/* Check for overflow, if it's enabled */
if (ctx->mmcr0_pmcjce) {
tcg_gen_brcondi_tl(TCG_COND_LT, t0, PMC_COUNTER_NEGATIVE_VAL, l);
gen_helper_handle_pmc5_overflow(cpu_env);
}
gen_set_label(l);
tcg_temp_free(t0);
} else {
gen_helper_insns_inc(cpu_env, tcg_constant_i32(ctx->base.num_insns));
}
#else
/*
* User mode can read (but not write) PMC5 and start/stop
* the PMU via MMCR0_FC. In this case just increment
@ -4283,7 +4315,7 @@ static void pmu_count_insns(DisasContext *ctx)
gen_store_spr(SPR_POWER_PMC5, t0);
tcg_temp_free(t0);
#endif /* #if !defined(CONFIG_USER_ONLY) */
#endif /* #if !defined(CONFIG_USER_ONLY) */
}
#else
static void pmu_count_insns(DisasContext *ctx)
@ -6161,7 +6193,6 @@ static void gen_tlbilx_booke206(DisasContext *ctx)
#endif /* defined(CONFIG_USER_ONLY) */
}
/* wrtee */
static void gen_wrtee(DisasContext *ctx)
{
@ -6175,6 +6206,7 @@ static void gen_wrtee(DisasContext *ctx)
tcg_gen_andi_tl(t0, cpu_gpr[rD(ctx->opcode)], (1 << MSR_EE));
tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(1 << MSR_EE));
tcg_gen_or_tl(cpu_msr, cpu_msr, t0);
gen_ppc_maybe_interrupt(ctx);
tcg_temp_free(t0);
/*
* Stop translation to have a chance to raise an exception if we
@ -6193,6 +6225,7 @@ static void gen_wrteei(DisasContext *ctx)
CHK_SV(ctx);
if (ctx->opcode & 0x00008000) {
tcg_gen_ori_tl(cpu_msr, cpu_msr, (1 << MSR_EE));
gen_ppc_maybe_interrupt(ctx);
/* Stop translation to have a chance to raise an exception */
ctx->base.is_jmp = DISAS_EXIT_UPDATE;
} else {
@ -6239,68 +6272,6 @@ static void gen_icbt_440(DisasContext *ctx)
*/
}
/* Embedded.Processor Control */
static void gen_msgclr(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
GEN_PRIV(ctx);
#else
CHK_HV(ctx);
if (is_book3s_arch2x(ctx)) {
gen_helper_book3s_msgclr(cpu_env, cpu_gpr[rB(ctx->opcode)]);
} else {
gen_helper_msgclr(cpu_env, cpu_gpr[rB(ctx->opcode)]);
}
#endif /* defined(CONFIG_USER_ONLY) */
}
static void gen_msgsnd(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
GEN_PRIV(ctx);
#else
CHK_HV(ctx);
if (is_book3s_arch2x(ctx)) {
gen_helper_book3s_msgsnd(cpu_gpr[rB(ctx->opcode)]);
} else {
gen_helper_msgsnd(cpu_gpr[rB(ctx->opcode)]);
}
#endif /* defined(CONFIG_USER_ONLY) */
}
#if defined(TARGET_PPC64)
static void gen_msgclrp(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
GEN_PRIV(ctx);
#else
CHK_SV(ctx);
gen_helper_book3s_msgclrp(cpu_env, cpu_gpr[rB(ctx->opcode)]);
#endif /* defined(CONFIG_USER_ONLY) */
}
static void gen_msgsndp(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
GEN_PRIV(ctx);
#else
CHK_SV(ctx);
gen_helper_book3s_msgsndp(cpu_env, cpu_gpr[rB(ctx->opcode)]);
#endif /* defined(CONFIG_USER_ONLY) */
}
#endif
static void gen_msgsync(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
GEN_PRIV(ctx);
#else
CHK_HV(ctx);
#endif /* defined(CONFIG_USER_ONLY) */
/* interpreted as no-op */
}
#if defined(TARGET_PPC64)
static void gen_maddld(DisasContext *ctx)
{
@ -6545,12 +6516,12 @@ static int64_t dw_compose_ea(DisasContext *ctx, int x)
} \
} while (0)
#define REQUIRE_HV(CTX) \
do { \
if (unlikely((CTX)->pr || !(CTX)->hv)) \
gen_priv_opc(CTX); \
return true; \
} \
#define REQUIRE_HV(CTX) \
do { \
if (unlikely((CTX)->pr || !(CTX)->hv)) { \
gen_priv_opc(CTX); \
return true; \
} \
} while (0)
#else
#define REQUIRE_SV(CTX) do { gen_priv_opc(CTX); return true; } while (0)
@ -6628,6 +6599,8 @@ static bool resolve_PLS_D(DisasContext *ctx, arg_D *d, arg_PLS_D *a)
#include "translate/branch-impl.c.inc"
#include "translate/processor-ctrl-impl.c.inc"
#include "translate/storage-ctrl-impl.c.inc"
/* Handles lfdp */
@ -6901,12 +6874,6 @@ GEN_HANDLER2_E(tlbivax_booke206, "tlbivax", 0x1F, 0x12, 0x18, 0x00000001,
PPC_NONE, PPC2_BOOKE206),
GEN_HANDLER2_E(tlbilx_booke206, "tlbilx", 0x1F, 0x12, 0x00, 0x03800001,
PPC_NONE, PPC2_BOOKE206),
GEN_HANDLER2_E(msgsnd, "msgsnd", 0x1F, 0x0E, 0x06, 0x03ff0001,
PPC_NONE, PPC2_PRCNTL),
GEN_HANDLER2_E(msgclr, "msgclr", 0x1F, 0x0E, 0x07, 0x03ff0001,
PPC_NONE, PPC2_PRCNTL),
GEN_HANDLER2_E(msgsync, "msgsync", 0x1F, 0x16, 0x1B, 0x00000000,
PPC_NONE, PPC2_PRCNTL),
GEN_HANDLER(wrtee, 0x1F, 0x03, 0x04, 0x000FFC01, PPC_WRTEE),
GEN_HANDLER(wrteei, 0x1F, 0x03, 0x05, 0x000E7C01, PPC_WRTEE),
GEN_HANDLER(dlmzb, 0x1F, 0x0E, 0x02, 0x00000000, PPC_440_SPEC),
@ -6921,15 +6888,10 @@ GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC),
GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC),
GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC),
GEN_HANDLER(mtvscr, 0x04, 0x2, 0x19, 0x03ff0000, PPC_ALTIVEC),
GEN_HANDLER(vmladduhm, 0x04, 0x11, 0xFF, 0x00000000, PPC_ALTIVEC),
#if defined(TARGET_PPC64)
GEN_HANDLER_E(maddhd_maddhdu, 0x04, 0x18, 0xFF, 0x00000000, PPC_NONE,
PPC2_ISA300),
GEN_HANDLER_E(maddld, 0x04, 0x19, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA300),
GEN_HANDLER2_E(msgsndp, "msgsndp", 0x1F, 0x0E, 0x04, 0x03ff0001,
PPC_NONE, PPC2_ISA207S),
GEN_HANDLER2_E(msgclrp, "msgclrp", 0x1F, 0x0E, 0x05, 0x03ff0001,
PPC_NONE, PPC2_ISA207S),
#endif
#undef GEN_INT_ARITH_ADD
@ -7574,6 +7536,8 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
ctx->hr = (hflags >> HFLAGS_HR) & 1;
ctx->mmcr0_pmcc0 = (hflags >> HFLAGS_PMCC0) & 1;
ctx->mmcr0_pmcc1 = (hflags >> HFLAGS_PMCC1) & 1;
ctx->mmcr0_pmcjce = (hflags >> HFLAGS_PMCJCE) & 1;
ctx->pmc_other = (hflags >> HFLAGS_PMC_OTHER) & 1;
ctx->pmu_insn_cnt = (hflags >> HFLAGS_INSN_CNT) & 1;
ctx->singlestep_enabled = 0;

View File

@ -0,0 +1,105 @@
/*
* Power ISA decode for Storage Control instructions
*
* Copyright (c) 2022 Instituto de Pesquisas Eldorado (eldorado.org.br)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* Processor Control Instructions
*/
static bool trans_MSGCLR(DisasContext *ctx, arg_X_rb *a)
{
if (!(ctx->insns_flags2 & PPC2_ISA207S)) {
/*
* Before Power ISA 2.07, processor control instructions were only
* implemented in the "Embedded.Processor Control" category.
*/
REQUIRE_INSNS_FLAGS2(ctx, PRCNTL);
}
REQUIRE_HV(ctx);
#if !defined(CONFIG_USER_ONLY)
if (is_book3s_arch2x(ctx)) {
gen_helper_book3s_msgclr(cpu_env, cpu_gpr[a->rb]);
} else {
gen_helper_msgclr(cpu_env, cpu_gpr[a->rb]);
}
#else
qemu_build_not_reached();
#endif
return true;
}
static bool trans_MSGSND(DisasContext *ctx, arg_X_rb *a)
{
if (!(ctx->insns_flags2 & PPC2_ISA207S)) {
/*
* Before Power ISA 2.07, processor control instructions were only
* implemented in the "Embedded.Processor Control" category.
*/
REQUIRE_INSNS_FLAGS2(ctx, PRCNTL);
}
REQUIRE_HV(ctx);
#if !defined(CONFIG_USER_ONLY)
if (is_book3s_arch2x(ctx)) {
gen_helper_book3s_msgsnd(cpu_gpr[a->rb]);
} else {
gen_helper_msgsnd(cpu_gpr[a->rb]);
}
#else
qemu_build_not_reached();
#endif
return true;
}
static bool trans_MSGCLRP(DisasContext *ctx, arg_X_rb *a)
{
REQUIRE_64BIT(ctx);
REQUIRE_INSNS_FLAGS2(ctx, ISA207S);
REQUIRE_SV(ctx);
#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64)
gen_helper_book3s_msgclrp(cpu_env, cpu_gpr[a->rb]);
#else
qemu_build_not_reached();
#endif
return true;
}
static bool trans_MSGSNDP(DisasContext *ctx, arg_X_rb *a)
{
REQUIRE_64BIT(ctx);
REQUIRE_INSNS_FLAGS2(ctx, ISA207S);
REQUIRE_SV(ctx);
#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64)
gen_helper_book3s_msgsndp(cpu_env, cpu_gpr[a->rb]);
#else
qemu_build_not_reached();
#endif
return true;
}
static bool trans_MSGSYNC(DisasContext *ctx, arg_MSGSYNC *a)
{
REQUIRE_INSNS_FLAGS2(ctx, ISA300);
REQUIRE_HV(ctx);
/* interpreted as no-op */
return true;
}

View File

@ -431,21 +431,6 @@ GEN_VXFORM_V(vminsb, MO_8, tcg_gen_gvec_smin, 1, 12);
GEN_VXFORM_V(vminsh, MO_16, tcg_gen_gvec_smin, 1, 13);
GEN_VXFORM_V(vminsw, MO_32, tcg_gen_gvec_smin, 1, 14);
GEN_VXFORM_V(vminsd, MO_64, tcg_gen_gvec_smin, 1, 15);
GEN_VXFORM(vavgub, 1, 16);
GEN_VXFORM(vabsdub, 1, 16);
GEN_VXFORM_DUAL(vavgub, PPC_ALTIVEC, PPC_NONE, \
vabsdub, PPC_NONE, PPC2_ISA300)
GEN_VXFORM(vavguh, 1, 17);
GEN_VXFORM(vabsduh, 1, 17);
GEN_VXFORM_DUAL(vavguh, PPC_ALTIVEC, PPC_NONE, \
vabsduh, PPC_NONE, PPC2_ISA300)
GEN_VXFORM(vavguw, 1, 18);
GEN_VXFORM(vabsduw, 1, 18);
GEN_VXFORM_DUAL(vavguw, PPC_ALTIVEC, PPC_NONE, \
vabsduw, PPC_NONE, PPC2_ISA300)
GEN_VXFORM(vavgsb, 1, 20);
GEN_VXFORM(vavgsh, 1, 21);
GEN_VXFORM(vavgsw, 1, 22);
GEN_VXFORM(vmrghb, 6, 0);
GEN_VXFORM(vmrghh, 6, 1);
GEN_VXFORM(vmrghw, 6, 2);
@ -803,8 +788,6 @@ GEN_VXFORM(vsrv, 2, 28);
GEN_VXFORM(vslv, 2, 29);
GEN_VXFORM(vslo, 6, 16);
GEN_VXFORM(vsro, 6, 17);
GEN_VXFORM(vaddcuw, 0, 6);
GEN_VXFORM(vsubcuw, 0, 22);
static bool do_vector_gvec3_VX(DisasContext *ctx, arg_VX *a, int vece,
void (*gen_gvec)(unsigned, uint32_t, uint32_t,
@ -1661,9 +1644,71 @@ GEN_VXFORM_NOA_ENV(vrfim, 5, 11);
GEN_VXFORM_NOA_ENV(vrfin, 5, 8);
GEN_VXFORM_NOA_ENV(vrfip, 5, 10);
GEN_VXFORM_NOA_ENV(vrfiz, 5, 9);
GEN_VXFORM_NOA(vprtybw, 1, 24);
GEN_VXFORM_NOA(vprtybd, 1, 24);
GEN_VXFORM_NOA(vprtybq, 1, 24);
static void gen_vprtyb_vec(unsigned vece, TCGv_vec t, TCGv_vec b)
{
int i;
TCGv_vec tmp = tcg_temp_new_vec_matching(b);
/* MO_32 is 2, so 2 iteractions for MO_32 and 3 for MO_64 */
for (i = 0; i < vece; i++) {
tcg_gen_shri_vec(vece, tmp, b, (4 << (vece - i)));
tcg_gen_xor_vec(vece, b, tmp, b);
}
tcg_gen_and_vec(vece, t, b, tcg_constant_vec_matching(t, vece, 1));
tcg_temp_free_vec(tmp);
}
/* vprtybw */
static void gen_vprtyb_i32(TCGv_i32 t, TCGv_i32 b)
{
tcg_gen_ctpop_i32(t, b);
tcg_gen_and_i32(t, t, tcg_constant_i32(1));
}
/* vprtybd */
static void gen_vprtyb_i64(TCGv_i64 t, TCGv_i64 b)
{
tcg_gen_ctpop_i64(t, b);
tcg_gen_and_i64(t, t, tcg_constant_i64(1));
}
static bool do_vx_vprtyb(DisasContext *ctx, arg_VX_tb *a, unsigned vece)
{
static const TCGOpcode vecop_list[] = {
INDEX_op_shri_vec, 0
};
static const GVecGen2 op[] = {
{
.fniv = gen_vprtyb_vec,
.fni4 = gen_vprtyb_i32,
.opt_opc = vecop_list,
.vece = MO_32
},
{
.fniv = gen_vprtyb_vec,
.fni8 = gen_vprtyb_i64,
.opt_opc = vecop_list,
.vece = MO_64
},
{
.fno = gen_helper_VPRTYBQ,
.vece = MO_128
},
};
REQUIRE_INSNS_FLAGS2(ctx, ISA300);
REQUIRE_VECTOR(ctx);
tcg_gen_gvec_2(avr_full_offset(a->vrt), avr_full_offset(a->vrb),
16, 16, &op[vece - MO_32]);
return true;
}
TRANS(VPRTYBW, do_vx_vprtyb, MO_32)
TRANS(VPRTYBD, do_vx_vprtyb, MO_64)
TRANS(VPRTYBQ, do_vx_vprtyb, MO_128)
static void gen_vsplt(DisasContext *ctx, int vece)
{
@ -2521,25 +2566,7 @@ static void glue(gen_, name0##_##name1)(DisasContext *ctx) \
tcg_temp_free_ptr(rd); \
}
GEN_VAFORM_PAIRED(vmhaddshs, vmhraddshs, 16)
static void gen_vmladduhm(DisasContext *ctx)
{
TCGv_ptr ra, rb, rc, rd;
if (unlikely(!ctx->altivec_enabled)) {
gen_exception(ctx, POWERPC_EXCP_VPU);
return;
}
ra = gen_avr_ptr(rA(ctx->opcode));
rb = gen_avr_ptr(rB(ctx->opcode));
rc = gen_avr_ptr(rC(ctx->opcode));
rd = gen_avr_ptr(rD(ctx->opcode));
gen_helper_vmladduhm(rd, ra, rb, rc);
tcg_temp_free_ptr(ra);
tcg_temp_free_ptr(rb);
tcg_temp_free_ptr(rc);
tcg_temp_free_ptr(rd);
}
GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23)
static bool do_va_helper(DisasContext *ctx, arg_VA *a,
void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr))
@ -2569,6 +2596,36 @@ TRANS_FLAGS2(ALTIVEC_207, VSUBECUQ, do_va_helper, gen_helper_VSUBECUQ)
TRANS_FLAGS(ALTIVEC, VPERM, do_va_helper, gen_helper_VPERM)
TRANS_FLAGS2(ISA300, VPERMR, do_va_helper, gen_helper_VPERMR)
static void gen_vmladduhm_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b,
TCGv_vec c)
{
tcg_gen_mul_vec(vece, t, a, b);
tcg_gen_add_vec(vece, t, t, c);
}
static bool trans_VMLADDUHM(DisasContext *ctx, arg_VA *a)
{
static const TCGOpcode vecop_list[] = {
INDEX_op_add_vec, INDEX_op_mul_vec, 0
};
static const GVecGen4 op = {
.fno = gen_helper_VMLADDUHM,
.fniv = gen_vmladduhm_vec,
.opt_opc = vecop_list,
.vece = MO_16
};
REQUIRE_INSNS_FLAGS(ctx, ALTIVEC);
REQUIRE_VECTOR(ctx);
tcg_gen_gvec_4(avr_full_offset(a->vrt), avr_full_offset(a->vra),
avr_full_offset(a->vrb), avr_full_offset(a->rc),
16, 16, &op);
return true;
}
static bool trans_VSEL(DisasContext *ctx, arg_VA *a)
{
REQUIRE_INSNS_FLAGS(ctx, ALTIVEC);
@ -2608,14 +2665,26 @@ static bool do_va_env_helper(DisasContext *ctx, arg_VA *a,
TRANS_FLAGS(ALTIVEC, VMSUMUHS, do_va_env_helper, gen_helper_VMSUMUHS)
TRANS_FLAGS(ALTIVEC, VMSUMSHS, do_va_env_helper, gen_helper_VMSUMSHS)
GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23)
TRANS_FLAGS(ALTIVEC, VMHADDSHS, do_va_env_helper, gen_helper_VMHADDSHS)
TRANS_FLAGS(ALTIVEC, VMHRADDSHS, do_va_env_helper, gen_helper_VMHRADDSHS)
GEN_VXFORM_NOA(vclzb, 1, 28)
GEN_VXFORM_NOA(vclzh, 1, 29)
GEN_VXFORM_TRANS(vclzw, 1, 30)
GEN_VXFORM_TRANS(vclzd, 1, 31)
GEN_VXFORM_NOA_2(vnegw, 1, 24, 6)
GEN_VXFORM_NOA_2(vnegd, 1, 24, 7)
static bool do_vneg(DisasContext *ctx, arg_VX_tb *a, unsigned vece)
{
REQUIRE_INSNS_FLAGS2(ctx, ISA300);
REQUIRE_VECTOR(ctx);
tcg_gen_gvec_neg(vece, avr_full_offset(a->vrt), avr_full_offset(a->vrb),
16, 16);
return true;
}
TRANS(VNEGW, do_vneg, MO_32)
TRANS(VNEGD, do_vneg, MO_64)
static void gen_vexts_i64(TCGv_i64 t, TCGv_i64 b, int64_t s)
{
@ -2834,8 +2903,6 @@ static void gen_xpnd04_2(DisasContext *ctx)
}
GEN_VXFORM_DUAL(vsubcuw, PPC_ALTIVEC, PPC_NONE, \
xpnd04_1, PPC_NONE, PPC2_ISA300)
GEN_VXFORM_DUAL(vsubsws, PPC_ALTIVEC, PPC_NONE, \
xpnd04_2, PPC_NONE, PPC2_ISA300)
@ -3097,6 +3164,63 @@ TRANS_FLAGS2(ALTIVEC_207, VPMSUMD, do_vx_helper, gen_helper_VPMSUMD)
TRANS_FLAGS2(ALTIVEC_207, VSUBCUQ, do_vx_helper, gen_helper_VSUBCUQ)
TRANS_FLAGS2(ALTIVEC_207, VSUBUQM, do_vx_helper, gen_helper_VSUBUQM)
static void gen_VADDCUW_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b)
{
tcg_gen_not_vec(vece, a, a);
tcg_gen_cmp_vec(TCG_COND_LTU, vece, t, a, b);
tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(t, vece, 1));
}
static void gen_VADDCUW_i32(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b)
{
tcg_gen_not_i32(a, a);
tcg_gen_setcond_i32(TCG_COND_LTU, t, a, b);
}
static void gen_VSUBCUW_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b)
{
tcg_gen_cmp_vec(TCG_COND_GEU, vece, t, a, b);
tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(t, vece, 1));
}
static void gen_VSUBCUW_i32(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b)
{
tcg_gen_setcond_i32(TCG_COND_GEU, t, a, b);
}
static bool do_vx_vaddsubcuw(DisasContext *ctx, arg_VX *a, int add)
{
static const TCGOpcode vecop_list[] = {
INDEX_op_cmp_vec, 0
};
static const GVecGen3 op[] = {
{
.fniv = gen_VSUBCUW_vec,
.fni4 = gen_VSUBCUW_i32,
.opt_opc = vecop_list,
.vece = MO_32
},
{
.fniv = gen_VADDCUW_vec,
.fni4 = gen_VADDCUW_i32,
.opt_opc = vecop_list,
.vece = MO_32
},
};
REQUIRE_INSNS_FLAGS(ctx, ALTIVEC);
REQUIRE_VECTOR(ctx);
tcg_gen_gvec_3(avr_full_offset(a->vrt), avr_full_offset(a->vra),
avr_full_offset(a->vrb), 16, 16, &op[add]);
return true;
}
TRANS(VSUBCUW, do_vx_vaddsubcuw, 0)
TRANS(VADDCUW, do_vx_vaddsubcuw, 1)
static bool do_vx_vmuleo(DisasContext *ctx, arg_VX *a, bool even,
void (*gen_mul)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64))
{
@ -3234,6 +3358,146 @@ TRANS(VMULHSD, do_vx_mulh, true , do_vx_vmulhd_i64)
TRANS(VMULHUW, do_vx_mulh, false, do_vx_vmulhw_i64)
TRANS(VMULHUD, do_vx_mulh, false, do_vx_vmulhd_i64)
static void do_vavg(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b,
void (*gen_shr_vec)(unsigned, TCGv_vec, TCGv_vec, int64_t))
{
TCGv_vec tmp = tcg_temp_new_vec_matching(t);
tcg_gen_or_vec(vece, tmp, a, b);
tcg_gen_and_vec(vece, tmp, tmp, tcg_constant_vec_matching(t, vece, 1));
gen_shr_vec(vece, a, a, 1);
gen_shr_vec(vece, b, b, 1);
tcg_gen_add_vec(vece, t, a, b);
tcg_gen_add_vec(vece, t, t, tmp);
tcg_temp_free_vec(tmp);
}
QEMU_FLATTEN
static void gen_vavgu(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b)
{
do_vavg(vece, t, a, b, tcg_gen_shri_vec);
}
QEMU_FLATTEN
static void gen_vavgs(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b)
{
do_vavg(vece, t, a, b, tcg_gen_sari_vec);
}
static bool do_vx_vavg(DisasContext *ctx, arg_VX *a, int sign, int vece)
{
static const TCGOpcode vecop_list_s[] = {
INDEX_op_add_vec, INDEX_op_sari_vec, 0
};
static const TCGOpcode vecop_list_u[] = {
INDEX_op_add_vec, INDEX_op_shri_vec, 0
};
static const GVecGen3 op[2][3] = {
{
{
.fniv = gen_vavgu,
.fno = gen_helper_VAVGUB,
.opt_opc = vecop_list_u,
.vece = MO_8
},
{
.fniv = gen_vavgu,
.fno = gen_helper_VAVGUH,
.opt_opc = vecop_list_u,
.vece = MO_16
},
{
.fniv = gen_vavgu,
.fno = gen_helper_VAVGUW,
.opt_opc = vecop_list_u,
.vece = MO_32
},
},
{
{
.fniv = gen_vavgs,
.fno = gen_helper_VAVGSB,
.opt_opc = vecop_list_s,
.vece = MO_8
},
{
.fniv = gen_vavgs,
.fno = gen_helper_VAVGSH,
.opt_opc = vecop_list_s,
.vece = MO_16
},
{
.fniv = gen_vavgs,
.fno = gen_helper_VAVGSW,
.opt_opc = vecop_list_s,
.vece = MO_32
},
},
};
REQUIRE_VECTOR(ctx);
tcg_gen_gvec_3(avr_full_offset(a->vrt), avr_full_offset(a->vra),
avr_full_offset(a->vrb), 16, 16, &op[sign][vece]);
return true;
}
TRANS_FLAGS(ALTIVEC, VAVGSB, do_vx_vavg, 1, MO_8)
TRANS_FLAGS(ALTIVEC, VAVGSH, do_vx_vavg, 1, MO_16)
TRANS_FLAGS(ALTIVEC, VAVGSW, do_vx_vavg, 1, MO_32)
TRANS_FLAGS(ALTIVEC, VAVGUB, do_vx_vavg, 0, MO_8)
TRANS_FLAGS(ALTIVEC, VAVGUH, do_vx_vavg, 0, MO_16)
TRANS_FLAGS(ALTIVEC, VAVGUW, do_vx_vavg, 0, MO_32)
static void gen_vabsdu(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b)
{
tcg_gen_umax_vec(vece, t, a, b);
tcg_gen_umin_vec(vece, a, a, b);
tcg_gen_sub_vec(vece, t, t, a);
}
static bool do_vabsdu(DisasContext *ctx, arg_VX *a, const int vece)
{
static const TCGOpcode vecop_list[] = {
INDEX_op_umax_vec, INDEX_op_umin_vec, INDEX_op_sub_vec, 0
};
static const GVecGen3 op[] = {
{
.fniv = gen_vabsdu,
.fno = gen_helper_VABSDUB,
.opt_opc = vecop_list,
.vece = MO_8
},
{
.fniv = gen_vabsdu,
.fno = gen_helper_VABSDUH,
.opt_opc = vecop_list,
.vece = MO_16
},
{
.fniv = gen_vabsdu,
.fno = gen_helper_VABSDUW,
.opt_opc = vecop_list,
.vece = MO_32
},
};
REQUIRE_VECTOR(ctx);
tcg_gen_gvec_3(avr_full_offset(a->vrt), avr_full_offset(a->vra),
avr_full_offset(a->vrb), 16, 16, &op[vece]);
return true;
}
TRANS_FLAGS2(ISA300, VABSDUB, do_vabsdu, MO_8)
TRANS_FLAGS2(ISA300, VABSDUH, do_vabsdu, MO_16)
TRANS_FLAGS2(ISA300, VABSDUW, do_vabsdu, MO_32)
static bool do_vdiv_vmod(DisasContext *ctx, arg_VX *a, const int vece,
void (*func_32)(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b),
void (*func_64)(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b))

View File

@ -83,12 +83,6 @@ GEN_VXFORM(vminsb, 1, 12),
GEN_VXFORM(vminsh, 1, 13),
GEN_VXFORM(vminsw, 1, 14),
GEN_VXFORM_207(vminsd, 1, 15),
GEN_VXFORM_DUAL(vavgub, vabsdub, 1, 16, PPC_ALTIVEC, PPC_NONE),
GEN_VXFORM_DUAL(vavguh, vabsduh, 1, 17, PPC_ALTIVEC, PPC_NONE),
GEN_VXFORM_DUAL(vavguw, vabsduw, 1, 18, PPC_ALTIVEC, PPC_NONE),
GEN_VXFORM(vavgsb, 1, 20),
GEN_VXFORM(vavgsh, 1, 21),
GEN_VXFORM(vavgsw, 1, 22),
GEN_VXFORM(vmrghb, 6, 0),
GEN_VXFORM(vmrghh, 6, 1),
GEN_VXFORM(vmrghw, 6, 2),
@ -106,12 +100,8 @@ GEN_VXFORM_300(vsrv, 2, 28),
GEN_VXFORM_300(vslv, 2, 29),
GEN_VXFORM(vslo, 6, 16),
GEN_VXFORM(vsro, 6, 17),
GEN_VXFORM(vaddcuw, 0, 6),
GEN_HANDLER_E_2(vprtybw, 0x4, 0x1, 0x18, 8, 0, PPC_NONE, PPC2_ISA300),
GEN_HANDLER_E_2(vprtybd, 0x4, 0x1, 0x18, 9, 0, PPC_NONE, PPC2_ISA300),
GEN_HANDLER_E_2(vprtybq, 0x4, 0x1, 0x18, 10, 0, PPC_NONE, PPC2_ISA300),
GEN_VXFORM_DUAL(vsubcuw, xpnd04_1, 0, 22, PPC_ALTIVEC, PPC_NONE),
GEN_VXFORM(xpnd04_1, 0, 22),
GEN_VXFORM_300(bcdsr, 0, 23),
GEN_VXFORM_300(bcdsr, 0, 31),
GEN_VXFORM_DUAL(vaddubs, vmul10uq, 0, 8, PPC_ALTIVEC, PPC_NONE),
@ -182,8 +172,6 @@ GEN_VXFORM_300_EXT(vextractd, 6, 11, 0x100000),
GEN_VXFORM(vspltisb, 6, 12),
GEN_VXFORM(vspltish, 6, 13),
GEN_VXFORM(vspltisw, 6, 14),
GEN_VXFORM_300_EO(vnegw, 0x01, 0x18, 0x06),
GEN_VXFORM_300_EO(vnegd, 0x01, 0x18, 0x07),
GEN_VXFORM_300_EO(vctzb, 0x01, 0x18, 0x1C),
GEN_VXFORM_300_EO(vctzh, 0x01, 0x18, 0x1D),
GEN_VXFORM_300_EO(vctzw, 0x01, 0x18, 0x1E),
@ -219,7 +207,6 @@ GEN_VXFORM_UIMM(vctsxs, 5, 15),
#define GEN_VAFORM_PAIRED(name0, name1, opc2) \
GEN_HANDLER(name0##_##name1, 0x04, opc2, 0xFF, 0x00000000, PPC_ALTIVEC)
GEN_VAFORM_PAIRED(vmhaddshs, vmhraddshs, 16),
GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23),
GEN_VXFORM_DUAL(vclzb, vpopcntb, 1, 28, PPC_NONE, PPC2_ALTIVEC_207),

View File

@ -630,6 +630,10 @@ static void gen_mtvsrws(DisasContext *ctx)
#define OP_CPSGN 4
#define SGN_MASK_DP 0x8000000000000000ull
#define SGN_MASK_SP 0x8000000080000000ull
#define EXP_MASK_DP 0x7FF0000000000000ull
#define EXP_MASK_SP 0x7F8000007F800000ull
#define FRC_MASK_DP (~(SGN_MASK_DP | EXP_MASK_DP))
#define FRC_MASK_SP (~(SGN_MASK_SP | EXP_MASK_SP))
#define VSX_SCALAR_MOVE(name, op, sgn_mask) \
static void glue(gen_, name)(DisasContext *ctx) \
@ -729,67 +733,125 @@ 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) \
{ \
TCGv_i64 xbh, xbl, sgm; \
if (unlikely(!ctx->vsx_enabled)) { \
gen_exception(ctx, POWERPC_EXCP_VSXU); \
return; \
} \
xbh = tcg_temp_new_i64(); \
xbl = tcg_temp_new_i64(); \
sgm = tcg_temp_new_i64(); \
get_cpu_vsr(xbh, xB(ctx->opcode), true); \
get_cpu_vsr(xbl, xB(ctx->opcode), false); \
tcg_gen_movi_i64(sgm, sgn_mask); \
switch (op) { \
case OP_ABS: { \
tcg_gen_andc_i64(xbh, xbh, sgm); \
tcg_gen_andc_i64(xbl, xbl, sgm); \
break; \
} \
case OP_NABS: { \
tcg_gen_or_i64(xbh, xbh, sgm); \
tcg_gen_or_i64(xbl, xbl, sgm); \
break; \
} \
case OP_NEG: { \
tcg_gen_xor_i64(xbh, xbh, sgm); \
tcg_gen_xor_i64(xbl, xbl, sgm); \
break; \
} \
case OP_CPSGN: { \
TCGv_i64 xah = tcg_temp_new_i64(); \
TCGv_i64 xal = tcg_temp_new_i64(); \
get_cpu_vsr(xah, xA(ctx->opcode), true); \
get_cpu_vsr(xal, xA(ctx->opcode), false); \
tcg_gen_and_i64(xah, xah, sgm); \
tcg_gen_and_i64(xal, xal, sgm); \
tcg_gen_andc_i64(xbh, xbh, sgm); \
tcg_gen_andc_i64(xbl, xbl, sgm); \
tcg_gen_or_i64(xbh, xbh, xah); \
tcg_gen_or_i64(xbl, xbl, xal); \
tcg_temp_free_i64(xah); \
tcg_temp_free_i64(xal); \
break; \
} \
} \
set_cpu_vsr(xT(ctx->opcode), xbh, true); \
set_cpu_vsr(xT(ctx->opcode), xbl, false); \
tcg_temp_free_i64(xbh); \
tcg_temp_free_i64(xbl); \
tcg_temp_free_i64(sgm); \
#define TCG_OP_IMM_i64(FUNC, OP, IMM) \
static void FUNC(TCGv_i64 t, TCGv_i64 b) \
{ \
OP(t, b, IMM); \
}
VSX_VECTOR_MOVE(xvabsdp, OP_ABS, SGN_MASK_DP)
VSX_VECTOR_MOVE(xvnabsdp, OP_NABS, SGN_MASK_DP)
VSX_VECTOR_MOVE(xvnegdp, OP_NEG, SGN_MASK_DP)
VSX_VECTOR_MOVE(xvcpsgndp, OP_CPSGN, SGN_MASK_DP)
VSX_VECTOR_MOVE(xvabssp, OP_ABS, SGN_MASK_SP)
VSX_VECTOR_MOVE(xvnabssp, OP_NABS, SGN_MASK_SP)
VSX_VECTOR_MOVE(xvnegsp, OP_NEG, SGN_MASK_SP)
VSX_VECTOR_MOVE(xvcpsgnsp, OP_CPSGN, SGN_MASK_SP)
TCG_OP_IMM_i64(do_xvabssp_i64, tcg_gen_andi_i64, ~SGN_MASK_SP)
TCG_OP_IMM_i64(do_xvnabssp_i64, tcg_gen_ori_i64, SGN_MASK_SP)
TCG_OP_IMM_i64(do_xvnegsp_i64, tcg_gen_xori_i64, SGN_MASK_SP)
TCG_OP_IMM_i64(do_xvabsdp_i64, tcg_gen_andi_i64, ~SGN_MASK_DP)
TCG_OP_IMM_i64(do_xvnabsdp_i64, tcg_gen_ori_i64, SGN_MASK_DP)
TCG_OP_IMM_i64(do_xvnegdp_i64, tcg_gen_xori_i64, SGN_MASK_DP)
#undef TCG_OP_IMM_i64
static void xv_msb_op1(unsigned vece, TCGv_vec t, TCGv_vec b,
void (*tcg_gen_op_vec)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec))
{
uint64_t msb = (vece == MO_32) ? SGN_MASK_SP : SGN_MASK_DP;
tcg_gen_op_vec(vece, t, b, tcg_constant_vec_matching(t, vece, msb));
}
static void do_xvabs_vec(unsigned vece, TCGv_vec t, TCGv_vec b)
{
xv_msb_op1(vece, t, b, tcg_gen_andc_vec);
}
static void do_xvnabs_vec(unsigned vece, TCGv_vec t, TCGv_vec b)
{
xv_msb_op1(vece, t, b, tcg_gen_or_vec);
}
static void do_xvneg_vec(unsigned vece, TCGv_vec t, TCGv_vec b)
{
xv_msb_op1(vece, t, b, tcg_gen_xor_vec);
}
static bool do_vsx_msb_op(DisasContext *ctx, arg_XX2 *a, unsigned vece,
void (*vec)(unsigned, TCGv_vec, TCGv_vec),
void (*i64)(TCGv_i64, TCGv_i64))
{
static const TCGOpcode vecop_list[] = {
0
};
const GVecGen2 op = {
.fni8 = i64,
.fniv = vec,
.opt_opc = vecop_list,
.vece = vece
};
REQUIRE_INSNS_FLAGS2(ctx, VSX);
REQUIRE_VSX(ctx);
tcg_gen_gvec_2(vsr_full_offset(a->xt), vsr_full_offset(a->xb),
16, 16, &op);
return true;
}
TRANS(XVABSDP, do_vsx_msb_op, MO_64, do_xvabs_vec, do_xvabsdp_i64)
TRANS(XVNABSDP, do_vsx_msb_op, MO_64, do_xvnabs_vec, do_xvnabsdp_i64)
TRANS(XVNEGDP, do_vsx_msb_op, MO_64, do_xvneg_vec, do_xvnegdp_i64)
TRANS(XVABSSP, do_vsx_msb_op, MO_32, do_xvabs_vec, do_xvabssp_i64)
TRANS(XVNABSSP, do_vsx_msb_op, MO_32, do_xvnabs_vec, do_xvnabssp_i64)
TRANS(XVNEGSP, do_vsx_msb_op, MO_32, do_xvneg_vec, do_xvnegsp_i64)
static void do_xvcpsgndp_i64(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b)
{
tcg_gen_andi_i64(a, a, SGN_MASK_DP);
tcg_gen_andi_i64(b, b, ~SGN_MASK_DP);
tcg_gen_or_i64(t, a, b);
}
static void do_xvcpsgnsp_i64(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b)
{
tcg_gen_andi_i64(a, a, SGN_MASK_SP);
tcg_gen_andi_i64(b, b, ~SGN_MASK_SP);
tcg_gen_or_i64(t, a, b);
}
static void do_xvcpsgn_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b)
{
uint64_t msb = (vece == MO_32) ? SGN_MASK_SP : SGN_MASK_DP;
tcg_gen_bitsel_vec(vece, t, tcg_constant_vec_matching(t, vece, msb), a, b);
}
static bool do_xvcpsgn(DisasContext *ctx, arg_XX3 *a, unsigned vece)
{
static const TCGOpcode vecop_list[] = {
0
};
static const GVecGen3 op[] = {
{
.fni8 = do_xvcpsgnsp_i64,
.fniv = do_xvcpsgn_vec,
.opt_opc = vecop_list,
.vece = MO_32
},
{
.fni8 = do_xvcpsgndp_i64,
.fniv = do_xvcpsgn_vec,
.opt_opc = vecop_list,
.vece = MO_64
},
};
REQUIRE_INSNS_FLAGS2(ctx, VSX);
REQUIRE_VSX(ctx);
tcg_gen_gvec_3(vsr_full_offset(a->xt), vsr_full_offset(a->xa),
vsr_full_offset(a->xb), 16, 16, &op[vece - MO_32]);
return true;
}
TRANS(XVCPSGNSP, do_xvcpsgn, MO_32)
TRANS(XVCPSGNDP, do_xvcpsgn, MO_64)
#define VSX_CMP(name, op1, op2, inval, type) \
static void gen_##name(DisasContext *ctx) \
@ -1052,6 +1114,192 @@ GEN_VSX_HELPER_X2(xscvhpdp, 0x16, 0x15, 0x10, PPC2_ISA300)
GEN_VSX_HELPER_R2(xscvsdqp, 0x04, 0x1A, 0x0A, PPC2_ISA300)
GEN_VSX_HELPER_X2(xscvspdp, 0x12, 0x14, 0, PPC2_VSX)
/* test if +Inf */
static void gen_is_pos_inf(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v)
{
uint64_t exp_msk = (vece == MO_32) ? (uint32_t)EXP_MASK_SP : EXP_MASK_DP;
tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b,
tcg_constant_vec_matching(t, vece, exp_msk));
}
/* test if -Inf */
static void gen_is_neg_inf(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v)
{
uint64_t exp_msk = (vece == MO_32) ? (uint32_t)EXP_MASK_SP : EXP_MASK_DP;
uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP;
tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b,
tcg_constant_vec_matching(t, vece, sgn_msk | exp_msk));
}
/* test if +Inf or -Inf */
static void gen_is_any_inf(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v)
{
uint64_t exp_msk = (vece == MO_32) ? (uint32_t)EXP_MASK_SP : EXP_MASK_DP;
uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP;
tcg_gen_andc_vec(vece, b, b, tcg_constant_vec_matching(t, vece, sgn_msk));
tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b,
tcg_constant_vec_matching(t, vece, exp_msk));
}
/* test if +0 */
static void gen_is_pos_zero(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v)
{
tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b,
tcg_constant_vec_matching(t, vece, 0));
}
/* test if -0 */
static void gen_is_neg_zero(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v)
{
uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP;
tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b,
tcg_constant_vec_matching(t, vece, sgn_msk));
}
/* test if +0 or -0 */
static void gen_is_any_zero(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v)
{
uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP;
tcg_gen_andc_vec(vece, b, b, tcg_constant_vec_matching(t, vece, sgn_msk));
tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b,
tcg_constant_vec_matching(t, vece, 0));
}
/* test if +Denormal */
static void gen_is_pos_denormal(unsigned vece, TCGv_vec t,
TCGv_vec b, int64_t v)
{
uint64_t frc_msk = (vece == MO_32) ? (uint32_t)FRC_MASK_SP : FRC_MASK_DP;
tcg_gen_cmp_vec(TCG_COND_LEU, vece, t, b,
tcg_constant_vec_matching(t, vece, frc_msk));
tcg_gen_cmp_vec(TCG_COND_NE, vece, b, b,
tcg_constant_vec_matching(t, vece, 0));
tcg_gen_and_vec(vece, t, t, b);
}
/* test if -Denormal */
static void gen_is_neg_denormal(unsigned vece, TCGv_vec t,
TCGv_vec b, int64_t v)
{
uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP;
uint64_t frc_msk = (vece == MO_32) ? (uint32_t)FRC_MASK_SP : FRC_MASK_DP;
tcg_gen_cmp_vec(TCG_COND_LEU, vece, t, b,
tcg_constant_vec_matching(t, vece, sgn_msk | frc_msk));
tcg_gen_cmp_vec(TCG_COND_GTU, vece, b, b,
tcg_constant_vec_matching(t, vece, sgn_msk));
tcg_gen_and_vec(vece, t, t, b);
}
/* test if +Denormal or -Denormal */
static void gen_is_any_denormal(unsigned vece, TCGv_vec t,
TCGv_vec b, int64_t v)
{
uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP;
uint64_t frc_msk = (vece == MO_32) ? (uint32_t)FRC_MASK_SP : FRC_MASK_DP;
tcg_gen_andc_vec(vece, b, b, tcg_constant_vec_matching(t, vece, sgn_msk));
tcg_gen_cmp_vec(TCG_COND_LE, vece, t, b,
tcg_constant_vec_matching(t, vece, frc_msk));
tcg_gen_cmp_vec(TCG_COND_NE, vece, b, b,
tcg_constant_vec_matching(t, vece, 0));
tcg_gen_and_vec(vece, t, t, b);
}
/* test if NaN */
static void gen_is_nan(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v)
{
uint64_t exp_msk = (vece == MO_32) ? (uint32_t)EXP_MASK_SP : EXP_MASK_DP;
uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP;
tcg_gen_and_vec(vece, b, b, tcg_constant_vec_matching(t, vece, ~sgn_msk));
tcg_gen_cmp_vec(TCG_COND_GT, vece, t, b,
tcg_constant_vec_matching(t, vece, exp_msk));
}
static bool do_xvtstdc(DisasContext *ctx, arg_XX2_uim *a, unsigned vece)
{
static const TCGOpcode vecop_list[] = {
INDEX_op_cmp_vec, 0
};
GVecGen2i op = {
.fnoi = (vece == MO_32) ? gen_helper_XVTSTDCSP : gen_helper_XVTSTDCDP,
.vece = vece,
.opt_opc = vecop_list
};
REQUIRE_VSX(ctx);
switch (a->uim) {
case 0:
set_cpu_vsr(a->xt, tcg_constant_i64(0), true);
set_cpu_vsr(a->xt, tcg_constant_i64(0), false);
return true;
case ((1 << 0) | (1 << 1)):
/* test if +Denormal or -Denormal */
op.fniv = gen_is_any_denormal;
break;
case (1 << 0):
/* test if -Denormal */
op.fniv = gen_is_neg_denormal;
break;
case (1 << 1):
/* test if +Denormal */
op.fniv = gen_is_pos_denormal;
break;
case ((1 << 2) | (1 << 3)):
/* test if +0 or -0 */
op.fniv = gen_is_any_zero;
break;
case (1 << 2):
/* test if -0 */
op.fniv = gen_is_neg_zero;
break;
case (1 << 3):
/* test if +0 */
op.fniv = gen_is_pos_zero;
break;
case ((1 << 4) | (1 << 5)):
/* test if +Inf or -Inf */
op.fniv = gen_is_any_inf;
break;
case (1 << 4):
/* test if -Inf */
op.fniv = gen_is_neg_inf;
break;
case (1 << 5):
/* test if +Inf */
op.fniv = gen_is_pos_inf;
break;
case (1 << 6):
/* test if NaN */
op.fniv = gen_is_nan;
break;
}
tcg_gen_gvec_2i(vsr_full_offset(a->xt), vsr_full_offset(a->xb),
16, 16, a->uim, &op);
return true;
}
TRANS_FLAGS2(VSX, XVTSTDCSP, do_xvtstdc, MO_32)
TRANS_FLAGS2(VSX, XVTSTDCDP, do_xvtstdc, MO_64)
static bool do_XX2_bf_uim(DisasContext *ctx, arg_XX2_bf_uim *a, bool vsr,
void (*gen_helper)(TCGv_env, TCGv_i32, TCGv_i32, TCGv_ptr))
{
TCGv_ptr xb;
REQUIRE_VSX(ctx);
xb = vsr ? gen_vsr_ptr(a->xb) : gen_avr_ptr(a->xb);
gen_helper(cpu_env, tcg_constant_i32(a->bf), tcg_constant_i32(a->uim), xb);
tcg_temp_free_ptr(xb);
return true;
}
TRANS_FLAGS2(ISA300, XSTSTDCSP, do_XX2_bf_uim, true, gen_helper_XSTSTDCSP)
TRANS_FLAGS2(ISA300, XSTSTDCDP, do_XX2_bf_uim, true, gen_helper_XSTSTDCDP)
TRANS_FLAGS2(ISA300, XSTSTDCQP, do_XX2_bf_uim, false, gen_helper_XSTSTDCQP)
bool trans_XSCVSPDPN(DisasContext *ctx, arg_XX2 *a)
{
TCGv_i64 tmp;
@ -1098,9 +1346,6 @@ GEN_VSX_HELPER_X2(xssqrtsp, 0x16, 0x00, 0, PPC2_VSX207)
GEN_VSX_HELPER_X2(xsrsqrtesp, 0x14, 0x00, 0, PPC2_VSX207)
GEN_VSX_HELPER_X2(xscvsxdsp, 0x10, 0x13, 0, PPC2_VSX207)
GEN_VSX_HELPER_X2(xscvuxdsp, 0x10, 0x12, 0, PPC2_VSX207)
GEN_VSX_HELPER_X1(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_X3(xvadddp, 0x00, 0x0C, 0, PPC2_VSX)
GEN_VSX_HELPER_X3(xvsubdp, 0x00, 0x0D, 0, PPC2_VSX)
@ -1155,8 +1400,6 @@ GEN_VSX_HELPER_X2(xvrspic, 0x16, 0x0A, 0, PPC2_VSX)
GEN_VSX_HELPER_X2(xvrspim, 0x12, 0x0B, 0, PPC2_VSX)
GEN_VSX_HELPER_X2(xvrspip, 0x12, 0x0A, 0, PPC2_VSX)
GEN_VSX_HELPER_X2(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)
static bool trans_XXPERM(DisasContext *ctx, arg_XX3 *a)
{

View File

@ -147,33 +147,12 @@ 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),
/* 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),
GEN_XX3FORM(xvcpsgndp, 0x00, 0x1E, PPC2_VSX),
GEN_XX2FORM(xvabssp, 0x12, 0x19, PPC2_VSX),
GEN_XX2FORM(xvnabssp, 0x12, 0x1A, PPC2_VSX),
GEN_XX2FORM(xvnegsp, 0x12, 0x1B, PPC2_VSX),
GEN_XX3FORM(xvcpsgnsp, 0x00, 0x1A, PPC2_VSX),
GEN_XX3FORM(xsadddp, 0x00, 0x04, PPC2_VSX),
GEN_VSX_XFORM_300(xsaddqp, 0x04, 0x00, 0x0),
GEN_XX3FORM(xssubdp, 0x00, 0x05, PPC2_VSX),