summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Hajnoczi2022-10-31 11:28:43 +0100
committerStefan Hajnoczi2022-10-31 11:28:43 +0100
commit179938097df4de34b13527ad1fd469f501548b07 (patch)
treef18f1182b784086b1c35c3ef7f0caf8c4f17fd4d
parentMerge tag 'pull-request-2022-10-28' of https://gitlab.com/thuth/qemu into sta... (diff)
parenttarget/ppc: Fix regression in Radix MMU (diff)
downloadqemu-179938097df4de34b13527ad1fd469f501548b07.tar.gz
qemu-179938097df4de34b13527ad1fd469f501548b07.tar.xz
qemu-179938097df4de34b13527ad1fd469f501548b07.zip
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>
-rw-r--r--docs/system/ppc/ppce500.rst25
-rw-r--r--hw/block/pflash_cfi01.c8
-rw-r--r--hw/block/pflash_cfi02.c5
-rw-r--r--hw/ppc/Kconfig1
-rw-r--r--hw/ppc/e500.c79
-rw-r--r--hw/ppc/meson.build3
-rw-r--r--hw/ppc/pnv_core.c1
-rw-r--r--hw/ppc/ppc.c17
-rw-r--r--hw/ppc/ppc440_uc.c332
-rw-r--r--hw/ppc/ppc4xx_devs.c414
-rw-r--r--hw/ppc/ppc4xx_sdram.c757
-rw-r--r--hw/ppc/spapr_hcall.c6
-rw-r--r--hw/ppc/spapr_rtas.c2
-rw-r--r--hw/ppc/trace-events3
-rw-r--r--hw/sd/sdhci-internal.h20
-rw-r--r--hw/sd/sdhci.c63
-rw-r--r--include/hw/ppc/ppc4xx.h20
-rw-r--r--target/ppc/cpu.c4
-rw-r--r--target/ppc/cpu.h47
-rw-r--r--target/ppc/cpu_init.c212
-rw-r--r--target/ppc/excp_helper.c863
-rw-r--r--target/ppc/fpu_helper.c137
-rw-r--r--target/ppc/helper.h44
-rw-r--r--target/ppc/helper_regs.c8
-rw-r--r--target/ppc/insn32.decode58
-rw-r--r--target/ppc/int_helper.c107
-rw-r--r--target/ppc/misc_helper.c11
-rw-r--r--target/ppc/mmu-radix64.c29
-rw-r--r--target/ppc/power8-pmu.c74
-rw-r--r--target/ppc/power8-pmu.h3
-rw-r--r--target/ppc/translate.c130
-rw-r--r--target/ppc/translate/processor-ctrl-impl.c.inc105
-rw-r--r--target/ppc/translate/vmx-impl.c.inc352
-rw-r--r--target/ppc/translate/vmx-ops.c.inc15
-rw-r--r--target/ppc/translate/vsx-impl.c.inc375
-rw-r--r--target/ppc/translate/vsx-ops.c.inc21
36 files changed, 2736 insertions, 1615 deletions
diff --git a/docs/system/ppc/ppce500.rst b/docs/system/ppc/ppce500.rst
index ba6bcb7314..fa40e57d18 100644
--- a/docs/system/ppc/ppce500.rst
+++ b/docs/system/ppc/ppce500.rst
@@ -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"
diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c
index 0cbc2fb4cb..9c235bf66e 100644
--- a/hw/block/pflash_cfi01.c
+++ b/hw/block/pflash_cfi01.c
@@ -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)
diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c
index 2a99b286b0..ff2fe154c1 100644
--- a/hw/block/pflash_cfi02.c
+++ b/hw/block/pflash_cfi02.c
@@ -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);
diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index 791fe78a50..769a1ead1c 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -126,6 +126,7 @@ config E500
select ETSEC
select GPIO_MPC8XXX
select OPENPIC
+ select PFLASH_CFI01
select PLATFORM_BUS
select PPCE500_PCI
select SERIAL
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index 3e950ea3ba..2fe496677c 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -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!
*
diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build
index 32babc9b48..c927337da0 100644
--- a/hw/ppc/meson.build
+++ b/hw/ppc/meson.build
@@ -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'))
diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
index 19e8eb885f..9ee79192dd 100644
--- a/hw/ppc/pnv_core.c
+++ b/hw/ppc/pnv_core.c
@@ -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);
}
diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
index 690f448cb9..dc86c1c7db 100644
--- a/hw/ppc/ppc.c
+++ b/hw/ppc/ppc.c
@@ -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) {
diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c
index 5fbf44009e..651263926e 100644
--- a/hw/ppc/ppc440_uc.c
+++ b/hw/ppc/ppc440_uc.c
@@ -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 */
@@ -479,331 +472,6 @@ void ppc4xx_sdr_init(CPUPPCState *env)
}
/*****************************************************************************/
-/* 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 {
AHB_TOP = 0xA4,
diff --git a/hw/ppc/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c
index 12af90f244..c1d111465d 100644
--- a/hw/ppc/ppc4xx_devs.c
+++ b/hw/ppc/ppc4xx_devs.c
@@ -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),
diff --git a/hw/ppc/ppc4xx_sdram.c b/hw/ppc/ppc4xx_sdram.c
new file mode 100644
index 0000000000..8d7137faf3
--- /dev/null
+++ b/hw/ppc/ppc4xx_sdram.c
@@ -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)
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index 891206e893..925ff523cc 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -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 */
diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c
index d58b65e88f..3f664ea02c 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -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);
}
diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events
index a07d5aca0f..f670e8906c 100644
--- a/hw/ppc/trace-events
+++ b/hw/ppc/trace-events
@@ -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"
diff --git a/hw/sd/sdhci-internal.h b/hw/sd/sdhci-internal.h
index e8c753d6d1..964570f8e8 100644
--- a/hw/sd/sdhci-internal.h
+++ b/hw/sd/sdhci-internal.h
@@ -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.
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index 0e5e988927..306070c872 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -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
diff --git a/include/hw/ppc/ppc4xx.h b/include/hw/ppc/ppc4xx.h
index 10c6dd535f..f8c86e09ec 100644
--- a/include/hw/ppc/ppc4xx.h
+++ b/include/hw/ppc/ppc4xx.h
@@ -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
diff --git a/target/ppc/cpu.c b/target/ppc/cpu.c
index 0ebac04bc4..1a97b41c6b 100644
--- a/target/ppc/cpu.c
+++ b/target/ppc/cpu.c
@@ -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)
{
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index cca6c4e51c..81d4263a07 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -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) */
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
index 335351c226..32e94153d1 100644
--- a/target/ppc/cpu_init.c
+++ b/target/ppc/cpu_init.c
@@ -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)
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index 43f2480e94..09a81561d4 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -23,6 +23,7 @@
#include "exec/exec-all.h"
#include "internal.h"
#include "helper_regs.h"
+#include "hw/ppc/ppc.h"
#include "trace.h"
@@ -389,6 +390,7 @@ static void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong vector,
env->nip = vector;
env->msr = msr;
hreg_compute_hflags(env);
+ ppc_maybe_interrupt(env);
powerpc_reset_excp_state(cpu);
@@ -1683,29 +1685,355 @@ void ppc_cpu_do_interrupt(CPUState *cs)
powerpc_excp(cpu, cs->exception_index);
}
-static void ppc_hw_interrupt(CPUPPCState *env)
+#if defined(TARGET_PPC64)
+#define P7_UNUSED_INTERRUPTS \
+ (PPC_INTERRUPT_RESET | PPC_INTERRUPT_HVIRT | PPC_INTERRUPT_CEXT | \
+ PPC_INTERRUPT_WDT | PPC_INTERRUPT_CDOORBELL | PPC_INTERRUPT_FIT | \
+ PPC_INTERRUPT_PIT | PPC_INTERRUPT_DOORBELL | PPC_INTERRUPT_HDOORBELL | \
+ PPC_INTERRUPT_THERM | PPC_INTERRUPT_EBB)
+
+static int p7_interrupt_powersave(CPUPPCState *env)
+{
+ if ((env->pending_interrupts & PPC_INTERRUPT_EXT) &&
+ (env->spr[SPR_LPCR] & LPCR_P7_PECE0)) {
+ return PPC_INTERRUPT_EXT;
+ }
+ if ((env->pending_interrupts & PPC_INTERRUPT_DECR) &&
+ (env->spr[SPR_LPCR] & LPCR_P7_PECE1)) {
+ return PPC_INTERRUPT_DECR;
+ }
+ if ((env->pending_interrupts & PPC_INTERRUPT_MCK) &&
+ (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) {
+ return PPC_INTERRUPT_MCK;
+ }
+ if ((env->pending_interrupts & PPC_INTERRUPT_HMI) &&
+ (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) {
+ return PPC_INTERRUPT_HMI;
+ }
+ if (env->pending_interrupts & PPC_INTERRUPT_RESET) {
+ return PPC_INTERRUPT_RESET;
+ }
+ return 0;
+}
+
+static int p7_next_unmasked_interrupt(CPUPPCState *env)
{
PowerPCCPU *cpu = env_archcpu(env);
+ CPUState *cs = CPU(cpu);
+ /* Ignore MSR[EE] when coming out of some power management states */
+ bool msr_ee = FIELD_EX64(env->msr, MSR, EE) || env->resume_as_sreset;
+
+ assert((env->pending_interrupts & P7_UNUSED_INTERRUPTS) == 0);
+
+ if (cs->halted) {
+ /* LPCR[PECE] controls which interrupts can exit power-saving mode */
+ return p7_interrupt_powersave(env);
+ }
+
+ /* Machine check exception */
+ if (env->pending_interrupts & PPC_INTERRUPT_MCK) {
+ return PPC_INTERRUPT_MCK;
+ }
+
+ /* Hypervisor decrementer exception */
+ if (env->pending_interrupts & PPC_INTERRUPT_HDECR) {
+ /* LPCR will be clear when not supported so this will work */
+ bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE);
+ if ((msr_ee || !FIELD_EX64_HV(env->msr)) && hdice) {
+ /* HDEC clears on delivery */
+ return PPC_INTERRUPT_HDECR;
+ }
+ }
+
+ /* External interrupt can ignore MSR:EE under some circumstances */
+ if (env->pending_interrupts & PPC_INTERRUPT_EXT) {
+ bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
+ bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC);
+ /* HEIC blocks delivery to the hypervisor */
+ if ((msr_ee && !(heic && FIELD_EX64_HV(env->msr) &&
+ !FIELD_EX64(env->msr, MSR, PR))) ||
+ (env->has_hv_mode && !FIELD_EX64_HV(env->msr) && !lpes0)) {
+ return PPC_INTERRUPT_EXT;
+ }
+ }
+ if (msr_ee != 0) {
+ /* Decrementer exception */
+ if (env->pending_interrupts & PPC_INTERRUPT_DECR) {
+ return PPC_INTERRUPT_DECR;
+ }
+ if (env->pending_interrupts & PPC_INTERRUPT_PERFM) {
+ return PPC_INTERRUPT_PERFM;
+ }
+ }
+
+ return 0;
+}
+
+#define P8_UNUSED_INTERRUPTS \
+ (PPC_INTERRUPT_RESET | PPC_INTERRUPT_DEBUG | PPC_INTERRUPT_HVIRT | \
+ PPC_INTERRUPT_CEXT | PPC_INTERRUPT_WDT | PPC_INTERRUPT_CDOORBELL | \
+ PPC_INTERRUPT_FIT | PPC_INTERRUPT_PIT | PPC_INTERRUPT_THERM)
+
+static int p8_interrupt_powersave(CPUPPCState *env)
+{
+ if ((env->pending_interrupts & PPC_INTERRUPT_EXT) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE2)) {
+ return PPC_INTERRUPT_EXT;
+ }
+ if ((env->pending_interrupts & PPC_INTERRUPT_DECR) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE3)) {
+ return PPC_INTERRUPT_DECR;
+ }
+ if ((env->pending_interrupts & PPC_INTERRUPT_MCK) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) {
+ return PPC_INTERRUPT_MCK;
+ }
+ if ((env->pending_interrupts & PPC_INTERRUPT_HMI) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) {
+ return PPC_INTERRUPT_HMI;
+ }
+ if ((env->pending_interrupts & PPC_INTERRUPT_DOORBELL) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE0)) {
+ return PPC_INTERRUPT_DOORBELL;
+ }
+ if ((env->pending_interrupts & PPC_INTERRUPT_HDOORBELL) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE1)) {
+ return PPC_INTERRUPT_HDOORBELL;
+ }
+ if (env->pending_interrupts & PPC_INTERRUPT_RESET) {
+ return PPC_INTERRUPT_RESET;
+ }
+ return 0;
+}
+
+static int p8_next_unmasked_interrupt(CPUPPCState *env)
+{
+ PowerPCCPU *cpu = env_archcpu(env);
+ CPUState *cs = CPU(cpu);
+ /* Ignore MSR[EE] when coming out of some power management states */
+ bool msr_ee = FIELD_EX64(env->msr, MSR, EE) || env->resume_as_sreset;
+
+ assert((env->pending_interrupts & P8_UNUSED_INTERRUPTS) == 0);
+
+ if (cs->halted) {
+ /* LPCR[PECE] controls which interrupts can exit power-saving mode */
+ return p8_interrupt_powersave(env);
+ }
+
+ /* Machine check exception */
+ if (env->pending_interrupts & PPC_INTERRUPT_MCK) {
+ return PPC_INTERRUPT_MCK;
+ }
+
+ /* Hypervisor decrementer exception */
+ if (env->pending_interrupts & PPC_INTERRUPT_HDECR) {
+ /* LPCR will be clear when not supported so this will work */
+ bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE);
+ if ((msr_ee || !FIELD_EX64_HV(env->msr)) && hdice) {
+ /* HDEC clears on delivery */
+ return PPC_INTERRUPT_HDECR;
+ }
+ }
+
+ /* External interrupt can ignore MSR:EE under some circumstances */
+ if (env->pending_interrupts & PPC_INTERRUPT_EXT) {
+ bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
+ bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC);
+ /* HEIC blocks delivery to the hypervisor */
+ if ((msr_ee && !(heic && FIELD_EX64_HV(env->msr) &&
+ !FIELD_EX64(env->msr, MSR, PR))) ||
+ (env->has_hv_mode && !FIELD_EX64_HV(env->msr) && !lpes0)) {
+ return PPC_INTERRUPT_EXT;
+ }
+ }
+ if (msr_ee != 0) {
+ /* Decrementer exception */
+ if (env->pending_interrupts & PPC_INTERRUPT_DECR) {
+ return PPC_INTERRUPT_DECR;
+ }
+ if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) {
+ return PPC_INTERRUPT_DOORBELL;
+ }
+ if (env->pending_interrupts & PPC_INTERRUPT_HDOORBELL) {
+ return PPC_INTERRUPT_HDOORBELL;
+ }
+ if (env->pending_interrupts & PPC_INTERRUPT_PERFM) {
+ return PPC_INTERRUPT_PERFM;
+ }
+ /* EBB exception */
+ if (env->pending_interrupts & PPC_INTERRUPT_EBB) {
+ /*
+ * EBB exception must be taken in problem state and
+ * with BESCR_GE set.
+ */
+ if (FIELD_EX64(env->msr, MSR, PR) &&
+ (env->spr[SPR_BESCR] & BESCR_GE)) {
+ return PPC_INTERRUPT_EBB;
+ }
+ }
+ }
+
+ return 0;
+}
+
+#define P9_UNUSED_INTERRUPTS \
+ (PPC_INTERRUPT_RESET | PPC_INTERRUPT_DEBUG | PPC_INTERRUPT_CEXT | \
+ PPC_INTERRUPT_WDT | PPC_INTERRUPT_CDOORBELL | PPC_INTERRUPT_FIT | \
+ PPC_INTERRUPT_PIT | PPC_INTERRUPT_THERM)
+
+static int p9_interrupt_powersave(CPUPPCState *env)
+{
+ /* External Exception */
+ if ((env->pending_interrupts & 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 PPC_INTERRUPT_EXT;
+ }
+ }
+ /* Decrementer Exception */
+ if ((env->pending_interrupts & PPC_INTERRUPT_DECR) &&
+ (env->spr[SPR_LPCR] & LPCR_DEE)) {
+ return PPC_INTERRUPT_DECR;
+ }
+ /* Machine Check or Hypervisor Maintenance Exception */
+ if (env->spr[SPR_LPCR] & LPCR_OEE) {
+ if (env->pending_interrupts & PPC_INTERRUPT_MCK) {
+ return PPC_INTERRUPT_MCK;
+ }
+ if (env->pending_interrupts & PPC_INTERRUPT_HMI) {
+ return PPC_INTERRUPT_HMI;
+ }
+ }
+ /* Privileged Doorbell Exception */
+ if ((env->pending_interrupts & PPC_INTERRUPT_DOORBELL) &&
+ (env->spr[SPR_LPCR] & LPCR_PDEE)) {
+ return PPC_INTERRUPT_DOORBELL;
+ }
+ /* Hypervisor Doorbell Exception */
+ if ((env->pending_interrupts & PPC_INTERRUPT_HDOORBELL) &&
+ (env->spr[SPR_LPCR] & LPCR_HDEE)) {
+ return PPC_INTERRUPT_HDOORBELL;
+ }
+ /* Hypervisor virtualization exception */
+ if ((env->pending_interrupts & PPC_INTERRUPT_HVIRT) &&
+ (env->spr[SPR_LPCR] & LPCR_HVEE)) {
+ return PPC_INTERRUPT_HVIRT;
+ }
+ if (env->pending_interrupts & PPC_INTERRUPT_RESET) {
+ return PPC_INTERRUPT_RESET;
+ }
+ return 0;
+}
+
+static int p9_next_unmasked_interrupt(CPUPPCState *env)
+{
+ PowerPCCPU *cpu = env_archcpu(env);
+ CPUState *cs = CPU(cpu);
+ /* Ignore MSR[EE] when coming out of some power management states */
+ bool msr_ee = FIELD_EX64(env->msr, MSR, EE) || env->resume_as_sreset;
+
+ assert((env->pending_interrupts & P9_UNUSED_INTERRUPTS) == 0);
+
+ if (cs->halted) {
+ if (env->spr[SPR_PSSCR] & PSSCR_EC) {
+ /*
+ * When PSSCR[EC] is set, LPCR[PECE] controls which interrupts can
+ * wakeup the processor
+ */
+ return p9_interrupt_powersave(env);
+ } else {
+ /*
+ * When it's clear, any system-caused exception exits power-saving
+ * mode, even the ones that gate on MSR[EE].
+ */
+ msr_ee = true;
+ }
+ }
+
+ /* Machine check exception */
+ if (env->pending_interrupts & PPC_INTERRUPT_MCK) {
+ return PPC_INTERRUPT_MCK;
+ }
+
+ /* Hypervisor decrementer exception */
+ if (env->pending_interrupts & PPC_INTERRUPT_HDECR) {
+ /* LPCR will be clear when not supported so this will work */
+ bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE);
+ if ((msr_ee || !FIELD_EX64_HV(env->msr)) && hdice) {
+ /* HDEC clears on delivery */
+ return PPC_INTERRUPT_HDECR;
+ }
+ }
+
+ /* Hypervisor virtualization interrupt */
+ if (env->pending_interrupts & PPC_INTERRUPT_HVIRT) {
+ /* LPCR will be clear when not supported so this will work */
+ bool hvice = !!(env->spr[SPR_LPCR] & LPCR_HVICE);
+ if ((msr_ee || !FIELD_EX64_HV(env->msr)) && hvice) {
+ return PPC_INTERRUPT_HVIRT;
+ }
+ }
+
+ /* External interrupt can ignore MSR:EE under some circumstances */
+ if (env->pending_interrupts & PPC_INTERRUPT_EXT) {
+ bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
+ bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC);
+ /* HEIC blocks delivery to the hypervisor */
+ if ((msr_ee && !(heic && FIELD_EX64_HV(env->msr) &&
+ !FIELD_EX64(env->msr, MSR, PR))) ||
+ (env->has_hv_mode && !FIELD_EX64_HV(env->msr) && !lpes0)) {
+ return PPC_INTERRUPT_EXT;
+ }
+ }
+ if (msr_ee != 0) {
+ /* Decrementer exception */
+ if (env->pending_interrupts & PPC_INTERRUPT_DECR) {
+ return PPC_INTERRUPT_DECR;
+ }
+ if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) {
+ return PPC_INTERRUPT_DOORBELL;
+ }
+ if (env->pending_interrupts & PPC_INTERRUPT_HDOORBELL) {
+ return PPC_INTERRUPT_HDOORBELL;
+ }
+ if (env->pending_interrupts & PPC_INTERRUPT_PERFM) {
+ return PPC_INTERRUPT_PERFM;
+ }
+ /* EBB exception */
+ if (env->pending_interrupts & PPC_INTERRUPT_EBB) {
+ /*
+ * EBB exception must be taken in problem state and
+ * with BESCR_GE set.
+ */
+ if (FIELD_EX64(env->msr, MSR, PR) &&
+ (env->spr[SPR_BESCR] & BESCR_GE)) {
+ return PPC_INTERRUPT_EBB;
+ }
+ }
+ }
+
+ return 0;
+}
+#endif
+
+static int ppc_next_unmasked_interrupt_generic(CPUPPCState *env)
+{
bool async_deliver;
/* External reset */
- if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) {
- env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET);
- powerpc_excp(cpu, POWERPC_EXCP_RESET);
- return;
+ if (env->pending_interrupts & PPC_INTERRUPT_RESET) {
+ return PPC_INTERRUPT_RESET;
}
/* Machine check exception */
- if (env->pending_interrupts & (1 << PPC_INTERRUPT_MCK)) {
- env->pending_interrupts &= ~(1 << PPC_INTERRUPT_MCK);
- powerpc_excp(cpu, POWERPC_EXCP_MCHECK);
- return;
+ if (env->pending_interrupts & PPC_INTERRUPT_MCK) {
+ return PPC_INTERRUPT_MCK;
}
#if 0 /* TODO */
/* External debug exception */
- if (env->pending_interrupts & (1 << PPC_INTERRUPT_DEBUG)) {
- env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DEBUG);
- powerpc_excp(cpu, POWERPC_EXCP_DEBUG);
- return;
+ if (env->pending_interrupts & PPC_INTERRUPT_DEBUG) {
+ return PPC_INTERRUPT_DEBUG;
}
#endif
@@ -1718,129 +2046,246 @@ static void ppc_hw_interrupt(CPUPPCState *env)
async_deliver = FIELD_EX64(env->msr, MSR, EE) || env->resume_as_sreset;
/* Hypervisor decrementer exception */
- if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) {
+ if (env->pending_interrupts & PPC_INTERRUPT_HDECR) {
/* LPCR will be clear when not supported so this will work */
bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE);
if ((async_deliver || !FIELD_EX64_HV(env->msr)) && hdice) {
/* HDEC clears on delivery */
- env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
- powerpc_excp(cpu, POWERPC_EXCP_HDECR);
- return;
+ return PPC_INTERRUPT_HDECR;
}
}
/* Hypervisor virtualization interrupt */
- if (env->pending_interrupts & (1 << PPC_INTERRUPT_HVIRT)) {
+ if (env->pending_interrupts & PPC_INTERRUPT_HVIRT) {
/* LPCR will be clear when not supported so this will work */
bool hvice = !!(env->spr[SPR_LPCR] & LPCR_HVICE);
if ((async_deliver || !FIELD_EX64_HV(env->msr)) && hvice) {
- powerpc_excp(cpu, POWERPC_EXCP_HVIRT);
- return;
+ return PPC_INTERRUPT_HVIRT;
}
}
/* External interrupt can ignore MSR:EE under some circumstances */
- if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) {
+ if (env->pending_interrupts & PPC_INTERRUPT_EXT) {
bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC);
/* HEIC blocks delivery to the hypervisor */
if ((async_deliver && !(heic && FIELD_EX64_HV(env->msr) &&
!FIELD_EX64(env->msr, MSR, PR))) ||
(env->has_hv_mode && !FIELD_EX64_HV(env->msr) && !lpes0)) {
- if (books_vhyp_promotes_external_to_hvirt(cpu)) {
- powerpc_excp(cpu, POWERPC_EXCP_HVIRT);
- } else {
- powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL);
- }
- return;
+ return PPC_INTERRUPT_EXT;
}
}
if (FIELD_EX64(env->msr, MSR, CE)) {
/* External critical interrupt */
- if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) {
- powerpc_excp(cpu, POWERPC_EXCP_CRITICAL);
- return;
+ if (env->pending_interrupts & PPC_INTERRUPT_CEXT) {
+ return PPC_INTERRUPT_CEXT;
}
}
if (async_deliver != 0) {
/* Watchdog timer on embedded PowerPC */
- if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) {
- env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT);
- powerpc_excp(cpu, POWERPC_EXCP_WDT);
- return;
+ if (env->pending_interrupts & PPC_INTERRUPT_WDT) {
+ return PPC_INTERRUPT_WDT;
}
- if (env->pending_interrupts & (1 << PPC_INTERRUPT_CDOORBELL)) {
- env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CDOORBELL);
- powerpc_excp(cpu, POWERPC_EXCP_DOORCI);
- return;
+ if (env->pending_interrupts & PPC_INTERRUPT_CDOORBELL) {
+ return PPC_INTERRUPT_CDOORBELL;
}
/* Fixed interval timer on embedded PowerPC */
- if (env->pending_interrupts & (1 << PPC_INTERRUPT_FIT)) {
- env->pending_interrupts &= ~(1 << PPC_INTERRUPT_FIT);
- powerpc_excp(cpu, POWERPC_EXCP_FIT);
- return;
+ if (env->pending_interrupts & PPC_INTERRUPT_FIT) {
+ return PPC_INTERRUPT_FIT;
}
/* Programmable interval timer on embedded PowerPC */
- if (env->pending_interrupts & (1 << PPC_INTERRUPT_PIT)) {
- env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PIT);
- powerpc_excp(cpu, POWERPC_EXCP_PIT);
- return;
+ if (env->pending_interrupts & PPC_INTERRUPT_PIT) {
+ return PPC_INTERRUPT_PIT;
}
/* Decrementer exception */
- if (env->pending_interrupts & (1 << PPC_INTERRUPT_DECR)) {
- if (ppc_decr_clear_on_delivery(env)) {
- env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DECR);
- }
- powerpc_excp(cpu, POWERPC_EXCP_DECR);
- return;
+ if (env->pending_interrupts & PPC_INTERRUPT_DECR) {
+ return PPC_INTERRUPT_DECR;
}
- if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) {
- env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL);
- if (is_book3s_arch2x(env)) {
- powerpc_excp(cpu, POWERPC_EXCP_SDOOR);
- } else {
- powerpc_excp(cpu, POWERPC_EXCP_DOORI);
- }
- return;
+ if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) {
+ return PPC_INTERRUPT_DOORBELL;
}
- if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDOORBELL)) {
- env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDOORBELL);
- powerpc_excp(cpu, POWERPC_EXCP_SDOOR_HV);
- return;
+ if (env->pending_interrupts & PPC_INTERRUPT_HDOORBELL) {
+ return PPC_INTERRUPT_HDOORBELL;
}
- if (env->pending_interrupts & (1 << PPC_INTERRUPT_PERFM)) {
- env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PERFM);
- powerpc_excp(cpu, POWERPC_EXCP_PERFM);
- return;
+ if (env->pending_interrupts & PPC_INTERRUPT_PERFM) {
+ return PPC_INTERRUPT_PERFM;
}
/* Thermal interrupt */
- if (env->pending_interrupts & (1 << PPC_INTERRUPT_THERM)) {
- env->pending_interrupts &= ~(1 << PPC_INTERRUPT_THERM);
- powerpc_excp(cpu, POWERPC_EXCP_THERM);
- return;
+ if (env->pending_interrupts & PPC_INTERRUPT_THERM) {
+ return PPC_INTERRUPT_THERM;
}
/* EBB exception */
- if (env->pending_interrupts & (1 << PPC_INTERRUPT_EBB)) {
+ if (env->pending_interrupts & PPC_INTERRUPT_EBB) {
/*
* EBB exception must be taken in problem state and
* with BESCR_GE set.
*/
if (FIELD_EX64(env->msr, MSR, PR) &&
(env->spr[SPR_BESCR] & BESCR_GE)) {
- env->pending_interrupts &= ~(1 << PPC_INTERRUPT_EBB);
+ return PPC_INTERRUPT_EBB;
+ }
+ }
+ }
- if (env->spr[SPR_BESCR] & BESCR_PMEO) {
- powerpc_excp(cpu, POWERPC_EXCP_PERFM_EBB);
- } else if (env->spr[SPR_BESCR] & BESCR_EEO) {
- powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL_EBB);
- }
+ return 0;
+}
- return;
- }
+static int ppc_next_unmasked_interrupt(CPUPPCState *env)
+{
+ switch (env->excp_model) {
+#if defined(TARGET_PPC64)
+ case POWERPC_EXCP_POWER7:
+ return p7_next_unmasked_interrupt(env);
+ case POWERPC_EXCP_POWER8:
+ return p8_next_unmasked_interrupt(env);
+ case POWERPC_EXCP_POWER9:
+ case POWERPC_EXCP_POWER10:
+ return p9_next_unmasked_interrupt(env);
+#endif
+ default:
+ return ppc_next_unmasked_interrupt_generic(env);
+ }
+}
+
+/*
+ * Sets CPU_INTERRUPT_HARD if there is at least one unmasked interrupt to be
+ * delivered and clears CPU_INTERRUPT_HARD otherwise.
+ *
+ * This method is called by ppc_set_interrupt when an interrupt is raised or
+ * lowered, and should also be called whenever an interrupt masking condition
+ * is changed, e.g.:
+ * - When relevant bits of MSR are altered, like EE, HV, PR, etc.;
+ * - When relevant bits of LPCR are altered, like PECE, HDICE, HVICE, etc.;
+ * - When PSSCR[EC] or env->resume_as_sreset are changed;
+ * - When cs->halted is changed and the CPU has a different interrupt masking
+ * logic in power-saving mode (e.g., POWER7/8/9/10);
+ */
+void ppc_maybe_interrupt(CPUPPCState *env)
+{
+ CPUState *cs = env_cpu(env);
+ bool locked = false;
+
+ if (!qemu_mutex_iothread_locked()) {
+ locked = true;
+ qemu_mutex_lock_iothread();
+ }
+
+ if (ppc_next_unmasked_interrupt(env)) {
+ cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+ } else {
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+ }
+
+ if (locked) {
+ qemu_mutex_unlock_iothread();
+ }
+}
+
+#if defined(TARGET_PPC64)
+static void p7_deliver_interrupt(CPUPPCState *env, int interrupt)
+{
+ PowerPCCPU *cpu = env_archcpu(env);
+ CPUState *cs = env_cpu(env);
+
+ switch (interrupt) {
+ case PPC_INTERRUPT_MCK: /* Machine check exception */
+ env->pending_interrupts &= ~PPC_INTERRUPT_MCK;
+ powerpc_excp(cpu, POWERPC_EXCP_MCHECK);
+ break;
+
+ case PPC_INTERRUPT_HDECR: /* Hypervisor decrementer exception */
+ /* HDEC clears on delivery */
+ env->pending_interrupts &= ~PPC_INTERRUPT_HDECR;
+ powerpc_excp(cpu, POWERPC_EXCP_HDECR);
+ break;
+
+ case PPC_INTERRUPT_EXT:
+ if (books_vhyp_promotes_external_to_hvirt(cpu)) {
+ powerpc_excp(cpu, POWERPC_EXCP_HVIRT);
+ } else {
+ powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL);
}
+ break;
+
+ case PPC_INTERRUPT_DECR: /* Decrementer exception */
+ powerpc_excp(cpu, POWERPC_EXCP_DECR);
+ break;
+ case PPC_INTERRUPT_PERFM:
+ env->pending_interrupts &= ~PPC_INTERRUPT_PERFM;
+ powerpc_excp(cpu, POWERPC_EXCP_PERFM);
+ break;
+ case 0:
+ /*
+ * This is a bug ! It means that has_work took us out of halt without
+ * anything to deliver while in a PM state that requires getting
+ * out via a 0x100
+ *
+ * This means we will incorrectly execute past the power management
+ * instruction instead of triggering a reset.
+ *
+ * It generally means a discrepancy between the wakeup conditions in the
+ * processor has_work implementation and the logic in this function.
+ */
+ assert(!env->resume_as_sreset);
+ break;
+ default:
+ cpu_abort(cs, "Invalid PowerPC interrupt %d. Aborting\n", interrupt);
}
+}
- if (env->resume_as_sreset) {
+static void p8_deliver_interrupt(CPUPPCState *env, int interrupt)
+{
+ PowerPCCPU *cpu = env_archcpu(env);
+ CPUState *cs = env_cpu(env);
+
+ switch (interrupt) {
+ case PPC_INTERRUPT_MCK: /* Machine check exception */
+ env->pending_interrupts &= ~PPC_INTERRUPT_MCK;
+ powerpc_excp(cpu, POWERPC_EXCP_MCHECK);
+ break;
+
+ case PPC_INTERRUPT_HDECR: /* Hypervisor decrementer exception */
+ /* HDEC clears on delivery */
+ env->pending_interrupts &= ~PPC_INTERRUPT_HDECR;
+ powerpc_excp(cpu, POWERPC_EXCP_HDECR);
+ break;
+
+ case PPC_INTERRUPT_EXT:
+ if (books_vhyp_promotes_external_to_hvirt(cpu)) {
+ powerpc_excp(cpu, POWERPC_EXCP_HVIRT);
+ } else {
+ powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL);
+ }
+ break;
+
+ case PPC_INTERRUPT_DECR: /* Decrementer exception */
+ powerpc_excp(cpu, POWERPC_EXCP_DECR);
+ break;
+ case PPC_INTERRUPT_DOORBELL:
+ env->pending_interrupts &= ~PPC_INTERRUPT_DOORBELL;
+ if (is_book3s_arch2x(env)) {
+ powerpc_excp(cpu, POWERPC_EXCP_SDOOR);
+ } else {
+ powerpc_excp(cpu, POWERPC_EXCP_DOORI);
+ }
+ break;
+ case PPC_INTERRUPT_HDOORBELL:
+ env->pending_interrupts &= ~PPC_INTERRUPT_HDOORBELL;
+ powerpc_excp(cpu, POWERPC_EXCP_SDOOR_HV);
+ break;
+ case PPC_INTERRUPT_PERFM:
+ env->pending_interrupts &= ~PPC_INTERRUPT_PERFM;
+ powerpc_excp(cpu, POWERPC_EXCP_PERFM);
+ break;
+ case PPC_INTERRUPT_EBB: /* EBB exception */
+ env->pending_interrupts &= ~PPC_INTERRUPT_EBB;
+ if (env->spr[SPR_BESCR] & BESCR_PMEO) {
+ powerpc_excp(cpu, POWERPC_EXCP_PERFM_EBB);
+ } else if (env->spr[SPR_BESCR] & BESCR_EEO) {
+ powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL_EBB);
+ }
+ break;
+ case 0:
/*
* This is a bug ! It means that has_work took us out of halt without
* anything to deliver while in a PM state that requires getting
@@ -1852,8 +2297,214 @@ static void ppc_hw_interrupt(CPUPPCState *env)
* It generally means a discrepancy between the wakeup conditions in the
* processor has_work implementation and the logic in this function.
*/
- cpu_abort(env_cpu(env),
- "Wakeup from PM state but interrupt Undelivered");
+ assert(!env->resume_as_sreset);
+ break;
+ default:
+ cpu_abort(cs, "Invalid PowerPC interrupt %d. Aborting\n", interrupt);
+ }
+}
+
+static void p9_deliver_interrupt(CPUPPCState *env, int interrupt)
+{
+ PowerPCCPU *cpu = env_archcpu(env);
+ CPUState *cs = env_cpu(env);
+
+ if (cs->halted && !(env->spr[SPR_PSSCR] & PSSCR_EC) &&
+ !FIELD_EX64(env->msr, MSR, EE)) {
+ /*
+ * A pending interrupt took us out of power-saving, but MSR[EE] says
+ * that we should return to NIP+4 instead of delivering it.
+ */
+ return;
+ }
+
+ switch (interrupt) {
+ case PPC_INTERRUPT_MCK: /* Machine check exception */
+ env->pending_interrupts &= ~PPC_INTERRUPT_MCK;
+ powerpc_excp(cpu, POWERPC_EXCP_MCHECK);
+ break;
+
+ case PPC_INTERRUPT_HDECR: /* Hypervisor decrementer exception */
+ /* HDEC clears on delivery */
+ env->pending_interrupts &= ~PPC_INTERRUPT_HDECR;
+ powerpc_excp(cpu, POWERPC_EXCP_HDECR);
+ break;
+ case PPC_INTERRUPT_HVIRT: /* Hypervisor virtualization interrupt */
+ powerpc_excp(cpu, POWERPC_EXCP_HVIRT);
+ break;
+
+ case PPC_INTERRUPT_EXT:
+ if (books_vhyp_promotes_external_to_hvirt(cpu)) {
+ powerpc_excp(cpu, POWERPC_EXCP_HVIRT);
+ } else {
+ powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL);
+ }
+ break;
+
+ case PPC_INTERRUPT_DECR: /* Decrementer exception */
+ powerpc_excp(cpu, POWERPC_EXCP_DECR);
+ break;
+ case PPC_INTERRUPT_DOORBELL:
+ env->pending_interrupts &= ~PPC_INTERRUPT_DOORBELL;
+ powerpc_excp(cpu, POWERPC_EXCP_SDOOR);
+ break;
+ case PPC_INTERRUPT_HDOORBELL:
+ env->pending_interrupts &= ~PPC_INTERRUPT_HDOORBELL;
+ powerpc_excp(cpu, POWERPC_EXCP_SDOOR_HV);
+ break;
+ case PPC_INTERRUPT_PERFM:
+ env->pending_interrupts &= ~PPC_INTERRUPT_PERFM;
+ powerpc_excp(cpu, POWERPC_EXCP_PERFM);
+ break;
+ case PPC_INTERRUPT_EBB: /* EBB exception */
+ env->pending_interrupts &= ~PPC_INTERRUPT_EBB;
+ if (env->spr[SPR_BESCR] & BESCR_PMEO) {
+ powerpc_excp(cpu, POWERPC_EXCP_PERFM_EBB);
+ } else if (env->spr[SPR_BESCR] & BESCR_EEO) {
+ powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL_EBB);
+ }
+ break;
+ case 0:
+ /*
+ * This is a bug ! It means that has_work took us out of halt without
+ * anything to deliver while in a PM state that requires getting
+ * out via a 0x100
+ *
+ * This means we will incorrectly execute past the power management
+ * instruction instead of triggering a reset.
+ *
+ * It generally means a discrepancy between the wakeup conditions in the
+ * processor has_work implementation and the logic in this function.
+ */
+ assert(!env->resume_as_sreset);
+ break;
+ default:
+ cpu_abort(cs, "Invalid PowerPC interrupt %d. Aborting\n", interrupt);
+ }
+}
+#endif
+
+static void ppc_deliver_interrupt_generic(CPUPPCState *env, int interrupt)
+{
+ PowerPCCPU *cpu = env_archcpu(env);
+ CPUState *cs = env_cpu(env);
+
+ switch (interrupt) {
+ case PPC_INTERRUPT_RESET: /* External reset */
+ env->pending_interrupts &= ~PPC_INTERRUPT_RESET;
+ powerpc_excp(cpu, POWERPC_EXCP_RESET);
+ break;
+ case PPC_INTERRUPT_MCK: /* Machine check exception */
+ env->pending_interrupts &= ~PPC_INTERRUPT_MCK;
+ powerpc_excp(cpu, POWERPC_EXCP_MCHECK);
+ break;
+
+ case PPC_INTERRUPT_HDECR: /* Hypervisor decrementer exception */
+ /* HDEC clears on delivery */
+ env->pending_interrupts &= ~PPC_INTERRUPT_HDECR;
+ powerpc_excp(cpu, POWERPC_EXCP_HDECR);
+ break;
+ case PPC_INTERRUPT_HVIRT: /* Hypervisor virtualization interrupt */
+ powerpc_excp(cpu, POWERPC_EXCP_HVIRT);
+ break;
+
+ case PPC_INTERRUPT_EXT:
+ if (books_vhyp_promotes_external_to_hvirt(cpu)) {
+ powerpc_excp(cpu, POWERPC_EXCP_HVIRT);
+ } else {
+ powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL);
+ }
+ break;
+ case PPC_INTERRUPT_CEXT: /* External critical interrupt */
+ powerpc_excp(cpu, POWERPC_EXCP_CRITICAL);
+ break;
+
+ case PPC_INTERRUPT_WDT: /* Watchdog timer on embedded PowerPC */
+ env->pending_interrupts &= ~PPC_INTERRUPT_WDT;
+ powerpc_excp(cpu, POWERPC_EXCP_WDT);
+ break;
+ case PPC_INTERRUPT_CDOORBELL:
+ env->pending_interrupts &= ~PPC_INTERRUPT_CDOORBELL;
+ powerpc_excp(cpu, POWERPC_EXCP_DOORCI);
+ break;
+ case PPC_INTERRUPT_FIT: /* Fixed interval timer on embedded PowerPC */
+ env->pending_interrupts &= ~PPC_INTERRUPT_FIT;
+ powerpc_excp(cpu, POWERPC_EXCP_FIT);
+ break;
+ case PPC_INTERRUPT_PIT: /* Programmable interval timer on embedded ppc */
+ env->pending_interrupts &= ~PPC_INTERRUPT_PIT;
+ powerpc_excp(cpu, POWERPC_EXCP_PIT);
+ break;
+ case PPC_INTERRUPT_DECR: /* Decrementer exception */
+ if (ppc_decr_clear_on_delivery(env)) {
+ env->pending_interrupts &= ~PPC_INTERRUPT_DECR;
+ }
+ powerpc_excp(cpu, POWERPC_EXCP_DECR);
+ break;
+ case PPC_INTERRUPT_DOORBELL:
+ env->pending_interrupts &= ~PPC_INTERRUPT_DOORBELL;
+ if (is_book3s_arch2x(env)) {
+ powerpc_excp(cpu, POWERPC_EXCP_SDOOR);
+ } else {
+ powerpc_excp(cpu, POWERPC_EXCP_DOORI);
+ }
+ break;
+ case PPC_INTERRUPT_HDOORBELL:
+ env->pending_interrupts &= ~PPC_INTERRUPT_HDOORBELL;
+ powerpc_excp(cpu, POWERPC_EXCP_SDOOR_HV);
+ break;
+ case PPC_INTERRUPT_PERFM:
+ env->pending_interrupts &= ~PPC_INTERRUPT_PERFM;
+ powerpc_excp(cpu, POWERPC_EXCP_PERFM);
+ break;
+ case PPC_INTERRUPT_THERM: /* Thermal interrupt */
+ env->pending_interrupts &= ~PPC_INTERRUPT_THERM;
+ powerpc_excp(cpu, POWERPC_EXCP_THERM);
+ break;
+ case PPC_INTERRUPT_EBB: /* EBB exception */
+ env->pending_interrupts &= ~PPC_INTERRUPT_EBB;
+ if (env->spr[SPR_BESCR] & BESCR_PMEO) {
+ powerpc_excp(cpu, POWERPC_EXCP_PERFM_EBB);
+ } else if (env->spr[SPR_BESCR] & BESCR_EEO) {
+ powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL_EBB);
+ }
+ break;
+ case 0:
+ /*
+ * This is a bug ! It means that has_work took us out of halt without
+ * anything to deliver while in a PM state that requires getting
+ * out via a 0x100
+ *
+ * This means we will incorrectly execute past the power management
+ * instruction instead of triggering a reset.
+ *
+ * It generally means a discrepancy between the wakeup conditions in the
+ * processor has_work implementation and the logic in this function.
+ */
+ assert(!env->resume_as_sreset);
+ break;
+ default:
+ cpu_abort(cs, "Invalid PowerPC interrupt %d. Aborting\n", interrupt);
+ }
+}
+
+static void ppc_deliver_interrupt(CPUPPCState *env, int interrupt)
+{
+ switch (env->excp_model) {
+#if defined(TARGET_PPC64)
+ case POWERPC_EXCP_POWER7:
+ p7_deliver_interrupt(env, interrupt);
+ break;
+ case POWERPC_EXCP_POWER8:
+ p8_deliver_interrupt(env, interrupt);
+ break;
+ case POWERPC_EXCP_POWER9:
+ case POWERPC_EXCP_POWER10:
+ p9_deliver_interrupt(env, interrupt);
+ break;
+#endif
+ default:
+ ppc_deliver_interrupt_generic(env, interrupt);
}
}
@@ -1889,15 +2540,22 @@ bool ppc_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);
CPUPPCState *env = &cpu->env;
+ int interrupt;
- if (interrupt_request & CPU_INTERRUPT_HARD) {
- ppc_hw_interrupt(env);
- if (env->pending_interrupts == 0) {
- cs->interrupt_request &= ~CPU_INTERRUPT_HARD;
- }
- return true;
+ if ((interrupt_request & CPU_INTERRUPT_HARD) == 0) {
+ return false;
}
- return false;
+
+ interrupt = ppc_next_unmasked_interrupt(env);
+ if (interrupt == 0) {
+ return false;
+ }
+
+ ppc_deliver_interrupt(env, interrupt);
+ if (env->pending_interrupts == 0) {
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+ }
+ return true;
}
#endif /* !CONFIG_USER_ONLY */
@@ -1958,6 +2616,11 @@ void helper_store_msr(CPUPPCState *env, target_ulong val)
}
}
+void helper_ppc_maybe_interrupt(CPUPPCState *env)
+{
+ ppc_maybe_interrupt(env);
+}
+
#if defined(TARGET_PPC64)
void helper_scv(CPUPPCState *env, uint32_t lev)
{
@@ -1978,6 +2641,8 @@ void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn)
/* Condition for waking up at 0x100 */
env->resume_as_sreset = (insn != PPC_PM_STOP) ||
(env->spr[SPR_PSSCR] & PSSCR_EC);
+
+ ppc_maybe_interrupt(env);
}
#endif /* defined(TARGET_PPC64) */
@@ -2086,7 +2751,6 @@ void helper_rfebb(CPUPPCState *env, target_ulong s)
static void do_ebb(CPUPPCState *env, int ebb_excp)
{
PowerPCCPU *cpu = env_archcpu(env);
- CPUState *cs = CPU(cpu);
/*
* FSCR_EBB and FSCR_IC_EBB are the same bits used with
@@ -2104,8 +2768,7 @@ static void do_ebb(CPUPPCState *env, int ebb_excp)
if (FIELD_EX64(env->msr, MSR, PR)) {
powerpc_excp(cpu, ebb_excp);
} else {
- env->pending_interrupts |= 1 << PPC_INTERRUPT_EBB;
- cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+ ppc_set_irq(cpu, PPC_INTERRUPT_EBB, 1);
}
}
@@ -2298,7 +2961,7 @@ void helper_msgclr(CPUPPCState *env, target_ulong rb)
return;
}
- env->pending_interrupts &= ~(1 << irq);
+ ppc_set_irq(env_archcpu(env), irq, 0);
}
void helper_msgsnd(target_ulong rb)
@@ -2317,8 +2980,7 @@ void helper_msgsnd(target_ulong rb)
CPUPPCState *cenv = &cpu->env;
if ((rb & DBELL_BRDCAST) || (cenv->spr[SPR_BOOKE_PIR] == pir)) {
- cenv->pending_interrupts |= 1 << irq;
- cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+ ppc_set_irq(cpu, irq, 1);
}
}
qemu_mutex_unlock_iothread();
@@ -2342,7 +3004,7 @@ void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb)
return;
}
- env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDOORBELL);
+ ppc_set_irq(env_archcpu(env), PPC_INTERRUPT_HDOORBELL, 0);
}
static void book3s_msgsnd_common(int pir, int irq)
@@ -2356,8 +3018,7 @@ static void book3s_msgsnd_common(int pir, int irq)
/* TODO: broadcast message to all threads of the same processor */
if (cenv->spr_cb[SPR_PIR].default_value == pir) {
- cenv->pending_interrupts |= 1 << irq;
- cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+ ppc_set_irq(cpu, irq, 1);
}
}
qemu_mutex_unlock_iothread();
@@ -2383,7 +3044,7 @@ void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb)
return;
}
- env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL);
+ ppc_set_irq(env_archcpu(env), PPC_INTERRUPT_HDOORBELL, 0);
}
/*
diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c
index ae25f32d6e..a66e16c212 100644
--- a/target/ppc/fpu_helper.c
+++ b/target/ppc/fpu_helper.c
@@ -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;
-
- dcmx = DCMX(opcode);
- exp = (arg >> 52) & 0x7FF;
- sign = float64_is_neg(arg);
+ int i;
+ for (i = 0; i < 2; i++) {
+ t->s64[i] = (int64_t)-float64_tstdc(b->f64[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);
+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);
}
+}
- arg_sp = helper_todouble(helper_tosingle(arg));
- not_sp = arg != arg_sp;
+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; \
+ }
+
+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,
diff --git a/target/ppc/helper.h b/target/ppc/helper.h
index 57eee07256..8dd22a35e4 100644
--- a/target/ppc/helper.h
+++ b/target/ppc/helper.h
@@ -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)
diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c
index 12235ea2e9..c0aee5855b 100644
--- a/target/ppc/helper_regs.c
+++ b/target/ppc/helper_regs.c
@@ -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;
diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode
index a5249ee32c..f8f589e9fd 100644
--- a/target/ppc/insn32.decode
+++ b/target/ppc/insn32.decode
@@ -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 -
diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c
index 696096100b..d97a7f1f28 100644
--- a/target/ppc/int_helper.c
+++ b/target/ppc/int_helper.c
@@ -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;
diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c
index b0a5e7ce76..a9bc1522e2 100644
--- a/target/ppc/misc_helper.c
+++ b/target/ppc/misc_helper.c
@@ -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) */
diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c
index 00f2e9fa2e..031efda0df 100644
--- a/target/ppc/mmu-radix64.c
+++ b/target/ppc/mmu-radix64.c
@@ -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) {
diff --git a/target/ppc/power8-pmu.c b/target/ppc/power8-pmu.c
index beeab5c494..1381072b9e 100644
--- a/target/ppc/power8-pmu.c
+++ b/target/ppc/power8-pmu.c
@@ -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];
- 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 << 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 << 3)) {
- tmp = env->spr[SPR_POWER_PMC3];
+ 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_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)
{
diff --git a/target/ppc/power8-pmu.h b/target/ppc/power8-pmu.h
index 9692dd765e..c0093e2219 100644
--- a/target/ppc/power8-pmu.h
+++ b/target/ppc/power8-pmu.h
@@ -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
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 7228857e23..19c1d17cb0 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -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;
diff --git a/target/ppc/translate/processor-ctrl-impl.c.inc b/target/ppc/translate/processor-ctrl-impl.c.inc
new file mode 100644
index 0000000000..cc7a50d579
--- /dev/null
+++ b/target/ppc/translate/processor-ctrl-impl.c.inc
@@ -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;
+}
diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc
index e644ad3236..7741f2eb49 100644
--- a/target/ppc/translate/vmx-impl.c.inc
+++ b/target/ppc/translate/vmx-impl.c.inc
@@ -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))
diff --git a/target/ppc/translate/vmx-ops.c.inc b/target/ppc/translate/vmx-ops.c.inc
index a3a0fd0650..33fec8aca4 100644
--- a/target/ppc/translate/vmx-ops.c.inc
+++ b/target/ppc/translate/vmx-ops.c.inc
@@ -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),
diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc
index e6e5c45ffd..4deb29ee42 100644
--- a/target/ppc/translate/vsx-impl.c.inc
+++ b/target/ppc/translate/vsx-impl.c.inc
@@ -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); \
- }
-
-VSX_VECTOR_MOVE(xvabsdp, OP_ABS, SGN_MASK_DP)
-VSX_VECTOR_MOVE(xvnabsdp, OP_NABS, SGN_MASK_DP)
-VSX_VECTOR_MOVE(xvnegdp, OP_NEG, SGN_MASK_DP)
-VSX_VECTOR_MOVE(xvcpsgndp, OP_CPSGN, SGN_MASK_DP)
-VSX_VECTOR_MOVE(xvabssp, OP_ABS, SGN_MASK_SP)
-VSX_VECTOR_MOVE(xvnabssp, OP_NABS, SGN_MASK_SP)
-VSX_VECTOR_MOVE(xvnegsp, OP_NEG, SGN_MASK_SP)
-VSX_VECTOR_MOVE(xvcpsgnsp, OP_CPSGN, SGN_MASK_SP)
+#define TCG_OP_IMM_i64(FUNC, OP, IMM) \
+ static void FUNC(TCGv_i64 t, TCGv_i64 b) \
+ { \
+ OP(t, b, IMM); \
+ }
+
+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)
{
diff --git a/target/ppc/translate/vsx-ops.c.inc b/target/ppc/translate/vsx-ops.c.inc
index bff14bbece..a3ba094d62 100644
--- a/target/ppc/translate/vsx-ops.c.inc
+++ b/target/ppc/translate/vsx-ops.c.inc
@@ -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),