aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS4
-rw-r--r--docs/about/deprecated.rst6
-rw-r--r--docs/devel/nested-papr.txt119
-rw-r--r--docs/system/ppc/amigang.rst161
-rw-r--r--docs/system/target-ppc.rst1
-rw-r--r--hw/ppc/pnv.c175
-rw-r--r--hw/ppc/pnv_core.c8
-rw-r--r--hw/ppc/ppc.c10
-rw-r--r--hw/ppc/spapr.c98
-rw-r--r--hw/ppc/spapr_caps.c54
-rw-r--r--hw/ppc/spapr_cpu_core.c8
-rw-r--r--hw/ppc/spapr_hcall.c24
-rw-r--r--hw/ppc/spapr_nested.c1556
-rw-r--r--include/hw/ppc/pnv_chip.h2
-rw-r--r--include/hw/ppc/pnv_core.h1
-rw-r--r--include/hw/ppc/spapr.h27
-rw-r--r--include/hw/ppc/spapr_nested.h427
-rw-r--r--target/ppc/cpu-models.c4
-rw-r--r--target/ppc/cpu.h11
-rw-r--r--target/ppc/cpu_init.c46
-rw-r--r--target/ppc/excp_helper.c418
-rw-r--r--target/ppc/gdbstub.c7
-rw-r--r--target/ppc/helper_regs.c5
-rw-r--r--target/ppc/insn32.decode26
-rw-r--r--target/ppc/kvm.c11
-rw-r--r--target/ppc/misc_helper.c3
-rw-r--r--target/ppc/translate.c164
-rw-r--r--target/ppc/translate/fixedpoint-impl.c.inc70
28 files changed, 2883 insertions, 563 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 42e59143b7..a3130f64fd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -316,7 +316,6 @@ F: tests/tcg/openrisc/
PowerPC TCG CPUs
M: Nicholas Piggin <npiggin@gmail.com>
M: Daniel Henrique Barboza <danielhb413@gmail.com>
-R: Cédric Le Goater <clg@kaod.org>
L: qemu-ppc@nongnu.org
S: Odd Fixes
F: target/ppc/
@@ -468,7 +467,6 @@ F: target/mips/sysemu/
PPC KVM CPUs
M: Nicholas Piggin <npiggin@gmail.com>
R: Daniel Henrique Barboza <danielhb413@gmail.com>
-R: Cédric Le Goater <clg@kaod.org>
S: Odd Fixes
F: target/ppc/kvm.c
@@ -1508,7 +1506,6 @@ F: tests/avocado/ppc_prep_40p.py
sPAPR (pseries)
M: Nicholas Piggin <npiggin@gmail.com>
R: Daniel Henrique Barboza <danielhb413@gmail.com>
-R: Cédric Le Goater <clg@kaod.org>
R: David Gibson <david@gibson.dropbear.id.au>
R: Harsh Prateek Bora <harshpb@linux.ibm.com>
L: qemu-ppc@nongnu.org
@@ -1575,6 +1572,7 @@ F: hw/rtc/m41t80.c
F: pc-bios/canyonlands.dt[sb]
F: pc-bios/u-boot-sam460ex-20100605.bin
F: roms/u-boot-sam460ex
+F: docs/system/ppc/amigang.rst
pegasos2
M: BALATON Zoltan <balaton@eik.bme.hu>
diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst
index 4fb86608e4..7b548519b5 100644
--- a/docs/about/deprecated.rst
+++ b/docs/about/deprecated.rst
@@ -237,13 +237,13 @@ The Nios II architecture is orphan.
The machine is no longer in existence and has been long unmaintained
in QEMU. This also holds for the TC51828 16MiB flash that it uses.
-``pseries-2.1`` up to ``pseries-2.11`` (since 9.0)
+``pseries-2.1`` up to ``pseries-2.12`` (since 9.0)
''''''''''''''''''''''''''''''''''''''''''''''''''
-Older pseries machines before version 2.12 have undergone many changes
+Older pseries machines before version 3.0 have undergone many changes
to correct issues, mostly regarding migration compatibility. These are
no longer maintained and removing them will make the code easier to
-read and maintain. Use versions 2.12 and above as a replacement.
+read and maintain. Use versions 3.0 and above as a replacement.
Arm machines ``akita``, ``borzoi``, ``cheetah``, ``connex``, ``mainstone``, ``n800``, ``n810``, ``spitz``, ``terrier``, ``tosa``, ``verdex``, ``z2`` (since 9.0)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
diff --git a/docs/devel/nested-papr.txt b/docs/devel/nested-papr.txt
new file mode 100644
index 0000000000..90943650db
--- /dev/null
+++ b/docs/devel/nested-papr.txt
@@ -0,0 +1,119 @@
+Nested PAPR API (aka KVM on PowerVM)
+====================================
+
+This API aims at providing support to enable nested virtualization with
+KVM on PowerVM. While the existing support for nested KVM on PowerNV was
+introduced with cap-nested-hv option, however, with a slight design change,
+to enable this on papr/pseries, a new cap-nested-papr option is added. eg:
+
+ qemu-system-ppc64 -cpu POWER10 -machine pseries,cap-nested-papr=true ...
+
+Work by:
+ Michael Neuling <mikey@neuling.org>
+ Vaibhav Jain <vaibhav@linux.ibm.com>
+ Jordan Niethe <jniethe5@gmail.com>
+ Harsh Prateek Bora <harshpb@linux.ibm.com>
+ Shivaprasad G Bhat <sbhat@linux.ibm.com>
+ Kautuk Consul <kconsul@linux.vnet.ibm.com>
+
+Below taken from the kernel documentation:
+
+Introduction
+============
+
+This document explains how a guest operating system can act as a
+hypervisor and run nested guests through the use of hypercalls, if the
+hypervisor has implemented them. The terms L0, L1, and L2 are used to
+refer to different software entities. L0 is the hypervisor mode entity
+that would normally be called the "host" or "hypervisor". L1 is a
+guest virtual machine that is directly run under L0 and is initiated
+and controlled by L0. L2 is a guest virtual machine that is initiated
+and controlled by L1 acting as a hypervisor. A significant design change
+wrt existing API is that now the entire L2 state is maintained within L0.
+
+Existing Nested-HV API
+======================
+
+Linux/KVM has had support for Nesting as an L0 or L1 since 2018
+
+The L0 code was added::
+
+ commit 8e3f5fc1045dc49fd175b978c5457f5f51e7a2ce
+ Author: Paul Mackerras <paulus@ozlabs.org>
+ Date: Mon Oct 8 16:31:03 2018 +1100
+ KVM: PPC: Book3S HV: Framework and hcall stubs for nested virtualization
+
+The L1 code was added::
+
+ commit 360cae313702cdd0b90f82c261a8302fecef030a
+ Author: Paul Mackerras <paulus@ozlabs.org>
+ Date: Mon Oct 8 16:31:04 2018 +1100
+ KVM: PPC: Book3S HV: Nested guest entry via hypercall
+
+This API works primarily using a signal hcall h_enter_nested(). This
+call made by the L1 to tell the L0 to start an L2 vCPU with the given
+state. The L0 then starts this L2 and runs until an L2 exit condition
+is reached. Once the L2 exits, the state of the L2 is given back to
+the L1 by the L0. The full L2 vCPU state is always transferred from
+and to L1 when the L2 is run. The L0 doesn't keep any state on the L2
+vCPU (except in the short sequence in the L0 on L1 -> L2 entry and L2
+-> L1 exit).
+
+The only state kept by the L0 is the partition table. The L1 registers
+it's partition table using the h_set_partition_table() hcall. All
+other state held by the L0 about the L2s is cached state (such as
+shadow page tables).
+
+The L1 may run any L2 or vCPU without first informing the L0. It
+simply starts the vCPU using h_enter_nested(). The creation of L2s and
+vCPUs is done implicitly whenever h_enter_nested() is called.
+
+In this document, we call this existing API the v1 API.
+
+New PAPR API
+===============
+
+The new PAPR API changes from the v1 API such that the creating L2 and
+associated vCPUs is explicit. In this document, we call this the v2
+API.
+
+h_enter_nested() is replaced with H_GUEST_VCPU_RUN(). Before this can
+be called the L1 must explicitly create the L2 using h_guest_create()
+and any associated vCPUs() created with h_guest_create_vCPU(). Getting
+and setting vCPU state can also be performed using h_guest_{g|s}et
+hcall.
+
+The basic execution flow is for an L1 to create an L2, run it, and
+delete it is:
+
+- L1 and L0 negotiate capabilities with H_GUEST_{G,S}ET_CAPABILITIES()
+ (normally at L1 boot time).
+
+- L1 requests the L0 to create an L2 with H_GUEST_CREATE() and receives a token
+
+- L1 requests the L0 to create an L2 vCPU with H_GUEST_CREATE_VCPU()
+
+- L1 and L0 communicate the vCPU state using the H_GUEST_{G,S}ET() hcall
+
+- L1 requests the L0 to run the vCPU using H_GUEST_RUN_VCPU() hcall
+
+- L1 deletes L2 with H_GUEST_DELETE()
+
+For more details, please refer:
+
+[1] Linux Kernel documentation (upstream documentation commit):
+
+commit 476652297f94a2e5e5ef29e734b0da37ade94110
+Author: Michael Neuling <mikey@neuling.org>
+Date: Thu Sep 14 13:06:00 2023 +1000
+
+ docs: powerpc: Document nested KVM on POWER
+
+ Document support for nested KVM on POWER using the existing API as well
+ as the new PAPR API. This includes the new HCALL interface and how it
+ used by KVM.
+
+ Signed-off-by: Michael Neuling <mikey@neuling.org>
+ Signed-off-by: Jordan Niethe <jniethe5@gmail.com>
+ Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
+ Link: https://msgid.link/20230914030600.16993-12-jniethe5@gmail.com
diff --git a/docs/system/ppc/amigang.rst b/docs/system/ppc/amigang.rst
new file mode 100644
index 0000000000..ba1a3d80b9
--- /dev/null
+++ b/docs/system/ppc/amigang.rst
@@ -0,0 +1,161 @@
+=========================================================
+AmigaNG boards (``amigaone``, ``pegasos2``, ``sam460ex``)
+=========================================================
+
+These PowerPC machines emulate boards that are primarily used for
+running Amiga like OSes (AmigaOS 4, MorphOS and AROS) but these can
+also run Linux which is what this section documents.
+
+Eyetech AmigaOne/Mai Logic Teron (``amigaone``)
+===============================================
+
+The ``amigaone`` machine emulates an AmigaOne XE mainboard by Eyetech
+which is a rebranded Mai Logic Teron board with modified U-Boot
+firmware to support AmigaOS 4.
+
+Emulated devices
+----------------
+
+ * PowerPC 7457 CPU (can also use``-cpu g3, 750cxe, 750fx`` or ``750gx``)
+ * Articia S north bridge
+ * VIA VT82C686B south bridge
+ * PCI VGA compatible card (guests may need other card instead)
+ * PS/2 keyboard and mouse
+
+Firmware
+--------
+
+A firmware binary is necessary for the boot process. It is a modified
+U-Boot under GPL but its source is lost so it cannot be included in
+QEMU. A binary is available at
+https://www.hyperion-entertainment.com/index.php/downloads?view=files&parent=28.
+The ROM image is in the last 512kB which can be extracted with the
+following command:
+
+.. code-block:: bash
+
+ $ tail -c 524288 updater.image > u-boot-amigaone.bin
+
+The BIOS emulator in the firmware is unable to run QEMU‘s standard
+vgabios so ``VGABIOS-lgpl-latest.bin`` is needed instead which can be
+downloaded from http://www.nongnu.org/vgabios.
+
+Running Linux
+-------------
+
+There are some Linux images under the following link that work on the
+``amigaone`` machine:
+https://sourceforge.net/projects/amigaone-linux/files/debian-installer/.
+To boot the system run:
+
+.. code-block:: bash
+
+ $ qemu-system-ppc -machine amigaone -bios u-boot-amigaone.bin \
+ -cdrom "A1 Linux Net Installer.iso" \
+ -device ati-vga,model=rv100,romfile=VGABIOS-lgpl-latest.bin
+
+From the firmware menu that appears select ``Boot sequence`` →
+``Amiga Multiboot Options`` and set ``Boot device 1`` to
+``Onboard VIA IDE CDROM``. Then hit escape until the main screen appears again,
+hit escape once more and from the exit menu that appears select either
+``Save settings and exit`` or ``Use settings for this session only``. It may
+take a long time loading the kernel into memory but eventually it boots and the
+installer becomes visible. The ``ati-vga`` RV100 emulation is not
+complete yet so only frame buffer works, DRM and 3D is not available.
+
+Genesi/bPlan Pegasos II (``pegasos2``)
+======================================
+
+The ``pegasos2`` machine emulates the Pegasos II sold by Genesi and
+designed by bPlan. Its schematics are available at
+https://www.powerdeveloper.org/platforms/pegasos/schematics.
+
+Emulated devices
+----------------
+
+ * PowerPC 7457 CPU (can also use``-cpu g3`` or ``750cxe``)
+ * Marvell MV64361 Discovery II north bridge
+ * VIA VT8231 south bridge
+ * PCI VGA compatible card (guests may need other card instead)
+ * PS/2 keyboard and mouse
+
+Firmware
+--------
+
+The Pegasos II board has an Open Firmware compliant ROM based on
+SmartFirmware with some changes that are not open-sourced therefore
+the ROM binary cannot be included in QEMU. An updater was available
+from bPlan, it can be found in the `Internet Archive
+<http://web.archive.org/web/20071021223056/http://www.bplan-gmbh.de/up050404/up050404>`_.
+The ROM image can be extracted from it with the following command:
+
+.. code-block:: bash
+
+ $ tail -c +85581 up050404 | head -c 524288 > pegasos2.rom
+
+Running Linux
+-------------
+
+The PowerPC version of Debian 8.11 supported Pegasos II. The BIOS
+emulator in the firmware binary is unable to run QEMU‘s standard
+vgabios so it needs to be disabled. To boot the system run:
+
+.. code-block:: bash
+
+ $ qemu-system-ppc -machine pegasos2 -bios pegasos2.rom \
+ -cdrom debian-8.11.0-powerpc-netinst.iso \
+ -device VGA,romfile="" -serial stdio
+
+At the firmware ``ok`` prompt enter ``boot cd install/pegasos``.
+
+Alternatively, it is possible to boot the kernel directly without
+firmware ROM using the QEMU built-in minimal Virtual Open Firmware
+(VOF) emulation which is also supported on ``pegasos2``. For this,
+extract the kernel ``install/powerpc/vmlinuz-chrp.initrd`` from the CD
+image, then run:
+
+.. code-block:: bash
+
+ $ qemu-system-ppc -machine pegasos2 -serial stdio \
+ -kernel vmlinuz-chrp.initrd -append "---" \
+ -cdrom debian-8.11.0-powerpc-netinst.iso
+
+aCube Sam460ex (``sam460ex``)
+=============================
+
+The ``sam460ex`` machine emulates the Sam460ex board by aCube which is
+based on the AMCC PowerPC 460EX SoC (that despite its name has a
+PPC440 CPU core).
+
+Firmware
+--------
+
+The board has a firmware based on an older U-Boot version with
+modifications to support booting AmigaOS 4. The firmware ROM is
+included with QEMU.
+
+Emulated devices
+----------------
+
+ * PowerPC 460EX SoC
+ * M41T80 serial RTC chip
+ * Silicon Motion SM501 display parts (identical to SM502 on real board)
+ * Silicon Image SiI3112 2 port SATA controller
+ * USB keyboard and mouse
+
+Running Linux
+-------------
+
+The only Linux distro that supported Sam460ex out of box was CruxPPC
+2.x. It can be booted by running:
+
+.. code-block:: bash
+
+ $ qemu-system-ppc -machine sam460ex -serial stdio \
+ -drive if=none,id=cd,format=raw,file=crux-ppc-2.7a.iso \
+ -device ide-cd,drive=cd,bus=ide.1
+
+There are some other kernels and instructions for booting other
+distros on aCube's product page at
+https://www.acube-systems.biz/index.php?page=hardware&pid=5
+but those are untested.
diff --git a/docs/system/target-ppc.rst b/docs/system/target-ppc.rst
index 4f6eb93b17..87bf412ce5 100644
--- a/docs/system/target-ppc.rst
+++ b/docs/system/target-ppc.rst
@@ -17,6 +17,7 @@ help``.
.. toctree::
:maxdepth: 1
+ ppc/amigang
ppc/embedded
ppc/powermac
ppc/powernv
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index c2f2cc27be..6e3a5ccdec 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -133,7 +133,7 @@ static int get_cpus_node(void *fdt)
* device tree, used in XSCOM to address cores and in interrupt
* servers.
*/
-static void pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt)
+static int pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt)
{
PowerPCCPU *cpu = pc->threads[0];
CPUState *cs = CPU(cpu);
@@ -141,32 +141,31 @@ static void pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt)
int smt_threads = CPU_CORE(pc)->nr_threads;
CPUPPCState *env = &cpu->env;
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
+ PnvChipClass *pnv_cc = PNV_CHIP_GET_CLASS(chip);
g_autofree uint32_t *servers_prop = g_new(uint32_t, smt_threads);
int i;
+ uint32_t pir;
uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40),
0xffffffff, 0xffffffff};
uint32_t tbfreq = PNV_TIMEBASE_FREQ;
uint32_t cpufreq = 1000000000;
uint32_t page_sizes_prop[64];
size_t page_sizes_prop_size;
- const uint8_t pa_features[] = { 24, 0,
- 0xf6, 0x3f, 0xc7, 0xc0, 0x80, 0xf0,
- 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
- 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 };
int offset;
char *nodename;
int cpus_offset = get_cpus_node(fdt);
- nodename = g_strdup_printf("%s@%x", dc->fw_name, pc->pir);
+ pir = pnv_cc->chip_pir(chip, pc->hwid, 0);
+
+ nodename = g_strdup_printf("%s@%x", dc->fw_name, pir);
offset = fdt_add_subnode(fdt, cpus_offset, nodename);
_FDT(offset);
g_free(nodename);
_FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", chip->chip_id)));
- _FDT((fdt_setprop_cell(fdt, offset, "reg", pc->pir)));
- _FDT((fdt_setprop_cell(fdt, offset, "ibm,pir", pc->pir)));
+ _FDT((fdt_setprop_cell(fdt, offset, "reg", pir)));
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,pir", pir)));
_FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu")));
_FDT((fdt_setprop_cell(fdt, offset, "cpu-version", env->spr[SPR_PVR])));
@@ -236,20 +235,21 @@ static void pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt)
page_sizes_prop, page_sizes_prop_size)));
}
- _FDT((fdt_setprop(fdt, offset, "ibm,pa-features",
- pa_features, sizeof(pa_features))));
-
/* Build interrupt servers properties */
for (i = 0; i < smt_threads; i++) {
- servers_prop[i] = cpu_to_be32(pc->pir + i);
+ servers_prop[i] = cpu_to_be32(pnv_cc->chip_pir(chip, pc->hwid, i));
}
_FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s",
servers_prop, sizeof(*servers_prop) * smt_threads)));
+
+ return offset;
}
-static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t pir,
+static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t hwid,
uint32_t nr_threads)
{
+ PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+ uint32_t pir = pcc->chip_pir(chip, hwid, 0);
uint64_t addr = PNV_ICP_BASE(chip) | (pir << 12);
char *name;
const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp";
@@ -263,6 +263,7 @@ static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t pir,
rsize = sizeof(uint64_t) * 2 * nr_threads;
reg = g_malloc(rsize);
for (i = 0; i < nr_threads; i++) {
+ /* We know P8 PIR is linear with thread id */
reg[i * 2] = cpu_to_be64(addr | ((pir + i) * 0x1000));
reg[i * 2 + 1] = cpu_to_be64(0x1000);
}
@@ -299,6 +300,17 @@ PnvChip *pnv_chip_add_phb(PnvChip *chip, PnvPHB *phb)
return chip;
}
+/*
+ * Same as spapr pa_features_207 except pnv always enables CI largepages bit.
+ * HTM is always enabled because TCG does implement HTM, it's just a
+ * degenerate implementation.
+ */
+static const uint8_t pa_features_207[] = { 24, 0,
+ 0xf6, 0x3f, 0xc7, 0xc0, 0x00, 0xf0,
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 };
+
static void pnv_chip_power8_dt_populate(PnvChip *chip, void *fdt)
{
static const char compat[] = "ibm,power8-xscom\0ibm,xscom";
@@ -311,11 +323,15 @@ static void pnv_chip_power8_dt_populate(PnvChip *chip, void *fdt)
for (i = 0; i < chip->nr_cores; i++) {
PnvCore *pnv_core = chip->cores[i];
+ int offset;
+
+ offset = pnv_dt_core(chip, pnv_core, fdt);
- pnv_dt_core(chip, pnv_core, fdt);
+ _FDT((fdt_setprop(fdt, offset, "ibm,pa-features",
+ pa_features_207, sizeof(pa_features_207))));
/* Interrupt Control Presenters (ICP). One per core. */
- pnv_dt_icp(chip, fdt, pnv_core->pir, CPU_CORE(pnv_core)->nr_threads);
+ pnv_dt_icp(chip, fdt, pnv_core->hwid, CPU_CORE(pnv_core)->nr_threads);
}
if (chip->ram_size) {
@@ -323,6 +339,35 @@ static void pnv_chip_power8_dt_populate(PnvChip *chip, void *fdt)
}
}
+/*
+ * Same as spapr pa_features_300 except pnv always enables CI largepages bit.
+ */
+static const uint8_t pa_features_300[] = { 66, 0,
+ /* 0: MMU|FPU|SLB|RUN|DABR|NX, 1: CILRG|fri[nzpm]|DABRX|SPRG3|SLB0|PP110 */
+ /* 2: VPM|DS205|PPR|DS202|DS206, 3: LSD|URG, 5: LE|CFAR|EB|LSQ */
+ 0xf6, 0x3f, 0xc7, 0xc0, 0x00, 0xf0, /* 0 - 5 */
+ /* 6: DS207 */
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 6 - 11 */
+ /* 16: Vector */
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 12 - 17 */
+ /* 18: Vec. Scalar, 20: Vec. XOR, 22: HTM */
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 18 - 23 */
+ /* 24: Ext. Dec, 26: 64 bit ftrs, 28: PM ftrs */
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 24 - 29 */
+ /* 32: LE atomic, 34: EBB + ext EBB */
+ 0x00, 0x00, 0x80, 0x00, 0xC0, 0x00, /* 30 - 35 */
+ /* 40: Radix MMU */
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 36 - 41 */
+ /* 42: PM, 44: PC RA, 46: SC vec'd */
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 42 - 47 */
+ /* 48: SIMD, 50: QP BFP, 52: String */
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 48 - 53 */
+ /* 54: DecFP, 56: DecI, 58: SHA */
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 54 - 59 */
+ /* 60: NM atomic, 62: RNG */
+ 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 60 - 65 */
+};
+
static void pnv_chip_power9_dt_populate(PnvChip *chip, void *fdt)
{
static const char compat[] = "ibm,power9-xscom\0ibm,xscom";
@@ -335,8 +380,12 @@ static void pnv_chip_power9_dt_populate(PnvChip *chip, void *fdt)
for (i = 0; i < chip->nr_cores; i++) {
PnvCore *pnv_core = chip->cores[i];
+ int offset;
- pnv_dt_core(chip, pnv_core, fdt);
+ offset = pnv_dt_core(chip, pnv_core, fdt);
+
+ _FDT((fdt_setprop(fdt, offset, "ibm,pa-features",
+ pa_features_300, sizeof(pa_features_300))));
}
if (chip->ram_size) {
@@ -346,6 +395,40 @@ static void pnv_chip_power9_dt_populate(PnvChip *chip, void *fdt)
pnv_dt_lpc(chip, fdt, 0, PNV9_LPCM_BASE(chip), PNV9_LPCM_SIZE);
}
+/*
+ * Same as spapr pa_features_31 except pnv always enables CI largepages bit,
+ * always disables copy/paste.
+ */
+static const uint8_t pa_features_31[] = { 74, 0,
+ /* 0: MMU|FPU|SLB|RUN|DABR|NX, 1: CILRG|fri[nzpm]|DABRX|SPRG3|SLB0|PP110 */
+ /* 2: VPM|DS205|PPR|DS202|DS206, 3: LSD|URG, 5: LE|CFAR|EB|LSQ */
+ 0xf6, 0x3f, 0xc7, 0xc0, 0x00, 0xf0, /* 0 - 5 */
+ /* 6: DS207 */
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 6 - 11 */
+ /* 16: Vector */
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 12 - 17 */
+ /* 18: Vec. Scalar, 20: Vec. XOR */
+ 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 18 - 23 */
+ /* 24: Ext. Dec, 26: 64 bit ftrs, 28: PM ftrs */
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 24 - 29 */
+ /* 32: LE atomic, 34: EBB + ext EBB */
+ 0x00, 0x00, 0x80, 0x00, 0xC0, 0x00, /* 30 - 35 */
+ /* 40: Radix MMU */
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 36 - 41 */
+ /* 42: PM, 44: PC RA, 46: SC vec'd */
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 42 - 47 */
+ /* 48: SIMD, 50: QP BFP, 52: String */
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 48 - 53 */
+ /* 54: DecFP, 56: DecI, 58: SHA */
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 54 - 59 */
+ /* 60: NM atomic, 62: RNG */
+ 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 60 - 65 */
+ /* 68: DEXCR[SBHE|IBRTPDUS|SRAPD|NPHIE|PHIE] */
+ 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, /* 66 - 71 */
+ /* 72: [P]HASHST/[P]HASHCHK */
+ 0x80, 0x00, /* 72 - 73 */
+};
+
static void pnv_chip_power10_dt_populate(PnvChip *chip, void *fdt)
{
static const char compat[] = "ibm,power10-xscom\0ibm,xscom";
@@ -358,8 +441,12 @@ static void pnv_chip_power10_dt_populate(PnvChip *chip, void *fdt)
for (i = 0; i < chip->nr_cores; i++) {
PnvCore *pnv_core = chip->cores[i];
+ int offset;
+
+ offset = pnv_dt_core(chip, pnv_core, fdt);
- pnv_dt_core(chip, pnv_core, fdt);
+ _FDT((fdt_setprop(fdt, offset, "ibm,pa-features",
+ pa_features_31, sizeof(pa_features_31))));
}
if (chip->ram_size) {
@@ -995,9 +1082,10 @@ static void pnv_init(MachineState *machine)
* 25:28 Core number
* 29:31 Thread ID
*/
-static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t core_id)
+static uint32_t pnv_chip_pir_p8(PnvChip *chip, uint32_t core_id,
+ uint32_t thread_id)
{
- return (chip->chip_id << 7) | (core_id << 3);
+ return (chip->chip_id << 7) | (core_id << 3) | thread_id;
}
static void pnv_chip_power8_intc_create(PnvChip *chip, PowerPCCPU *cpu,
@@ -1049,14 +1137,37 @@ static void pnv_chip_power8_intc_print_info(PnvChip *chip, PowerPCCPU *cpu,
*
* We only care about the lower bits. uint32_t is fine for the moment.
*/
-static uint32_t pnv_chip_core_pir_p9(PnvChip *chip, uint32_t core_id)
+static uint32_t pnv_chip_pir_p9(PnvChip *chip, uint32_t core_id,
+ uint32_t thread_id)
{
- return (chip->chip_id << 8) | (core_id << 2);
+ if (chip->nr_threads == 8) {
+ return (chip->chip_id << 8) | ((thread_id & 1) << 2) | (core_id << 3) |
+ (thread_id >> 1);
+ } else {
+ return (chip->chip_id << 8) | (core_id << 2) | thread_id;
+ }
}
-static uint32_t pnv_chip_core_pir_p10(PnvChip *chip, uint32_t core_id)
+/*
+ * 0:48 Reserved - Read as zeroes
+ * 49:52 Node ID
+ * 53:55 Chip ID
+ * 56 Reserved - Read as zero
+ * 57:59 Quad ID
+ * 60 Core Chiplet Pair ID
+ * 61:63 Thread/Core Chiplet ID t0-t2
+ *
+ * We only care about the lower bits. uint32_t is fine for the moment.
+ */
+static uint32_t pnv_chip_pir_p10(PnvChip *chip, uint32_t core_id,
+ uint32_t thread_id)
{
- return (chip->chip_id << 8) | (core_id << 2);
+ if (chip->nr_threads == 8) {
+ return (chip->chip_id << 8) | ((core_id / 4) << 4) |
+ ((core_id % 2) << 3) | thread_id;
+ } else {
+ return (chip->chip_id << 8) | (core_id << 2) | thread_id;
+ }
}
static void pnv_chip_power9_intc_create(PnvChip *chip, PowerPCCPU *cpu,
@@ -1235,7 +1346,7 @@ static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp)
int core_hwid = CPU_CORE(pnv_core)->core_id;
for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) {
- uint32_t pir = pcc->core_pir(chip, core_hwid) + j;
+ uint32_t pir = pcc->chip_pir(chip, core_hwid, j);
PnvICPState *icp = PNV_ICP(xics_icp_get(chip8->xics, pir));
memory_region_add_subregion(&chip8->icp_mmio, pir << 12,
@@ -1348,7 +1459,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
k->chip_cfam_id = 0x221ef04980000000ull; /* P8 Murano DD2.1 */
k->cores_mask = POWER8E_CORE_MASK;
k->num_phbs = 3;
- k->core_pir = pnv_chip_core_pir_p8;
+ k->chip_pir = pnv_chip_pir_p8;
k->intc_create = pnv_chip_power8_intc_create;
k->intc_reset = pnv_chip_power8_intc_reset;
k->intc_destroy = pnv_chip_power8_intc_destroy;
@@ -1372,7 +1483,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */
k->cores_mask = POWER8_CORE_MASK;
k->num_phbs = 3;
- k->core_pir = pnv_chip_core_pir_p8;
+ k->chip_pir = pnv_chip_pir_p8;
k->intc_create = pnv_chip_power8_intc_create;
k->intc_reset = pnv_chip_power8_intc_reset;
k->intc_destroy = pnv_chip_power8_intc_destroy;
@@ -1396,7 +1507,7 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
k->chip_cfam_id = 0x120d304980000000ull; /* P8 Naples DD1.0 */
k->cores_mask = POWER8_CORE_MASK;
k->num_phbs = 4;
- k->core_pir = pnv_chip_core_pir_p8;
+ k->chip_pir = pnv_chip_pir_p8;
k->intc_create = pnv_chip_power8_intc_create;
k->intc_reset = pnv_chip_power8_intc_reset;
k->intc_destroy = pnv_chip_power8_intc_destroy;
@@ -1669,7 +1780,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
k->chip_cfam_id = 0x220d104900008000ull; /* P9 Nimbus DD2.0 */
k->cores_mask = POWER9_CORE_MASK;
- k->core_pir = pnv_chip_core_pir_p9;
+ k->chip_pir = pnv_chip_pir_p9;
k->intc_create = pnv_chip_power9_intc_create;
k->intc_reset = pnv_chip_power9_intc_reset;
k->intc_destroy = pnv_chip_power9_intc_destroy;
@@ -1981,7 +2092,7 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, void *data)
k->chip_cfam_id = 0x120da04900008000ull; /* P10 DD1.0 (with NX) */
k->cores_mask = POWER10_CORE_MASK;
- k->core_pir = pnv_chip_core_pir_p10;
+ k->chip_pir = pnv_chip_pir_p10;
k->intc_create = pnv_chip_power10_intc_create;
k->intc_reset = pnv_chip_power10_intc_reset;
k->intc_destroy = pnv_chip_power10_intc_destroy;
@@ -2071,8 +2182,8 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp)
chip->nr_threads, &error_fatal);
object_property_set_int(OBJECT(pnv_core), CPU_CORE_PROP_CORE_ID,
core_hwid, &error_fatal);
- object_property_set_int(OBJECT(pnv_core), "pir",
- pcc->core_pir(chip, core_hwid), &error_fatal);
+ object_property_set_int(OBJECT(pnv_core), "hwid", core_hwid,
+ &error_fatal);
object_property_set_int(OBJECT(pnv_core), "hrmor", pnv->fw_load_addr,
&error_fatal);
object_property_set_link(OBJECT(pnv_core), "chip", OBJECT(chip),
diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
index 8c7afe037f..f40ab721d6 100644
--- a/hw/ppc/pnv_core.c
+++ b/hw/ppc/pnv_core.c
@@ -226,7 +226,7 @@ static void pnv_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp,
int thread_index)
{
CPUPPCState *env = &cpu->env;
- int core_pir;
+ int core_hwid;
ppc_spr_t *pir = &env->spr_cb[SPR_PIR];
ppc_spr_t *tir = &env->spr_cb[SPR_TIR];
Error *local_err = NULL;
@@ -242,10 +242,10 @@ static void pnv_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp,
return;
}
- core_pir = object_property_get_uint(OBJECT(pc), "pir", &error_abort);
+ core_hwid = object_property_get_uint(OBJECT(pc), "hwid", &error_abort);
tir->default_value = thread_index;
- pir->default_value = core_pir + thread_index;
+ pir->default_value = pcc->chip_pir(pc->chip, core_hwid, thread_index);
/* Set time-base frequency to 512 MHz */
cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ);
@@ -342,7 +342,7 @@ static void pnv_core_unrealize(DeviceState *dev)
}
static Property pnv_core_properties[] = {
- DEFINE_PROP_UINT32("pir", PnvCore, pir, 0),
+ DEFINE_PROP_UINT32("hwid", PnvCore, hwid, 0),
DEFINE_PROP_UINT64("hrmor", PnvCore, hrmor, 0),
DEFINE_PROP_LINK("chip", PnvCore, chip, TYPE_PNV_CHIP, PnvChip *),
DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
index fadb8f5239..e6fa5580c0 100644
--- a/hw/ppc/ppc.c
+++ b/hw/ppc/ppc.c
@@ -633,6 +633,16 @@ void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value)
((uint64_t)value << 32) | tb);
}
+void cpu_ppc_increase_tb_by_offset(CPUPPCState *env, int64_t offset)
+{
+ env->tb_env->tb_offset += offset;
+}
+
+void cpu_ppc_decrease_tb_by_offset(CPUPPCState *env, int64_t offset)
+{
+ env->tb_env->tb_offset -= offset;
+}
+
uint64_t cpu_ppc_load_vtb(CPUPPCState *env)
{
ppc_tb_t *tb_env = env->tb_env;
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 394091830d..c417f9dd52 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -233,29 +233,66 @@ static void spapr_dt_pa_features(SpaprMachineState *spapr,
PowerPCCPU *cpu,
void *fdt, int offset)
{
+ /*
+ * SSO (SAO) ordering is supported on KVM and thread=single hosts,
+ * but not MTTCG, so disable it. To advertise it, a cap would have
+ * to be added, or support implemented for MTTCG.
+ *
+ * Copy/paste is not supported by TCG, so it is not advertised. KVM
+ * can execute them but it has no accelerator drivers which are usable,
+ * so there isn't much need for it anyway.
+ */
+
+ /* These should be kept in sync with pnv */
uint8_t pa_features_206[] = { 6, 0,
- 0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 };
+ 0xf6, 0x1f, 0xc7, 0x00, 0x00, 0xc0 };
uint8_t pa_features_207[] = { 24, 0,
- 0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0,
+ 0xf6, 0x1f, 0xc7, 0xc0, 0x00, 0xf0,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
0x80, 0x00, 0x80, 0x00, 0x00, 0x00 };
uint8_t pa_features_300[] = { 66, 0,
/* 0: MMU|FPU|SLB|RUN|DABR|NX, 1: fri[nzpm]|DABRX|SPRG3|SLB0|PP110 */
- /* 2: VPM|DS205|PPR|DS202|DS206, 3: LSD|URG, SSO, 5: LE|CFAR|EB|LSQ */
- 0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, /* 0 - 5 */
+ /* 2: VPM|DS205|PPR|DS202|DS206, 3: LSD|URG, 5: LE|CFAR|EB|LSQ */
+ 0xf6, 0x1f, 0xc7, 0xc0, 0x00, 0xf0, /* 0 - 5 */
+ /* 6: DS207 */
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 6 - 11 */
+ /* 16: Vector */
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 12 - 17 */
+ /* 18: Vec. Scalar, 20: Vec. XOR */
+ 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 18 - 23 */
+ /* 24: Ext. Dec, 26: 64 bit ftrs, 28: PM ftrs */
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 24 - 29 */
+ /* 32: LE atomic, 34: EBB + ext EBB */
+ 0x00, 0x00, 0x80, 0x00, 0xC0, 0x00, /* 30 - 35 */
+ /* 40: Radix MMU */
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 36 - 41 */
+ /* 42: PM, 44: PC RA, 46: SC vec'd */
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 42 - 47 */
+ /* 48: SIMD, 50: QP BFP, 52: String */
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 48 - 53 */
+ /* 54: DecFP, 56: DecI, 58: SHA */
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 54 - 59 */
+ /* 60: NM atomic, 62: RNG */
+ 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 60 - 65 */
+ };
+ /* 3.1 removes SAO, HTM support */
+ uint8_t pa_features_31[] = { 74, 0,
+ /* 0: MMU|FPU|SLB|RUN|DABR|NX, 1: fri[nzpm]|DABRX|SPRG3|SLB0|PP110 */
+ /* 2: VPM|DS205|PPR|DS202|DS206, 3: LSD|URG, 5: LE|CFAR|EB|LSQ */
+ 0xf6, 0x1f, 0xc7, 0xc0, 0x00, 0xf0, /* 0 - 5 */
/* 6: DS207 */
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 6 - 11 */
/* 16: Vector */
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 12 - 17 */
- /* 18: Vec. Scalar, 20: Vec. XOR, 22: HTM */
+ /* 18: Vec. Scalar, 20: Vec. XOR */
0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 18 - 23 */
/* 24: Ext. Dec, 26: 64 bit ftrs, 28: PM ftrs */
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 24 - 29 */
- /* 30: MMR, 32: LE atomic, 34: EBB + ext EBB */
- 0x80, 0x00, 0x80, 0x00, 0xC0, 0x00, /* 30 - 35 */
- /* 36: SPR SO, 38: Copy/Paste, 40: Radix MMU */
- 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 36 - 41 */
+ /* 32: LE atomic, 34: EBB + ext EBB */
+ 0x00, 0x00, 0x80, 0x00, 0xC0, 0x00, /* 30 - 35 */
+ /* 40: Radix MMU */
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 36 - 41 */
/* 42: PM, 44: PC RA, 46: SC vec'd */
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 42 - 47 */
/* 48: SIMD, 50: QP BFP, 52: String */
@@ -264,6 +301,10 @@ static void spapr_dt_pa_features(SpaprMachineState *spapr,
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 54 - 59 */
/* 60: NM atomic, 62: RNG */
0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 60 - 65 */
+ /* 68: DEXCR[SBHE|IBRTPDUS|SRAPD|NPHIE|PHIE] */
+ 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, /* 66 - 71 */
+ /* 72: [P]HASHST/[P]HASHCHK */
+ 0x80, 0x00, /* 72 - 73 */
};
uint8_t *pa_features = NULL;
size_t pa_size;
@@ -280,6 +321,10 @@ static void spapr_dt_pa_features(SpaprMachineState *spapr,
pa_features = pa_features_300;
pa_size = sizeof(pa_features_300);
}
+ if (ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_10, 0, cpu->compat_pvr)) {
+ pa_features = pa_features_31;
+ pa_size = sizeof(pa_features_31);
+ }
if (!pa_features) {
return;
}
@@ -1362,7 +1407,6 @@ void spapr_init_all_lpcrs(target_ulong value, target_ulong mask)
}
}
-
static bool spapr_get_pate(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu,
target_ulong lpid, ppc_v3_pate_t *entry)
{
@@ -1375,33 +1419,16 @@ static bool spapr_get_pate(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu,
/* Copy PATE1:GR into PATE0:HR */
entry->dw0 = spapr->patb_entry & PATE0_HR;
entry->dw1 = spapr->patb_entry;
-
+ return true;
} else {
- uint64_t patb, pats;
-
- assert(lpid != 0);
-
- patb = spapr->nested_ptcr & PTCR_PATB;
- pats = spapr->nested_ptcr & PTCR_PATS;
-
- /* Check if partition table is properly aligned */
- if (patb & MAKE_64BIT_MASK(0, pats + 12)) {
- return false;
- }
-
- /* Calculate number of entries */
- pats = 1ull << (pats + 12 - 4);
- if (pats <= lpid) {
- return false;
+ if (spapr_nested_api(spapr) == NESTED_API_KVM_HV) {
+ return spapr_get_pate_nested_hv(spapr, cpu, lpid, entry);
+ } else if (spapr_nested_api(spapr) == NESTED_API_PAPR) {
+ return spapr_get_pate_nested_papr(spapr, cpu, lpid, entry);
+ } else {
+ g_assert_not_reached();
}
-
- /* Grab entry */
- patb += 16 * lpid;
- entry->dw0 = ldq_phys(CPU(cpu)->as, patb);
- entry->dw1 = ldq_phys(CPU(cpu)->as, patb + 8);
}
-
- return true;
}
#define HPTE(_table, _i) (void *)(((uint64_t *)(_table)) + ((_i) * 2))
@@ -1689,6 +1716,7 @@ static void spapr_machine_reset(MachineState *machine, ShutdownCause reason)
pef_kvm_reset(machine->cgs, &error_fatal);
spapr_caps_apply(spapr);
+ spapr_nested_reset(spapr);
first_ppc_cpu = POWERPC_CPU(first_cpu);
if (kvm_enabled() && kvmppc_has_cap_mmu_radix() &&
@@ -2138,6 +2166,7 @@ static const VMStateDescription vmstate_spapr = {
&vmstate_spapr_cap_fwnmi,
&vmstate_spapr_fwnmi,
&vmstate_spapr_cap_rpt_invalidate,
+ &vmstate_spapr_cap_nested_papr,
NULL
}
};
@@ -4702,6 +4731,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_WORKAROUND;
smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = 16; /* 64kiB */
smc->default_caps.caps[SPAPR_CAP_NESTED_KVM_HV] = SPAPR_CAP_OFF;
+ smc->default_caps.caps[SPAPR_CAP_NESTED_PAPR] = SPAPR_CAP_OFF;
smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_ON;
smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_ON;
smc->default_caps.caps[SPAPR_CAP_FWNMI] = SPAPR_CAP_ON;
diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c
index cc91d59c57..0a15415a1d 100644
--- a/hw/ppc/spapr_caps.c
+++ b/hw/ppc/spapr_caps.c
@@ -484,6 +484,50 @@ static void cap_nested_kvm_hv_apply(SpaprMachineState *spapr,
error_append_hint(errp, "Try appending -machine cap-nested-hv=off "
"or use threads=1 with -smp\n");
}
+ if (spapr_nested_api(spapr) &&
+ spapr_nested_api(spapr) != NESTED_API_KVM_HV) {
+ error_setg(errp, "Nested-HV APIs are mutually exclusive");
+ error_append_hint(errp, "Please use either cap-nested-hv or "
+ "cap-nested-papr to proceed.\n");
+ return;
+ } else {
+ spapr->nested.api = NESTED_API_KVM_HV;
+ }
+ }
+}
+
+static void cap_nested_papr_apply(SpaprMachineState *spapr,
+ uint8_t val, Error **errp)
+{
+ ERRP_GUARD();
+ PowerPCCPU *cpu = POWERPC_CPU(first_cpu);
+ CPUPPCState *env = &cpu->env;
+
+ if (!val) {
+ /* capability disabled by default */
+ return;
+ }
+
+ if (tcg_enabled()) {
+ if (!(env->insns_flags2 & PPC2_ISA300)) {
+ error_setg(errp, "Nested-PAPR only supported on POWER9 and later");
+ error_append_hint(errp,
+ "Try appending -machine cap-nested-papr=off\n");
+ return;
+ }
+ if (spapr_nested_api(spapr) &&
+ spapr_nested_api(spapr) != NESTED_API_PAPR) {
+ error_setg(errp, "Nested-HV APIs are mutually exclusive");
+ error_append_hint(errp, "Please use either cap-nested-hv or "
+ "cap-nested-papr to proceed.\n");
+ return;
+ } else {
+ spapr->nested.api = NESTED_API_PAPR;
+ }
+ } else if (kvm_enabled()) {
+ error_setg(errp, "KVM implementation does not support Nested-PAPR");
+ error_append_hint(errp,
+ "Try appending -machine cap-nested-papr=off\n");
}
}
@@ -732,6 +776,15 @@ SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = {
.type = "bool",
.apply = cap_nested_kvm_hv_apply,
},
+ [SPAPR_CAP_NESTED_PAPR] = {
+ .name = "nested-papr",
+ .description = "Allow Nested HV (PAPR API)",
+ .index = SPAPR_CAP_NESTED_PAPR,
+ .get = spapr_cap_get_bool,
+ .set = spapr_cap_set_bool,
+ .type = "bool",
+ .apply = cap_nested_papr_apply,
+ },
[SPAPR_CAP_LARGE_DECREMENTER] = {
.name = "large-decr",
.description = "Allow Large Decrementer",
@@ -916,6 +969,7 @@ SPAPR_CAP_MIG_STATE(sbbc, SPAPR_CAP_SBBC);
SPAPR_CAP_MIG_STATE(ibs, SPAPR_CAP_IBS);
SPAPR_CAP_MIG_STATE(hpt_maxpagesize, SPAPR_CAP_HPT_MAXPAGESIZE);
SPAPR_CAP_MIG_STATE(nested_kvm_hv, SPAPR_CAP_NESTED_KVM_HV);
+SPAPR_CAP_MIG_STATE(nested_papr, SPAPR_CAP_NESTED_PAPR);
SPAPR_CAP_MIG_STATE(large_decr, SPAPR_CAP_LARGE_DECREMENTER);
SPAPR_CAP_MIG_STATE(ccf_assist, SPAPR_CAP_CCF_ASSIST);
SPAPR_CAP_MIG_STATE(fwnmi, SPAPR_CAP_FWNMI);
diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c
index 40b7c52f7f..e7c9edd033 100644
--- a/hw/ppc/spapr_cpu_core.c
+++ b/hw/ppc/spapr_cpu_core.c
@@ -39,9 +39,13 @@ static void spapr_reset_vcpu(PowerPCCPU *cpu)
/*
* "PowerPC Processor binding to IEEE 1275" defines the initial MSR state
- * as 32bit (MSR_SF=0) in "8.2.1. Initial Register Values".
+ * as 32bit (MSR_SF=0) with MSR_ME=1 and MSR_FP=1 in "8.2.1. Initial
+ * Register Values". This can also be found in "LoPAPR 1.1" "C.9.2.1
+ * Initial Register Values".
*/
env->msr &= ~(1ULL << MSR_SF);
+ env->msr |= (1ULL << MSR_ME) | (1ULL << MSR_FP);
+
env->spr[SPR_HIOR] = 0;
lpcr = env->spr[SPR_LPCR];
@@ -394,10 +398,8 @@ static const TypeInfo spapr_cpu_core_type_infos[] = {
DEFINE_SPAPR_CPU_CORE_TYPE("power8_v2.0"),
DEFINE_SPAPR_CPU_CORE_TYPE("power8e_v2.1"),
DEFINE_SPAPR_CPU_CORE_TYPE("power8nvl_v1.0"),
- DEFINE_SPAPR_CPU_CORE_TYPE("power9_v1.0"),
DEFINE_SPAPR_CPU_CORE_TYPE("power9_v2.0"),
DEFINE_SPAPR_CPU_CORE_TYPE("power9_v2.2"),
- DEFINE_SPAPR_CPU_CORE_TYPE("power10_v1.0"),
DEFINE_SPAPR_CPU_CORE_TYPE("power10_v2.0"),
#ifdef CONFIG_KVM
DEFINE_SPAPR_CPU_CORE_TYPE("host"),
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index 75c2d12978..5e1d020e3d 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -1525,6 +1525,28 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn)
*slot = fn;
}
+void spapr_unregister_hypercall(target_ulong opcode)
+{
+ spapr_hcall_fn *slot;
+
+ if (opcode <= MAX_HCALL_OPCODE) {
+ assert((opcode & 0x3) == 0);
+
+ slot = &papr_hypercall_table[opcode / 4];
+ } else if (opcode >= SVM_HCALL_BASE && opcode <= SVM_HCALL_MAX) {
+ /* we only have SVM-related hcall numbers assigned in multiples of 4 */
+ assert((opcode & 0x3) == 0);
+
+ slot = &svm_hypercall_table[(opcode - SVM_HCALL_BASE) / 4];
+ } else {
+ assert((opcode >= KVMPPC_HCALL_BASE) && (opcode <= KVMPPC_HCALL_MAX));
+
+ slot = &kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE];
+ }
+
+ *slot = NULL;
+}
+
target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode,
target_ulong *args)
{
@@ -1638,8 +1660,6 @@ static void hypercall_register_types(void)
spapr_register_hypercall(KVMPPC_H_CAS, h_client_architecture_support);
spapr_register_hypercall(KVMPPC_H_UPDATE_DT, h_update_dt);
-
- spapr_register_nested();
}
type_init(hypercall_register_types)
diff --git a/hw/ppc/spapr_nested.c b/hw/ppc/spapr_nested.c
index 121aa96ddc..936659b4c0 100644
--- a/hw/ppc/spapr_nested.c
+++ b/hw/ppc/spapr_nested.c
@@ -6,8 +6,85 @@
#include "hw/ppc/spapr.h"
#include "hw/ppc/spapr_cpu_core.h"
#include "hw/ppc/spapr_nested.h"
+#include "mmu-book3s-v3.h"
+#include "cpu-models.h"
+#include "qemu/log.h"
+
+void spapr_nested_reset(SpaprMachineState *spapr)
+{
+ if (spapr_get_cap(spapr, SPAPR_CAP_NESTED_KVM_HV)) {
+ spapr_unregister_nested_hv();
+ spapr_register_nested_hv();
+ } else if (spapr_get_cap(spapr, SPAPR_CAP_NESTED_PAPR)) {
+ spapr->nested.capabilities_set = false;
+ spapr_unregister_nested_papr();
+ spapr_register_nested_papr();
+ spapr_nested_gsb_init();
+ } else {
+ spapr->nested.api = 0;
+ }
+}
+
+uint8_t spapr_nested_api(SpaprMachineState *spapr)
+{
+ return spapr->nested.api;
+}
#ifdef CONFIG_TCG
+
+bool spapr_get_pate_nested_hv(SpaprMachineState *spapr, PowerPCCPU *cpu,
+ target_ulong lpid, ppc_v3_pate_t *entry)
+{
+ uint64_t patb, pats;
+
+ assert(lpid != 0);
+
+ patb = spapr->nested.ptcr & PTCR_PATB;
+ pats = spapr->nested.ptcr & PTCR_PATS;
+
+ /* Check if partition table is properly aligned */
+ if (patb & MAKE_64BIT_MASK(0, pats + 12)) {
+ return false;
+ }
+
+ /* Calculate number of entries */
+ pats = 1ull << (pats + 12 - 4);
+ if (pats <= lpid) {
+ return false;
+ }
+
+ /* Grab entry */
+ patb += 16 * lpid;
+ entry->dw0 = ldq_phys(CPU(cpu)->as, patb);
+ entry->dw1 = ldq_phys(CPU(cpu)->as, patb + 8);
+ return true;
+}
+
+static
+SpaprMachineStateNestedGuest *spapr_get_nested_guest(SpaprMachineState *spapr,
+ target_ulong guestid)
+{
+ SpaprMachineStateNestedGuest *guest;
+
+ guest = g_hash_table_lookup(spapr->nested.guests, GINT_TO_POINTER(guestid));
+ return guest;
+}
+
+bool spapr_get_pate_nested_papr(SpaprMachineState *spapr, PowerPCCPU *cpu,
+ target_ulong lpid, ppc_v3_pate_t *entry)
+{
+ SpaprMachineStateNestedGuest *guest;
+ assert(lpid != 0);
+ guest = spapr_get_nested_guest(spapr, lpid);
+ if (!guest) {
+ return false;
+ }
+
+ entry->dw0 = guest->parttbl[0];
+ entry->dw1 = guest->parttbl[1];
+ return true;
+}
+
#define PRTS_MASK 0x1f
static target_ulong h_set_ptbl(PowerPCCPU *cpu,
@@ -25,7 +102,7 @@ static target_ulong h_set_ptbl(PowerPCCPU *cpu,
return H_PARAMETER;
}
- spapr->nested_ptcr = ptcr; /* Save new partition table */
+ spapr->nested.ptcr = ptcr; /* Save new partition table */
return H_SUCCESS;
}
@@ -59,6 +136,7 @@ static target_ulong h_copy_tofrom_guest(PowerPCCPU *cpu,
static void nested_save_state(struct nested_ppc_state *save, PowerPCCPU *cpu)
{
CPUPPCState *env = &cpu->env;
+ SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
memcpy(save->gpr, env->gpr, sizeof(save->gpr));
@@ -85,13 +163,79 @@ static void nested_save_state(struct nested_ppc_state *save, PowerPCCPU *cpu)
save->pidr = env->spr[SPR_BOOKS_PID];
save->ppr = env->spr[SPR_PPR];
- save->tb_offset = env->tb_env->tb_offset;
+ if (spapr_nested_api(spapr) == NESTED_API_PAPR) {
+ save->amor = env->spr[SPR_AMOR];
+ save->dawr0 = env->spr[SPR_DAWR0];
+ save->dawrx0 = env->spr[SPR_DAWRX0];
+ save->ciabr = env->spr[SPR_CIABR];
+ save->purr = env->spr[SPR_PURR];
+ save->spurr = env->spr[SPR_SPURR];
+ save->ic = env->spr[SPR_IC];
+ save->vtb = env->spr[SPR_VTB];
+ save->hdar = env->spr[SPR_HDAR];
+ save->hdsisr = env->spr[SPR_HDSISR];
+ save->heir = env->spr[SPR_HEIR];
+ save->asdr = env->spr[SPR_ASDR];
+ save->dawr1 = env->spr[SPR_DAWR1];
+ save->dawrx1 = env->spr[SPR_DAWRX1];
+ save->dexcr = env->spr[SPR_DEXCR];
+ save->hdexcr = env->spr[SPR_HDEXCR];
+ save->hashkeyr = env->spr[SPR_HASHKEYR];
+ save->hashpkeyr = env->spr[SPR_HASHPKEYR];
+ memcpy(save->vsr, env->vsr, sizeof(save->vsr));
+ save->ebbhr = env->spr[SPR_EBBHR];
+ save->tar = env->spr[SPR_TAR];
+ save->ebbrr = env->spr[SPR_EBBRR];
+ save->bescr = env->spr[SPR_BESCR];
+ save->iamr = env->spr[SPR_IAMR];
+ save->amr = env->spr[SPR_AMR];
+ save->uamor = env->spr[SPR_UAMOR];
+ save->dscr = env->spr[SPR_DSCR];
+ save->fscr = env->spr[SPR_FSCR];
+ save->pspb = env->spr[SPR_PSPB];
+ save->ctrl = env->spr[SPR_CTRL];
+ save->vrsave = env->spr[SPR_VRSAVE];
+ save->dar = env->spr[SPR_DAR];
+ save->dsisr = env->spr[SPR_DSISR];
+ save->pmc1 = env->spr[SPR_POWER_PMC1];
+ save->pmc2 = env->spr[SPR_POWER_PMC2];
+ save->pmc3 = env->spr[SPR_POWER_PMC3];
+ save->pmc4 = env->spr[SPR_POWER_PMC4];
+ save->pmc5 = env->spr[SPR_POWER_PMC5];
+ save->pmc6 = env->spr[SPR_POWER_PMC6];
+ save->mmcr0 = env->spr[SPR_POWER_MMCR0];
+ save->mmcr1 = env->spr[SPR_POWER_MMCR1];
+ save->mmcr2 = env->spr[SPR_POWER_MMCR2];
+ save->mmcra = env->spr[SPR_POWER_MMCRA];
+ save->sdar = env->spr[SPR_POWER_SDAR];
+ save->siar = env->spr[SPR_POWER_SIAR];
+ save->sier = env->spr[SPR_POWER_SIER];
+ save->vscr = ppc_get_vscr(env);
+ save->fpscr = env->fpscr;
+ } else if (spapr_nested_api(spapr) == NESTED_API_KVM_HV) {
+ save->tb_offset = env->tb_env->tb_offset;
+ }
+}
+
+static void nested_post_load_state(CPUPPCState *env, CPUState *cs)
+{
+ /*
+ * compute hflags and possible interrupts.
+ */
+ hreg_compute_hflags(env);
+ ppc_maybe_interrupt(env);
+ /*
+ * Nested HV does not tag TLB entries between L1 and L2, so must
+ * flush on transition.
+ */
+ tlb_flush(cs);
+ env->reserve_addr = -1; /* Reset the reservation */
}
static void nested_load_state(PowerPCCPU *cpu, struct nested_ppc_state *load)
{
- CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
+ SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
memcpy(env->gpr, load->gpr, sizeof(env->gpr));
@@ -118,20 +262,58 @@ static void nested_load_state(PowerPCCPU *cpu, struct nested_ppc_state *load)
env->spr[SPR_BOOKS_PID] = load->pidr;
env->spr[SPR_PPR] = load->ppr;
- env->tb_env->tb_offset = load->tb_offset;
-
- /*
- * MSR updated, compute hflags and possible interrupts.
- */
- hreg_compute_hflags(env);
- ppc_maybe_interrupt(env);
-
- /*
- * Nested HV does not tag TLB entries between L1 and L2, so must
- * flush on transition.
- */
- tlb_flush(cs);
- env->reserve_addr = -1; /* Reset the reservation */
+ if (spapr_nested_api(spapr) == NESTED_API_PAPR) {
+ env->spr[SPR_AMOR] = load->amor;
+ env->spr[SPR_DAWR0] = load->dawr0;
+ env->spr[SPR_DAWRX0] = load->dawrx0;
+ env->spr[SPR_CIABR] = load->ciabr;
+ env->spr[SPR_PURR] = load->purr;
+ env->spr[SPR_SPURR] = load->purr;
+ env->spr[SPR_IC] = load->ic;
+ env->spr[SPR_VTB] = load->vtb;
+ env->spr[SPR_HDAR] = load->hdar;
+ env->spr[SPR_HDSISR] = load->hdsisr;
+ env->spr[SPR_HEIR] = load->heir;
+ env->spr[SPR_ASDR] = load->asdr;
+ env->spr[SPR_DAWR1] = load->dawr1;
+ env->spr[SPR_DAWRX1] = load->dawrx1;
+ env->spr[SPR_DEXCR] = load->dexcr;
+ env->spr[SPR_HDEXCR] = load->hdexcr;
+ env->spr[SPR_HASHKEYR] = load->hashkeyr;
+ env->spr[SPR_HASHPKEYR] = load->hashpkeyr;
+ memcpy(env->vsr, load->vsr, sizeof(env->vsr));
+ env->spr[SPR_EBBHR] = load->ebbhr;
+ env->spr[SPR_TAR] = load->tar;
+ env->spr[SPR_EBBRR] = load->ebbrr;
+ env->spr[SPR_BESCR] = load->bescr;
+ env->spr[SPR_IAMR] = load->iamr;
+ env->spr[SPR_AMR] = load->amr;
+ env->spr[SPR_UAMOR] = load->uamor;
+ env->spr[SPR_DSCR] = load->dscr;
+ env->spr[SPR_FSCR] = load->fscr;
+ env->spr[SPR_PSPB] = load->pspb;
+ env->spr[SPR_CTRL] = load->ctrl;
+ env->spr[SPR_VRSAVE] = load->vrsave;
+ env->spr[SPR_DAR] = load->dar;
+ env->spr[SPR_DSISR] = load->dsisr;
+ env->spr[SPR_POWER_PMC1] = load->pmc1;
+ env->spr[SPR_POWER_PMC2] = load->pmc2;
+ env->spr[SPR_POWER_PMC3] = load->pmc3;
+ env->spr[SPR_POWER_PMC4] = load->pmc4;
+ env->spr[SPR_POWER_PMC5] = load->pmc5;
+ env->spr[SPR_POWER_PMC6] = load->pmc6;
+ env->spr[SPR_POWER_MMCR0] = load->mmcr0;
+ env->spr[SPR_POWER_MMCR1] = load->mmcr1;
+ env->spr[SPR_POWER_MMCR2] = load->mmcr2;
+ env->spr[SPR_POWER_MMCRA] = load->mmcra;
+ env->spr[SPR_POWER_SDAR] = load->sdar;
+ env->spr[SPR_POWER_SIAR] = load->siar;
+ env->spr[SPR_POWER_SIER] = load->sier;
+ ppc_store_vscr(env, load->vscr);
+ ppc_store_fpscr(env, load->fpscr);
+ } else if (spapr_nested_api(spapr) == NESTED_API_KVM_HV) {
+ env->tb_env->tb_offset = load->tb_offset;
+ }
}
/*
@@ -146,6 +328,7 @@ static target_ulong h_enter_nested(PowerPCCPU *cpu,
{
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
CPUPPCState *env = &cpu->env;
+ CPUState *cs = CPU(cpu);
SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu);
struct nested_ppc_state l2_state;
target_ulong hv_ptr = args[0];
@@ -157,7 +340,7 @@ static target_ulong h_enter_nested(PowerPCCPU *cpu,
struct kvmppc_pt_regs *regs;
hwaddr len;
- if (spapr->nested_ptcr == 0) {
+ if (spapr->nested.ptcr == 0) {
return H_NOT_AVAILABLE;
}
@@ -244,6 +427,7 @@ static target_ulong h_enter_nested(PowerPCCPU *cpu,
* Switch to the nested guest environment and start the "hdec" timer.
*/
nested_load_state(cpu, &l2_state);
+ nested_post_load_state(env, cs);
hdec = hv_state.hdec_expiry - now;
cpu_ppc_hdecr_init(env);
@@ -272,9 +456,10 @@ static target_ulong h_enter_nested(PowerPCCPU *cpu,
return env->gpr[3];
}
-void spapr_exit_nested(PowerPCCPU *cpu, int excp)
+static void spapr_exit_nested_hv(PowerPCCPU *cpu, int excp)
{
CPUPPCState *env = &cpu->env;
+ CPUState *cs = CPU(cpu);
SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu);
struct nested_ppc_state l2_state;
target_ulong hv_ptr = spapr_cpu->nested_host_state->gpr[4];
@@ -284,8 +469,6 @@ void spapr_exit_nested(PowerPCCPU *cpu, int excp)
struct kvmppc_pt_regs *regs;
hwaddr len;
- assert(spapr_cpu->in_nested);
-
nested_save_state(&l2_state, cpu);
hsrr0 = env->spr[SPR_HSRR0];
hsrr1 = env->spr[SPR_HSRR1];
@@ -298,6 +481,7 @@ void spapr_exit_nested(PowerPCCPU *cpu, int excp)
*/
assert(env->spr[SPR_LPIDR] != 0);
nested_load_state(cpu, spapr_cpu->nested_host_state);
+ nested_post_load_state(env, cs);
env->gpr[3] = env->excp_vectors[excp]; /* hcall return value */
cpu_ppc_hdecr_exit(env);
@@ -375,21 +559,1347 @@ void spapr_exit_nested(PowerPCCPU *cpu, int excp)
address_space_unmap(CPU(cpu)->as, regs, len, len, true);
}
-void spapr_register_nested(void)
+static bool spapr_nested_vcpu_check(SpaprMachineStateNestedGuest *guest,
+ target_ulong vcpuid, bool inoutbuf)
+{
+ struct SpaprMachineStateNestedGuestVcpu *vcpu;
+ /*
+ * Perform sanity checks for the provided vcpuid of a guest.
+ * For now, ensure its valid, allocated and enabled for use.
+ */
+
+ if (vcpuid >= PAPR_NESTED_GUEST_VCPU_MAX) {
+ return false;
+ }
+
+ if (!(vcpuid < guest->nr_vcpus)) {
+ return false;
+ }
+
+ vcpu = &guest->vcpus[vcpuid];
+ if (!vcpu->enabled) {
+ return false;
+ }
+
+ if (!inoutbuf) {
+ return true;
+ }
+
+ /* Check to see if the in/out buffers are registered */
+ if (vcpu->runbufin.addr && vcpu->runbufout.addr) {
+ return true;
+ }
+
+ return false;
+}
+
+static void *get_vcpu_state_ptr(SpaprMachineStateNestedGuest *guest,
+ target_ulong vcpuid)
+{
+ assert(spapr_nested_vcpu_check(guest, vcpuid, false));
+ return &guest->vcpus[vcpuid].state;
+}
+
+static void *get_vcpu_ptr(SpaprMachineStateNestedGuest *guest,
+ target_ulong vcpuid)
+{
+ assert(spapr_nested_vcpu_check(guest, vcpuid, false));
+ return &guest->vcpus[vcpuid];
+}
+
+static void *get_guest_ptr(SpaprMachineStateNestedGuest *guest,
+ target_ulong vcpuid)
+{
+ return guest; /* for GSBE_NESTED */
+}
+
+/*
+ * set=1 means the L1 is trying to set some state
+ * set=0 means the L1 is trying to get some state
+ */
+static void copy_state_8to8(void *a, void *b, bool set)
+{
+ /* set takes from the Big endian element_buf and sets internal buffer */
+
+ if (set) {
+ *(uint64_t *)a = be64_to_cpu(*(uint64_t *)b);
+ } else {
+ *(uint64_t *)b = cpu_to_be64(*(uint64_t *)a);
+ }
+}
+
+static void copy_state_4to4(void *a, void *b, bool set)
+{
+ if (set) {
+ *(uint32_t *)a = be32_to_cpu(*(uint32_t *)b);
+ } else {
+ *(uint32_t *)b = cpu_to_be32(*((uint32_t *)a));
+ }
+}
+
+static void copy_state_16to16(void *a, void *b, bool set)
+{
+ uint64_t *src, *dst;
+
+ if (set) {
+ src = b;
+ dst = a;
+
+ dst[1] = be64_to_cpu(src[0]);
+ dst[0] = be64_to_cpu(src[1]);
+ } else {
+ src = a;
+ dst = b;
+
+ dst[1] = cpu_to_be64(src[0]);
+ dst[0] = cpu_to_be64(src[1]);
+ }
+}
+
+static void copy_state_4to8(void *a, void *b, bool set)
+{
+ if (set) {
+ *(uint64_t *)a = (uint64_t) be32_to_cpu(*(uint32_t *)b);
+ } else {
+ *(uint32_t *)b = cpu_to_be32((uint32_t) (*((uint64_t *)a)));
+ }
+}
+
+static void copy_state_pagetbl(void *a, void *b, bool set)
+{
+ uint64_t *pagetbl;
+ uint64_t *buf; /* 3 double words */
+ uint64_t rts;
+
+ assert(set);
+
+ pagetbl = a;
+ buf = b;
+
+ *pagetbl = be64_to_cpu(buf[0]);
+ /* as per ISA section 6.7.6.1 */
+ *pagetbl |= PATE0_HR; /* Host Radix bit is 1 */
+
+ /* RTS */
+ rts = be64_to_cpu(buf[1]);
+ assert(rts == 52);
+ rts = rts - 31; /* since radix tree size = 2^(RTS+31) */
+ *pagetbl |= ((rts & 0x7) << 5); /* RTS2 is bit 56:58 */
+ *pagetbl |= (((rts >> 3) & 0x3) << 61); /* RTS1 is bit 1:2 */
+
+ /* RPDS {Size = 2^(RPDS+3) , RPDS >=5} */
+ *pagetbl |= 63 - clz64(be64_to_cpu(buf[2])) - 3;
+}
+
+static void copy_state_proctbl(void *a, void *b, bool set)
+{
+ uint64_t *proctbl;
+ uint64_t *buf; /* 2 double words */
+
+ assert(set);
+
+ proctbl = a;
+ buf = b;
+ /* PRTB: Process Table Base */
+ *proctbl = be64_to_cpu(buf[0]);
+ /* PRTS: Process Table Size = 2^(12+PRTS) */
+ if (be64_to_cpu(buf[1]) == (1ULL << 12)) {
+ *proctbl |= 0;
+ } else if (be64_to_cpu(buf[1]) == (1ULL << 24)) {
+ *proctbl |= 12;
+ } else {
+ g_assert_not_reached();
+ }
+}
+
+static void copy_state_runbuf(void *a, void *b, bool set)
+{
+ uint64_t *buf; /* 2 double words */
+ struct SpaprMachineStateNestedGuestVcpuRunBuf *runbuf;
+
+ assert(set);
+
+ runbuf = a;
+ buf = b;
+
+ runbuf->addr = be64_to_cpu(buf[0]);
+ assert(runbuf->addr);
+
+ /* per spec */
+ assert(be64_to_cpu(buf[1]) <= 16384);
+
+ /*
+ * This will also hit in the input buffer but should be fine for
+ * now. If not we can split this function.
+ */
+ assert(be64_to_cpu(buf[1]) >= VCPU_OUT_BUF_MIN_SZ);
+
+ runbuf->size = be64_to_cpu(buf[1]);
+}
+
+/* tell the L1 how big we want the output vcpu run buffer */
+static void out_buf_min_size(void *a, void *b, bool set)
+{
+ uint64_t *buf; /* 1 double word */
+
+ assert(!set);
+
+ buf = b;
+
+ buf[0] = cpu_to_be64(VCPU_OUT_BUF_MIN_SZ);
+}
+
+static void copy_logical_pvr(void *a, void *b, bool set)
+{
+ SpaprMachineStateNestedGuest *guest;
+ uint32_t *buf; /* 1 word */
+ uint32_t *pvr_logical_ptr;
+ uint32_t pvr_logical;
+ target_ulong pcr = 0;
+
+ pvr_logical_ptr = a;
+ buf = b;
+
+ if (!set) {
+ buf[0] = cpu_to_be32(*pvr_logical_ptr);
+ return;
+ }
+
+ pvr_logical = be32_to_cpu(buf[0]);
+
+ *pvr_logical_ptr = pvr_logical;
+
+ if (*pvr_logical_ptr) {
+ switch (*pvr_logical_ptr) {
+ case CPU_POWERPC_LOGICAL_3_10:
+ pcr = PCR_COMPAT_3_10 | PCR_COMPAT_3_00;
+ break;
+ case CPU_POWERPC_LOGICAL_3_00:
+ pcr = PCR_COMPAT_3_00;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Could not set PCR for LPVR=0x%08x\n",
+ *pvr_logical_ptr);
+ return;
+ }
+ }
+
+ guest = container_of(pvr_logical_ptr,
+ struct SpaprMachineStateNestedGuest,
+ pvr_logical);
+ for (int i = 0; i < guest->nr_vcpus; i++) {
+ guest->vcpus[i].state.pcr = ~pcr | HVMASK_PCR;
+ }
+}
+
+static void copy_tb_offset(void *a, void *b, bool set)
+{
+ SpaprMachineStateNestedGuest *guest;
+ uint64_t *buf; /* 1 double word */
+ uint64_t *tb_offset_ptr;
+ uint64_t tb_offset;
+
+ tb_offset_ptr = a;
+ buf = b;
+
+ if (!set) {
+ buf[0] = cpu_to_be64(*tb_offset_ptr);
+ return;
+ }
+
+ tb_offset = be64_to_cpu(buf[0]);
+ /* need to copy this to the individual tb_offset for each vcpu */
+ guest = container_of(tb_offset_ptr,
+ struct SpaprMachineStateNestedGuest,
+ tb_offset);
+ for (int i = 0; i < guest->nr_vcpus; i++) {
+ guest->vcpus[i].tb_offset = tb_offset;
+ }
+}
+
+static void copy_state_hdecr(void *a, void *b, bool set)
+{
+ uint64_t *buf; /* 1 double word */
+ uint64_t *hdecr_expiry_tb;
+
+ hdecr_expiry_tb = a;
+ buf = b;
+
+ if (!set) {
+ buf[0] = cpu_to_be64(*hdecr_expiry_tb);
+ return;
+ }
+
+ *hdecr_expiry_tb = be64_to_cpu(buf[0]);
+}
+
+struct guest_state_element_type guest_state_element_types[] = {
+ GUEST_STATE_ELEMENT_NOP(GSB_HV_VCPU_IGNORED_ID, 0),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR0, gpr[0]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR1, gpr[1]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR2, gpr[2]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR3, gpr[3]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR4, gpr[4]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR5, gpr[5]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR6, gpr[6]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR7, gpr[7]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR8, gpr[8]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR9, gpr[9]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR10, gpr[10]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR11, gpr[11]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR12, gpr[12]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR13, gpr[13]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR14, gpr[14]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR15, gpr[15]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR16, gpr[16]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR17, gpr[17]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR18, gpr[18]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR19, gpr[19]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR20, gpr[20]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR21, gpr[21]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR22, gpr[22]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR23, gpr[23]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR24, gpr[24]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR25, gpr[25]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR26, gpr[26]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR27, gpr[27]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR28, gpr[28]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR29, gpr[29]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR30, gpr[30]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR31, gpr[31]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_NIA, nip),
+ GSE_ENV_DWM(GSB_VCPU_SPR_MSR, msr, HVMASK_MSR),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_CTR, ctr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_LR, lr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_XER, xer),
+ GUEST_STATE_ELEMENT_ENV_WW(GSB_VCPU_SPR_CR, cr),
+ GUEST_STATE_ELEMENT_NOP_DW(GSB_VCPU_SPR_MMCR3),
+ GUEST_STATE_ELEMENT_NOP_DW(GSB_VCPU_SPR_SIER2),
+ GUEST_STATE_ELEMENT_NOP_DW(GSB_VCPU_SPR_SIER3),
+ GUEST_STATE_ELEMENT_NOP_W(GSB_VCPU_SPR_WORT),
+ GSE_ENV_DWM(GSB_VCPU_SPR_LPCR, lpcr, HVMASK_LPCR),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_AMOR, amor),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_HFSCR, hfscr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_DAWR0, dawr0),
+ GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_DAWRX0, dawrx0),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_CIABR, ciabr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_PURR, purr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SPURR, spurr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_IC, ic),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_VTB, vtb),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_HDAR, hdar),
+ GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_HDSISR, hdsisr),
+ GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_HEIR, heir),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_ASDR, asdr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SRR0, srr0),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SRR1, srr1),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SPRG0, sprg0),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SPRG1, sprg1),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SPRG2, sprg2),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SPRG3, sprg3),
+ GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_PIDR, pidr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_CFAR, cfar),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_PPR, ppr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_DAWR1, dawr1),
+ GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_DAWRX1, dawrx1),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_DEXCR, dexcr),
+ GSE_ENV_DWM(GSB_VCPU_SPR_HDEXCR, hdexcr, HVMASK_HDEXCR),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_HASHKEYR, hashkeyr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_HASHPKEYR, hashpkeyr),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR0, vsr[0]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR1, vsr[1]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR2, vsr[2]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR3, vsr[3]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR4, vsr[4]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR5, vsr[5]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR6, vsr[6]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR7, vsr[7]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR8, vsr[8]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR9, vsr[9]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR10, vsr[10]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR11, vsr[11]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR12, vsr[12]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR13, vsr[13]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR14, vsr[14]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR15, vsr[15]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR16, vsr[16]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR17, vsr[17]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR18, vsr[18]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR19, vsr[19]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR20, vsr[20]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR21, vsr[21]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR22, vsr[22]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR23, vsr[23]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR24, vsr[24]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR25, vsr[25]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR26, vsr[26]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR27, vsr[27]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR28, vsr[28]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR29, vsr[29]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR30, vsr[30]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR31, vsr[31]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR32, vsr[32]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR33, vsr[33]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR34, vsr[34]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR35, vsr[35]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR36, vsr[36]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR37, vsr[37]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR38, vsr[38]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR39, vsr[39]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR40, vsr[40]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR41, vsr[41]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR42, vsr[42]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR43, vsr[43]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR44, vsr[44]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR45, vsr[45]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR46, vsr[46]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR47, vsr[47]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR48, vsr[48]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR49, vsr[49]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR50, vsr[50]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR51, vsr[51]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR52, vsr[52]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR53, vsr[53]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR54, vsr[54]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR55, vsr[55]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR56, vsr[56]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR57, vsr[57]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR58, vsr[58]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR59, vsr[59]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR60, vsr[60]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR61, vsr[61]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR62, vsr[62]),
+ GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR63, vsr[63]),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_EBBHR, ebbhr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_TAR, tar),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_EBBRR, ebbrr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_BESCR, bescr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_IAMR, iamr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_AMR, amr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_UAMOR, uamor),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_DSCR, dscr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_FSCR, fscr),
+ GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_PSPB, pspb),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_CTRL, ctrl),
+ GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_VRSAVE, vrsave),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_DAR, dar),
+ GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_DSISR, dsisr),
+ GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_PMC1, pmc1),
+ GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_PMC2, pmc2),
+ GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_PMC3, pmc3),
+ GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_PMC4, pmc4),
+ GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_PMC5, pmc5),
+ GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_PMC6, pmc6),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_MMCR0, mmcr0),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_MMCR1, mmcr1),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_MMCR2, mmcr2),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_MMCRA, mmcra),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SDAR , sdar),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SIAR , siar),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SIER , sier),
+ GUEST_STATE_ELEMENT_ENV_WW(GSB_VCPU_SPR_VSCR, vscr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_FPSCR, fpscr),
+ GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_DEC_EXPIRE_TB, dec_expiry_tb),
+ GSBE_NESTED(GSB_PART_SCOPED_PAGETBL, 0x18, parttbl[0], copy_state_pagetbl),
+ GSBE_NESTED(GSB_PROCESS_TBL, 0x10, parttbl[1], copy_state_proctbl),
+ GSBE_NESTED(GSB_VCPU_LPVR, 0x4, pvr_logical, copy_logical_pvr),
+ GSBE_NESTED_MSK(GSB_TB_OFFSET, 0x8, tb_offset, copy_tb_offset,
+ HVMASK_TB_OFFSET),
+ GSBE_NESTED_VCPU(GSB_VCPU_IN_BUFFER, 0x10, runbufin, copy_state_runbuf),
+ GSBE_NESTED_VCPU(GSB_VCPU_OUT_BUFFER, 0x10, runbufout, copy_state_runbuf),
+ GSBE_NESTED_VCPU(GSB_VCPU_OUT_BUF_MIN_SZ, 0x8, runbufout, out_buf_min_size),
+ GSBE_NESTED_VCPU(GSB_VCPU_HDEC_EXPIRY_TB, 0x8, hdecr_expiry_tb,
+ copy_state_hdecr)
+};
+
+void spapr_nested_gsb_init(void)
+{
+ struct guest_state_element_type *type;
+
+ /* Init the guest state elements lookup table, flags for now */
+ for (int i = 0; i < ARRAY_SIZE(guest_state_element_types); i++) {
+ type = &guest_state_element_types[i];
+
+ assert(type->id <= GSB_LAST);
+ if (type->id >= GSB_VCPU_SPR_HDAR)
+ /* 0xf000 - 0xf005 Thread + RO */
+ type->flags = GUEST_STATE_ELEMENT_TYPE_FLAG_READ_ONLY;
+ else if (type->id >= GSB_VCPU_IN_BUFFER)
+ /* 0x0c00 - 0xf000 Thread + RW */
+ type->flags = 0;
+ else if (type->id >= GSB_VCPU_LPVR)
+ /* 0x0003 - 0x0bff Guest + RW */
+ type->flags = GUEST_STATE_ELEMENT_TYPE_FLAG_GUEST_WIDE;
+ else if (type->id >= GSB_HV_VCPU_STATE_SIZE)
+ /* 0x0001 - 0x0002 Guest + RO */
+ type->flags = GUEST_STATE_ELEMENT_TYPE_FLAG_READ_ONLY |
+ GUEST_STATE_ELEMENT_TYPE_FLAG_GUEST_WIDE;
+ }
+}
+
+static struct guest_state_element *guest_state_element_next(
+ struct guest_state_element *element,
+ int64_t *len,
+ int64_t *num_elements)
+{
+ uint16_t size;
+
+ /* size is of element->value[] only. Not whole guest_state_element */
+ size = be16_to_cpu(element->size);
+
+ if (len) {
+ *len -= size + offsetof(struct guest_state_element, value);
+ }
+
+ if (num_elements) {
+ *num_elements -= 1;
+ }
+
+ return (struct guest_state_element *)(element->value + size);
+}
+
+static
+struct guest_state_element_type *guest_state_element_type_find(uint16_t id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(guest_state_element_types); i++)
+ if (id == guest_state_element_types[i].id) {
+ return &guest_state_element_types[i];
+ }
+
+ return NULL;
+}
+
+static void log_element(struct guest_state_element *element,
+ struct guest_state_request *gsr)
+{
+ qemu_log_mask(LOG_GUEST_ERROR, "h_guest_%s_state id:0x%04x size:0x%04x",
+ gsr->flags & GUEST_STATE_REQUEST_SET ? "set" : "get",
+ be16_to_cpu(element->id), be16_to_cpu(element->size));
+ qemu_log_mask(LOG_GUEST_ERROR, "buf:0x%016"PRIx64" ...\n",
+ be64_to_cpu(*(uint64_t *)element->value));
+}
+
+static bool guest_state_request_check(struct guest_state_request *gsr)
+{
+ int64_t num_elements, len = gsr->len;
+ struct guest_state_buffer *gsb = gsr->gsb;
+ struct guest_state_element *element;
+ struct guest_state_element_type *type;
+ uint16_t id, size;
+
+ /* gsb->num_elements = 0 == 32 bits long */
+ assert(len >= 4);
+
+ num_elements = be32_to_cpu(gsb->num_elements);
+ element = gsb->elements;
+ len -= sizeof(gsb->num_elements);
+
+ /* Walk the buffer to validate the length */
+ while (num_elements) {
+
+ id = be16_to_cpu(element->id);
+ size = be16_to_cpu(element->size);
+
+ if (false) {
+ log_element(element, gsr);
+ }
+ /* buffer size too small */
+ if (len < 0) {
+ return false;
+ }
+
+ type = guest_state_element_type_find(id);
+ if (!type) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Element ID %04x unknown\n", id);
+ log_element(element, gsr);
+ return false;
+ }
+
+ if (id == GSB_HV_VCPU_IGNORED_ID) {
+ goto next_element;
+ }
+
+ if (size != type->size) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Size mismatch. Element ID:%04x."
+ "Size Exp:%i Got:%i\n", id, type->size, size);
+ log_element(element, gsr);
+ return false;
+ }
+
+ if ((type->flags & GUEST_STATE_ELEMENT_TYPE_FLAG_READ_ONLY) &&
+ (gsr->flags & GUEST_STATE_REQUEST_SET)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Trying to set a read-only Element "
+ "ID:%04x.\n", id);
+ return false;
+ }
+
+ if (type->flags & GUEST_STATE_ELEMENT_TYPE_FLAG_GUEST_WIDE) {
+ /* guest wide element type */
+ if (!(gsr->flags & GUEST_STATE_REQUEST_GUEST_WIDE)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "trying to set a guest wide "
+ "Element ID:%04x.\n", id);
+ return false;
+ }
+ } else {
+ /* thread wide element type */
+ if (gsr->flags & GUEST_STATE_REQUEST_GUEST_WIDE) {
+ qemu_log_mask(LOG_GUEST_ERROR, "trying to set a thread wide "
+ "Element ID:%04x.\n", id);
+ return false;
+ }
+ }
+next_element:
+ element = guest_state_element_next(element, &len, &num_elements);
+
+ }
+ return true;
+}
+
+static bool is_gsr_invalid(struct guest_state_request *gsr,
+ struct guest_state_element *element,
+ struct guest_state_element_type *type)
+{
+ if ((gsr->flags & GUEST_STATE_REQUEST_SET) &&
+ (*(uint64_t *)(element->value) & ~(type->mask))) {
+ log_element(element, gsr);
+ qemu_log_mask(LOG_GUEST_ERROR, "L1 can't set reserved bits "
+ "(allowed mask: 0x%08"PRIx64")\n", type->mask);
+ return true;
+ }
+ return false;
+}
+
+static target_ulong h_guest_get_capabilities(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong opcode,
+ target_ulong *args)
+{
+ CPUPPCState *env = &cpu->env;
+ target_ulong flags = args[0];
+
+ if (flags) { /* don't handle any flags capabilities for now */
+ return H_PARAMETER;
+ }
+
+ /* P10 capabilities */
+ if (ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_10, 0,
+ spapr->max_compat_pvr)) {
+ env->gpr[4] |= H_GUEST_CAPABILITIES_P10_MODE;
+ }
+
+ /* P9 capabilities */
+ if (ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_00, 0,
+ spapr->max_compat_pvr)) {
+ env->gpr[4] |= H_GUEST_CAPABILITIES_P9_MODE;
+ }
+
+ return H_SUCCESS;
+}
+
+static target_ulong h_guest_set_capabilities(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong opcode,
+ target_ulong *args)
+{
+ CPUPPCState *env = &cpu->env;
+ target_ulong flags = args[0];
+ target_ulong capabilities = args[1];
+ env->gpr[4] = 0;
+
+ if (flags) { /* don't handle any flags capabilities for now */
+ return H_PARAMETER;
+ }
+
+ if (capabilities & H_GUEST_CAPABILITIES_COPY_MEM) {
+ env->gpr[4] = 1;
+ return H_P2; /* isn't supported */
+ }
+
+ /*
+ * If there are no capabilities configured, set the R5 to the index of
+ * the first supported Power Processor Mode
+ */
+ if (!capabilities) {
+ env->gpr[4] = 1;
+
+ /* set R5 to the first supported Power Processor Mode */
+ if (ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_10, 0,
+ spapr->max_compat_pvr)) {
+ env->gpr[5] = H_GUEST_CAP_P10_MODE_BMAP;
+ } else if (ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_00, 0,
+ spapr->max_compat_pvr)) {
+ env->gpr[5] = H_GUEST_CAP_P9_MODE_BMAP;
+ }
+
+ return H_P2;
+ }
+
+ /*
+ * If an invalid capability is set, R5 should contain the index of the
+ * invalid capability bit
+ */
+ if (capabilities & ~H_GUEST_CAP_VALID_MASK) {
+ env->gpr[4] = 1;
+
+ /* Set R5 to the index of the invalid capability */
+ env->gpr[5] = 63 - ctz64(capabilities);
+
+ return H_P2;
+ }
+
+ if (!spapr->nested.capabilities_set) {
+ spapr->nested.capabilities_set = true;
+ spapr->nested.pvr_base = env->spr[SPR_PVR];
+ return H_SUCCESS;
+ } else {
+ return H_STATE;
+ }
+}
+
+static void
+destroy_guest_helper(gpointer value)
+{
+ struct SpaprMachineStateNestedGuest *guest = value;
+ g_free(guest->vcpus);
+ g_free(guest);
+}
+
+static target_ulong h_guest_create(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong opcode,
+ target_ulong *args)
+{
+ CPUPPCState *env = &cpu->env;
+ target_ulong flags = args[0];
+ target_ulong continue_token = args[1];
+ uint64_t guestid;
+ int nguests = 0;
+ struct SpaprMachineStateNestedGuest *guest;
+
+ if (flags) { /* don't handle any flags for now */
+ return H_UNSUPPORTED_FLAG;
+ }
+
+ if (continue_token != -1) {
+ return H_P2;
+ }
+
+ if (!spapr->nested.capabilities_set) {
+ return H_STATE;
+ }
+
+ if (!spapr->nested.guests) {
+ spapr->nested.guests = g_hash_table_new_full(NULL,
+ NULL,
+ NULL,
+ destroy_guest_helper);
+ }
+
+ nguests = g_hash_table_size(spapr->nested.guests);
+
+ if (nguests == PAPR_NESTED_GUEST_MAX) {
+ return H_NO_MEM;
+ }
+
+ /* Lookup for available guestid */
+ for (guestid = 1; guestid < PAPR_NESTED_GUEST_MAX; guestid++) {
+ if (!(g_hash_table_lookup(spapr->nested.guests,
+ GINT_TO_POINTER(guestid)))) {
+ break;
+ }
+ }
+
+ if (guestid == PAPR_NESTED_GUEST_MAX) {
+ return H_NO_MEM;
+ }
+
+ guest = g_try_new0(struct SpaprMachineStateNestedGuest, 1);
+ if (!guest) {
+ return H_NO_MEM;
+ }
+
+ guest->pvr_logical = spapr->nested.pvr_base;
+ g_hash_table_insert(spapr->nested.guests, GINT_TO_POINTER(guestid), guest);
+ env->gpr[4] = guestid;
+
+ return H_SUCCESS;
+}
+
+static target_ulong h_guest_delete(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong opcode,
+ target_ulong *args)
+{
+ target_ulong flags = args[0];
+ target_ulong guestid = args[1];
+ struct SpaprMachineStateNestedGuest *guest;
+
+ /*
+ * handle flag deleteAllGuests, if set:
+ * guestid is ignored and all guests are deleted
+ *
+ */
+ if (flags & ~H_GUEST_DELETE_ALL_FLAG) {
+ return H_UNSUPPORTED_FLAG; /* other flag bits reserved */
+ } else if (flags & H_GUEST_DELETE_ALL_FLAG) {
+ g_hash_table_destroy(spapr->nested.guests);
+ return H_SUCCESS;
+ }
+
+ guest = g_hash_table_lookup(spapr->nested.guests, GINT_TO_POINTER(guestid));
+ if (!guest) {
+ return H_P2;
+ }
+
+ g_hash_table_remove(spapr->nested.guests, GINT_TO_POINTER(guestid));
+
+ return H_SUCCESS;
+}
+
+static target_ulong h_guest_create_vcpu(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong opcode,
+ target_ulong *args)
+{
+ target_ulong flags = args[0];
+ target_ulong guestid = args[1];
+ target_ulong vcpuid = args[2];
+ SpaprMachineStateNestedGuest *guest;
+
+ if (flags) { /* don't handle any flags for now */
+ return H_UNSUPPORTED_FLAG;
+ }
+
+ guest = spapr_get_nested_guest(spapr, guestid);
+ if (!guest) {
+ return H_P2;
+ }
+
+ if (vcpuid < guest->nr_vcpus) {
+ qemu_log_mask(LOG_UNIMP, "vcpuid " TARGET_FMT_ld " already in use.",
+ vcpuid);
+ return H_IN_USE;
+ }
+ /* linear vcpuid allocation only */
+ assert(vcpuid == guest->nr_vcpus);
+
+ if (guest->nr_vcpus >= PAPR_NESTED_GUEST_VCPU_MAX) {
+ return H_P3;
+ }
+
+ SpaprMachineStateNestedGuestVcpu *vcpus, *curr_vcpu;
+ vcpus = g_try_renew(struct SpaprMachineStateNestedGuestVcpu,
+ guest->vcpus,
+ guest->nr_vcpus + 1);
+ if (!vcpus) {
+ return H_NO_MEM;
+ }
+ guest->vcpus = vcpus;
+ curr_vcpu = &vcpus[guest->nr_vcpus];
+ memset(curr_vcpu, 0, sizeof(SpaprMachineStateNestedGuestVcpu));
+
+ curr_vcpu->enabled = true;
+ guest->nr_vcpus++;
+
+ return H_SUCCESS;
+}
+
+static target_ulong getset_state(SpaprMachineStateNestedGuest *guest,
+ uint64_t vcpuid,
+ struct guest_state_request *gsr)
+{
+ void *ptr;
+ uint16_t id;
+ struct guest_state_element *element;
+ struct guest_state_element_type *type;
+ int64_t lenleft, num_elements;
+
+ lenleft = gsr->len;
+
+ if (!guest_state_request_check(gsr)) {
+ return H_P3;
+ }
+
+ num_elements = be32_to_cpu(gsr->gsb->num_elements);
+ element = gsr->gsb->elements;
+ /* Process the elements */
+ while (num_elements) {
+ type = NULL;
+ /* log_element(element, gsr); */
+
+ id = be16_to_cpu(element->id);
+ if (id == GSB_HV_VCPU_IGNORED_ID) {
+ goto next_element;
+ }
+
+ type = guest_state_element_type_find(id);
+ assert(type);
+
+ /* Get pointer to guest data to get/set */
+ if (type->location && type->copy) {
+ ptr = type->location(guest, vcpuid);
+ assert(ptr);
+ if (!~(type->mask) && is_gsr_invalid(gsr, element, type)) {
+ return H_INVALID_ELEMENT_VALUE;
+ }
+ type->copy(ptr + type->offset, element->value,
+ gsr->flags & GUEST_STATE_REQUEST_SET ? true : false);
+ }
+
+next_element:
+ element = guest_state_element_next(element, &lenleft, &num_elements);
+ }
+
+ return H_SUCCESS;
+}
+
+static target_ulong map_and_getset_state(PowerPCCPU *cpu,
+ SpaprMachineStateNestedGuest *guest,
+ uint64_t vcpuid,
+ struct guest_state_request *gsr)
+{
+ target_ulong rc;
+ int64_t len;
+ bool is_write;
+
+ len = gsr->len;
+ /* only get_state would require write access to the provided buffer */
+ is_write = (gsr->flags & GUEST_STATE_REQUEST_SET) ? false : true;
+ gsr->gsb = address_space_map(CPU(cpu)->as, gsr->buf, (uint64_t *)&len,
+ is_write, MEMTXATTRS_UNSPECIFIED);
+ if (!gsr->gsb) {
+ rc = H_P3;
+ goto out1;
+ }
+
+ if (len != gsr->len) {
+ rc = H_P3;
+ goto out1;
+ }
+
+ rc = getset_state(guest, vcpuid, gsr);
+
+out1:
+ address_space_unmap(CPU(cpu)->as, gsr->gsb, len, is_write, len);
+ return rc;
+}
+
+static target_ulong h_guest_getset_state(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong *args,
+ bool set)
+{
+ target_ulong flags = args[0];
+ target_ulong lpid = args[1];
+ target_ulong vcpuid = args[2];
+ target_ulong buf = args[3];
+ target_ulong buflen = args[4];
+ struct guest_state_request gsr;
+ SpaprMachineStateNestedGuest *guest;
+
+ guest = spapr_get_nested_guest(spapr, lpid);
+ if (!guest) {
+ return H_P2;
+ }
+ gsr.buf = buf;
+ assert(buflen <= GSB_MAX_BUF_SIZE);
+ gsr.len = buflen;
+ gsr.flags = 0;
+ if (flags & H_GUEST_GETSET_STATE_FLAG_GUEST_WIDE) {
+ gsr.flags |= GUEST_STATE_REQUEST_GUEST_WIDE;
+ }
+ if (flags & !H_GUEST_GETSET_STATE_FLAG_GUEST_WIDE) {
+ return H_PARAMETER; /* flag not supported yet */
+ }
+
+ if (set) {
+ gsr.flags |= GUEST_STATE_REQUEST_SET;
+ }
+ return map_and_getset_state(cpu, guest, vcpuid, &gsr);
+}
+
+static target_ulong h_guest_set_state(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong opcode,
+ target_ulong *args)
+{
+ return h_guest_getset_state(cpu, spapr, args, true);
+}
+
+static target_ulong h_guest_get_state(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong opcode,
+ target_ulong *args)
+{
+ return h_guest_getset_state(cpu, spapr, args, false);
+}
+
+static void exit_nested_store_l2(PowerPCCPU *cpu, int excp,
+ SpaprMachineStateNestedGuestVcpu *vcpu)
+{
+ CPUPPCState *env = &cpu->env;
+ SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu);
+ target_ulong now, hdar, hdsisr, asdr;
+
+ assert(sizeof(env->gpr) == sizeof(vcpu->state.gpr)); /* sanity check */
+
+ now = cpu_ppc_load_tbl(env); /* L2 timebase */
+ now -= vcpu->tb_offset; /* L1 timebase */
+ vcpu->state.dec_expiry_tb = now - cpu_ppc_load_decr(env);
+ cpu_ppc_store_decr(env, spapr_cpu->nested_host_state->dec_expiry_tb - now);
+ /* backup hdar, hdsisr, asdr if reqd later below */
+ hdar = vcpu->state.hdar;
+ hdsisr = vcpu->state.hdsisr;
+ asdr = vcpu->state.asdr;
+
+ nested_save_state(&vcpu->state, cpu);
+
+ if (excp == POWERPC_EXCP_MCHECK ||
+ excp == POWERPC_EXCP_RESET ||
+ excp == POWERPC_EXCP_SYSCALL) {
+ vcpu->state.nip = env->spr[SPR_SRR0];
+ vcpu->state.msr = env->spr[SPR_SRR1] & env->msr_mask;
+ } else {
+ vcpu->state.nip = env->spr[SPR_HSRR0];
+ vcpu->state.msr = env->spr[SPR_HSRR1] & env->msr_mask;
+ }
+
+ /* hdar, hdsisr, asdr should be retained unless certain exceptions */
+ if ((excp != POWERPC_EXCP_HDSI) && (excp != POWERPC_EXCP_HISI)) {
+ vcpu->state.asdr = asdr;
+ } else if (excp != POWERPC_EXCP_HDSI) {
+ vcpu->state.hdar = hdar;
+ vcpu->state.hdsisr = hdsisr;
+ }
+}
+
+static int get_exit_ids(uint64_t srr0, uint16_t ids[16])
+{
+ int nr;
+
+ switch (srr0) {
+ case 0xc00:
+ nr = 10;
+ ids[0] = GSB_VCPU_GPR3;
+ ids[1] = GSB_VCPU_GPR4;
+ ids[2] = GSB_VCPU_GPR5;
+ ids[3] = GSB_VCPU_GPR6;
+ ids[4] = GSB_VCPU_GPR7;
+ ids[5] = GSB_VCPU_GPR8;
+ ids[6] = GSB_VCPU_GPR9;
+ ids[7] = GSB_VCPU_GPR10;
+ ids[8] = GSB_VCPU_GPR11;
+ ids[9] = GSB_VCPU_GPR12;
+ break;
+ case 0xe00:
+ nr = 5;
+ ids[0] = GSB_VCPU_SPR_HDAR;
+ ids[1] = GSB_VCPU_SPR_HDSISR;
+ ids[2] = GSB_VCPU_SPR_ASDR;
+ ids[3] = GSB_VCPU_SPR_NIA;
+ ids[4] = GSB_VCPU_SPR_MSR;
+ break;
+ case 0xe20:
+ nr = 4;
+ ids[0] = GSB_VCPU_SPR_HDAR;
+ ids[1] = GSB_VCPU_SPR_ASDR;
+ ids[2] = GSB_VCPU_SPR_NIA;
+ ids[3] = GSB_VCPU_SPR_MSR;
+ break;
+ case 0xe40:
+ nr = 3;
+ ids[0] = GSB_VCPU_SPR_HEIR;
+ ids[1] = GSB_VCPU_SPR_NIA;
+ ids[2] = GSB_VCPU_SPR_MSR;
+ break;
+ case 0xf80:
+ nr = 3;
+ ids[0] = GSB_VCPU_SPR_HFSCR;
+ ids[1] = GSB_VCPU_SPR_NIA;
+ ids[2] = GSB_VCPU_SPR_MSR;
+ break;
+ default:
+ nr = 0;
+ break;
+ }
+
+ return nr;
+}
+
+static void exit_process_output_buffer(PowerPCCPU *cpu,
+ SpaprMachineStateNestedGuest *guest,
+ target_ulong vcpuid,
+ target_ulong *r3)
+{
+ SpaprMachineStateNestedGuestVcpu *vcpu = &guest->vcpus[vcpuid];
+ struct guest_state_request gsr;
+ struct guest_state_buffer *gsb;
+ struct guest_state_element *element;
+ struct guest_state_element_type *type;
+ int exit_id_count = 0;
+ uint16_t exit_cause_ids[16];
+ hwaddr len;
+
+ len = vcpu->runbufout.size;
+ gsb = address_space_map(CPU(cpu)->as, vcpu->runbufout.addr, &len, true,
+ MEMTXATTRS_UNSPECIFIED);
+ if (!gsb || len != vcpu->runbufout.size) {
+ address_space_unmap(CPU(cpu)->as, gsb, len, true, len);
+ *r3 = H_P2;
+ return;
+ }
+
+ exit_id_count = get_exit_ids(*r3, exit_cause_ids);
+
+ /* Create a buffer of elements to send back */
+ gsb->num_elements = cpu_to_be32(exit_id_count);
+ element = gsb->elements;
+ for (int i = 0; i < exit_id_count; i++) {
+ type = guest_state_element_type_find(exit_cause_ids[i]);
+ assert(type);
+ element->id = cpu_to_be16(exit_cause_ids[i]);
+ element->size = cpu_to_be16(type->size);
+ element = guest_state_element_next(element, NULL, NULL);
+ }
+ gsr.gsb = gsb;
+ gsr.len = VCPU_OUT_BUF_MIN_SZ;
+ gsr.flags = 0; /* get + never guest wide */
+ getset_state(guest, vcpuid, &gsr);
+
+ address_space_unmap(CPU(cpu)->as, gsb, len, true, len);
+ return;
+}
+
+static
+void spapr_exit_nested_papr(SpaprMachineState *spapr, PowerPCCPU *cpu, int excp)
+{
+ CPUPPCState *env = &cpu->env;
+ CPUState *cs = CPU(cpu);
+ SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu);
+ target_ulong r3_return = env->excp_vectors[excp]; /* hcall return value */
+ target_ulong lpid = 0, vcpuid = 0;
+ struct SpaprMachineStateNestedGuestVcpu *vcpu = NULL;
+ struct SpaprMachineStateNestedGuest *guest = NULL;
+
+ lpid = spapr_cpu->nested_host_state->gpr[5];
+ vcpuid = spapr_cpu->nested_host_state->gpr[6];
+ guest = spapr_get_nested_guest(spapr, lpid);
+ assert(guest);
+ spapr_nested_vcpu_check(guest, vcpuid, false);
+ vcpu = &guest->vcpus[vcpuid];
+
+ exit_nested_store_l2(cpu, excp, vcpu);
+ /* do the output buffer for run_vcpu*/
+ exit_process_output_buffer(cpu, guest, vcpuid, &r3_return);
+
+ assert(env->spr[SPR_LPIDR] != 0);
+ nested_load_state(cpu, spapr_cpu->nested_host_state);
+ cpu_ppc_decrease_tb_by_offset(env, vcpu->tb_offset);
+ env->gpr[3] = H_SUCCESS;
+ env->gpr[4] = r3_return;
+ nested_post_load_state(env, cs);
+ cpu_ppc_hdecr_exit(env);
+
+ spapr_cpu->in_nested = false;
+ g_free(spapr_cpu->nested_host_state);
+ spapr_cpu->nested_host_state = NULL;
+}
+
+void spapr_exit_nested(PowerPCCPU *cpu, int excp)
+{
+ SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+ SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu);
+
+ assert(spapr_cpu->in_nested);
+ if (spapr_nested_api(spapr) == NESTED_API_KVM_HV) {
+ spapr_exit_nested_hv(cpu, excp);
+ } else if (spapr_nested_api(spapr) == NESTED_API_PAPR) {
+ spapr_exit_nested_papr(spapr, cpu, excp);
+ } else {
+ g_assert_not_reached();
+ }
+}
+
+static void nested_papr_load_l2(PowerPCCPU *cpu,
+ CPUPPCState *env,
+ SpaprMachineStateNestedGuestVcpu *vcpu,
+ target_ulong now)
+{
+ PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
+ target_ulong lpcr, lpcr_mask, hdec;
+ lpcr_mask = LPCR_DPFD | LPCR_ILE | LPCR_AIL | LPCR_LD | LPCR_MER;
+
+ assert(vcpu);
+ assert(sizeof(env->gpr) == sizeof(vcpu->state.gpr));
+ nested_load_state(cpu, &vcpu->state);
+ lpcr = (env->spr[SPR_LPCR] & ~lpcr_mask) |
+ (vcpu->state.lpcr & lpcr_mask);
+ lpcr |= LPCR_HR | LPCR_UPRT | LPCR_GTSE | LPCR_HVICE | LPCR_HDICE;
+ lpcr &= ~LPCR_LPES0;
+ env->spr[SPR_LPCR] = lpcr & pcc->lpcr_mask;
+
+ hdec = vcpu->hdecr_expiry_tb - now;
+ cpu_ppc_store_decr(env, vcpu->state.dec_expiry_tb - now);
+ cpu_ppc_hdecr_init(env);
+ cpu_ppc_store_hdecr(env, hdec);
+
+ cpu_ppc_increase_tb_by_offset(env, vcpu->tb_offset);
+}
+
+static void nested_papr_run_vcpu(PowerPCCPU *cpu,
+ uint64_t lpid,
+ SpaprMachineStateNestedGuestVcpu *vcpu)
+{
+ SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+ CPUPPCState *env = &cpu->env;
+ CPUState *cs = CPU(cpu);
+ SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu);
+ target_ulong now = cpu_ppc_load_tbl(env);
+
+ assert(env->spr[SPR_LPIDR] == 0);
+ assert(spapr->nested.api); /* ensure API version is initialized */
+ spapr_cpu->nested_host_state = g_try_new(struct nested_ppc_state, 1);
+ assert(spapr_cpu->nested_host_state);
+ nested_save_state(spapr_cpu->nested_host_state, cpu);
+ spapr_cpu->nested_host_state->dec_expiry_tb = now - cpu_ppc_load_decr(env);
+ nested_papr_load_l2(cpu, env, vcpu, now);
+ env->spr[SPR_LPIDR] = lpid; /* post load l2 */
+
+ spapr_cpu->in_nested = true;
+ nested_post_load_state(env, cs);
+}
+
+static target_ulong h_guest_run_vcpu(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong opcode,
+ target_ulong *args)
+{
+ CPUPPCState *env = &cpu->env;
+ target_ulong flags = args[0];
+ target_ulong lpid = args[1];
+ target_ulong vcpuid = args[2];
+ struct SpaprMachineStateNestedGuestVcpu *vcpu;
+ struct guest_state_request gsr;
+ SpaprMachineStateNestedGuest *guest;
+ target_ulong rc;
+
+ if (flags) /* don't handle any flags for now */
+ return H_PARAMETER;
+
+ guest = spapr_get_nested_guest(spapr, lpid);
+ if (!guest) {
+ return H_P2;
+ }
+ if (!spapr_nested_vcpu_check(guest, vcpuid, true)) {
+ return H_P3;
+ }
+
+ if (guest->parttbl[0] == 0) {
+ /* At least need a partition scoped radix tree */
+ return H_NOT_AVAILABLE;
+ }
+
+ vcpu = &guest->vcpus[vcpuid];
+
+ /* Read run_vcpu input buffer to update state */
+ gsr.buf = vcpu->runbufin.addr;
+ gsr.len = vcpu->runbufin.size;
+ gsr.flags = GUEST_STATE_REQUEST_SET; /* Thread wide + writing */
+ rc = map_and_getset_state(cpu, guest, vcpuid, &gsr);
+ if (rc == H_SUCCESS) {
+ nested_papr_run_vcpu(cpu, lpid, vcpu);
+ } else {
+ env->gpr[3] = rc;
+ }
+ return env->gpr[3];
+}
+
+void spapr_register_nested_hv(void)
{
spapr_register_hypercall(KVMPPC_H_SET_PARTITION_TABLE, h_set_ptbl);
spapr_register_hypercall(KVMPPC_H_ENTER_NESTED, h_enter_nested);
spapr_register_hypercall(KVMPPC_H_TLB_INVALIDATE, h_tlb_invalidate);
spapr_register_hypercall(KVMPPC_H_COPY_TOFROM_GUEST, h_copy_tofrom_guest);
}
+
+void spapr_unregister_nested_hv(void)
+{
+ spapr_unregister_hypercall(KVMPPC_H_SET_PARTITION_TABLE);
+ spapr_unregister_hypercall(KVMPPC_H_ENTER_NESTED);
+ spapr_unregister_hypercall(KVMPPC_H_TLB_INVALIDATE);
+ spapr_unregister_hypercall(KVMPPC_H_COPY_TOFROM_GUEST);
+}
+
+void spapr_register_nested_papr(void)
+{
+ spapr_register_hypercall(H_GUEST_GET_CAPABILITIES,
+ h_guest_get_capabilities);
+ spapr_register_hypercall(H_GUEST_SET_CAPABILITIES,
+ h_guest_set_capabilities);
+ spapr_register_hypercall(H_GUEST_CREATE, h_guest_create);
+ spapr_register_hypercall(H_GUEST_DELETE, h_guest_delete);
+ spapr_register_hypercall(H_GUEST_CREATE_VCPU, h_guest_create_vcpu);
+ spapr_register_hypercall(H_GUEST_SET_STATE, h_guest_set_state);
+ spapr_register_hypercall(H_GUEST_GET_STATE, h_guest_get_state);
+ spapr_register_hypercall(H_GUEST_RUN_VCPU, h_guest_run_vcpu);
+}
+
+void spapr_unregister_nested_papr(void)
+{
+ spapr_unregister_hypercall(H_GUEST_GET_CAPABILITIES);
+ spapr_unregister_hypercall(H_GUEST_SET_CAPABILITIES);
+ spapr_unregister_hypercall(H_GUEST_CREATE);
+ spapr_unregister_hypercall(H_GUEST_DELETE);
+ spapr_unregister_hypercall(H_GUEST_CREATE_VCPU);
+ spapr_unregister_hypercall(H_GUEST_SET_STATE);
+ spapr_unregister_hypercall(H_GUEST_GET_STATE);
+ spapr_unregister_hypercall(H_GUEST_RUN_VCPU);
+}
+
#else
void spapr_exit_nested(PowerPCCPU *cpu, int excp)
{
g_assert_not_reached();
}
-void spapr_register_nested(void)
+void spapr_register_nested_hv(void)
{
/* DO NOTHING */
}
+
+void spapr_unregister_nested_hv(void)
+{
+ /* DO NOTHING */
+}
+
+bool spapr_get_pate_nested_hv(SpaprMachineState *spapr, PowerPCCPU *cpu,
+ target_ulong lpid, ppc_v3_pate_t *entry)
+{
+ return false;
+}
+
+bool spapr_get_pate_nested_papr(SpaprMachineState *spapr, PowerPCCPU *cpu,
+ target_ulong lpid, ppc_v3_pate_t *entry)
+{
+ return false;
+}
+
+void spapr_register_nested_papr(void)
+{
+ /* DO NOTHING */
+}
+
+void spapr_unregister_nested_papr(void)
+{
+ /* DO NOTHING */
+}
+
+void spapr_nested_gsb_init(void)
+{
+ /* DO NOTHING */
+}
+
#endif
diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h
index af4cd7a8b8..8589f3291e 100644
--- a/include/hw/ppc/pnv_chip.h
+++ b/include/hw/ppc/pnv_chip.h
@@ -147,7 +147,7 @@ struct PnvChipClass {
DeviceRealize parent_realize;
- uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
+ uint32_t (*chip_pir)(PnvChip *chip, uint32_t core_id, uint32_t thread_id);
void (*intc_create)(PnvChip *chip, PowerPCCPU *cpu, Error **errp);
void (*intc_reset)(PnvChip *chip, PowerPCCPU *cpu);
void (*intc_destroy)(PnvChip *chip, PowerPCCPU *cpu);
diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h
index 4db21229a6..c6d62fd145 100644
--- a/include/hw/ppc/pnv_core.h
+++ b/include/hw/ppc/pnv_core.h
@@ -36,6 +36,7 @@ struct PnvCore {
/*< public >*/
PowerPCCPU **threads;
uint32_t pir;
+ uint32_t hwid;
uint64_t hrmor;
PnvChip *chip;
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index 5b5ba9ef77..4aaf23d28f 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -12,6 +12,7 @@
#include "hw/ppc/spapr_xive.h" /* For SpaprXive */
#include "hw/ppc/xics.h" /* For ICSState */
#include "hw/ppc/spapr_tpm_proxy.h"
+#include "hw/ppc/spapr_nested.h" /* For SpaprMachineStateNested */
struct SpaprVioBus;
struct SpaprPhbState;
@@ -80,8 +81,10 @@ typedef enum {
#define SPAPR_CAP_RPT_INVALIDATE 0x0B
/* Support for AIL modes */
#define SPAPR_CAP_AIL_MODE_3 0x0C
+/* Nested PAPR */
+#define SPAPR_CAP_NESTED_PAPR 0x0D
/* Num Caps */
-#define SPAPR_CAP_NUM (SPAPR_CAP_AIL_MODE_3 + 1)
+#define SPAPR_CAP_NUM (SPAPR_CAP_NESTED_PAPR + 1)
/*
* Capability Values
@@ -213,7 +216,7 @@ struct SpaprMachineState {
uint32_t vsmt; /* Virtual SMT mode (KVM's "core stride") */
/* Nested HV support (TCG only) */
- uint64_t nested_ptcr;
+ SpaprMachineStateNested nested;
Notifier epow_notifier;
QTAILQ_HEAD(, SpaprEventLogEntry) pending_events;
@@ -363,6 +366,9 @@ struct SpaprMachineState {
#define H_NOOP -63
#define H_UNSUPPORTED -67
#define H_OVERLAP -68
+#define H_STATE -75
+#define H_IN_USE -77
+#define H_INVALID_ELEMENT_VALUE -81
#define H_UNSUPPORTED_FLAG -256
#define H_MULTI_THREADS_ACTIVE -9005
@@ -582,8 +588,16 @@ struct SpaprMachineState {
#define H_RPT_INVALIDATE 0x448
#define H_SCM_FLUSH 0x44C
#define H_WATCHDOG 0x45C
+#define H_GUEST_GET_CAPABILITIES 0x460
+#define H_GUEST_SET_CAPABILITIES 0x464
+#define H_GUEST_CREATE 0x470
+#define H_GUEST_CREATE_VCPU 0x474
+#define H_GUEST_GET_STATE 0x478
+#define H_GUEST_SET_STATE 0x47C
+#define H_GUEST_RUN_VCPU 0x480
+#define H_GUEST_DELETE 0x488
-#define MAX_HCALL_OPCODE H_WATCHDOG
+#define MAX_HCALL_OPCODE H_GUEST_DELETE
/* The hcalls above are standardized in PAPR and implemented by pHyp
* as well.
@@ -631,6 +645,7 @@ typedef target_ulong (*spapr_hcall_fn)(PowerPCCPU *cpu, SpaprMachineState *sm,
target_ulong *args);
void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn);
+void spapr_unregister_hypercall(target_ulong opcode);
target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode,
target_ulong *args);
@@ -984,6 +999,7 @@ extern const VMStateDescription vmstate_spapr_cap_sbbc;
extern const VMStateDescription vmstate_spapr_cap_ibs;
extern const VMStateDescription vmstate_spapr_cap_hpt_maxpagesize;
extern const VMStateDescription vmstate_spapr_cap_nested_kvm_hv;
+extern const VMStateDescription vmstate_spapr_cap_nested_papr;
extern const VMStateDescription vmstate_spapr_cap_large_decr;
extern const VMStateDescription vmstate_spapr_cap_ccf_assist;
extern const VMStateDescription vmstate_spapr_cap_fwnmi;
@@ -1028,5 +1044,10 @@ void spapr_vof_client_dt_finalize(SpaprMachineState *spapr, void *fdt);
/* H_WATCHDOG */
void spapr_watchdog_init(SpaprMachineState *spapr);
+void spapr_register_nested_hv(void);
+void spapr_unregister_nested_hv(void);
+void spapr_nested_reset(SpaprMachineState *spapr);
+void spapr_register_nested_papr(void);
+void spapr_unregister_nested_papr(void);
#endif /* HW_SPAPR_H */
diff --git a/include/hw/ppc/spapr_nested.h b/include/hw/ppc/spapr_nested.h
index d312a5d61d..93ef14adcc 100644
--- a/include/hw/ppc/spapr_nested.h
+++ b/include/hw/ppc/spapr_nested.h
@@ -3,6 +3,348 @@
#include "target/ppc/cpu.h"
+/* Guest State Buffer Element IDs */
+#define GSB_HV_VCPU_IGNORED_ID 0x0000 /* An element whose value is ignored */
+#define GSB_HV_VCPU_STATE_SIZE 0x0001 /* HV internal format VCPU state size */
+#define GSB_VCPU_OUT_BUF_MIN_SZ 0x0002 /* Min size of the Run VCPU o/p buffer */
+#define GSB_VCPU_LPVR 0x0003 /* Logical PVR */
+#define GSB_TB_OFFSET 0x0004 /* Timebase Offset */
+#define GSB_PART_SCOPED_PAGETBL 0x0005 /* Partition Scoped Page Table */
+#define GSB_PROCESS_TBL 0x0006 /* Process Table */
+ /* RESERVED 0x0007 - 0x0BFF */
+#define GSB_VCPU_IN_BUFFER 0x0C00 /* Run VCPU Input Buffer */
+#define GSB_VCPU_OUT_BUFFER 0x0C01 /* Run VCPU Out Buffer */
+#define GSB_VCPU_VPA 0x0C02 /* HRA to Guest VCPU VPA */
+ /* RESERVED 0x0C03 - 0x0FFF */
+#define GSB_VCPU_GPR0 0x1000
+#define GSB_VCPU_GPR1 0x1001
+#define GSB_VCPU_GPR2 0x1002
+#define GSB_VCPU_GPR3 0x1003
+#define GSB_VCPU_GPR4 0x1004
+#define GSB_VCPU_GPR5 0x1005
+#define GSB_VCPU_GPR6 0x1006
+#define GSB_VCPU_GPR7 0x1007
+#define GSB_VCPU_GPR8 0x1008
+#define GSB_VCPU_GPR9 0x1009
+#define GSB_VCPU_GPR10 0x100A
+#define GSB_VCPU_GPR11 0x100B
+#define GSB_VCPU_GPR12 0x100C
+#define GSB_VCPU_GPR13 0x100D
+#define GSB_VCPU_GPR14 0x100E
+#define GSB_VCPU_GPR15 0x100F
+#define GSB_VCPU_GPR16 0x1010
+#define GSB_VCPU_GPR17 0x1011
+#define GSB_VCPU_GPR18 0x1012
+#define GSB_VCPU_GPR19 0x1013
+#define GSB_VCPU_GPR20 0x1014
+#define GSB_VCPU_GPR21 0x1015
+#define GSB_VCPU_GPR22 0x1016
+#define GSB_VCPU_GPR23 0x1017
+#define GSB_VCPU_GPR24 0x1018
+#define GSB_VCPU_GPR25 0x1019
+#define GSB_VCPU_GPR26 0x101A
+#define GSB_VCPU_GPR27 0x101B
+#define GSB_VCPU_GPR28 0x101C
+#define GSB_VCPU_GPR29 0x101D
+#define GSB_VCPU_GPR30 0x101E
+#define GSB_VCPU_GPR31 0x101F
+#define GSB_VCPU_HDEC_EXPIRY_TB 0x1020
+#define GSB_VCPU_SPR_NIA 0x1021
+#define GSB_VCPU_SPR_MSR 0x1022
+#define GSB_VCPU_SPR_LR 0x1023
+#define GSB_VCPU_SPR_XER 0x1024
+#define GSB_VCPU_SPR_CTR 0x1025
+#define GSB_VCPU_SPR_CFAR 0x1026
+#define GSB_VCPU_SPR_SRR0 0x1027
+#define GSB_VCPU_SPR_SRR1 0x1028
+#define GSB_VCPU_SPR_DAR 0x1029
+#define GSB_VCPU_DEC_EXPIRE_TB 0x102A
+#define GSB_VCPU_SPR_VTB 0x102B
+#define GSB_VCPU_SPR_LPCR 0x102C
+#define GSB_VCPU_SPR_HFSCR 0x102D
+#define GSB_VCPU_SPR_FSCR 0x102E
+#define GSB_VCPU_SPR_FPSCR 0x102F
+#define GSB_VCPU_SPR_DAWR0 0x1030
+#define GSB_VCPU_SPR_DAWR1 0x1031
+#define GSB_VCPU_SPR_CIABR 0x1032
+#define GSB_VCPU_SPR_PURR 0x1033
+#define GSB_VCPU_SPR_SPURR 0x1034
+#define GSB_VCPU_SPR_IC 0x1035
+#define GSB_VCPU_SPR_SPRG0 0x1036
+#define GSB_VCPU_SPR_SPRG1 0x1037
+#define GSB_VCPU_SPR_SPRG2 0x1038
+#define GSB_VCPU_SPR_SPRG3 0x1039
+#define GSB_VCPU_SPR_PPR 0x103A
+#define GSB_VCPU_SPR_MMCR0 0x103B
+#define GSB_VCPU_SPR_MMCR1 0x103C
+#define GSB_VCPU_SPR_MMCR2 0x103D
+#define GSB_VCPU_SPR_MMCR3 0x103E
+#define GSB_VCPU_SPR_MMCRA 0x103F
+#define GSB_VCPU_SPR_SIER 0x1040
+#define GSB_VCPU_SPR_SIER2 0x1041
+#define GSB_VCPU_SPR_SIER3 0x1042
+#define GSB_VCPU_SPR_BESCR 0x1043
+#define GSB_VCPU_SPR_EBBHR 0x1044
+#define GSB_VCPU_SPR_EBBRR 0x1045
+#define GSB_VCPU_SPR_AMR 0x1046
+#define GSB_VCPU_SPR_IAMR 0x1047
+#define GSB_VCPU_SPR_AMOR 0x1048
+#define GSB_VCPU_SPR_UAMOR 0x1049
+#define GSB_VCPU_SPR_SDAR 0x104A
+#define GSB_VCPU_SPR_SIAR 0x104B
+#define GSB_VCPU_SPR_DSCR 0x104C
+#define GSB_VCPU_SPR_TAR 0x104D
+#define GSB_VCPU_SPR_DEXCR 0x104E
+#define GSB_VCPU_SPR_HDEXCR 0x104F
+#define GSB_VCPU_SPR_HASHKEYR 0x1050
+#define GSB_VCPU_SPR_HASHPKEYR 0x1051
+#define GSB_VCPU_SPR_CTRL 0x1052
+ /* RESERVED 0x1053 - 0x1FFF */
+#define GSB_VCPU_SPR_CR 0x2000
+#define GSB_VCPU_SPR_PIDR 0x2001
+#define GSB_VCPU_SPR_DSISR 0x2002
+#define GSB_VCPU_SPR_VSCR 0x2003
+#define GSB_VCPU_SPR_VRSAVE 0x2004
+#define GSB_VCPU_SPR_DAWRX0 0x2005
+#define GSB_VCPU_SPR_DAWRX1 0x2006
+#define GSB_VCPU_SPR_PMC1 0x2007
+#define GSB_VCPU_SPR_PMC2 0x2008
+#define GSB_VCPU_SPR_PMC3 0x2009
+#define GSB_VCPU_SPR_PMC4 0x200A
+#define GSB_VCPU_SPR_PMC5 0x200B
+#define GSB_VCPU_SPR_PMC6 0x200C
+#define GSB_VCPU_SPR_WORT 0x200D
+#define GSB_VCPU_SPR_PSPB 0x200E
+ /* RESERVED 0x200F - 0x2FFF */
+#define GSB_VCPU_SPR_VSR0 0x3000
+#define GSB_VCPU_SPR_VSR1 0x3001
+#define GSB_VCPU_SPR_VSR2 0x3002
+#define GSB_VCPU_SPR_VSR3 0x3003
+#define GSB_VCPU_SPR_VSR4 0x3004
+#define GSB_VCPU_SPR_VSR5 0x3005
+#define GSB_VCPU_SPR_VSR6 0x3006
+#define GSB_VCPU_SPR_VSR7 0x3007
+#define GSB_VCPU_SPR_VSR8 0x3008
+#define GSB_VCPU_SPR_VSR9 0x3009
+#define GSB_VCPU_SPR_VSR10 0x300A
+#define GSB_VCPU_SPR_VSR11 0x300B
+#define GSB_VCPU_SPR_VSR12 0x300C
+#define GSB_VCPU_SPR_VSR13 0x300D
+#define GSB_VCPU_SPR_VSR14 0x300E
+#define GSB_VCPU_SPR_VSR15 0x300F
+#define GSB_VCPU_SPR_VSR16 0x3010
+#define GSB_VCPU_SPR_VSR17 0x3011
+#define GSB_VCPU_SPR_VSR18 0x3012
+#define GSB_VCPU_SPR_VSR19 0x3013
+#define GSB_VCPU_SPR_VSR20 0x3014
+#define GSB_VCPU_SPR_VSR21 0x3015
+#define GSB_VCPU_SPR_VSR22 0x3016
+#define GSB_VCPU_SPR_VSR23 0x3017
+#define GSB_VCPU_SPR_VSR24 0x3018
+#define GSB_VCPU_SPR_VSR25 0x3019
+#define GSB_VCPU_SPR_VSR26 0x301A
+#define GSB_VCPU_SPR_VSR27 0x301B
+#define GSB_VCPU_SPR_VSR28 0x301C
+#define GSB_VCPU_SPR_VSR29 0x301D
+#define GSB_VCPU_SPR_VSR30 0x301E
+#define GSB_VCPU_SPR_VSR31 0x301F
+#define GSB_VCPU_SPR_VSR32 0x3020
+#define GSB_VCPU_SPR_VSR33 0x3021
+#define GSB_VCPU_SPR_VSR34 0x3022
+#define GSB_VCPU_SPR_VSR35 0x3023
+#define GSB_VCPU_SPR_VSR36 0x3024
+#define GSB_VCPU_SPR_VSR37 0x3025
+#define GSB_VCPU_SPR_VSR38 0x3026
+#define GSB_VCPU_SPR_VSR39 0x3027
+#define GSB_VCPU_SPR_VSR40 0x3028
+#define GSB_VCPU_SPR_VSR41 0x3029
+#define GSB_VCPU_SPR_VSR42 0x302A
+#define GSB_VCPU_SPR_VSR43 0x302B
+#define GSB_VCPU_SPR_VSR44 0x302C
+#define GSB_VCPU_SPR_VSR45 0x302D
+#define GSB_VCPU_SPR_VSR46 0x302E
+#define GSB_VCPU_SPR_VSR47 0x302F
+#define GSB_VCPU_SPR_VSR48 0x3030
+#define GSB_VCPU_SPR_VSR49 0x3031
+#define GSB_VCPU_SPR_VSR50 0x3032
+#define GSB_VCPU_SPR_VSR51 0x3033
+#define GSB_VCPU_SPR_VSR52 0x3034
+#define GSB_VCPU_SPR_VSR53 0x3035
+#define GSB_VCPU_SPR_VSR54 0x3036
+#define GSB_VCPU_SPR_VSR55 0x3037
+#define GSB_VCPU_SPR_VSR56 0x3038
+#define GSB_VCPU_SPR_VSR57 0x3039
+#define GSB_VCPU_SPR_VSR58 0x303A
+#define GSB_VCPU_SPR_VSR59 0x303B
+#define GSB_VCPU_SPR_VSR60 0x303C
+#define GSB_VCPU_SPR_VSR61 0x303D
+#define GSB_VCPU_SPR_VSR62 0x303E
+#define GSB_VCPU_SPR_VSR63 0x303F
+ /* RESERVED 0x3040 - 0xEFFF */
+#define GSB_VCPU_SPR_HDAR 0xF000
+#define GSB_VCPU_SPR_HDSISR 0xF001
+#define GSB_VCPU_SPR_HEIR 0xF002
+#define GSB_VCPU_SPR_ASDR 0xF003
+/* End of list of Guest State Buffer Element IDs */
+#define GSB_LAST GSB_VCPU_SPR_ASDR
+
+typedef struct SpaprMachineStateNested {
+ uint64_t ptcr;
+ uint8_t api;
+#define NESTED_API_KVM_HV 1
+#define NESTED_API_PAPR 2
+ bool capabilities_set;
+ uint32_t pvr_base;
+ GHashTable *guests;
+} SpaprMachineStateNested;
+
+typedef struct SpaprMachineStateNestedGuest {
+ uint32_t pvr_logical;
+ unsigned long nr_vcpus;
+ uint64_t parttbl[2];
+ uint64_t tb_offset;
+ struct SpaprMachineStateNestedGuestVcpu *vcpus;
+} SpaprMachineStateNestedGuest;
+
+/* Nested PAPR API related macros */
+#define H_GUEST_CAPABILITIES_COPY_MEM 0x8000000000000000
+#define H_GUEST_CAPABILITIES_P9_MODE 0x4000000000000000
+#define H_GUEST_CAPABILITIES_P10_MODE 0x2000000000000000
+#define H_GUEST_CAP_VALID_MASK (H_GUEST_CAPABILITIES_P10_MODE | \
+ H_GUEST_CAPABILITIES_P9_MODE)
+#define H_GUEST_CAP_COPY_MEM_BMAP 0
+#define H_GUEST_CAP_P9_MODE_BMAP 1
+#define H_GUEST_CAP_P10_MODE_BMAP 2
+#define PAPR_NESTED_GUEST_MAX 4096
+#define H_GUEST_DELETE_ALL_FLAG 0x8000000000000000ULL
+#define PAPR_NESTED_GUEST_VCPU_MAX 2048
+#define VCPU_OUT_BUF_MIN_SZ 0x80ULL
+#define HVMASK_DEFAULT 0xffffffffffffffff
+#define HVMASK_LPCR 0x0070000003820800
+#define HVMASK_MSR 0xEBFFFFFFFFBFEFFF
+#define HVMASK_HDEXCR 0x00000000FFFFFFFF
+#define HVMASK_TB_OFFSET 0x000000FFFFFFFFFF
+#define GSB_MAX_BUF_SIZE (1024 * 1024)
+#define H_GUEST_GETSET_STATE_FLAG_GUEST_WIDE 0x8000000000000000
+#define GUEST_STATE_REQUEST_GUEST_WIDE 0x1
+#define GUEST_STATE_REQUEST_SET 0x2
+
+/*
+ * As per ISA v3.1B, following bits are reserved:
+ * 0:2
+ * 4:57 (ISA mentions bit 58 as well but it should be used for P10)
+ * 61:63 (hence, haven't included PCR bits for v2.06 and v2.05
+ * in LOW BITS)
+ */
+#define PCR_LOW_BITS (PCR_COMPAT_3_10 | PCR_COMPAT_3_00)
+#define HVMASK_PCR (~PCR_LOW_BITS)
+
+#define GUEST_STATE_ELEMENT(i, sz, s, f, ptr, c) { \
+ .id = (i), \
+ .size = (sz), \
+ .location = ptr, \
+ .offset = offsetof(struct s, f), \
+ .copy = (c) \
+}
+
+#define GSBE_NESTED(i, sz, f, c) { \
+ .id = (i), \
+ .size = (sz), \
+ .location = get_guest_ptr, \
+ .offset = offsetof(struct SpaprMachineStateNestedGuest, f),\
+ .copy = (c), \
+ .mask = HVMASK_DEFAULT \
+}
+
+#define GSBE_NESTED_MSK(i, sz, f, c, m) { \
+ .id = (i), \
+ .size = (sz), \
+ .location = get_guest_ptr, \
+ .offset = offsetof(struct SpaprMachineStateNestedGuest, f),\
+ .copy = (c), \
+ .mask = (m) \
+}
+
+#define GSBE_NESTED_VCPU(i, sz, f, c) { \
+ .id = (i), \
+ .size = (sz), \
+ .location = get_vcpu_ptr, \
+ .offset = offsetof(struct SpaprMachineStateNestedGuestVcpu, f),\
+ .copy = (c), \
+ .mask = HVMASK_DEFAULT \
+}
+
+#define GUEST_STATE_ELEMENT_NOP(i, sz) { \
+ .id = (i), \
+ .size = (sz), \
+ .location = NULL, \
+ .offset = 0, \
+ .copy = NULL, \
+ .mask = HVMASK_DEFAULT \
+}
+
+#define GUEST_STATE_ELEMENT_NOP_DW(i) \
+ GUEST_STATE_ELEMENT_NOP(i, 8)
+#define GUEST_STATE_ELEMENT_NOP_W(i) \
+ GUEST_STATE_ELEMENT_NOP(i, 4)
+
+#define GUEST_STATE_ELEMENT_BASE(i, s, c) { \
+ .id = (i), \
+ .size = (s), \
+ .location = get_vcpu_state_ptr, \
+ .offset = 0, \
+ .copy = (c), \
+ .mask = HVMASK_DEFAULT \
+ }
+
+#define GUEST_STATE_ELEMENT_OFF(i, s, f, c) { \
+ .id = (i), \
+ .size = (s), \
+ .location = get_vcpu_state_ptr, \
+ .offset = offsetof(struct nested_ppc_state, f), \
+ .copy = (c), \
+ .mask = HVMASK_DEFAULT \
+ }
+
+#define GUEST_STATE_ELEMENT_MSK(i, s, f, c, m) { \
+ .id = (i), \
+ .size = (s), \
+ .location = get_vcpu_state_ptr, \
+ .offset = offsetof(struct nested_ppc_state, f), \
+ .copy = (c), \
+ .mask = (m) \
+ }
+
+#define GUEST_STATE_ELEMENT_ENV_QW(i, f) \
+ GUEST_STATE_ELEMENT_OFF(i, 16, f, copy_state_16to16)
+#define GUEST_STATE_ELEMENT_ENV_DW(i, f) \
+ GUEST_STATE_ELEMENT_OFF(i, 8, f, copy_state_8to8)
+#define GUEST_STATE_ELEMENT_ENV_W(i, f) \
+ GUEST_STATE_ELEMENT_OFF(i, 4, f, copy_state_4to8)
+#define GUEST_STATE_ELEMENT_ENV_WW(i, f) \
+ GUEST_STATE_ELEMENT_OFF(i, 4, f, copy_state_4to4)
+#define GSE_ENV_DWM(i, f, m) \
+ GUEST_STATE_ELEMENT_MSK(i, 8, f, copy_state_8to8, m)
+
+struct guest_state_element {
+ uint16_t id;
+ uint16_t size;
+ uint8_t value[];
+} QEMU_PACKED;
+
+struct guest_state_buffer {
+ uint32_t num_elements;
+ struct guest_state_element elements[];
+} QEMU_PACKED;
+
+/* Actual buffer plus some metadata about the request */
+struct guest_state_request {
+ struct guest_state_buffer *gsb;
+ int64_t buf;
+ int64_t len;
+ uint16_t flags;
+};
+
/*
* Register state for entering a nested guest with H_ENTER_NESTED.
* New member must be added at the end.
@@ -93,9 +435,90 @@ struct nested_ppc_state {
uint64_t ppr;
int64_t tb_offset;
+ /* Nested PAPR API */
+ uint64_t amor;
+ uint64_t dawr0;
+ uint64_t dawrx0;
+ uint64_t ciabr;
+ uint64_t purr;
+ uint64_t spurr;
+ uint64_t ic;
+ uint64_t vtb;
+ uint64_t hdar;
+ uint64_t hdsisr;
+ uint64_t heir;
+ uint64_t asdr;
+ uint64_t dawr1;
+ uint64_t dawrx1;
+ uint64_t dexcr;
+ uint64_t hdexcr;
+ uint64_t hashkeyr;
+ uint64_t hashpkeyr;
+ ppc_vsr_t vsr[64] QEMU_ALIGNED(16);
+ uint64_t ebbhr;
+ uint64_t tar;
+ uint64_t ebbrr;
+ uint64_t bescr;
+ uint64_t iamr;
+ uint64_t amr;
+ uint64_t uamor;
+ uint64_t dscr;
+ uint64_t fscr;
+ uint64_t pspb;
+ uint64_t ctrl;
+ uint64_t vrsave;
+ uint64_t dar;
+ uint64_t dsisr;
+ uint64_t pmc1;
+ uint64_t pmc2;
+ uint64_t pmc3;
+ uint64_t pmc4;
+ uint64_t pmc5;
+ uint64_t pmc6;
+ uint64_t mmcr0;
+ uint64_t mmcr1;
+ uint64_t mmcr2;
+ uint64_t mmcra;
+ uint64_t sdar;
+ uint64_t siar;
+ uint64_t sier;
+ uint32_t vscr;
+ uint64_t fpscr;
+ int64_t dec_expiry_tb;
};
-void spapr_register_nested(void);
-void spapr_exit_nested(PowerPCCPU *cpu, int excp);
+struct SpaprMachineStateNestedGuestVcpuRunBuf {
+ uint64_t addr;
+ uint64_t size;
+};
+typedef struct SpaprMachineStateNestedGuestVcpu {
+ bool enabled;
+ struct nested_ppc_state state;
+ struct SpaprMachineStateNestedGuestVcpuRunBuf runbufin;
+ struct SpaprMachineStateNestedGuestVcpuRunBuf runbufout;
+ int64_t tb_offset;
+ uint64_t hdecr_expiry_tb;
+} SpaprMachineStateNestedGuestVcpu;
+
+struct guest_state_element_type {
+ uint16_t id;
+ int size;
+#define GUEST_STATE_ELEMENT_TYPE_FLAG_GUEST_WIDE 0x1
+#define GUEST_STATE_ELEMENT_TYPE_FLAG_READ_ONLY 0x2
+ uint16_t flags;
+ void *(*location)(SpaprMachineStateNestedGuest *, target_ulong);
+ size_t offset;
+ void (*copy)(void *, void *, bool);
+ uint64_t mask;
+};
+
+void spapr_exit_nested(PowerPCCPU *cpu, int excp);
+typedef struct SpaprMachineState SpaprMachineState;
+bool spapr_get_pate_nested_hv(SpaprMachineState *spapr, PowerPCCPU *cpu,
+ target_ulong lpid, ppc_v3_pate_t *entry);
+uint8_t spapr_nested_api(SpaprMachineState *spapr);
+void spapr_nested_gsb_init(void);
+bool spapr_get_pate_nested_papr(SpaprMachineState *spapr, PowerPCCPU *cpu,
+ target_ulong lpid, ppc_v3_pate_t *entry);
#endif /* HW_SPAPR_NESTED_H */
diff --git a/target/ppc/cpu-models.c b/target/ppc/cpu-models.c
index 36e465b390..f2301b43f7 100644
--- a/target/ppc/cpu-models.c
+++ b/target/ppc/cpu-models.c
@@ -728,14 +728,10 @@
"POWER8 v2.0")
POWERPC_DEF("power8nvl_v1.0", CPU_POWERPC_POWER8NVL_v10, POWER8,
"POWER8NVL v1.0")
- POWERPC_DEF("power9_v1.0", CPU_POWERPC_POWER9_DD1, POWER9,
- "POWER9 v1.0")
POWERPC_DEF("power9_v2.0", CPU_POWERPC_POWER9_DD20, POWER9,
"POWER9 v2.0")
POWERPC_DEF("power9_v2.2", CPU_POWERPC_POWER9_DD22, POWER9,
"POWER9 v2.2")
- POWERPC_DEF("power10_v1.0", CPU_POWERPC_POWER10_DD1, POWER10,
- "POWER10 v1.0")
POWERPC_DEF("power10_v2.0", CPU_POWERPC_POWER10_DD20, POWER10,
"POWER10 v2.0")
#endif /* defined (TARGET_PPC64) */
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 0133da4e07..67e6b2effd 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -1584,6 +1584,8 @@ uint64_t cpu_ppc_load_atbl(CPUPPCState *env);
uint32_t cpu_ppc_load_atbu(CPUPPCState *env);
void cpu_ppc_store_atbl(CPUPPCState *env, uint32_t value);
void cpu_ppc_store_atbu(CPUPPCState *env, uint32_t value);
+void cpu_ppc_increase_tb_by_offset(CPUPPCState *env, int64_t offset);
+void cpu_ppc_decrease_tb_by_offset(CPUPPCState *env, int64_t offset);
uint64_t cpu_ppc_load_vtb(CPUPPCState *env);
void cpu_ppc_store_vtb(CPUPPCState *env, uint64_t value);
bool ppc_decr_clear_on_delivery(CPUPPCState *env);
@@ -1755,9 +1757,11 @@ void ppc_compat_add_property(Object *obj, const char *name,
#define SPR_PSPB (0x09F)
#define SPR_DPDES (0x0B0)
#define SPR_DAWR0 (0x0B4)
+#define SPR_DAWR1 (0x0B5)
#define SPR_RPR (0x0BA)
#define SPR_CIABR (0x0BB)
#define SPR_DAWRX0 (0x0BC)
+#define SPR_DAWRX1 (0x0BD)
#define SPR_HFSCR (0x0BE)
#define SPR_VRSAVE (0x100)
#define SPR_USPRG0 (0x100)
@@ -1951,6 +1955,12 @@ void ppc_compat_add_property(Object *obj, const char *name,
#define SPR_BOOKE_TLB2CFG (0x2B2)
#define SPR_BOOKE_TLB3CFG (0x2B3)
#define SPR_BOOKE_EPR (0x2BE)
+#define SPR_POWER_USIER2 (0x2E0)
+#define SPR_POWER_USIER3 (0x2E1)
+#define SPR_POWER_UMMCR3 (0x2E2)
+#define SPR_POWER_SIER2 (0x2F0)
+#define SPR_POWER_SIER3 (0x2F1)
+#define SPR_POWER_MMCR3 (0x2F2)
#define SPR_PERF0 (0x300)
#define SPR_RCPU_MI_RBA0 (0x300)
#define SPR_MPC_MI_CTR (0x300)
@@ -2948,6 +2958,7 @@ static inline bool ppc_has_spr(PowerPCCPU *cpu, int spr)
}
#if !defined(CONFIG_USER_ONLY)
+/* Sort out endianness of interrupt. Depends on the CPU, HV mode, etc. */
static inline bool ppc_interrupts_little_endian(PowerPCCPU *cpu, bool hv)
{
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
index 1487e2e3c0..7e65f08147 100644
--- a/target/ppc/cpu_init.c
+++ b/target/ppc/cpu_init.c
@@ -5308,6 +5308,38 @@ static void register_power8_pmu_user_sprs(CPUPPCState *env)
0x00000000);
}
+static void register_power10_pmu_sup_sprs(CPUPPCState *env)
+{
+ spr_register_kvm(env, SPR_POWER_MMCR3, "MMCR3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ KVM_REG_PPC_MMCR3, 0x00000000);
+ spr_register_kvm(env, SPR_POWER_SIER2, "SIER2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ KVM_REG_PPC_SIER2, 0x00000000);
+ spr_register_kvm(env, SPR_POWER_SIER3, "SIER3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ KVM_REG_PPC_SIER3, 0x00000000);
+}
+
+static void register_power10_pmu_user_sprs(CPUPPCState *env)
+{
+ spr_register(env, SPR_POWER_UMMCR3, "UMMCR3",
+ &spr_read_generic, &spr_write_generic,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_POWER_USIER2, "USIER2",
+ &spr_read_generic, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_POWER_USIER3, "USIER3",
+ &spr_read_generic, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
static void register_power5p_ear_sprs(CPUPPCState *env)
{
/* External access control */
@@ -6350,10 +6382,7 @@ static bool ppc_pvr_match_power9(PowerPCCPUClass *pcc, uint32_t pvr, bool best)
return false;
}
- if ((pvr & 0x0f00) == 0x100) {
- /* DD1.x always matches power9_v1.0 */
- return true;
- } else if ((pvr & 0x0f00) == 0x200) {
+ if ((pvr & 0x0f00) == 0x200) {
if ((pvr & 0xf) < 2) {
/* DD2.0, DD2.1 match power9_v2.0 */
if ((pcc->pvr & 0xf) == 0) {
@@ -6505,6 +6534,8 @@ static void init_proc_POWER10(CPUPPCState *env)
register_power9_mmu_sprs(env);
register_power10_hash_sprs(env);
register_power10_dexcr_sprs(env);
+ register_power10_pmu_sup_sprs(env);
+ register_power10_pmu_user_sprs(env);
/* FIXME: Filter fields properly based on privilege level */
spr_register_kvm_hv(env, SPR_PSSCR, "PSSCR", NULL, NULL, NULL, NULL,
@@ -6536,7 +6567,7 @@ static bool ppc_pvr_match_power10(PowerPCCPUClass *pcc, uint32_t pvr, bool best)
}
if ((pvr & 0x0f00) == (pcc->pvr & 0x0f00)) {
- /* Major DD version matches to power10_v1.0 and power10_v2.0 */
+ /* Major DD version matches power10_v2.0 */
return true;
}
@@ -6576,11 +6607,10 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data)
PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 |
PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 |
PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 |
- PPC2_TM | PPC2_ISA300 | PPC2_PRCNTL | PPC2_ISA310 |
+ PPC2_ISA300 | PPC2_PRCNTL | PPC2_ISA310 |
PPC2_MEM_LWSYNC | PPC2_BCDA_ISA206;
pcc->msr_mask = (1ull << MSR_SF) |
(1ull << MSR_HV) |
- (1ull << MSR_TM) |
(1ull << MSR_VR) |
(1ull << MSR_VSX) |
(1ull << MSR_EE) |
@@ -6620,7 +6650,7 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data)
pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE |
POWERPC_FLAG_BE | POWERPC_FLAG_PMM |
POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR |
- POWERPC_FLAG_VSX | POWERPC_FLAG_TM | POWERPC_FLAG_SCV;
+ POWERPC_FLAG_VSX | POWERPC_FLAG_SCV;
pcc->l1_dcache_size = 0x8000;
pcc->l1_icache_size = 0x8000;
}
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index 6bb6fee8f6..80f584f933 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -35,7 +35,7 @@
/*****************************************************************************/
/* Exception processing */
-#if !defined(CONFIG_USER_ONLY)
+#ifndef CONFIG_USER_ONLY
static const char *powerpc_excp_name(int excp)
{
@@ -186,7 +186,7 @@ static void ppc_excp_debug_sw_tlb(CPUPPCState *env, int excp)
env->error_code);
}
-#if defined(TARGET_PPC64)
+#ifdef TARGET_PPC64
static int powerpc_reset_wakeup(CPUPPCState *env, int excp, target_ulong *msr)
{
/* We no longer are in a PM state */
@@ -380,7 +380,7 @@ static void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp, target_ulong msr,
}
}
}
-#endif
+#endif /* TARGET_PPC64 */
static void powerpc_reset_excp_state(PowerPCCPU *cpu)
{
@@ -403,9 +403,8 @@ static void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong vector,
* We don't use hreg_store_msr here as already have treated any
* special case that could occur. Just store MSR and update hflags
*
- * Note: We *MUST* not use hreg_store_msr() as-is anyway because it
- * will prevent setting of the HV bit which some exceptions might need
- * to do.
+ * Note: We *MUST* not use hreg_store_msr() as-is anyway because it will
+ * prevent setting of the HV bit which some exceptions might need to do.
*/
env->nip = vector;
env->msr = msr;
@@ -445,38 +444,26 @@ static void powerpc_mcheck_checkstop(CPUPPCState *env)
static void powerpc_excp_40x(PowerPCCPU *cpu, int excp)
{
- CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
target_ulong msr, new_msr, vector;
- int srr0, srr1;
+ int srr0 = SPR_SRR0, srr1 = SPR_SRR1;
/* new srr1 value excluding must-be-zero bits */
msr = env->msr & ~0x783f0000ULL;
- /*
- * new interrupt handler msr preserves existing ME unless
- * explicitly overridden.
- */
+ /* new interrupt handler msr preserves ME unless explicitly overridden */
new_msr = env->msr & (((target_ulong)1 << MSR_ME));
- /* target registers */
- srr0 = SPR_SRR0;
- srr1 = SPR_SRR1;
-
- /*
- * Hypervisor emulation assistance interrupt only exists on server
- * arch 2.05 server or later.
- */
+ /* HV emu assistance interrupt only exists on server arch 2.05 or later */
if (excp == POWERPC_EXCP_HV_EMU) {
excp = POWERPC_EXCP_PROGRAM;
}
vector = env->excp_vectors[excp];
if (vector == (target_ulong)-1ULL) {
- cpu_abort(cs, "Raised an exception without defined vector %d\n",
- excp);
+ cpu_abort(env_cpu(env),
+ "Raised an exception without defined vector %d\n", excp);
}
-
vector |= env->excp_prefix;
switch (excp) {
@@ -488,7 +475,6 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp)
powerpc_mcheck_checkstop(env);
/* machine check exceptions don't have ME set */
new_msr &= ~((target_ulong)1 << MSR_ME);
-
srr0 = SPR_40x_SRR2;
srr1 = SPR_40x_SRR3;
break;
@@ -523,7 +509,7 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp)
env->spr[SPR_40x_ESR] = ESR_PTR;
break;
default:
- cpu_abort(cs, "Invalid program exception %d. Aborting\n",
+ cpu_abort(env_cpu(env), "Invalid program exception %d. Aborting\n",
env->error_code);
break;
}
@@ -550,52 +536,41 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp)
trace_ppc_excp_print("PIT");
break;
case POWERPC_EXCP_DEBUG: /* Debug interrupt */
- cpu_abort(cs, "%s exception not implemented\n",
+ cpu_abort(env_cpu(env), "%s exception not implemented\n",
powerpc_excp_name(excp));
break;
default:
- cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
+ cpu_abort(env_cpu(env), "Invalid PowerPC exception %d. Aborting\n",
+ excp);
break;
}
- /* Save PC */
env->spr[srr0] = env->nip;
-
- /* Save MSR */
env->spr[srr1] = msr;
-
powerpc_set_excp_state(cpu, vector, new_msr);
}
static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp)
{
- CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
target_ulong msr, new_msr, vector;
/* new srr1 value excluding must-be-zero bits */
msr = env->msr & ~0x783f0000ULL;
- /*
- * new interrupt handler msr preserves existing ME unless
- * explicitly overridden
- */
+ /* new interrupt handler msr preserves ME unless explicitly overridden */
new_msr = env->msr & ((target_ulong)1 << MSR_ME);
- /*
- * Hypervisor emulation assistance interrupt only exists on server
- * arch 2.05 server or later.
- */
+ /* HV emu assistance interrupt only exists on server arch 2.05 or later */
if (excp == POWERPC_EXCP_HV_EMU) {
excp = POWERPC_EXCP_PROGRAM;
}
vector = env->excp_vectors[excp];
if (vector == (target_ulong)-1ULL) {
- cpu_abort(cs, "Raised an exception without defined vector %d\n",
- excp);
+ cpu_abort(env_cpu(env),
+ "Raised an exception without defined vector %d\n", excp);
}
-
vector |= env->excp_prefix;
switch (excp) {
@@ -605,7 +580,6 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp)
powerpc_mcheck_checkstop(env);
/* machine check exceptions don't have ME set */
new_msr &= ~((target_ulong)1 << MSR_ME);
-
break;
case POWERPC_EXCP_DSI: /* Data storage exception */
trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]);
@@ -633,11 +607,9 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp)
powerpc_reset_excp_state(cpu);
return;
}
-
/*
- * FP exceptions always have NIP pointing to the faulting
- * instruction, so always use store_next and claim we are
- * precise in the MSR.
+ * NIP always points to the faulting instruction for FP exceptions,
+ * so always use store_next and claim we are precise in the MSR.
*/
msr |= 0x00100000;
break;
@@ -653,7 +625,7 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp)
break;
default:
/* Should never occur */
- cpu_abort(cs, "Invalid program exception %d. Aborting\n",
+ cpu_abort(env_cpu(env), "Invalid program exception %d. Aborting\n",
env->error_code);
break;
}
@@ -675,8 +647,9 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp)
break;
case POWERPC_EXCP_RESET: /* System reset exception */
if (FIELD_EX64(env->msr, MSR, POW)) {
- cpu_abort(cs, "Trying to deliver power-saving system reset "
- "exception %d with no HV support\n", excp);
+ cpu_abort(env_cpu(env),
+ "Trying to deliver power-saving system reset exception "
+ "%d with no HV support\n", excp);
}
break;
case POWERPC_EXCP_TRACE: /* Trace exception */
@@ -703,60 +676,44 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp)
case POWERPC_EXCP_SMI: /* System management interrupt */
case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */
case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */
- cpu_abort(cs, "%s exception not implemented\n",
+ cpu_abort(env_cpu(env), "%s exception not implemented\n",
powerpc_excp_name(excp));
break;
default:
- cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
+ cpu_abort(env_cpu(env), "Invalid PowerPC exception %d. Aborting\n",
+ excp);
break;
}
- /*
- * Sort out endianness of interrupt, this differs depending on the
- * CPU, the HV mode, etc...
- */
if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) {
new_msr |= (target_ulong)1 << MSR_LE;
}
-
- /* Save PC */
env->spr[SPR_SRR0] = env->nip;
-
- /* Save MSR */
env->spr[SPR_SRR1] = msr;
-
powerpc_set_excp_state(cpu, vector, new_msr);
}
static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp)
{
- CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
target_ulong msr, new_msr, vector;
/* new srr1 value excluding must-be-zero bits */
msr = env->msr & ~0x783f0000ULL;
- /*
- * new interrupt handler msr preserves existing ME unless
- * explicitly overridden
- */
+ /* new interrupt handler msr preserves ME unless explicitly overridden */
new_msr = env->msr & ((target_ulong)1 << MSR_ME);
- /*
- * Hypervisor emulation assistance interrupt only exists on server
- * arch 2.05 server or later.
- */
+ /* HV emu assistance interrupt only exists on server arch 2.05 or later */
if (excp == POWERPC_EXCP_HV_EMU) {
excp = POWERPC_EXCP_PROGRAM;
}
vector = env->excp_vectors[excp];
if (vector == (target_ulong)-1ULL) {
- cpu_abort(cs, "Raised an exception without defined vector %d\n",
- excp);
+ cpu_abort(env_cpu(env),
+ "Raised an exception without defined vector %d\n", excp);
}
-
vector |= env->excp_prefix;
switch (excp) {
@@ -764,7 +721,6 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp)
powerpc_mcheck_checkstop(env);
/* machine check exceptions don't have ME set */
new_msr &= ~((target_ulong)1 << MSR_ME);
-
break;
case POWERPC_EXCP_DSI: /* Data storage exception */
trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]);
@@ -792,11 +748,9 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp)
powerpc_reset_excp_state(cpu);
return;
}
-
/*
- * FP exceptions always have NIP pointing to the faulting
- * instruction, so always use store_next and claim we are
- * precise in the MSR.
+ * NIP always points to the faulting instruction for FP exceptions,
+ * so always use store_next and claim we are precise in the MSR.
*/
msr |= 0x00100000;
break;
@@ -812,7 +766,7 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp)
break;
default:
/* Should never occur */
- cpu_abort(cs, "Invalid program exception %d. Aborting\n",
+ cpu_abort(env_cpu(env), "Invalid program exception %d. Aborting\n",
env->error_code);
break;
}
@@ -854,8 +808,9 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp)
break;
case POWERPC_EXCP_RESET: /* System reset exception */
if (FIELD_EX64(env->msr, MSR, POW)) {
- cpu_abort(cs, "Trying to deliver power-saving system reset "
- "exception %d with no HV support\n", excp);
+ cpu_abort(env_cpu(env),
+ "Trying to deliver power-saving system reset exception "
+ "%d with no HV support\n", excp);
}
break;
case POWERPC_EXCP_TRACE: /* Trace exception */
@@ -864,71 +819,53 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp)
case POWERPC_EXCP_DLTLB: /* Data load TLB miss */
case POWERPC_EXCP_DSTLB: /* Data store TLB miss */
ppc_excp_debug_sw_tlb(env, excp);
-
msr |= env->crf[0] << 28;
msr |= env->error_code; /* key, D/I, S/L bits */
/* Set way using a LRU mechanism */
msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17;
-
break;
case POWERPC_EXCP_IABR: /* Instruction address breakpoint */
case POWERPC_EXCP_SMI: /* System management interrupt */
case POWERPC_EXCP_THERM: /* Thermal interrupt */
case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */
- cpu_abort(cs, "%s exception not implemented\n",
+ cpu_abort(env_cpu(env), "%s exception not implemented\n",
powerpc_excp_name(excp));
break;
default:
- cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
+ cpu_abort(env_cpu(env), "Invalid PowerPC exception %d. Aborting\n",
+ excp);
break;
}
- /*
- * Sort out endianness of interrupt, this differs depending on the
- * CPU, the HV mode, etc...
- */
if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) {
new_msr |= (target_ulong)1 << MSR_LE;
}
-
- /* Save PC */
env->spr[SPR_SRR0] = env->nip;
-
- /* Save MSR */
env->spr[SPR_SRR1] = msr;
-
powerpc_set_excp_state(cpu, vector, new_msr);
}
static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp)
{
- CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
target_ulong msr, new_msr, vector;
/* new srr1 value excluding must-be-zero bits */
msr = env->msr & ~0x783f0000ULL;
- /*
- * new interrupt handler msr preserves existing ME unless
- * explicitly overridden
- */
+ /* new interrupt handler msr preserves ME unless explicitly overridden */
new_msr = env->msr & ((target_ulong)1 << MSR_ME);
- /*
- * Hypervisor emulation assistance interrupt only exists on server
- * arch 2.05 server or later.
- */
+ /* HV emu assistance interrupt only exists on server arch 2.05 or later */
if (excp == POWERPC_EXCP_HV_EMU) {
excp = POWERPC_EXCP_PROGRAM;
}
vector = env->excp_vectors[excp];
if (vector == (target_ulong)-1ULL) {
- cpu_abort(cs, "Raised an exception without defined vector %d\n",
- excp);
+ cpu_abort(env_cpu(env),
+ "Raised an exception without defined vector %d\n", excp);
}
-
vector |= env->excp_prefix;
switch (excp) {
@@ -936,7 +873,6 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp)
powerpc_mcheck_checkstop(env);
/* machine check exceptions don't have ME set */
new_msr &= ~((target_ulong)1 << MSR_ME);
-
break;
case POWERPC_EXCP_DSI: /* Data storage exception */
trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]);
@@ -964,11 +900,9 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp)
powerpc_reset_excp_state(cpu);
return;
}
-
/*
- * FP exceptions always have NIP pointing to the faulting
- * instruction, so always use store_next and claim we are
- * precise in the MSR.
+ * NIP always points to the faulting instruction for FP exceptions,
+ * so always use store_next and claim we are precise in the MSR.
*/
msr |= 0x00100000;
break;
@@ -984,7 +918,7 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp)
break;
default:
/* Should never occur */
- cpu_abort(cs, "Invalid program exception %d. Aborting\n",
+ cpu_abort(env_cpu(env), "Invalid program exception %d. Aborting\n",
env->error_code);
break;
}
@@ -1026,7 +960,8 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp)
break;
case POWERPC_EXCP_RESET: /* System reset exception */
if (FIELD_EX64(env->msr, MSR, POW)) {
- cpu_abort(cs, "Trying to deliver power-saving system reset "
+ cpu_abort(env_cpu(env),
+ "Trying to deliver power-saving system reset "
"exception %d with no HV support\n", excp);
}
break;
@@ -1039,54 +974,39 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp)
case POWERPC_EXCP_THERM: /* Thermal interrupt */
case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */
case POWERPC_EXCP_VPUA: /* Vector assist exception */
- cpu_abort(cs, "%s exception not implemented\n",
+ cpu_abort(env_cpu(env), "%s exception not implemented\n",
powerpc_excp_name(excp));
break;
default:
- cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
+ cpu_abort(env_cpu(env), "Invalid PowerPC exception %d. Aborting\n",
+ excp);
break;
}
- /*
- * Sort out endianness of interrupt, this differs depending on the
- * CPU, the HV mode, etc...
- */
if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) {
new_msr |= (target_ulong)1 << MSR_LE;
}
-
- /* Save PC */
env->spr[SPR_SRR0] = env->nip;
-
- /* Save MSR */
env->spr[SPR_SRR1] = msr;
-
powerpc_set_excp_state(cpu, vector, new_msr);
}
static void powerpc_excp_booke(PowerPCCPU *cpu, int excp)
{
- CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
target_ulong msr, new_msr, vector;
- int srr0, srr1;
-
- msr = env->msr;
+ int srr0 = SPR_SRR0, srr1 = SPR_SRR1;
/*
- * new interrupt handler msr preserves existing ME unless
- * explicitly overridden
+ * Book E does not play games with certain bits of xSRR1 being MSR save
+ * bits and others being error status. xSRR1 is the old MSR, period.
*/
- new_msr = env->msr & ((target_ulong)1 << MSR_ME);
+ msr = env->msr;
- /* target registers */
- srr0 = SPR_SRR0;
- srr1 = SPR_SRR1;
+ /* new interrupt handler msr preserves ME unless explicitly overridden */
+ new_msr = env->msr & ((target_ulong)1 << MSR_ME);
- /*
- * Hypervisor emulation assistance interrupt only exists on server
- * arch 2.05 server or later.
- */
+ /* HV emu assistance interrupt only exists on server arch 2.05 or later */
if (excp == POWERPC_EXCP_HV_EMU) {
excp = POWERPC_EXCP_PROGRAM;
}
@@ -1103,10 +1023,9 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp)
vector = env->excp_vectors[excp];
if (vector == (target_ulong)-1ULL) {
- cpu_abort(cs, "Raised an exception without defined vector %d\n",
- excp);
+ cpu_abort(env_cpu(env),
+ "Raised an exception without defined vector %d\n", excp);
}
-
vector |= env->excp_prefix;
switch (excp) {
@@ -1135,6 +1054,7 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp)
break;
case POWERPC_EXCP_EXTERNAL: /* External input */
if (env->mpic_proxy) {
+ CPUState *cs = env_cpu(env);
/* IACK the IRQ on delivery */
env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack);
}
@@ -1149,11 +1069,9 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp)
powerpc_reset_excp_state(cpu);
return;
}
-
/*
- * FP exceptions always have NIP pointing to the faulting
- * instruction, so always use store_next and claim we are
- * precise in the MSR.
+ * NIP always points to the faulting instruction for FP exceptions,
+ * so always use store_next and claim we are precise in the MSR.
*/
msr |= 0x00100000;
env->spr[SPR_BOOKE_ESR] = ESR_FP;
@@ -1173,7 +1091,7 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp)
break;
default:
/* Should never occur */
- cpu_abort(cs, "Invalid program exception %d. Aborting\n",
+ cpu_abort(env_cpu(env), "Invalid program exception %d. Aborting\n",
env->error_code);
break;
}
@@ -1214,7 +1132,8 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp)
/* DBSR already modified by caller */
} else {
- cpu_abort(cs, "Debug exception triggered on unsupported model\n");
+ cpu_abort(env_cpu(env),
+ "Debug exception triggered on unsupported model\n");
}
break;
case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */
@@ -1228,21 +1147,23 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp)
break;
case POWERPC_EXCP_RESET: /* System reset exception */
if (FIELD_EX64(env->msr, MSR, POW)) {
- cpu_abort(cs, "Trying to deliver power-saving system reset "
+ cpu_abort(env_cpu(env),
+ "Trying to deliver power-saving system reset "
"exception %d with no HV support\n", excp);
}
break;
case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */
case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */
- cpu_abort(cs, "%s exception not implemented\n",
+ cpu_abort(env_cpu(env), "%s exception not implemented\n",
powerpc_excp_name(excp));
break;
default:
- cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
+ cpu_abort(env_cpu(env), "Invalid PowerPC exception %d. Aborting\n",
+ excp);
break;
}
-#if defined(TARGET_PPC64)
+#ifdef TARGET_PPC64
if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) {
/* Cat.64-bit: EPCR.ICM is copied to MSR.CM */
new_msr |= (target_ulong)1 << MSR_CM;
@@ -1251,12 +1172,8 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp)
}
#endif
- /* Save PC */
env->spr[srr0] = env->nip;
-
- /* Save MSR */
env->spr[srr1] = msr;
-
powerpc_set_excp_state(cpu, vector, new_msr);
}
@@ -1376,24 +1293,19 @@ static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp)
static void powerpc_excp_books(PowerPCCPU *cpu, int excp)
{
- CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
target_ulong msr, new_msr, vector;
- int srr0, srr1, lev = -1;
+ int srr0 = SPR_SRR0, srr1 = SPR_SRR1, lev = -1;
/* new srr1 value excluding must-be-zero bits */
msr = env->msr & ~0x783f0000ULL;
/*
- * new interrupt handler msr preserves existing HV and ME unless
- * explicitly overridden
+ * new interrupt handler msr preserves HV and ME unless explicitly
+ * overridden
*/
new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB);
- /* target registers */
- srr0 = SPR_SRR0;
- srr1 = SPR_SRR1;
-
/*
* check for special resume at 0x100 from doze/nap/sleep/winkle on
* P7/P8/P9
@@ -1415,10 +1327,9 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp)
vector = env->excp_vectors[excp];
if (vector == (target_ulong)-1ULL) {
- cpu_abort(cs, "Raised an exception without defined vector %d\n",
- excp);
+ cpu_abort(env_cpu(env),
+ "Raised an exception without defined vector %d\n", excp);
}
-
vector |= env->excp_prefix;
if (is_prefix_insn_excp(cpu, excp)) {
@@ -1435,7 +1346,6 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp)
*/
new_msr |= (target_ulong)MSR_HVB;
}
-
/* machine check exceptions don't have ME set */
new_msr &= ~((target_ulong)1 << MSR_ME);
@@ -1453,23 +1363,17 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp)
{
bool lpes0;
- /*
- * LPES0 is only taken into consideration if we support HV
- * mode for this CPU.
- */
+ /* LPES0 is only taken into consideration if we support HV mode */
if (!env->has_hv_mode) {
break;
}
-
lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
-
if (!lpes0) {
new_msr |= (target_ulong)MSR_HVB;
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
srr0 = SPR_HSRR0;
srr1 = SPR_HSRR1;
}
-
break;
}
case POWERPC_EXCP_ALIGN: /* Alignment exception */
@@ -1492,11 +1396,9 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp)
powerpc_reset_excp_state(cpu);
return;
}
-
/*
- * FP exceptions always have NIP pointing to the faulting
- * instruction, so always use store_next and claim we are
- * precise in the MSR.
+ * NIP always points to the faulting instruction for FP exceptions,
+ * so always use store_next and claim we are precise in the MSR.
*/
msr |= 0x00100000;
break;
@@ -1512,7 +1414,7 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp)
break;
default:
/* Should never occur */
- cpu_abort(cs, "Invalid program exception %d. Aborting\n",
+ cpu_abort(env_cpu(env), "Invalid program exception %d. Aborting\n",
env->error_code);
break;
}
@@ -1578,7 +1480,8 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp)
new_msr |= (target_ulong)MSR_HVB;
} else {
if (FIELD_EX64(env->msr, MSR, POW)) {
- cpu_abort(cs, "Trying to deliver power-saving system reset "
+ cpu_abort(env_cpu(env),
+ "Trying to deliver power-saving system reset "
"exception %d with no HV support\n", excp);
}
}
@@ -1650,29 +1553,22 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp)
case POWERPC_EXCP_VPUA: /* Vector assist exception */
case POWERPC_EXCP_MAINT: /* Maintenance exception */
case POWERPC_EXCP_HV_MAINT: /* Hypervisor Maintenance exception */
- cpu_abort(cs, "%s exception not implemented\n",
+ cpu_abort(env_cpu(env), "%s exception not implemented\n",
powerpc_excp_name(excp));
break;
default:
- cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
+ cpu_abort(env_cpu(env), "Invalid PowerPC exception %d. Aborting\n",
+ excp);
break;
}
- /*
- * Sort out endianness of interrupt, this differs depending on the
- * CPU, the HV mode, etc...
- */
if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) {
new_msr |= (target_ulong)1 << MSR_LE;
}
-
new_msr |= (target_ulong)1 << MSR_SF;
if (excp != POWERPC_EXCP_SYSCALL_VECTORED) {
- /* Save PC */
env->spr[srr0] = env->nip;
-
- /* Save MSR */
env->spr[srr1] = msr;
}
@@ -1681,19 +1577,15 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp)
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
/* Deliver interrupt to L1 by returning from the H_ENTER_NESTED call */
vhc->deliver_hv_excp(cpu, excp);
-
powerpc_reset_excp_state(cpu);
-
} else {
/* Sanity check */
if (!(env->msr_mask & MSR_HVB) && srr0 == SPR_HSRR0) {
- cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with "
- "no HV support\n", excp);
+ cpu_abort(env_cpu(env), "Trying to deliver HV exception (HSRR) %d "
+ "with no HV support\n", excp);
}
-
/* This can update new_msr and vector if AIL applies */
ppc_excp_apply_ail(cpu, excp, msr, &new_msr, &vector);
-
powerpc_set_excp_state(cpu, vector, new_msr);
}
}
@@ -1702,15 +1594,15 @@ static inline void powerpc_excp_books(PowerPCCPU *cpu, int excp)
{
g_assert_not_reached();
}
-#endif
+#endif /* TARGET_PPC64 */
static void powerpc_excp(PowerPCCPU *cpu, int excp)
{
- CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) {
- cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
+ cpu_abort(env_cpu(env), "Invalid PowerPC exception %d. Aborting\n",
+ excp);
}
qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx
@@ -1753,7 +1645,7 @@ void ppc_cpu_do_interrupt(CPUState *cs)
powerpc_excp(cpu, cs->exception_index);
}
-#if defined(TARGET_PPC64)
+#ifdef TARGET_PPC64
#define P7_UNUSED_INTERRUPTS \
(PPC_INTERRUPT_RESET | PPC_INTERRUPT_HVIRT | PPC_INTERRUPT_CEXT | \
PPC_INTERRUPT_WDT | PPC_INTERRUPT_CDOORBELL | PPC_INTERRUPT_FIT | \
@@ -2084,10 +1976,23 @@ static int p9_next_unmasked_interrupt(CPUPPCState *env)
return 0;
}
-#endif
+#endif /* TARGET_PPC64 */
-static int ppc_next_unmasked_interrupt_generic(CPUPPCState *env)
+static int ppc_next_unmasked_interrupt(CPUPPCState *env)
{
+#ifdef TARGET_PPC64
+ switch (env->excp_model) {
+ 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);
+ default:
+ break;
+ }
+#endif
bool async_deliver;
/* External reset */
@@ -2198,23 +2103,6 @@ static int ppc_next_unmasked_interrupt_generic(CPUPPCState *env)
return 0;
}
-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.
@@ -2240,11 +2128,10 @@ void ppc_maybe_interrupt(CPUPPCState *env)
}
}
-#if defined(TARGET_PPC64)
+#ifdef 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 */
@@ -2288,14 +2175,14 @@ static void p7_deliver_interrupt(CPUPPCState *env, int interrupt)
assert(!env->resume_as_sreset);
break;
default:
- cpu_abort(cs, "Invalid PowerPC interrupt %d. Aborting\n", interrupt);
+ cpu_abort(env_cpu(env), "Invalid PowerPC interrupt %d. Aborting\n",
+ interrupt);
}
}
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 */
@@ -2359,7 +2246,8 @@ static void p8_deliver_interrupt(CPUPPCState *env, int interrupt)
assert(!env->resume_as_sreset);
break;
default:
- cpu_abort(cs, "Invalid PowerPC interrupt %d. Aborting\n", interrupt);
+ cpu_abort(env_cpu(env), "Invalid PowerPC interrupt %d. Aborting\n",
+ interrupt);
}
}
@@ -2438,15 +2326,28 @@ static void p9_deliver_interrupt(CPUPPCState *env, int interrupt)
assert(!env->resume_as_sreset);
break;
default:
- cpu_abort(cs, "Invalid PowerPC interrupt %d. Aborting\n", interrupt);
+ cpu_abort(env_cpu(env), "Invalid PowerPC interrupt %d. Aborting\n",
+ interrupt);
}
}
-#endif
+#endif /* TARGET_PPC64 */
-static void ppc_deliver_interrupt_generic(CPUPPCState *env, int interrupt)
+static void ppc_deliver_interrupt(CPUPPCState *env, int interrupt)
{
+#ifdef TARGET_PPC64
+ switch (env->excp_model) {
+ case POWERPC_EXCP_POWER7:
+ return p7_deliver_interrupt(env, interrupt);
+ case POWERPC_EXCP_POWER8:
+ return p8_deliver_interrupt(env, interrupt);
+ case POWERPC_EXCP_POWER9:
+ case POWERPC_EXCP_POWER10:
+ return p9_deliver_interrupt(env, interrupt);
+ default:
+ break;
+ }
+#endif
PowerPCCPU *cpu = env_archcpu(env);
- CPUState *cs = env_cpu(env);
switch (interrupt) {
case PPC_INTERRUPT_RESET: /* External reset */
@@ -2543,27 +2444,8 @@ static void ppc_deliver_interrupt_generic(CPUPPCState *env, int interrupt)
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);
+ cpu_abort(env_cpu(env), "Invalid PowerPC interrupt %d. Aborting\n",
+ interrupt);
}
}
@@ -2659,10 +2541,8 @@ void helper_raise_exception(CPUPPCState *env, uint32_t exception)
{
raise_exception_err_ra(env, exception, 0, 0);
}
-#endif
-#if !defined(CONFIG_USER_ONLY)
-#ifdef CONFIG_TCG
+#ifndef CONFIG_USER_ONLY
void helper_store_msr(CPUPPCState *env, target_ulong val)
{
uint32_t excp = hreg_store_msr(env, val, 0);
@@ -2678,7 +2558,7 @@ void helper_ppc_maybe_interrupt(CPUPPCState *env)
ppc_maybe_interrupt(env);
}
-#if defined(TARGET_PPC64)
+#ifdef TARGET_PPC64
void helper_scv(CPUPPCState *env, uint32_t lev)
{
if (env->spr[SPR_FSCR] & (1ull << FSCR_SCV)) {
@@ -2706,7 +2586,7 @@ void helper_pminsn(CPUPPCState *env, uint32_t insn)
ppc_maybe_interrupt(env);
}
-#endif /* defined(TARGET_PPC64) */
+#endif /* TARGET_PPC64 */
static void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr)
{
@@ -2717,7 +2597,7 @@ static void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr)
if (env->flags & POWERPC_FLAG_TGPR)
msr &= ~(1ULL << MSR_TGPR);
-#if defined(TARGET_PPC64)
+#ifdef TARGET_PPC64
/* Switching to 32-bit ? Crop the nip */
if (!msr_is_64bit(env, msr)) {
nip = (uint32_t)nip;
@@ -2746,7 +2626,7 @@ void helper_rfi(CPUPPCState *env)
do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1] & 0xfffffffful);
}
-#if defined(TARGET_PPC64)
+#ifdef TARGET_PPC64
void helper_rfid(CPUPPCState *env)
{
/*
@@ -2767,9 +2647,7 @@ void helper_hrfid(CPUPPCState *env)
{
do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]);
}
-#endif
-#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
void helper_rfebb(CPUPPCState *env, target_ulong s)
{
target_ulong msr = env->msr;
@@ -2844,7 +2722,7 @@ void raise_ebb_perfm_exception(CPUPPCState *env)
do_ebb(env, POWERPC_EXCP_PERFM_EBB);
}
-#endif
+#endif /* TARGET_PPC64 */
/*****************************************************************************/
/* Embedded PowerPC specific helpers */
@@ -2869,10 +2747,8 @@ void helper_rfmci(CPUPPCState *env)
/* FIXME: choose CSRR1 or MCSRR1 based on cpu type */
do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1]);
}
-#endif /* CONFIG_TCG */
-#endif /* !defined(CONFIG_USER_ONLY) */
+#endif /* !CONFIG_USER_ONLY */
-#ifdef CONFIG_TCG
void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2,
uint32_t flags)
{
@@ -2886,7 +2762,7 @@ void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2,
}
}
-#if defined(TARGET_PPC64)
+#ifdef TARGET_PPC64
void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2,
uint32_t flags)
{
@@ -2899,10 +2775,8 @@ void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2,
POWERPC_EXCP_TRAP, GETPC());
}
}
-#endif
-#endif
+#endif /* TARGET_PPC64 */
-#ifdef CONFIG_TCG
static uint32_t helper_SIMON_LIKE_32_64(uint32_t x, uint64_t key, uint32_t lane)
{
const uint16_t c = 0xfffc;
@@ -3013,12 +2887,8 @@ HELPER_HASH(HASHST, env->spr[SPR_HASHKEYR], true, NPHIE)
HELPER_HASH(HASHCHK, env->spr[SPR_HASHKEYR], false, NPHIE)
HELPER_HASH(HASHSTP, env->spr[SPR_HASHPKEYR], true, PHIE)
HELPER_HASH(HASHCHKP, env->spr[SPR_HASHPKEYR], false, PHIE)
-#endif /* CONFIG_TCG */
-
-#if !defined(CONFIG_USER_ONLY)
-
-#ifdef CONFIG_TCG
+#ifndef CONFIG_USER_ONLY
/* Embedded.Processor Control */
static int dbell2irq(target_ulong rb)
{
@@ -3125,7 +2995,7 @@ void helper_book3s_msgsnd(target_ulong rb)
book3s_msgsnd_common(pir, PPC_INTERRUPT_HDOORBELL);
}
-#if defined(TARGET_PPC64)
+#ifdef TARGET_PPC64
void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb)
{
helper_hfscr_facility_check(env, HFSCR_MSGP, "msgclrp", HFSCR_IC_MSGP);
@@ -3365,5 +3235,5 @@ bool ppc_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp)
return false;
}
-#endif /* CONFIG_TCG */
#endif /* !CONFIG_USER_ONLY */
+#endif /* CONFIG_TCG */
diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c
index 769551e0ab..3f1e61bdb7 100644
--- a/target/ppc/gdbstub.c
+++ b/target/ppc/gdbstub.c
@@ -320,6 +320,9 @@ static void gdb_gen_spr_feature(CPUState *cs)
continue;
}
+ gdb_feature_builder_append_reg(&builder, g_ascii_strdown(spr->name, -1),
+ TARGET_LONG_BITS, num_regs,
+ "int", "spr");
/*
* GDB identifies registers based on the order they are
* presented in the XML. These ids will not match QEMU's
@@ -330,10 +333,6 @@ static void gdb_gen_spr_feature(CPUState *cs)
*/
spr->gdb_id = num_regs;
num_regs++;
-
- gdb_feature_builder_append_reg(&builder, g_ascii_strdown(spr->name, -1),
- TARGET_LONG_BITS, num_regs,
- "int", "spr");
}
gdb_feature_builder_end(&builder);
diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c
index 410b39c231..25258986e3 100644
--- a/target/ppc/helper_regs.c
+++ b/target/ppc/helper_regs.c
@@ -264,6 +264,11 @@ int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv)
value &= ~MSR_HVB;
value |= env->msr & MSR_HVB;
}
+ /* Attempt to modify MSR[ME] in guest state is ignored */
+ if (is_book3s_arch2x(env) && !(env->msr & MSR_HVB)) {
+ value &= ~(1 << MSR_ME);
+ value |= env->msr & (1 << MSR_ME);
+ }
if ((value ^ env->msr) & (R_MSR_IR_MASK | R_MSR_DR_MASK)) {
cpu_interrupt_exittb(cs);
}
diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode
index 4fcf3af8d0..eada59f59f 100644
--- a/target/ppc/insn32.decode
+++ b/target/ppc/insn32.decode
@@ -187,6 +187,12 @@
&X_a ra
@X_a ...... ra:3 .. ..... ..... .......... . &X_a
+&XO rt ra rb oe:bool rc:bool
+@XO ...... rt:5 ra:5 rb:5 oe:1 ......... rc:1 &XO
+
+&XO_ta rt ra oe:bool rc:bool
+@XO_ta ...... rt:5 ra:5 ..... oe:1 ......... rc:1 &XO_ta
+
%xx_xt 0:1 21:5
%xx_xb 1:1 11:5
%xx_xa 2:1 16:5
@@ -322,10 +328,30 @@ CMPLI 001010 ... - . ..... ................ @D_bfu
### Fixed-Point Arithmetic Instructions
+ADD 011111 ..... ..... ..... . 100001010 . @XO
+ADDC 011111 ..... ..... ..... . 000001010 . @XO
+ADDE 011111 ..... ..... ..... . 010001010 . @XO
+
+# ADDEX is Z23-form, with CY=0; all other values for CY are reserved.
+# This works out the same as X-form.
+ADDEX 011111 ..... ..... ..... 00 10101010 - @X
+
ADDI 001110 ..... ..... ................ @D
ADDIS 001111 ..... ..... ................ @D
+ADDIC 001100 ..... ..... ................ @D
+ADDIC_ 001101 ..... ..... ................ @D
ADDPCIS 010011 ..... ..... .......... 00010 . @DX
+ADDME 011111 ..... ..... ----- . 011101010 . @XO_ta
+ADDZE 011111 ..... ..... ----- . 011001010 . @XO_ta
+
+SUBF 011111 ..... ..... ..... . 000101000 . @XO
+SUBFIC 001000 ..... ..... ................ @D
+SUBFC 011111 ..... ..... ..... . 000001000 . @XO
+SUBFE 011111 ..... ..... ..... . 010001000 . @XO
+
+SUBFME 011111 ..... ..... ----- . 011101000 . @XO_ta
+SUBFZE 011111 ..... ..... ----- . 011001000 . @XO_ta
## Fixed-Point Logical Instructions
diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c
index 6dfa5de444..8231feb2d4 100644
--- a/target/ppc/kvm.c
+++ b/target/ppc/kvm.c
@@ -2365,17 +2365,6 @@ static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data)
#if defined(TARGET_PPC64)
pcc->radix_page_info = kvmppc_get_radix_page_info();
-
- if ((pcc->pvr & 0xffffff00) == CPU_POWERPC_POWER9_DD1) {
- /*
- * POWER9 DD1 has some bugs which make it not really ISA 3.00
- * compliant. More importantly, advertising ISA 3.00
- * architected mode may prevent guests from activating
- * necessary DD1 workarounds.
- */
- pcc->pcr_supported &= ~(PCR_COMPAT_3_00 | PCR_COMPAT_2_07
- | PCR_COMPAT_2_06 | PCR_COMPAT_2_05);
- }
#endif /* defined(TARGET_PPC64) */
}
diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c
index a9d41d2802..58e808dc96 100644
--- a/target/ppc/misc_helper.c
+++ b/target/ppc/misc_helper.c
@@ -49,9 +49,6 @@ void helper_spr_core_write_generic(CPUPPCState *env, uint32_t sprn,
CPUState *cs = env_cpu(env);
CPUState *ccs;
uint32_t nr_threads = cs->nr_threads;
- uint32_t core_id = env->spr[SPR_PIR] & ~(nr_threads - 1);
-
- assert(core_id == env->spr[SPR_PIR] - env->spr[SPR_TIR]);
if (nr_threads == 1) {
env->spr[sprn] = val;
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 28fc7791af..93ffec787c 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -296,33 +296,26 @@ static inline void gen_update_nip(DisasContext *ctx, target_ulong nip)
tcg_gen_movi_tl(cpu_nip, nip);
}
-static void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error)
+static void gen_exception_err_nip(DisasContext *ctx, uint32_t excp,
+ uint32_t error, target_ulong nip)
{
TCGv_i32 t0, t1;
- /*
- * These are all synchronous exceptions, we set the PC back to the
- * faulting instruction
- */
- gen_update_nip(ctx, ctx->cia);
+ gen_update_nip(ctx, nip);
t0 = tcg_constant_i32(excp);
t1 = tcg_constant_i32(error);
gen_helper_raise_exception_err(tcg_env, t0, t1);
ctx->base.is_jmp = DISAS_NORETURN;
}
-static void gen_exception(DisasContext *ctx, uint32_t excp)
+static inline void gen_exception_err(DisasContext *ctx, uint32_t excp,
+ uint32_t error)
{
- TCGv_i32 t0;
-
/*
* These are all synchronous exceptions, we set the PC back to the
* faulting instruction
*/
- gen_update_nip(ctx, ctx->cia);
- t0 = tcg_constant_i32(excp);
- gen_helper_raise_exception(tcg_env, t0);
- ctx->base.is_jmp = DISAS_NORETURN;
+ gen_exception_err_nip(ctx, excp, error, ctx->cia);
}
static void gen_exception_nip(DisasContext *ctx, uint32_t excp,
@@ -336,6 +329,15 @@ static void gen_exception_nip(DisasContext *ctx, uint32_t excp,
ctx->base.is_jmp = DISAS_NORETURN;
}
+static inline void gen_exception(DisasContext *ctx, uint32_t excp)
+{
+ /*
+ * These are all synchronous exceptions, we set the PC back to the
+ * faulting instruction
+ */
+ gen_exception_nip(ctx, excp, ctx->cia);
+}
+
#if !defined(CONFIG_USER_ONLY)
static void gen_ppc_maybe_interrupt(DisasContext *ctx)
{
@@ -1735,61 +1737,6 @@ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1,
tcg_gen_mov_tl(ret, t0);
}
}
-/* Add functions with two operands */
-#define GEN_INT_ARITH_ADD(name, opc3, ca, add_ca, compute_ca, compute_ov) \
-static void glue(gen_, name)(DisasContext *ctx) \
-{ \
- gen_op_arith_add(ctx, cpu_gpr[rD(ctx->opcode)], \
- cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \
- ca, glue(ca, 32), \
- add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \
-}
-/* Add functions with one operand and one immediate */
-#define GEN_INT_ARITH_ADD_CONST(name, opc3, const_val, ca, \
- add_ca, compute_ca, compute_ov) \
-static void glue(gen_, name)(DisasContext *ctx) \
-{ \
- TCGv t0 = tcg_constant_tl(const_val); \
- gen_op_arith_add(ctx, cpu_gpr[rD(ctx->opcode)], \
- cpu_gpr[rA(ctx->opcode)], t0, \
- ca, glue(ca, 32), \
- add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \
-}
-
-/* add add. addo addo. */
-GEN_INT_ARITH_ADD(add, 0x08, cpu_ca, 0, 0, 0)
-GEN_INT_ARITH_ADD(addo, 0x18, cpu_ca, 0, 0, 1)
-/* addc addc. addco addco. */
-GEN_INT_ARITH_ADD(addc, 0x00, cpu_ca, 0, 1, 0)
-GEN_INT_ARITH_ADD(addco, 0x10, cpu_ca, 0, 1, 1)
-/* adde adde. addeo addeo. */
-GEN_INT_ARITH_ADD(adde, 0x04, cpu_ca, 1, 1, 0)
-GEN_INT_ARITH_ADD(addeo, 0x14, cpu_ca, 1, 1, 1)
-/* addme addme. addmeo addmeo. */
-GEN_INT_ARITH_ADD_CONST(addme, 0x07, -1LL, cpu_ca, 1, 1, 0)
-GEN_INT_ARITH_ADD_CONST(addmeo, 0x17, -1LL, cpu_ca, 1, 1, 1)
-/* addex */
-GEN_INT_ARITH_ADD(addex, 0x05, cpu_ov, 1, 1, 0);
-/* addze addze. addzeo addzeo.*/
-GEN_INT_ARITH_ADD_CONST(addze, 0x06, 0, cpu_ca, 1, 1, 0)
-GEN_INT_ARITH_ADD_CONST(addzeo, 0x16, 0, cpu_ca, 1, 1, 1)
-/* addic addic.*/
-static inline void gen_op_addic(DisasContext *ctx, bool compute_rc0)
-{
- TCGv c = tcg_constant_tl(SIMM(ctx->opcode));
- gen_op_arith_add(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)],
- c, cpu_ca, cpu_ca32, 0, 1, 0, compute_rc0);
-}
-
-static void gen_addic(DisasContext *ctx)
-{
- gen_op_addic(ctx, 0);
-}
-
-static void gen_addic_(DisasContext *ctx)
-{
- gen_op_addic(ctx, 1);
-}
static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, TCGv arg1,
TCGv arg2, int sign, int compute_ov)
@@ -2210,47 +2157,6 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1,
tcg_gen_mov_tl(ret, t0);
}
}
-/* Sub functions with Two operands functions */
-#define GEN_INT_ARITH_SUBF(name, opc3, add_ca, compute_ca, compute_ov) \
-static void glue(gen_, name)(DisasContext *ctx) \
-{ \
- gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], \
- cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \
- add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \
-}
-/* Sub functions with one operand and one immediate */
-#define GEN_INT_ARITH_SUBF_CONST(name, opc3, const_val, \
- add_ca, compute_ca, compute_ov) \
-static void glue(gen_, name)(DisasContext *ctx) \
-{ \
- TCGv t0 = tcg_constant_tl(const_val); \
- gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], \
- cpu_gpr[rA(ctx->opcode)], t0, \
- add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \
-}
-/* subf subf. subfo subfo. */
-GEN_INT_ARITH_SUBF(subf, 0x01, 0, 0, 0)
-GEN_INT_ARITH_SUBF(subfo, 0x11, 0, 0, 1)
-/* subfc subfc. subfco subfco. */
-GEN_INT_ARITH_SUBF(subfc, 0x00, 0, 1, 0)
-GEN_INT_ARITH_SUBF(subfco, 0x10, 0, 1, 1)
-/* subfe subfe. subfeo subfo. */
-GEN_INT_ARITH_SUBF(subfe, 0x04, 1, 1, 0)
-GEN_INT_ARITH_SUBF(subfeo, 0x14, 1, 1, 1)
-/* subfme subfme. subfmeo subfmeo. */
-GEN_INT_ARITH_SUBF_CONST(subfme, 0x07, -1LL, 1, 1, 0)
-GEN_INT_ARITH_SUBF_CONST(subfmeo, 0x17, -1LL, 1, 1, 1)
-/* subfze subfze. subfzeo subfzeo.*/
-GEN_INT_ARITH_SUBF_CONST(subfze, 0x06, 0, 1, 1, 0)
-GEN_INT_ARITH_SUBF_CONST(subfzeo, 0x16, 0, 1, 1, 1)
-
-/* subfic */
-static void gen_subfic(DisasContext *ctx)
-{
- TCGv c = tcg_constant_tl(SIMM(ctx->opcode));
- gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)],
- c, 0, 1, 0, 0);
-}
/* neg neg. nego nego. */
static inline void gen_op_arith_neg(DisasContext *ctx, bool compute_ov)
@@ -6524,8 +6430,6 @@ GEN_HANDLER_E(cmpeqb, 0x1F, 0x00, 0x07, 0x00600000, PPC_NONE, PPC2_ISA300),
GEN_HANDLER_E(cmpb, 0x1F, 0x1C, 0x0F, 0x00000001, PPC_NONE, PPC2_ISA205),
GEN_HANDLER_E(cmprb, 0x1F, 0x00, 0x06, 0x00400001, PPC_NONE, PPC2_ISA300),
GEN_HANDLER(isel, 0x1F, 0x0F, 0xFF, 0x00000001, PPC_ISEL),
-GEN_HANDLER(addic, 0x0C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
-GEN_HANDLER2(addic_, "addic.", 0x0D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
GEN_HANDLER(mulhw, 0x1F, 0x0B, 0x02, 0x00000400, PPC_INTEGER),
GEN_HANDLER(mulhwu, 0x1F, 0x0B, 0x00, 0x00000400, PPC_INTEGER),
GEN_HANDLER(mullw, 0x1F, 0x0B, 0x07, 0x00000000, PPC_INTEGER),
@@ -6536,7 +6440,6 @@ GEN_HANDLER(mulld, 0x1F, 0x09, 0x07, 0x00000000, PPC_64B),
#endif
GEN_HANDLER(neg, 0x1F, 0x08, 0x03, 0x0000F800, PPC_INTEGER),
GEN_HANDLER(nego, 0x1F, 0x08, 0x13, 0x0000F800, PPC_INTEGER),
-GEN_HANDLER(subfic, 0x08, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
GEN_HANDLER2(andi_, "andi.", 0x1C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
GEN_HANDLER2(andis_, "andis.", 0x1D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
GEN_HANDLER(cntlzw, 0x1F, 0x1A, 0x00, 0x00000000, PPC_INTEGER),
@@ -6747,25 +6650,6 @@ GEN_HANDLER_E(maddhd_maddhdu, 0x04, 0x18, 0xFF, 0x00000000, PPC_NONE,
GEN_HANDLER_E(maddld, 0x04, 0x19, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA300),
#endif
-#undef GEN_INT_ARITH_ADD
-#undef GEN_INT_ARITH_ADD_CONST
-#define GEN_INT_ARITH_ADD(name, opc3, add_ca, compute_ca, compute_ov) \
-GEN_HANDLER(name, 0x1F, 0x0A, opc3, 0x00000000, PPC_INTEGER),
-#define GEN_INT_ARITH_ADD_CONST(name, opc3, const_val, \
- add_ca, compute_ca, compute_ov) \
-GEN_HANDLER(name, 0x1F, 0x0A, opc3, 0x0000F800, PPC_INTEGER),
-GEN_INT_ARITH_ADD(add, 0x08, 0, 0, 0)
-GEN_INT_ARITH_ADD(addo, 0x18, 0, 0, 1)
-GEN_INT_ARITH_ADD(addc, 0x00, 0, 1, 0)
-GEN_INT_ARITH_ADD(addco, 0x10, 0, 1, 1)
-GEN_INT_ARITH_ADD(adde, 0x04, 1, 1, 0)
-GEN_INT_ARITH_ADD(addeo, 0x14, 1, 1, 1)
-GEN_INT_ARITH_ADD_CONST(addme, 0x07, -1LL, 1, 1, 0)
-GEN_INT_ARITH_ADD_CONST(addmeo, 0x17, -1LL, 1, 1, 1)
-GEN_HANDLER_E(addex, 0x1F, 0x0A, 0x05, 0x00000000, PPC_NONE, PPC2_ISA300),
-GEN_INT_ARITH_ADD_CONST(addze, 0x06, 0, 1, 1, 0)
-GEN_INT_ARITH_ADD_CONST(addzeo, 0x16, 0, 1, 1, 1)
-
#undef GEN_INT_ARITH_DIVW
#define GEN_INT_ARITH_DIVW(name, opc3, sign, compute_ov) \
GEN_HANDLER(name, 0x1F, 0x0B, opc3, 0x00000000, PPC_INTEGER)
@@ -6804,24 +6688,6 @@ GEN_INT_ARITH_MUL_HELPER(mulhd, 0x02),
GEN_INT_ARITH_MUL_HELPER(mulldo, 0x17),
#endif
-#undef GEN_INT_ARITH_SUBF
-#undef GEN_INT_ARITH_SUBF_CONST
-#define GEN_INT_ARITH_SUBF(name, opc3, add_ca, compute_ca, compute_ov) \
-GEN_HANDLER(name, 0x1F, 0x08, opc3, 0x00000000, PPC_INTEGER),
-#define GEN_INT_ARITH_SUBF_CONST(name, opc3, const_val, \
- add_ca, compute_ca, compute_ov) \
-GEN_HANDLER(name, 0x1F, 0x08, opc3, 0x0000F800, PPC_INTEGER),
-GEN_INT_ARITH_SUBF(subf, 0x01, 0, 0, 0)
-GEN_INT_ARITH_SUBF(subfo, 0x11, 0, 0, 1)
-GEN_INT_ARITH_SUBF(subfc, 0x00, 0, 1, 0)
-GEN_INT_ARITH_SUBF(subfco, 0x10, 0, 1, 1)
-GEN_INT_ARITH_SUBF(subfe, 0x04, 1, 1, 0)
-GEN_INT_ARITH_SUBF(subfeo, 0x14, 1, 1, 1)
-GEN_INT_ARITH_SUBF_CONST(subfme, 0x07, -1LL, 1, 1, 0)
-GEN_INT_ARITH_SUBF_CONST(subfmeo, 0x17, -1LL, 1, 1, 1)
-GEN_INT_ARITH_SUBF_CONST(subfze, 0x06, 0, 1, 1, 0)
-GEN_INT_ARITH_SUBF_CONST(subfzeo, 0x16, 0, 1, 1, 1)
-
#undef GEN_LOGICAL1
#undef GEN_LOGICAL2
#define GEN_LOGICAL2(name, tcg_op, opc, type) \
diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc
index 51c6fa7330..0c66465d96 100644
--- a/target/ppc/translate/fixedpoint-impl.c.inc
+++ b/target/ppc/translate/fixedpoint-impl.c.inc
@@ -325,6 +325,76 @@ static bool trans_ADDPCIS(DisasContext *ctx, arg_DX *a)
return true;
}
+static bool trans_ADDEX(DisasContext *ctx, arg_X *a)
+{
+ REQUIRE_INSNS_FLAGS2(ctx, ISA300);
+ gen_op_arith_add(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb],
+ cpu_ov, cpu_ov32, true, true, false, false);
+ return true;
+}
+
+static bool do_add_D(DisasContext *ctx, arg_D *a, bool add_ca, bool compute_ca,
+ bool compute_ov, bool compute_rc0)
+{
+ gen_op_arith_add(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra],
+ tcg_constant_tl(a->si), cpu_ca, cpu_ca32,
+ add_ca, compute_ca, compute_ov, compute_rc0);
+ return true;
+}
+
+static bool do_add_XO(DisasContext *ctx, arg_XO *a, bool add_ca,
+ bool compute_ca)
+{
+ gen_op_arith_add(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb],
+ cpu_ca, cpu_ca32, add_ca, compute_ca, a->oe, a->rc);
+ return true;
+}
+
+static bool do_add_const_XO(DisasContext *ctx, arg_XO_ta *a, TCGv const_val,
+ bool add_ca, bool compute_ca)
+{
+ gen_op_arith_add(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], const_val,
+ cpu_ca, cpu_ca32, add_ca, compute_ca, a->oe, a->rc);
+ return true;
+}
+
+TRANS(ADD, do_add_XO, false, false);
+TRANS(ADDC, do_add_XO, false, true);
+TRANS(ADDE, do_add_XO, true, true);
+TRANS(ADDME, do_add_const_XO, tcg_constant_tl(-1LL), true, true);
+TRANS(ADDZE, do_add_const_XO, tcg_constant_tl(0), true, true);
+TRANS(ADDIC, do_add_D, false, true, false, false);
+TRANS(ADDIC_, do_add_D, false, true, false, true);
+
+static bool trans_SUBFIC(DisasContext *ctx, arg_D *a)
+{
+ gen_op_arith_subf(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra],
+ tcg_constant_tl(a->si), false, true, false, false);
+ return true;
+}
+
+static bool do_subf_XO(DisasContext *ctx, arg_XO *a, bool add_ca,
+ bool compute_ca)
+{
+ gen_op_arith_subf(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb],
+ add_ca, compute_ca, a->oe, a->rc);
+ return true;
+}
+
+static bool do_subf_const_XO(DisasContext *ctx, arg_XO_ta *a, TCGv const_val,
+ bool add_ca, bool compute_ca)
+{
+ gen_op_arith_subf(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], const_val,
+ add_ca, compute_ca, a->oe, a->rc);
+ return true;
+}
+
+TRANS(SUBF, do_subf_XO, false, false)
+TRANS(SUBFC, do_subf_XO, false, true)
+TRANS(SUBFE, do_subf_XO, true, true)
+TRANS(SUBFME, do_subf_const_XO, tcg_constant_tl(-1LL), true, true)
+TRANS(SUBFZE, do_subf_const_XO, tcg_constant_tl(0), true, true)
+
static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a)
{
gen_invalid(ctx);