mirror of https://github.com/xemu-project/xemu.git
ppc 7.0 queue:
* General cleanup for Mac machines (Peter) * Fixes for FPU exceptions (Lucas) * Support for new ISA31 instructions (Matheus) * Fixes for ivshmem (Daniel) * Cleanups for PowerNV PHB (Christophe and Cedric) * Updates of PowerNV and pSeries documentation (Leonardo and Daniel) * Fixes for PowerNV (Daniel) * Large cleanup of FPU implementation (Richard) * Removal of SoftTLBs support for PPC74x CPUs (Fabiano) * Fixes for exception models in MPCx and 60x CPUs (Fabiano) * Removal of 401/403 CPUs (Cedric) * Deprecation of taihu machine (Thomas) * Large rework of PPC405 machine (Cedric) * Fixes for VSX instructions (Victor and Matheus) * Fix for e6500 CPU (Fabiano) * Initial support for PMU (Daniel) -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmG8xt8ACgkQUaNDx8/7 7KG3Dg/9EXK3GslNgUNRvB1pgRSimnrUirGUiDmZPXxevIbsoPsYaXmUcD1zOnlb zXiCzQ2Bvi8ZUjT1uScP7dkFCdzs6gXYbTEcTzscX3k2VnTjXHXhQ3cnb0uModP5 U1QzrjV7K/q1usJW5OVSGZS1PoWOqWuZNdcp0mIUWcJHhSaYtUGGPohp7rH0JSug ncmkRA0KLgIX8eg8swyfJxrw9wCcXlFIcmwHipB8S/Dd/gUpmFEoaQsmugSJNYZe zi8Fd4jfzlRXVwb8EUSiOiaXSd/WKjEcQx/usbzzaBacbktk/nfy+rligUMryCpO vGFM5blxEX5SXD3Cd0vcFwYhCZImphD8K+Sxe6Us69rsUH11hJS+q29/Puk1MkHt DTubqB3k4BheiatOV1zeUMlbRm5svUhGj3VstFZYZeZ3Oh47Jsx3XH4hoytUuc/1 lP9UGkaf3nIx12vSqBA/3Crc7zalWX5OhaUV5RG30+jxd8zHOKcasKbd22710DNz 4WybQLb3bpUr091mWMKcaAkP6bxcE8S+mR4LE2kdELboAnkB+OgSmrdZ3slceaCv btV8qjNl4f8lBvyFQVxZ5bn05+TfxUXFlFxXipxf1fI64bYwRnyQQ3yRxMHipRYK CRta1akVgIgcBbeeRHBZLA12UgTQJY6WIoDaZMz9NxIDHJnX/jw= =APFd -----END PGP SIGNATURE----- Merge tag 'pull-ppc-20211217' of https://github.com/legoater/qemu into staging ppc 7.0 queue: * General cleanup for Mac machines (Peter) * Fixes for FPU exceptions (Lucas) * Support for new ISA31 instructions (Matheus) * Fixes for ivshmem (Daniel) * Cleanups for PowerNV PHB (Christophe and Cedric) * Updates of PowerNV and pSeries documentation (Leonardo and Daniel) * Fixes for PowerNV (Daniel) * Large cleanup of FPU implementation (Richard) * Removal of SoftTLBs support for PPC74x CPUs (Fabiano) * Fixes for exception models in MPCx and 60x CPUs (Fabiano) * Removal of 401/403 CPUs (Cedric) * Deprecation of taihu machine (Thomas) * Large rework of PPC405 machine (Cedric) * Fixes for VSX instructions (Victor and Matheus) * Fix for e6500 CPU (Fabiano) * Initial support for PMU (Daniel) # gpg: Signature made Fri 17 Dec 2021 09:20:31 AM PST # gpg: using RSA key A0F66548F04895EBFE6B0B6051A343C7CFFBECA1 # gpg: Good signature from "Cédric Le Goater <clg@kaod.org>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: A0F6 6548 F048 95EB FE6B 0B60 51A3 43C7 CFFB ECA1 * tag 'pull-ppc-20211217' of https://github.com/legoater/qemu: (101 commits) ppc/pnv: Use QOM hierarchy to scan PEC PHB4 devices ppc/pnv: Move realize of PEC stacks under the PEC model ppc/pnv: Remove "system-memory" property from PHB4 PEC ppc/pnv: Compute the PHB index from the PHB4 PEC model ppc/pnv: Introduce a num_stack class attribute ppc/pnv: Introduce a "chip" property under the PHB4 model ppc/pnv: Introduce version and device_id class atributes for PHB4 devices ppc/pnv: Introduce a num_pecs class attribute for PHB4 PEC devices ppc/pnv: Use QOM hierarchy to scan PHB3 devices ppc/pnv: Move mapping of the PHB3 CQ regions under pnv_pbcq_realize() ppc/pnv: Drop the "num-phbs" property ppc/pnv: Use the chip class to check the index of PHB3 devices ppc/pnv: Introduce a "chip" property under PHB3 PPC64/TCG: Implement 'rfebb' instruction target/ppc/power8-pmu.c: add PM_RUN_INST_CMPL (0xFA) event target/ppc: enable PMU instruction count target/ppc: enable PMU counter overflow with cycle events target/ppc: PMU: update counters on MMCR1 write target/ppc: PMU: update counters on PMCs r/w target/ppc: PMU basic cycle count for pseries TCG ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
93dc314c92
|
@ -315,6 +315,15 @@ This machine is deprecated because we have enough AST2500 based OpenPOWER
|
|||
machines. It can be easily replaced by the ``witherspoon-bmc`` or the
|
||||
``romulus-bmc`` machines.
|
||||
|
||||
PPC 405 ``taihu`` machine (since 7.0)
|
||||
'''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The PPC 405 CPU is a system-on-a-chip, so all 405 machines are very similar,
|
||||
except for some external periphery. However, the periphery of the ``taihu``
|
||||
machine is hardly emulated at all (e.g. neither the LCD nor the USB part had
|
||||
been implemented), so there is not much value added by this board. Use the
|
||||
``ref405ep`` machine instead.
|
||||
|
||||
Backend options
|
||||
---------------
|
||||
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
sPAPR hypervisor calls
|
||||
----------------------
|
||||
|
||||
When used with the ``pseries`` machine type, ``qemu-system-ppc64`` implements
|
||||
a set of hypervisor calls (a.k.a. hcalls) defined in the `Linux on Power
|
||||
Architecture Reference document (LoPAR)
|
||||
<https://cdn.openpowerfoundation.org/wp-content/uploads/2020/07/LoPAR-20200812.pdf>`_.
|
||||
This document is a subset of the Power Architecture Platform Reference (PAPR+)
|
||||
specification (IBM internal only), which is what PowerVM, the IBM proprietary
|
||||
hypervisor, adheres to.
|
||||
|
||||
The subset in LoPAR is selected based on the requirements of Linux as a guest.
|
||||
|
||||
In addition to those calls, we have added our own private hypervisor
|
||||
calls which are mostly used as a private interface between the firmware
|
||||
running in the guest and QEMU.
|
||||
|
||||
All those hypercalls start at hcall number 0xf000 which correspond
|
||||
to an implementation specific range in PAPR.
|
||||
|
||||
H_RTAS (0xf000)
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
RTAS stands for Run-Time Abstraction Sercies and is a set of runtime services
|
||||
generally provided by the firmware inside the guest to the operating system. It
|
||||
predates the existence of hypervisors (it was originally an extension to Open
|
||||
Firmware) and is still used by PAPR and LoPAR to provide various services that
|
||||
are not performance sensitive.
|
||||
|
||||
We currently implement the RTAS services in QEMU itself. The actual RTAS
|
||||
"firmware" blob in the guest is a small stub of a few instructions which
|
||||
calls our private H_RTAS hypervisor call to pass the RTAS calls to QEMU.
|
||||
|
||||
Arguments:
|
||||
|
||||
``r3``: ``H_RTAS (0xf000)``
|
||||
|
||||
``r4``: Guest physical address of RTAS parameter block.
|
||||
|
||||
Returns:
|
||||
|
||||
``H_SUCCESS``: Successfully called the RTAS function (RTAS result will have
|
||||
been stored in the parameter block).
|
||||
|
||||
``H_PARAMETER``: Unknown token.
|
||||
|
||||
H_LOGICAL_MEMOP (0xf001)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When the guest runs in "real mode" (in powerpc terminology this means with MMU
|
||||
disabled, i.e. guest effective address equals to guest physical address), it
|
||||
only has access to a subset of memory and no I/Os.
|
||||
|
||||
PAPR and LoPAR provides a set of hypervisor calls to perform cacheable or
|
||||
non-cacheable accesses to any guest physical addresses that the
|
||||
guest can use in order to access IO devices while in real mode.
|
||||
|
||||
This is typically used by the firmware running in the guest.
|
||||
|
||||
However, doing a hypercall for each access is extremely inefficient
|
||||
(even more so when running KVM) when accessing the frame buffer. In
|
||||
that case, things like scrolling become unusably slow.
|
||||
|
||||
This hypercall allows the guest to request a "memory op" to be applied
|
||||
to memory. The supported memory ops at this point are to copy a range
|
||||
of memory (supports overlap of source and destination) and XOR which
|
||||
is used by our SLOF firmware to invert the screen.
|
||||
|
||||
Arguments:
|
||||
|
||||
``r3 ``: ``H_LOGICAL_MEMOP (0xf001)``
|
||||
|
||||
``r4``: Guest physical address of destination.
|
||||
|
||||
``r5``: Guest physical address of source.
|
||||
|
||||
``r6``: Individual element size, defined by the binary logarithm of the
|
||||
desired size. Supported values are:
|
||||
|
||||
``0`` = 1 byte
|
||||
|
||||
``1`` = 2 bytes
|
||||
|
||||
``2`` = 4 bytes
|
||||
|
||||
``3`` = 8 bytes
|
||||
|
||||
``r7``: Number of elements.
|
||||
|
||||
``r8``: Operation. Supported values are:
|
||||
|
||||
``0``: copy
|
||||
|
||||
``1``: xor
|
||||
|
||||
Returns:
|
||||
|
||||
``H_SUCCESS``: Success.
|
||||
|
||||
``H_PARAMETER``: Invalid argument.
|
|
@ -1,78 +0,0 @@
|
|||
When used with the "pseries" machine type, QEMU-system-ppc64 implements
|
||||
a set of hypervisor calls using a subset of the server "PAPR" specification
|
||||
(IBM internal at this point), which is also what IBM's proprietary hypervisor
|
||||
adheres too.
|
||||
|
||||
The subset is selected based on the requirements of Linux as a guest.
|
||||
|
||||
In addition to those calls, we have added our own private hypervisor
|
||||
calls which are mostly used as a private interface between the firmware
|
||||
running in the guest and QEMU.
|
||||
|
||||
All those hypercalls start at hcall number 0xf000 which correspond
|
||||
to an implementation specific range in PAPR.
|
||||
|
||||
- H_RTAS (0xf000)
|
||||
|
||||
RTAS is a set of runtime services generally provided by the firmware
|
||||
inside the guest to the operating system. It predates the existence
|
||||
of hypervisors (it was originally an extension to Open Firmware) and
|
||||
is still used by PAPR to provide various services that aren't performance
|
||||
sensitive.
|
||||
|
||||
We currently implement the RTAS services in QEMU itself. The actual RTAS
|
||||
"firmware" blob in the guest is a small stub of a few instructions which
|
||||
calls our private H_RTAS hypervisor call to pass the RTAS calls to QEMU.
|
||||
|
||||
Arguments:
|
||||
|
||||
r3 : H_RTAS (0xf000)
|
||||
r4 : Guest physical address of RTAS parameter block
|
||||
|
||||
Returns:
|
||||
|
||||
H_SUCCESS : Successfully called the RTAS function (RTAS result
|
||||
will have been stored in the parameter block)
|
||||
H_PARAMETER : Unknown token
|
||||
|
||||
- H_LOGICAL_MEMOP (0xf001)
|
||||
|
||||
When the guest runs in "real mode" (in powerpc lingua this means
|
||||
with MMU disabled, ie guest effective == guest physical), it only
|
||||
has access to a subset of memory and no IOs.
|
||||
|
||||
PAPR provides a set of hypervisor calls to perform cacheable or
|
||||
non-cacheable accesses to any guest physical addresses that the
|
||||
guest can use in order to access IO devices while in real mode.
|
||||
|
||||
This is typically used by the firmware running in the guest.
|
||||
|
||||
However, doing a hypercall for each access is extremely inefficient
|
||||
(even more so when running KVM) when accessing the frame buffer. In
|
||||
that case, things like scrolling become unusably slow.
|
||||
|
||||
This hypercall allows the guest to request a "memory op" to be applied
|
||||
to memory. The supported memory ops at this point are to copy a range
|
||||
of memory (supports overlap of source and destination) and XOR which
|
||||
is used by our SLOF firmware to invert the screen.
|
||||
|
||||
Arguments:
|
||||
|
||||
r3: H_LOGICAL_MEMOP (0xf001)
|
||||
r4: Guest physical address of destination
|
||||
r5: Guest physical address of source
|
||||
r6: Individual element size
|
||||
0 = 1 byte
|
||||
1 = 2 bytes
|
||||
2 = 4 bytes
|
||||
3 = 8 bytes
|
||||
r7: Number of elements
|
||||
r8: Operation
|
||||
0 = copy
|
||||
1 = xor
|
||||
|
||||
Returns:
|
||||
|
||||
H_SUCCESS : Success
|
||||
H_PARAMETER : Invalid argument
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
PowerNV family boards (``powernv8``, ``powernv9``)
|
||||
PowerNV family boards (``powernv8``, ``powernv9``, ``powernv10``)
|
||||
==================================================================
|
||||
|
||||
PowerNV (as Non-Virtualized) is the "baremetal" platform using the
|
||||
PowerNV (as Non-Virtualized) is the "bare metal" platform using the
|
||||
OPAL firmware. It runs Linux on IBM and OpenPOWER systems and it can
|
||||
be used as an hypervisor OS, running KVM guests, or simply as a host
|
||||
OS.
|
||||
|
@ -16,16 +16,14 @@ Supported devices
|
|||
-----------------
|
||||
|
||||
* Multi processor support for POWER8, POWER8NVL and POWER9.
|
||||
* XSCOM, serial communication sideband bus to configure chiplets
|
||||
* Simple LPC Controller
|
||||
* Processor Service Interface (PSI) Controller
|
||||
* Interrupt Controller, XICS (POWER8) and XIVE (POWER9)
|
||||
* POWER8 PHB3 PCIe Host bridge and POWER9 PHB4 PCIe Host bridge
|
||||
* Simple OCC is an on-chip microcontroller used for power management
|
||||
tasks
|
||||
* iBT device to handle BMC communication, with the internal BMC
|
||||
simulator provided by QEMU or an external BMC such as an Aspeed
|
||||
QEMU machine.
|
||||
* XSCOM, serial communication sideband bus to configure chiplets.
|
||||
* Simple LPC Controller.
|
||||
* Processor Service Interface (PSI) Controller.
|
||||
* Interrupt Controller, XICS (POWER8) and XIVE (POWER9) and XIVE2 (Power10).
|
||||
* POWER8 PHB3 PCIe Host bridge and POWER9 PHB4 PCIe Host bridge.
|
||||
* Simple OCC is an on-chip micro-controller used for power management tasks.
|
||||
* iBT device to handle BMC communication, with the internal BMC simulator
|
||||
provided by QEMU or an external BMC such as an Aspeed QEMU machine.
|
||||
* PNOR containing the different firmware partitions.
|
||||
|
||||
Missing devices
|
||||
|
@ -33,31 +31,42 @@ Missing devices
|
|||
|
||||
A lot is missing, among which :
|
||||
|
||||
* POWER10 processor
|
||||
* XIVE2 (POWER10) interrupt controller
|
||||
* I2C controllers (yet to be merged)
|
||||
* NPU/NPU2/NPU3 controllers
|
||||
* EEH support for PCIe Host bridge controllers
|
||||
* NX controller
|
||||
* VAS controller
|
||||
* chipTOD (Time Of Day)
|
||||
* I2C controllers (yet to be merged).
|
||||
* NPU/NPU2/NPU3 controllers.
|
||||
* EEH support for PCIe Host bridge controllers.
|
||||
* NX controller.
|
||||
* VAS controller.
|
||||
* chipTOD (Time Of Day).
|
||||
* Self Boot Engine (SBE).
|
||||
* FSI bus
|
||||
* FSI bus.
|
||||
|
||||
Firmware
|
||||
--------
|
||||
|
||||
The OPAL firmware (OpenPower Abstraction Layer) for OpenPower systems
|
||||
includes the runtime services ``skiboot`` and the bootloader kernel and
|
||||
initramfs ``skiroot``. Source code can be found on GitHub:
|
||||
initramfs ``skiroot``. Source code can be found on the `OpenPOWER account at
|
||||
GitHub <https://github.com/open-power>`_.
|
||||
|
||||
https://github.com/open-power.
|
||||
|
||||
Prebuilt images of ``skiboot`` and ``skiroot`` are made available on the `OpenPOWER <https://github.com/open-power/op-build/releases/>`__ site.
|
||||
Prebuilt images of ``skiboot`` and ``skiroot`` are made available on the
|
||||
`OpenPOWER <https://github.com/open-power/op-build/releases/>`__ site.
|
||||
|
||||
QEMU includes a prebuilt image of ``skiboot`` which is updated when a
|
||||
more recent version is required by the models.
|
||||
|
||||
Current acceleration status
|
||||
---------------------------
|
||||
|
||||
KVM acceleration in Linux Power hosts is provided by the kvm-hv and
|
||||
kvm-pr modules. kvm-hv is adherent to PAPR and it's not compliant with
|
||||
powernv. kvm-pr in theory could be used as a valid accel option but
|
||||
this isn't supported by kvm-pr at this moment.
|
||||
|
||||
To spare users from dealing with not so informative errors when attempting
|
||||
to use accel=kvm, the powernv machine will throw an error informing that
|
||||
KVM is not supported. This can be revisited in the future if kvm-pr (or
|
||||
any other KVM alternative) is usable as KVM accel for this machine.
|
||||
|
||||
Boot options
|
||||
------------
|
||||
|
||||
|
@ -83,6 +92,7 @@ and a SATA disk :
|
|||
|
||||
Complex PCIe configuration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Six PHBs are defined per chip (POWER9) but no default PCI layout is
|
||||
provided (to be compatible with libvirt). One PCI device can be added
|
||||
on any of the available PCIe slots using command line options such as:
|
||||
|
@ -157,7 +167,7 @@ one on the command line :
|
|||
The files `palmetto-SDR.bin <http://www.kaod.org/qemu/powernv/palmetto-SDR.bin>`__
|
||||
and `palmetto-FRU.bin <http://www.kaod.org/qemu/powernv/palmetto-FRU.bin>`__
|
||||
define a Sensor Data Record repository and a Field Replaceable Unit
|
||||
inventory for a palmetto BMC. They can be used to extend the QEMU BMC
|
||||
inventory for a Palmetto BMC. They can be used to extend the QEMU BMC
|
||||
simulator.
|
||||
|
||||
.. code-block:: bash
|
||||
|
@ -189,4 +199,8 @@ CAVEATS
|
|||
-------
|
||||
|
||||
* No support for multiple HW threads (SMT=1). Same as pseries.
|
||||
* CPU can hang when doing intensive I/Os. Use ``-append powersave=off`` in that case.
|
||||
|
||||
Maintainer contact information
|
||||
------------------------------
|
||||
|
||||
Cédric Le Goater <clg@kaod.org>
|
||||
|
|
|
@ -1,12 +1,238 @@
|
|||
pSeries family boards (``pseries``)
|
||||
===================================
|
||||
|
||||
The Power machine para-virtualized environment described by the `Linux on Power
|
||||
Architecture Reference document (LoPAR)
|
||||
<https://openpowerfoundation.org/wp-content/uploads/2020/07/LoPAR-20200812.pdf>`_
|
||||
is called pSeries. This environment is also known as sPAPR, System p guests, or
|
||||
simply Power Linux guests (although it is capable of running other operating
|
||||
systems, such as AIX).
|
||||
|
||||
Even though pSeries is designed to behave as a guest environment, it is also
|
||||
capable of acting as a hypervisor OS, providing, on that role, nested
|
||||
virtualization capabilities.
|
||||
|
||||
Supported devices
|
||||
-----------------
|
||||
|
||||
* Multi processor support for many Power processors generations: POWER7,
|
||||
POWER7+, POWER8, POWER8NVL, POWER9, and Power10. Support for POWER5+ exists,
|
||||
but its state is unknown.
|
||||
* Interrupt Controller, XICS (POWER8) and XIVE (POWER9 and Power10)
|
||||
* vPHB PCIe Host bridge.
|
||||
* vscsi and vnet devices, compatible with the same devices available on a
|
||||
PowerVM hypervisor with VIOS managing LPARs.
|
||||
* Virtio based devices.
|
||||
* PCIe device pass through.
|
||||
|
||||
Missing devices
|
||||
---------------
|
||||
|
||||
* SPICE support.
|
||||
|
||||
Firmware
|
||||
--------
|
||||
|
||||
`SLOF <https://github.com/aik/SLOF>`_ (Slimline Open Firmware) is an
|
||||
implementation of the `IEEE 1275-1994, Standard for Boot (Initialization
|
||||
Configuration) Firmware: Core Requirements and Practices
|
||||
<https://standards.ieee.org/standard/1275-1994.html>`_.
|
||||
|
||||
QEMU includes a prebuilt image of SLOF which is updated when a more recent
|
||||
version is required.
|
||||
|
||||
Build directions
|
||||
----------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./configure --target-list=ppc64-softmmu && make
|
||||
|
||||
Running instructions
|
||||
--------------------
|
||||
|
||||
Someone can select the pSeries machine type by running QEMU with the following
|
||||
options:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
qemu-system-ppc64 -M pseries <other QEMU arguments>
|
||||
|
||||
sPAPR devices
|
||||
-------------
|
||||
|
||||
The sPAPR specification defines a set of para-virtualized devices, which are
|
||||
also supported by the pSeries machine in QEMU and can be instantiated with the
|
||||
``-device`` option:
|
||||
|
||||
* ``spapr-vlan`` : a virtual network interface.
|
||||
* ``spapr-vscsi`` : a virtual SCSI disk interface.
|
||||
* ``spapr-rng`` : a pseudo-device for passing random number generator data to the
|
||||
guest (see the `H_RANDOM hypercall feature
|
||||
<https://wiki.qemu.org/Features/HRandomHypercall>`_ for details).
|
||||
* ``spapr-vty``: a virtual teletype.
|
||||
* ``spapr-pci-host-bridge``: a PCI host bridge.
|
||||
* ``tpm-spapr``: a Trusted Platform Module (TPM).
|
||||
* ``spapr-tpm-proxy``: a TPM proxy.
|
||||
|
||||
These are compatible with the devices historically available for use when
|
||||
running the IBM PowerVM hypervisor with LPARs.
|
||||
|
||||
However, since these devices have originally been specified with another
|
||||
hypervisor and non-Linux guests in mind, you should use the virtio counterparts
|
||||
(virtio-net, virtio-blk/scsi and virtio-rng for instance) if possible instead,
|
||||
since they will most probably give you better performance with Linux guests in a
|
||||
QEMU environment.
|
||||
|
||||
The pSeries machine in QEMU is always instantiated with the following devices:
|
||||
|
||||
* A NVRAM device (``spapr-nvram``).
|
||||
* A virtual teletype (``spapr-vty``).
|
||||
* A PCI host bridge (``spapr-pci-host-bridge``).
|
||||
|
||||
Hence, it is not needed to add them manually, unless you use the ``-nodefaults``
|
||||
command line option in QEMU.
|
||||
|
||||
In the case of the default ``spapr-nvram`` device, if someone wants to make the
|
||||
contents of the NVRAM device persistent, they will need to specify a PFLASH
|
||||
device when starting QEMU, i.e. either use
|
||||
``-drive if=pflash,file=<filename>,format=raw`` to set the default PFLASH
|
||||
device, or specify one with an ID
|
||||
(``-drive if=none,file=<filename>,format=raw,id=pfid``) and pass that ID to the
|
||||
NVRAM device with ``-global spapr-nvram.drive=pfid``.
|
||||
|
||||
sPAPR specification
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The main source of documentation on the sPAPR standard is the `Linux on Power
|
||||
Architecture Reference document (LoPAR)
|
||||
<https://openpowerfoundation.org/wp-content/uploads/2020/07/LoPAR-20200812.pdf>`_.
|
||||
However, documentation specific to QEMU's implementation of the specification
|
||||
can also be found in QEMU documentation:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
../../specs/ppc-spapr-hcalls.rst
|
||||
../../specs/ppc-spapr-numa.rst
|
||||
../../specs/ppc-spapr-xive.rst
|
||||
|
||||
Other documentation available in QEMU docs directory:
|
||||
|
||||
* Hot plug (``/docs/specs/ppc-spapr-hotplug.txt``).
|
||||
* Hypervisor calls needed by the Ultravisor
|
||||
(``/docs/specs/ppc-spapr-uv-hcalls.txt``).
|
||||
|
||||
Switching between the KVM-PR and KVM-HV kernel module
|
||||
-----------------------------------------------------
|
||||
|
||||
Currently, there are two implementations of KVM on Power, ``kvm_hv.ko`` and
|
||||
``kvm_pr.ko``.
|
||||
|
||||
|
||||
If a host supports both KVM modes, and both KVM kernel modules are loaded, it is
|
||||
possible to switch between the two modes with the ``kvm-type`` parameter:
|
||||
|
||||
* Use ``qemu-system-ppc64 -M pseries,accel=kvm,kvm-type=PR`` to use the
|
||||
``kvm_pr.ko`` kernel module.
|
||||
* Use ``qemu-system-ppc64 -M pseries,accel=kvm,kvm-type=HV`` to use ``kvm_hv.ko``
|
||||
instead.
|
||||
|
||||
KVM-PR
|
||||
^^^^^^
|
||||
|
||||
KVM-PR uses the so-called **PR**\ oblem state of the PPC CPUs to run the guests,
|
||||
i.e. the virtual machine is run in user mode and all privileged instructions
|
||||
trap and have to be emulated by the host. That means you can run KVM-PR inside
|
||||
a pSeries guest (or a PowerVM LPAR for that matter), and that is where it has
|
||||
originated, as historically (prior to POWER7) it was not possible to run Linux
|
||||
on hypervisor mode on a Power processor (this function was restricted to
|
||||
PowerVM, the IBM proprietary hypervisor).
|
||||
|
||||
Because all privileged instructions are trapped, guests that use a lot of
|
||||
privileged instructions run quite slow with KVM-PR. On the other hand, because
|
||||
of that, this kernel module can run on pretty much every PPC hardware, and is
|
||||
able to emulate a lot of guests CPUs. This module can even be used to run other
|
||||
PowerPC guests like an emulated PowerMac.
|
||||
|
||||
As KVM-PR can be run inside a pSeries guest, it can also provide nested
|
||||
virtualization capabilities (i.e. running a guest from within a guest).
|
||||
|
||||
It is important to notice that, as KVM-HV provides a much better execution
|
||||
performance, maintenance work has been much more focused on it in the past
|
||||
years. Maintenance for KVM-PR has been minimal.
|
||||
|
||||
In order to run KVM-PR guests with POWER9 processors, someone will need to start
|
||||
QEMU with ``kernel_irqchip=off`` command line option.
|
||||
|
||||
KVM-HV
|
||||
^^^^^^
|
||||
|
||||
KVM-HV uses the hypervisor mode of more recent Power processors, that allow
|
||||
access to the bare metal hardware directly. Although POWER7 had this capability,
|
||||
it was only starting with POWER8 that this was officially supported by IBM.
|
||||
|
||||
Originally, KVM-HV was only available when running on a PowerNV platform (a.k.a.
|
||||
Power bare metal). Although it runs on a PowerNV platform, it can only be used
|
||||
to start pSeries guests. As the pSeries guest doesn't have access to the
|
||||
hypervisor mode of the Power CPU, it wasn't possible to run KVM-HV on a guest.
|
||||
This limitation has been lifted, and now it is possible to run KVM-HV inside
|
||||
pSeries guests as well, making nested virtualization possible with KVM-HV.
|
||||
|
||||
As KVM-HV has access to privileged instructions, guests that use a lot of these
|
||||
can run much faster than with KVM-PR. On the other hand, the guest CPU has to be
|
||||
of the same type as the host CPU this way, e.g. it is not possible to specify an
|
||||
embedded PPC CPU for the guest with KVM-HV. However, there is at least the
|
||||
possibility to run the guest in a backward-compatibility mode of the previous
|
||||
CPUs generations, e.g. you can run a POWER7 guest on a POWER8 host by using
|
||||
``-cpu POWER8,compat=power7`` as parameter to QEMU.
|
||||
|
||||
Modules support
|
||||
---------------
|
||||
|
||||
As noticed in the sections above, each module can run in a different
|
||||
environment. The following table shows with which environment each module can
|
||||
run. As long as you are in a supported environment, you can run KVM-PR or KVM-HV
|
||||
nested. Combinations not shown in the table are not available.
|
||||
|
||||
+--------------+------------+------+-------------------+----------+--------+
|
||||
| Platform | Host type | Bits | Page table format | KVM-HV | KVM-PR |
|
||||
+==============+============+======+===================+==========+========+
|
||||
| PowerNV | bare metal | 32 | hash | no | yes |
|
||||
| | | +-------------------+----------+--------+
|
||||
| | | | radix | N/A | N/A |
|
||||
| | +------+-------------------+----------+--------+
|
||||
| | | 64 | hash | yes | yes |
|
||||
| | | +-------------------+----------+--------+
|
||||
| | | | radix | yes | no |
|
||||
+--------------+------------+------+-------------------+----------+--------+
|
||||
| pSeries [1]_ | PowerNV | 32 | hash | no | yes |
|
||||
| | | +-------------------+----------+--------+
|
||||
| | | | radix | N/A | N/A |
|
||||
| | +------+-------------------+----------+--------+
|
||||
| | | 64 | hash | no | yes |
|
||||
| | | +-------------------+----------+--------+
|
||||
| | | | radix | yes [2]_ | no |
|
||||
| +------------+------+-------------------+----------+--------+
|
||||
| | PowerVM | 32 | hash | no | yes |
|
||||
| | | +-------------------+----------+--------+
|
||||
| | | | radix | N/A | N/A |
|
||||
| | +------+-------------------+----------+--------+
|
||||
| | | 64 | hash | no | yes |
|
||||
| | | +-------------------+----------+--------+
|
||||
| | | | radix [3]_ | no | yes |
|
||||
+--------------+------------+------+-------------------+----------+--------+
|
||||
|
||||
.. [1] On POWER9 DD2.1 processors, the page table format on the host and guest
|
||||
must be the same.
|
||||
|
||||
.. [2] KVM-HV cannot run nested on POWER8 machines.
|
||||
|
||||
.. [3] Introduced on Power10 machines.
|
||||
|
||||
Maintainer contact information
|
||||
------------------------------
|
||||
|
||||
Cédric Le Goater <clg@kaod.org>
|
||||
|
||||
Daniel Henrique Barboza <danielhb413@gmail.com>
|
||||
|
|
|
@ -19,7 +19,7 @@ static void partsN(return_nan)(FloatPartsN *a, float_status *s)
|
|||
{
|
||||
switch (a->cls) {
|
||||
case float_class_snan:
|
||||
float_raise(float_flag_invalid, s);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_snan, s);
|
||||
if (s->default_nan_mode) {
|
||||
parts_default_nan(a, s);
|
||||
} else {
|
||||
|
@ -40,7 +40,7 @@ static FloatPartsN *partsN(pick_nan)(FloatPartsN *a, FloatPartsN *b,
|
|||
float_status *s)
|
||||
{
|
||||
if (is_snan(a->cls) || is_snan(b->cls)) {
|
||||
float_raise(float_flag_invalid, s);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_snan, s);
|
||||
}
|
||||
|
||||
if (s->default_nan_mode) {
|
||||
|
@ -68,7 +68,7 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b,
|
|||
int which;
|
||||
|
||||
if (unlikely(abc_mask & float_cmask_snan)) {
|
||||
float_raise(float_flag_invalid, s);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_snan, s);
|
||||
}
|
||||
|
||||
which = pickNaNMulAdd(a->cls, b->cls, c->cls,
|
||||
|
@ -354,7 +354,7 @@ static FloatPartsN *partsN(addsub)(FloatPartsN *a, FloatPartsN *b,
|
|||
return a;
|
||||
}
|
||||
/* Inf - Inf */
|
||||
float_raise(float_flag_invalid, s);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_isi, s);
|
||||
parts_default_nan(a, s);
|
||||
return a;
|
||||
}
|
||||
|
@ -423,7 +423,7 @@ static FloatPartsN *partsN(mul)(FloatPartsN *a, FloatPartsN *b,
|
|||
|
||||
/* Inf * Zero == NaN */
|
||||
if (unlikely(ab_mask == float_cmask_infzero)) {
|
||||
float_raise(float_flag_invalid, s);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_imz, s);
|
||||
parts_default_nan(a, s);
|
||||
return a;
|
||||
}
|
||||
|
@ -489,11 +489,13 @@ static FloatPartsN *partsN(muladd)(FloatPartsN *a, FloatPartsN *b,
|
|||
|
||||
if (unlikely(ab_mask != float_cmask_normal)) {
|
||||
if (unlikely(ab_mask == float_cmask_infzero)) {
|
||||
float_raise(float_flag_invalid | float_flag_invalid_imz, s);
|
||||
goto d_nan;
|
||||
}
|
||||
|
||||
if (ab_mask & float_cmask_inf) {
|
||||
if (c->cls == float_class_inf && a->sign != c->sign) {
|
||||
float_raise(float_flag_invalid | float_flag_invalid_isi, s);
|
||||
goto d_nan;
|
||||
}
|
||||
goto return_inf;
|
||||
|
@ -566,7 +568,6 @@ static FloatPartsN *partsN(muladd)(FloatPartsN *a, FloatPartsN *b,
|
|||
goto finish_sign;
|
||||
|
||||
d_nan:
|
||||
float_raise(float_flag_invalid, s);
|
||||
parts_default_nan(a, s);
|
||||
return a;
|
||||
}
|
||||
|
@ -589,11 +590,13 @@ static FloatPartsN *partsN(div)(FloatPartsN *a, FloatPartsN *b,
|
|||
}
|
||||
|
||||
/* 0/0 or Inf/Inf => NaN */
|
||||
if (unlikely(ab_mask == float_cmask_zero) ||
|
||||
unlikely(ab_mask == float_cmask_inf)) {
|
||||
float_raise(float_flag_invalid, s);
|
||||
parts_default_nan(a, s);
|
||||
return a;
|
||||
if (unlikely(ab_mask == float_cmask_zero)) {
|
||||
float_raise(float_flag_invalid | float_flag_invalid_zdz, s);
|
||||
goto d_nan;
|
||||
}
|
||||
if (unlikely(ab_mask == float_cmask_inf)) {
|
||||
float_raise(float_flag_invalid | float_flag_invalid_idi, s);
|
||||
goto d_nan;
|
||||
}
|
||||
|
||||
/* All the NaN cases */
|
||||
|
@ -624,6 +627,10 @@ static FloatPartsN *partsN(div)(FloatPartsN *a, FloatPartsN *b,
|
|||
float_raise(float_flag_divbyzero, s);
|
||||
a->cls = float_class_inf;
|
||||
return a;
|
||||
|
||||
d_nan:
|
||||
parts_default_nan(a, s);
|
||||
return a;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -862,7 +869,7 @@ static void partsN(sqrt)(FloatPartsN *a, float_status *status,
|
|||
return;
|
||||
|
||||
d_nan:
|
||||
float_raise(float_flag_invalid, status);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_sqrt, status);
|
||||
parts_default_nan(a, status);
|
||||
}
|
||||
|
||||
|
@ -1042,13 +1049,15 @@ static int64_t partsN(float_to_sint)(FloatPartsN *p, FloatRoundMode rmode,
|
|||
|
||||
switch (p->cls) {
|
||||
case float_class_snan:
|
||||
flags |= float_flag_invalid_snan;
|
||||
/* fall through */
|
||||
case float_class_qnan:
|
||||
flags = float_flag_invalid;
|
||||
flags |= float_flag_invalid;
|
||||
r = max;
|
||||
break;
|
||||
|
||||
case float_class_inf:
|
||||
flags = float_flag_invalid;
|
||||
flags = float_flag_invalid | float_flag_invalid_cvti;
|
||||
r = p->sign ? min : max;
|
||||
break;
|
||||
|
||||
|
@ -1070,11 +1079,11 @@ static int64_t partsN(float_to_sint)(FloatPartsN *p, FloatRoundMode rmode,
|
|||
if (r <= -(uint64_t)min) {
|
||||
r = -r;
|
||||
} else {
|
||||
flags = float_flag_invalid;
|
||||
flags = float_flag_invalid | float_flag_invalid_cvti;
|
||||
r = min;
|
||||
}
|
||||
} else if (r > max) {
|
||||
flags = float_flag_invalid;
|
||||
flags = float_flag_invalid | float_flag_invalid_cvti;
|
||||
r = max;
|
||||
}
|
||||
break;
|
||||
|
@ -1107,13 +1116,15 @@ static uint64_t partsN(float_to_uint)(FloatPartsN *p, FloatRoundMode rmode,
|
|||
|
||||
switch (p->cls) {
|
||||
case float_class_snan:
|
||||
flags |= float_flag_invalid_snan;
|
||||
/* fall through */
|
||||
case float_class_qnan:
|
||||
flags = float_flag_invalid;
|
||||
flags |= float_flag_invalid;
|
||||
r = max;
|
||||
break;
|
||||
|
||||
case float_class_inf:
|
||||
flags = float_flag_invalid;
|
||||
flags = float_flag_invalid | float_flag_invalid_cvti;
|
||||
r = p->sign ? 0 : max;
|
||||
break;
|
||||
|
||||
|
@ -1131,15 +1142,15 @@ static uint64_t partsN(float_to_uint)(FloatPartsN *p, FloatRoundMode rmode,
|
|||
}
|
||||
|
||||
if (p->sign) {
|
||||
flags = float_flag_invalid;
|
||||
flags = float_flag_invalid | float_flag_invalid_cvti;
|
||||
r = 0;
|
||||
} else if (p->exp > DECOMPOSED_BINARY_POINT) {
|
||||
flags = float_flag_invalid;
|
||||
flags = float_flag_invalid | float_flag_invalid_cvti;
|
||||
r = max;
|
||||
} else {
|
||||
r = p->frac_hi >> (DECOMPOSED_BINARY_POINT - p->exp);
|
||||
if (r > max) {
|
||||
flags = float_flag_invalid;
|
||||
flags = float_flag_invalid | float_flag_invalid_cvti;
|
||||
r = max;
|
||||
}
|
||||
}
|
||||
|
@ -1334,7 +1345,9 @@ static FloatRelation partsN(compare)(FloatPartsN *a, FloatPartsN *b,
|
|||
}
|
||||
|
||||
if (unlikely(ab_mask & float_cmask_anynan)) {
|
||||
if (!is_quiet || (ab_mask & float_cmask_snan)) {
|
||||
if (ab_mask & float_cmask_snan) {
|
||||
float_raise(float_flag_invalid | float_flag_invalid_snan, s);
|
||||
} else if (!is_quiet) {
|
||||
float_raise(float_flag_invalid, s);
|
||||
}
|
||||
return float_relation_unordered;
|
||||
|
|
|
@ -506,7 +506,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
|||
* the default NaN
|
||||
*/
|
||||
if (infzero && is_qnan(c_cls)) {
|
||||
float_raise(float_flag_invalid, status);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_imz, status);
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
@ -533,7 +533,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
|||
* case sets InvalidOp and returns the default NaN
|
||||
*/
|
||||
if (infzero) {
|
||||
float_raise(float_flag_invalid, status);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_imz, status);
|
||||
return 3;
|
||||
}
|
||||
/* Prefer sNaN over qNaN, in the a, b, c order. */
|
||||
|
@ -556,7 +556,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
|||
* case sets InvalidOp and returns the input value 'c'
|
||||
*/
|
||||
if (infzero) {
|
||||
float_raise(float_flag_invalid, status);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_imz, status);
|
||||
return 2;
|
||||
}
|
||||
/* Prefer sNaN over qNaN, in the c, a, b order. */
|
||||
|
@ -580,7 +580,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
|||
* a default NaN
|
||||
*/
|
||||
if (infzero) {
|
||||
float_raise(float_flag_invalid, status);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_imz, status);
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
@ -597,7 +597,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
|||
#elif defined(TARGET_RISCV)
|
||||
/* For RISC-V, InvalidOp is set when multiplicands are Inf and zero */
|
||||
if (infzero) {
|
||||
float_raise(float_flag_invalid, status);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_imz, status);
|
||||
}
|
||||
return 3; /* default NaN */
|
||||
#elif defined(TARGET_XTENSA)
|
||||
|
@ -606,7 +606,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
|||
* an input NaN if we have one (ie c).
|
||||
*/
|
||||
if (infzero) {
|
||||
float_raise(float_flag_invalid, status);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_imz, status);
|
||||
return 2;
|
||||
}
|
||||
if (status->use_first_nan) {
|
||||
|
|
114
fpu/softfloat.c
114
fpu/softfloat.c
|
@ -1693,6 +1693,50 @@ static float64 float64_round_pack_canonical(FloatParts64 *p,
|
|||
return float64_pack_raw(p);
|
||||
}
|
||||
|
||||
static float64 float64r32_round_pack_canonical(FloatParts64 *p,
|
||||
float_status *s)
|
||||
{
|
||||
parts_uncanon(p, s, &float32_params);
|
||||
|
||||
/*
|
||||
* In parts_uncanon, we placed the fraction for float32 at the lsb.
|
||||
* We need to adjust the fraction higher so that the least N bits are
|
||||
* zero, and the fraction is adjacent to the float64 implicit bit.
|
||||
*/
|
||||
switch (p->cls) {
|
||||
case float_class_normal:
|
||||
if (unlikely(p->exp == 0)) {
|
||||
/*
|
||||
* The result is denormal for float32, but can be represented
|
||||
* in normalized form for float64. Adjust, per canonicalize.
|
||||
*/
|
||||
int shift = frac_normalize(p);
|
||||
p->exp = (float32_params.frac_shift -
|
||||
float32_params.exp_bias - shift + 1 +
|
||||
float64_params.exp_bias);
|
||||
frac_shr(p, float64_params.frac_shift);
|
||||
} else {
|
||||
frac_shl(p, float32_params.frac_shift - float64_params.frac_shift);
|
||||
p->exp += float64_params.exp_bias - float32_params.exp_bias;
|
||||
}
|
||||
break;
|
||||
case float_class_snan:
|
||||
case float_class_qnan:
|
||||
frac_shl(p, float32_params.frac_shift - float64_params.frac_shift);
|
||||
p->exp = float64_params.exp_max;
|
||||
break;
|
||||
case float_class_inf:
|
||||
p->exp = float64_params.exp_max;
|
||||
break;
|
||||
case float_class_zero:
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
return float64_pack_raw(p);
|
||||
}
|
||||
|
||||
static void float128_unpack_canonical(FloatParts128 *p, float128 f,
|
||||
float_status *s)
|
||||
{
|
||||
|
@ -1938,6 +1982,28 @@ float64_sub(float64 a, float64 b, float_status *s)
|
|||
return float64_addsub(a, b, s, hard_f64_sub, soft_f64_sub);
|
||||
}
|
||||
|
||||
static float64 float64r32_addsub(float64 a, float64 b, float_status *status,
|
||||
bool subtract)
|
||||
{
|
||||
FloatParts64 pa, pb, *pr;
|
||||
|
||||
float64_unpack_canonical(&pa, a, status);
|
||||
float64_unpack_canonical(&pb, b, status);
|
||||
pr = parts_addsub(&pa, &pb, status, subtract);
|
||||
|
||||
return float64r32_round_pack_canonical(pr, status);
|
||||
}
|
||||
|
||||
float64 float64r32_add(float64 a, float64 b, float_status *status)
|
||||
{
|
||||
return float64r32_addsub(a, b, status, false);
|
||||
}
|
||||
|
||||
float64 float64r32_sub(float64 a, float64 b, float_status *status)
|
||||
{
|
||||
return float64r32_addsub(a, b, status, true);
|
||||
}
|
||||
|
||||
static bfloat16 QEMU_FLATTEN
|
||||
bfloat16_addsub(bfloat16 a, bfloat16 b, float_status *status, bool subtract)
|
||||
{
|
||||
|
@ -2069,6 +2135,17 @@ float64_mul(float64 a, float64 b, float_status *s)
|
|||
f64_is_zon2, f64_addsubmul_post);
|
||||
}
|
||||
|
||||
float64 float64r32_mul(float64 a, float64 b, float_status *status)
|
||||
{
|
||||
FloatParts64 pa, pb, *pr;
|
||||
|
||||
float64_unpack_canonical(&pa, a, status);
|
||||
float64_unpack_canonical(&pb, b, status);
|
||||
pr = parts_mul(&pa, &pb, status);
|
||||
|
||||
return float64r32_round_pack_canonical(pr, status);
|
||||
}
|
||||
|
||||
bfloat16 QEMU_FLATTEN
|
||||
bfloat16_mul(bfloat16 a, bfloat16 b, float_status *status)
|
||||
{
|
||||
|
@ -2296,6 +2373,19 @@ float64_muladd(float64 xa, float64 xb, float64 xc, int flags, float_status *s)
|
|||
return soft_f64_muladd(ua.s, ub.s, uc.s, flags, s);
|
||||
}
|
||||
|
||||
float64 float64r32_muladd(float64 a, float64 b, float64 c,
|
||||
int flags, float_status *status)
|
||||
{
|
||||
FloatParts64 pa, pb, pc, *pr;
|
||||
|
||||
float64_unpack_canonical(&pa, a, status);
|
||||
float64_unpack_canonical(&pb, b, status);
|
||||
float64_unpack_canonical(&pc, c, status);
|
||||
pr = parts_muladd(&pa, &pb, &pc, flags, status);
|
||||
|
||||
return float64r32_round_pack_canonical(pr, status);
|
||||
}
|
||||
|
||||
bfloat16 QEMU_FLATTEN bfloat16_muladd(bfloat16 a, bfloat16 b, bfloat16 c,
|
||||
int flags, float_status *status)
|
||||
{
|
||||
|
@ -2419,6 +2509,17 @@ float64_div(float64 a, float64 b, float_status *s)
|
|||
f64_div_pre, f64_div_post);
|
||||
}
|
||||
|
||||
float64 float64r32_div(float64 a, float64 b, float_status *status)
|
||||
{
|
||||
FloatParts64 pa, pb, *pr;
|
||||
|
||||
float64_unpack_canonical(&pa, a, status);
|
||||
float64_unpack_canonical(&pb, b, status);
|
||||
pr = parts_div(&pa, &pb, status);
|
||||
|
||||
return float64r32_round_pack_canonical(pr, status);
|
||||
}
|
||||
|
||||
bfloat16 QEMU_FLATTEN
|
||||
bfloat16_div(bfloat16 a, bfloat16 b, float_status *status)
|
||||
{
|
||||
|
@ -2543,8 +2644,10 @@ floatx80 floatx80_mod(floatx80 a, floatx80 b, float_status *status)
|
|||
static void parts_float_to_ahp(FloatParts64 *a, float_status *s)
|
||||
{
|
||||
switch (a->cls) {
|
||||
case float_class_qnan:
|
||||
case float_class_snan:
|
||||
float_raise(float_flag_invalid_snan, s);
|
||||
/* fall through */
|
||||
case float_class_qnan:
|
||||
/*
|
||||
* There is no NaN in the destination format. Raise Invalid
|
||||
* and return a zero with the sign of the input NaN.
|
||||
|
@ -4283,6 +4386,15 @@ float64 QEMU_FLATTEN float64_sqrt(float64 xa, float_status *s)
|
|||
return soft_f64_sqrt(ua.s, s);
|
||||
}
|
||||
|
||||
float64 float64r32_sqrt(float64 a, float_status *status)
|
||||
{
|
||||
FloatParts64 p;
|
||||
|
||||
float64_unpack_canonical(&p, a, status);
|
||||
parts_sqrt(&p, status, &float64_params);
|
||||
return float64r32_round_pack_canonical(&p, status);
|
||||
}
|
||||
|
||||
bfloat16 QEMU_FLATTEN bfloat16_sqrt(bfloat16 a, float_status *status)
|
||||
{
|
||||
FloatParts64 p;
|
||||
|
|
|
@ -243,7 +243,7 @@ static uint64_t ivshmem_io_read(void *opaque, hwaddr addr,
|
|||
static const MemoryRegionOps ivshmem_mmio_ops = {
|
||||
.read = ivshmem_io_read,
|
||||
.write = ivshmem_io_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
|
|
|
@ -993,7 +993,7 @@ static void pnv_phb3_realize(DeviceState *dev, Error **errp)
|
|||
PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
|
||||
int i;
|
||||
|
||||
if (phb->phb_id >= PNV8_CHIP_PHB3_MAX) {
|
||||
if (phb->phb_id >= PNV_CHIP_GET_CLASS(phb->chip)->num_phbs) {
|
||||
error_setg(errp, "invalid PHB index: %d", phb->phb_id);
|
||||
return;
|
||||
}
|
||||
|
@ -1092,6 +1092,7 @@ static const char *pnv_phb3_root_bus_path(PCIHostState *host_bridge,
|
|||
static Property pnv_phb3_properties[] = {
|
||||
DEFINE_PROP_UINT32("index", PnvPHB3, phb_id, 0),
|
||||
DEFINE_PROP_UINT32("chip-id", PnvPHB3, chip_id, 0),
|
||||
DEFINE_PROP_LINK("chip", PnvPHB3, chip, TYPE_PNV_CHIP, PnvChip *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
|
|
@ -284,6 +284,17 @@ static void pnv_pbcq_realize(DeviceState *dev, Error **errp)
|
|||
pnv_xscom_region_init(&pbcq->xscom_spci_regs, OBJECT(dev),
|
||||
&pnv_pbcq_spci_xscom_ops, pbcq, name,
|
||||
PNV_XSCOM_PBCQ_SPCI_SIZE);
|
||||
|
||||
/* Populate the XSCOM address space. */
|
||||
pnv_xscom_add_subregion(phb->chip,
|
||||
PNV_XSCOM_PBCQ_NEST_BASE + 0x400 * phb->phb_id,
|
||||
&pbcq->xscom_nest_regs);
|
||||
pnv_xscom_add_subregion(phb->chip,
|
||||
PNV_XSCOM_PBCQ_PCI_BASE + 0x400 * phb->phb_id,
|
||||
&pbcq->xscom_pci_regs);
|
||||
pnv_xscom_add_subregion(phb->chip,
|
||||
PNV_XSCOM_PBCQ_SPCI_BASE + 0x040 * phb->phb_id,
|
||||
&pbcq->xscom_spci_regs);
|
||||
}
|
||||
|
||||
static int pnv_pbcq_dt_xscom(PnvXScomInterface *dev, void *fdt,
|
||||
|
|
|
@ -1205,6 +1205,7 @@ static void pnv_phb4_realize(DeviceState *dev, Error **errp)
|
|||
&phb->pci_mmio, &phb->pci_io,
|
||||
0, 4, TYPE_PNV_PHB4_ROOT_BUS);
|
||||
pci_setup_iommu(pci->bus, pnv_phb4_dma_iommu, phb);
|
||||
pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE;
|
||||
|
||||
/* Add a single Root port */
|
||||
qdev_prop_set_uint8(DEVICE(&phb->root), "chassis", phb->chip_id);
|
||||
|
|
|
@ -124,7 +124,7 @@ static uint64_t pnv_pec_stk_nest_xscom_read(void *opaque, hwaddr addr,
|
|||
static void pnv_pec_stk_update_map(PnvPhb4PecStack *stack)
|
||||
{
|
||||
PnvPhb4PecState *pec = stack->pec;
|
||||
MemoryRegion *sysmem = pec->system_memory;
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
uint64_t bar_en = stack->nest_regs[PEC_NEST_STK_BAR_EN];
|
||||
uint64_t bar, mask, size;
|
||||
char name[64];
|
||||
|
@ -374,20 +374,41 @@ static void pnv_pec_instance_init(Object *obj)
|
|||
}
|
||||
}
|
||||
|
||||
static int pnv_pec_phb_offset(PnvPhb4PecState *pec)
|
||||
{
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
|
||||
int index = pec->index;
|
||||
int offset = 0;
|
||||
|
||||
while (index--) {
|
||||
offset += pecc->num_stacks[index];
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static void pnv_pec_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvPhb4PecState *pec = PNV_PHB4_PEC(dev);
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
|
||||
char name[64];
|
||||
int i;
|
||||
|
||||
assert(pec->system_memory);
|
||||
if (pec->index >= PNV_CHIP_GET_CLASS(pec->chip)->num_pecs) {
|
||||
error_setg(errp, "invalid PEC index: %d", pec->index);
|
||||
return;
|
||||
}
|
||||
|
||||
pec->num_stacks = pecc->num_stacks[pec->index];
|
||||
|
||||
/* Create stacks */
|
||||
for (i = 0; i < pec->num_stacks; i++) {
|
||||
PnvPhb4PecStack *stack = &pec->stacks[i];
|
||||
Object *stk_obj = OBJECT(stack);
|
||||
int phb_id = pnv_pec_phb_offset(pec) + i;
|
||||
|
||||
object_property_set_int(stk_obj, "stack-no", i, &error_abort);
|
||||
object_property_set_int(stk_obj, "phb-id", phb_id, &error_abort);
|
||||
object_property_set_link(stk_obj, "pec", OBJECT(pec), &error_abort);
|
||||
if (!qdev_realize(DEVICE(stk_obj), NULL, errp)) {
|
||||
return;
|
||||
|
@ -460,10 +481,9 @@ static int pnv_pec_dt_xscom(PnvXScomInterface *dev, void *fdt,
|
|||
|
||||
static Property pnv_pec_properties[] = {
|
||||
DEFINE_PROP_UINT32("index", PnvPhb4PecState, index, 0),
|
||||
DEFINE_PROP_UINT32("num-stacks", PnvPhb4PecState, num_stacks, 0),
|
||||
DEFINE_PROP_UINT32("chip-id", PnvPhb4PecState, chip_id, 0),
|
||||
DEFINE_PROP_LINK("system-memory", PnvPhb4PecState, system_memory,
|
||||
TYPE_MEMORY_REGION, MemoryRegion *),
|
||||
DEFINE_PROP_LINK("chip", PnvPhb4PecState, chip, TYPE_PNV_CHIP,
|
||||
PnvChip *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
@ -477,6 +497,13 @@ static uint32_t pnv_pec_xscom_nest_base(PnvPhb4PecState *pec)
|
|||
return PNV9_XSCOM_PEC_NEST_BASE + 0x400 * pec->index;
|
||||
}
|
||||
|
||||
/*
|
||||
* PEC0 -> 1 stack
|
||||
* PEC1 -> 2 stacks
|
||||
* PEC2 -> 3 stacks
|
||||
*/
|
||||
static const uint32_t pnv_pec_num_stacks[] = { 1, 2, 3 };
|
||||
|
||||
static void pnv_pec_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
@ -499,6 +526,9 @@ static void pnv_pec_class_init(ObjectClass *klass, void *data)
|
|||
pecc->compat_size = sizeof(compat);
|
||||
pecc->stk_compat = stk_compat;
|
||||
pecc->stk_compat_size = sizeof(stk_compat);
|
||||
pecc->version = PNV_PHB4_VERSION;
|
||||
pecc->device_id = PNV_PHB4_DEVICE_ID;
|
||||
pecc->num_stacks = pnv_pec_num_stacks;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_pec_type_info = {
|
||||
|
@ -519,12 +549,17 @@ static void pnv_pec_stk_instance_init(Object *obj)
|
|||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(obj);
|
||||
|
||||
object_initialize_child(obj, "phb", &stack->phb, TYPE_PNV_PHB4);
|
||||
object_property_add_alias(obj, "phb-id", OBJECT(&stack->phb), "index");
|
||||
}
|
||||
|
||||
static void pnv_pec_stk_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(dev);
|
||||
PnvPhb4PecState *pec = stack->pec;
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
|
||||
PnvChip *chip = pec->chip;
|
||||
uint32_t pec_nest_base;
|
||||
uint32_t pec_pci_base;
|
||||
char name[64];
|
||||
|
||||
assert(pec);
|
||||
|
@ -548,10 +583,32 @@ static void pnv_pec_stk_realize(DeviceState *dev, Error **errp)
|
|||
pnv_xscom_region_init(&stack->phb_regs_mr, OBJECT(&stack->phb),
|
||||
&pnv_phb4_xscom_ops, &stack->phb, name, 0x40);
|
||||
|
||||
/*
|
||||
* Let the machine/chip realize the PHB object to customize more
|
||||
* easily some fields
|
||||
*/
|
||||
object_property_set_int(OBJECT(&stack->phb), "chip-id", pec->chip_id,
|
||||
&error_fatal);
|
||||
object_property_set_int(OBJECT(&stack->phb), "version", pecc->version,
|
||||
&error_fatal);
|
||||
object_property_set_int(OBJECT(&stack->phb), "device-id", pecc->device_id,
|
||||
&error_fatal);
|
||||
object_property_set_link(OBJECT(&stack->phb), "stack", OBJECT(stack),
|
||||
&error_abort);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&stack->phb), errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
pec_nest_base = pecc->xscom_nest_base(pec);
|
||||
pec_pci_base = pecc->xscom_pci_base(pec);
|
||||
|
||||
/* Populate the XSCOM address space. */
|
||||
pnv_xscom_add_subregion(chip,
|
||||
pec_nest_base + 0x40 * (stack->stack_no + 1),
|
||||
&stack->nest_regs_mr);
|
||||
pnv_xscom_add_subregion(chip,
|
||||
pec_pci_base + 0x40 * (stack->stack_no + 1),
|
||||
&stack->pci_regs_mr);
|
||||
pnv_xscom_add_subregion(chip,
|
||||
pec_pci_base + PNV9_XSCOM_PEC_PCI_STK0 +
|
||||
0x40 * stack->stack_no,
|
||||
&stack->phb_regs_mr);
|
||||
}
|
||||
|
||||
static Property pnv_pec_stk_properties[] = {
|
||||
|
|
|
@ -36,9 +36,6 @@
|
|||
#include "hw/pci-host/uninorth.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
/* SMP is not enabled, for now */
|
||||
#define MAX_CPUS 1
|
||||
|
||||
#define NVRAM_SIZE 0x2000
|
||||
#define PROM_FILENAME "openbios-ppc"
|
||||
|
||||
|
|
|
@ -581,7 +581,8 @@ static void core99_machine_class_init(ObjectClass *oc, void *data)
|
|||
mc->desc = "Mac99 based PowerMAC";
|
||||
mc->init = ppc_core99_init;
|
||||
mc->block_default_type = IF_IDE;
|
||||
mc->max_cpus = MAX_CPUS;
|
||||
/* SMP is not supported currently */
|
||||
mc->max_cpus = 1;
|
||||
mc->default_boot_order = "cd";
|
||||
mc->default_display = "std";
|
||||
mc->kvm_type = core99_kvm_type;
|
||||
|
|
|
@ -423,7 +423,8 @@ static void heathrow_class_init(ObjectClass *oc, void *data)
|
|||
mc->desc = "Heathrow based PowerMAC";
|
||||
mc->init = ppc_heathrow_init;
|
||||
mc->block_default_type = IF_IDE;
|
||||
mc->max_cpus = MAX_CPUS;
|
||||
/* SMP is not supported currently */
|
||||
mc->max_cpus = 1;
|
||||
#ifndef TARGET_PPC64
|
||||
mc->is_default = true;
|
||||
#endif
|
||||
|
|
177
hw/ppc/pnv.c
177
hw/ppc/pnv.c
|
@ -522,7 +522,7 @@ static void *pnv_dt_create(MachineState *machine)
|
|||
buf = qemu_uuid_unparse_strdup(&qemu_uuid);
|
||||
_FDT((fdt_setprop_string(fdt, 0, "vm,uuid", buf)));
|
||||
if (qemu_uuid_set) {
|
||||
_FDT((fdt_property_string(fdt, "system-id", buf)));
|
||||
_FDT((fdt_setprop_string(fdt, 0, "system-id", buf)));
|
||||
}
|
||||
g_free(buf);
|
||||
|
||||
|
@ -638,32 +638,47 @@ static ISABus *pnv_isa_create(PnvChip *chip, Error **errp)
|
|||
return PNV_CHIP_GET_CLASS(chip)->isa_create(chip, errp);
|
||||
}
|
||||
|
||||
static int pnv_chip_power8_pic_print_info_child(Object *child, void *opaque)
|
||||
{
|
||||
Monitor *mon = opaque;
|
||||
PnvPHB3 *phb3 = (PnvPHB3 *) object_dynamic_cast(child, TYPE_PNV_PHB3);
|
||||
|
||||
if (phb3) {
|
||||
pnv_phb3_msi_pic_print_info(&phb3->msis, mon);
|
||||
ics_pic_print_info(&phb3->lsis, mon);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pnv_chip_power8_pic_print_info(PnvChip *chip, Monitor *mon)
|
||||
{
|
||||
Pnv8Chip *chip8 = PNV8_CHIP(chip);
|
||||
int i;
|
||||
|
||||
ics_pic_print_info(&chip8->psi.ics, mon);
|
||||
for (i = 0; i < chip->num_phbs; i++) {
|
||||
pnv_phb3_msi_pic_print_info(&chip8->phbs[i].msis, mon);
|
||||
ics_pic_print_info(&chip8->phbs[i].lsis, mon);
|
||||
object_child_foreach(OBJECT(chip),
|
||||
pnv_chip_power8_pic_print_info_child, mon);
|
||||
}
|
||||
|
||||
static int pnv_chip_power9_pic_print_info_child(Object *child, void *opaque)
|
||||
{
|
||||
Monitor *mon = opaque;
|
||||
PnvPHB4 *phb4 = (PnvPHB4 *) object_dynamic_cast(child, TYPE_PNV_PHB4);
|
||||
|
||||
if (phb4) {
|
||||
pnv_phb4_pic_print_info(phb4, mon);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pnv_chip_power9_pic_print_info(PnvChip *chip, Monitor *mon)
|
||||
{
|
||||
Pnv9Chip *chip9 = PNV9_CHIP(chip);
|
||||
int i, j;
|
||||
|
||||
pnv_xive_pic_print_info(&chip9->xive, mon);
|
||||
pnv_psi_pic_print_info(&chip9->psi, mon);
|
||||
|
||||
for (i = 0; i < PNV9_CHIP_MAX_PEC; i++) {
|
||||
PnvPhb4PecState *pec = &chip9->pecs[i];
|
||||
for (j = 0; j < pec->num_stacks; j++) {
|
||||
pnv_phb4_pic_print_info(&pec->stacks[j].phb, mon);
|
||||
}
|
||||
}
|
||||
object_child_foreach_recursive(OBJECT(chip),
|
||||
pnv_chip_power9_pic_print_info_child, mon);
|
||||
}
|
||||
|
||||
static uint64_t pnv_chip_power8_xscom_core_base(PnvChip *chip,
|
||||
|
@ -742,6 +757,11 @@ static void pnv_init(MachineState *machine)
|
|||
DriveInfo *pnor = drive_get(IF_MTD, 0, 0);
|
||||
DeviceState *dev;
|
||||
|
||||
if (kvm_enabled()) {
|
||||
error_report("The powernv machine does not work with KVM acceleration");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* allocate RAM */
|
||||
if (machine->ram_size < mc->default_ram_size) {
|
||||
char *sz = size_to_str(mc->default_ram_size);
|
||||
|
@ -1221,25 +1241,15 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
|
|||
/* PHB3 controllers */
|
||||
for (i = 0; i < chip->num_phbs; i++) {
|
||||
PnvPHB3 *phb = &chip8->phbs[i];
|
||||
PnvPBCQState *pbcq = &phb->pbcq;
|
||||
|
||||
object_property_set_int(OBJECT(phb), "index", i, &error_fatal);
|
||||
object_property_set_int(OBJECT(phb), "chip-id", chip->chip_id,
|
||||
&error_fatal);
|
||||
object_property_set_link(OBJECT(phb), "chip", OBJECT(chip),
|
||||
&error_fatal);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(phb), errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Populate the XSCOM address space. */
|
||||
pnv_xscom_add_subregion(chip,
|
||||
PNV_XSCOM_PBCQ_NEST_BASE + 0x400 * phb->phb_id,
|
||||
&pbcq->xscom_nest_regs);
|
||||
pnv_xscom_add_subregion(chip,
|
||||
PNV_XSCOM_PBCQ_PCI_BASE + 0x400 * phb->phb_id,
|
||||
&pbcq->xscom_pci_regs);
|
||||
pnv_xscom_add_subregion(chip,
|
||||
PNV_XSCOM_PBCQ_SPCI_BASE + 0x040 * phb->phb_id,
|
||||
&pbcq->xscom_spci_regs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1340,15 +1350,13 @@ static void pnv_chip_power9_instance_init(Object *obj)
|
|||
|
||||
object_initialize_child(obj, "homer", &chip9->homer, TYPE_PNV9_HOMER);
|
||||
|
||||
for (i = 0; i < PNV9_CHIP_MAX_PEC; i++) {
|
||||
/* Number of PECs is the chip default */
|
||||
chip->num_pecs = pcc->num_pecs;
|
||||
|
||||
for (i = 0; i < chip->num_pecs; i++) {
|
||||
object_initialize_child(obj, "pec[*]", &chip9->pecs[i],
|
||||
TYPE_PNV_PHB4_PEC);
|
||||
}
|
||||
|
||||
/*
|
||||
* Number of PHBs is the chip default
|
||||
*/
|
||||
chip->num_phbs = pcc->num_phbs;
|
||||
}
|
||||
|
||||
static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp)
|
||||
|
@ -1378,30 +1386,22 @@ static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp)
|
|||
}
|
||||
}
|
||||
|
||||
static void pnv_chip_power9_phb_realize(PnvChip *chip, Error **errp)
|
||||
static void pnv_chip_power9_pec_realize(PnvChip *chip, Error **errp)
|
||||
{
|
||||
Pnv9Chip *chip9 = PNV9_CHIP(chip);
|
||||
int i, j;
|
||||
int phb_id = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PNV9_CHIP_MAX_PEC; i++) {
|
||||
for (i = 0; i < chip->num_pecs; i++) {
|
||||
PnvPhb4PecState *pec = &chip9->pecs[i];
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
|
||||
uint32_t pec_nest_base;
|
||||
uint32_t pec_pci_base;
|
||||
|
||||
object_property_set_int(OBJECT(pec), "index", i, &error_fatal);
|
||||
/*
|
||||
* PEC0 -> 1 stack
|
||||
* PEC1 -> 2 stacks
|
||||
* PEC2 -> 3 stacks
|
||||
*/
|
||||
object_property_set_int(OBJECT(pec), "num-stacks", i + 1,
|
||||
&error_fatal);
|
||||
object_property_set_int(OBJECT(pec), "chip-id", chip->chip_id,
|
||||
&error_fatal);
|
||||
object_property_set_link(OBJECT(pec), "system-memory",
|
||||
OBJECT(get_system_memory()), &error_abort);
|
||||
object_property_set_link(OBJECT(pec), "chip", OBJECT(chip),
|
||||
&error_fatal);
|
||||
if (!qdev_realize(DEVICE(pec), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
|
@ -1411,37 +1411,6 @@ static void pnv_chip_power9_phb_realize(PnvChip *chip, Error **errp)
|
|||
|
||||
pnv_xscom_add_subregion(chip, pec_nest_base, &pec->nest_regs_mr);
|
||||
pnv_xscom_add_subregion(chip, pec_pci_base, &pec->pci_regs_mr);
|
||||
|
||||
for (j = 0; j < pec->num_stacks && phb_id < chip->num_phbs;
|
||||
j++, phb_id++) {
|
||||
PnvPhb4PecStack *stack = &pec->stacks[j];
|
||||
Object *obj = OBJECT(&stack->phb);
|
||||
|
||||
object_property_set_int(obj, "index", phb_id, &error_fatal);
|
||||
object_property_set_int(obj, "chip-id", chip->chip_id,
|
||||
&error_fatal);
|
||||
object_property_set_int(obj, "version", PNV_PHB4_VERSION,
|
||||
&error_fatal);
|
||||
object_property_set_int(obj, "device-id", PNV_PHB4_DEVICE_ID,
|
||||
&error_fatal);
|
||||
object_property_set_link(obj, "stack", OBJECT(stack),
|
||||
&error_abort);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(obj), errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Populate the XSCOM address space. */
|
||||
pnv_xscom_add_subregion(chip,
|
||||
pec_nest_base + 0x40 * (stack->stack_no + 1),
|
||||
&stack->nest_regs_mr);
|
||||
pnv_xscom_add_subregion(chip,
|
||||
pec_pci_base + 0x40 * (stack->stack_no + 1),
|
||||
&stack->pci_regs_mr);
|
||||
pnv_xscom_add_subregion(chip,
|
||||
pec_pci_base + PNV9_XSCOM_PEC_PCI_STK0 +
|
||||
0x40 * stack->stack_no,
|
||||
&stack->phb_regs_mr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1537,8 +1506,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
|
|||
memory_region_add_subregion(get_system_memory(), PNV9_HOMER_BASE(chip),
|
||||
&chip9->homer.regs);
|
||||
|
||||
/* PHBs */
|
||||
pnv_chip_power9_phb_realize(chip, &local_err);
|
||||
/* PEC PHBs */
|
||||
pnv_chip_power9_pec_realize(chip, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
|
@ -1569,7 +1538,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
|
|||
k->xscom_core_base = pnv_chip_power9_xscom_core_base;
|
||||
k->xscom_pcba = pnv_chip_power9_xscom_pcba;
|
||||
dc->desc = "PowerNV Chip POWER9";
|
||||
k->num_phbs = 6;
|
||||
k->num_pecs = PNV9_CHIP_MAX_PEC;
|
||||
|
||||
device_class_set_parent_realize(dc, pnv_chip_power9_realize,
|
||||
&k->parent_realize);
|
||||
|
@ -1764,7 +1733,6 @@ static Property pnv_chip_properties[] = {
|
|||
DEFINE_PROP_UINT32("nr-cores", PnvChip, nr_cores, 1),
|
||||
DEFINE_PROP_UINT64("cores-mask", PnvChip, cores_mask, 0x0),
|
||||
DEFINE_PROP_UINT32("nr-threads", PnvChip, nr_threads, 1),
|
||||
DEFINE_PROP_UINT32("num-phbs", PnvChip, num_phbs, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
@ -1795,10 +1763,32 @@ PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
typedef struct ForeachPhb3Args {
|
||||
int irq;
|
||||
ICSState *ics;
|
||||
} ForeachPhb3Args;
|
||||
|
||||
static int pnv_ics_get_child(Object *child, void *opaque)
|
||||
{
|
||||
ForeachPhb3Args *args = opaque;
|
||||
PnvPHB3 *phb3 = (PnvPHB3 *) object_dynamic_cast(child, TYPE_PNV_PHB3);
|
||||
|
||||
if (phb3) {
|
||||
if (ics_valid_irq(&phb3->lsis, args->irq)) {
|
||||
args->ics = &phb3->lsis;
|
||||
}
|
||||
if (ics_valid_irq(ICS(&phb3->msis), args->irq)) {
|
||||
args->ics = ICS(&phb3->msis);
|
||||
}
|
||||
}
|
||||
return args->ics ? 1 : 0;
|
||||
}
|
||||
|
||||
static ICSState *pnv_ics_get(XICSFabric *xi, int irq)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(xi);
|
||||
int i, j;
|
||||
ForeachPhb3Args args = { irq, NULL };
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pnv->num_chips; i++) {
|
||||
PnvChip *chip = pnv->chips[i];
|
||||
|
@ -1807,32 +1797,37 @@ static ICSState *pnv_ics_get(XICSFabric *xi, int irq)
|
|||
if (ics_valid_irq(&chip8->psi.ics, irq)) {
|
||||
return &chip8->psi.ics;
|
||||
}
|
||||
for (j = 0; j < chip->num_phbs; j++) {
|
||||
if (ics_valid_irq(&chip8->phbs[j].lsis, irq)) {
|
||||
return &chip8->phbs[j].lsis;
|
||||
}
|
||||
if (ics_valid_irq(ICS(&chip8->phbs[j].msis), irq)) {
|
||||
return ICS(&chip8->phbs[j].msis);
|
||||
}
|
||||
|
||||
object_child_foreach(OBJECT(chip), pnv_ics_get_child, &args);
|
||||
if (args.ics) {
|
||||
return args.ics;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int pnv_ics_resend_child(Object *child, void *opaque)
|
||||
{
|
||||
PnvPHB3 *phb3 = (PnvPHB3 *) object_dynamic_cast(child, TYPE_PNV_PHB3);
|
||||
|
||||
if (phb3) {
|
||||
ics_resend(&phb3->lsis);
|
||||
ics_resend(ICS(&phb3->msis));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pnv_ics_resend(XICSFabric *xi)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(xi);
|
||||
int i, j;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pnv->num_chips; i++) {
|
||||
PnvChip *chip = pnv->chips[i];
|
||||
Pnv8Chip *chip8 = PNV8_CHIP(pnv->chips[i]);
|
||||
|
||||
ics_resend(&chip8->psi.ics);
|
||||
for (j = 0; j < chip->num_phbs; j++) {
|
||||
ics_resend(&chip8->phbs[j].lsis);
|
||||
ics_resend(ICS(&chip8->phbs[j].msis));
|
||||
}
|
||||
object_child_foreach(OBJECT(chip), pnv_ics_resend_child, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1367,6 +1367,7 @@ int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp)
|
|||
if (dcr->dcr_read == NULL)
|
||||
goto error;
|
||||
*valp = (*dcr->dcr_read)(dcr->opaque, dcrn);
|
||||
trace_ppc_dcr_read(dcrn, *valp);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -1386,6 +1387,7 @@ int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val)
|
|||
dcr = &dcr_env->dcrn[dcrn];
|
||||
if (dcr->dcr_write == NULL)
|
||||
goto error;
|
||||
trace_ppc_dcr_write(dcrn, val);
|
||||
(*dcr->dcr_write)(dcr->opaque, dcrn, val);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -27,6 +27,13 @@
|
|||
|
||||
#include "hw/ppc/ppc4xx.h"
|
||||
|
||||
#define PPC405EP_SDRAM_BASE 0x00000000
|
||||
#define PPC405EP_NVRAM_BASE 0xF0000000
|
||||
#define PPC405EP_FPGA_BASE 0xF0300000
|
||||
#define PPC405EP_SRAM_BASE 0xFFF00000
|
||||
#define PPC405EP_SRAM_SIZE (512 * KiB)
|
||||
#define PPC405EP_FLASH_BASE 0xFFF80000
|
||||
|
||||
/* Bootinfo as set-up by u-boot */
|
||||
typedef struct ppc4xx_bd_info_t ppc4xx_bd_info_t;
|
||||
struct ppc4xx_bd_info_t {
|
||||
|
@ -50,19 +57,18 @@ struct ppc4xx_bd_info_t {
|
|||
uint32_t bi_plb_busfreq;
|
||||
uint32_t bi_pci_busfreq;
|
||||
uint8_t bi_pci_enetaddr[6];
|
||||
uint32_t bi_pci_enetaddr2[6];
|
||||
uint8_t bi_pci_enetaddr2[6]; /* PPC405EP specific */
|
||||
uint32_t bi_opbfreq;
|
||||
uint32_t bi_iic_fast[2];
|
||||
};
|
||||
|
||||
/* PowerPC 405 core */
|
||||
ram_addr_t ppc405_set_bootinfo (CPUPPCState *env, ppc4xx_bd_info_t *bd,
|
||||
uint32_t flags);
|
||||
ram_addr_t ppc405_set_bootinfo(CPUPPCState *env, ram_addr_t ram_size);
|
||||
|
||||
void ppc4xx_plb_init(CPUPPCState *env);
|
||||
void ppc405_ebc_init(CPUPPCState *env);
|
||||
|
||||
CPUPPCState *ppc405ep_init(MemoryRegion *address_space_mem,
|
||||
PowerPCCPU *ppc405ep_init(MemoryRegion *address_space_mem,
|
||||
MemoryRegion ram_memories[2],
|
||||
hwaddr ram_bases[2],
|
||||
hwaddr ram_sizes[2],
|
||||
|
|
|
@ -41,11 +41,12 @@
|
|||
#include "qemu/error-report.h"
|
||||
#include "hw/loader.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "elf.h"
|
||||
|
||||
#define BIOS_FILENAME "ppc405_rom.bin"
|
||||
#define BIOS_SIZE (2 * MiB)
|
||||
|
||||
#define KERNEL_LOAD_ADDR 0x00000000
|
||||
#define KERNEL_LOAD_ADDR 0x01000000
|
||||
#define INITRD_LOAD_ADDR 0x01800000
|
||||
|
||||
#define USE_FLASH_BIOS
|
||||
|
@ -136,32 +137,101 @@ static void ref405ep_fpga_init(MemoryRegion *sysmem, uint32_t base)
|
|||
qemu_register_reset(&ref405ep_fpga_reset, fpga);
|
||||
}
|
||||
|
||||
/*
|
||||
* CPU reset handler when booting directly from a loaded kernel
|
||||
*/
|
||||
static struct boot_info {
|
||||
uint32_t entry;
|
||||
uint32_t bdloc;
|
||||
uint32_t initrd_base;
|
||||
uint32_t initrd_size;
|
||||
uint32_t cmdline_base;
|
||||
uint32_t cmdline_size;
|
||||
} boot_info;
|
||||
|
||||
static void main_cpu_reset(void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
struct boot_info *bi = env->load_info;
|
||||
|
||||
cpu_reset(CPU(cpu));
|
||||
|
||||
/* stack: top of sram */
|
||||
env->gpr[1] = PPC405EP_SRAM_BASE + PPC405EP_SRAM_SIZE - 8;
|
||||
|
||||
/* Tune our boot state */
|
||||
env->gpr[3] = bi->bdloc;
|
||||
env->gpr[4] = bi->initrd_base;
|
||||
env->gpr[5] = bi->initrd_base + bi->initrd_size;
|
||||
env->gpr[6] = bi->cmdline_base;
|
||||
env->gpr[7] = bi->cmdline_size;
|
||||
|
||||
env->nip = bi->entry;
|
||||
}
|
||||
|
||||
static void boot_from_kernel(MachineState *machine, PowerPCCPU *cpu)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
hwaddr boot_entry;
|
||||
hwaddr kernel_base;
|
||||
int kernel_size;
|
||||
hwaddr initrd_base;
|
||||
int initrd_size;
|
||||
ram_addr_t bdloc;
|
||||
int len;
|
||||
|
||||
bdloc = ppc405_set_bootinfo(env, machine->ram_size);
|
||||
boot_info.bdloc = bdloc;
|
||||
|
||||
kernel_size = load_elf(machine->kernel_filename, NULL, NULL, NULL,
|
||||
&boot_entry, &kernel_base, NULL, NULL,
|
||||
1, PPC_ELF_MACHINE, 0, 0);
|
||||
if (kernel_size < 0) {
|
||||
error_report("Could not load kernel '%s' : %s",
|
||||
machine->kernel_filename, load_elf_strerror(kernel_size));
|
||||
exit(1);
|
||||
}
|
||||
boot_info.entry = boot_entry;
|
||||
|
||||
/* load initrd */
|
||||
if (machine->initrd_filename) {
|
||||
initrd_base = INITRD_LOAD_ADDR;
|
||||
initrd_size = load_image_targphys(machine->initrd_filename, initrd_base,
|
||||
machine->ram_size - initrd_base);
|
||||
if (initrd_size < 0) {
|
||||
error_report("could not load initial ram disk '%s'",
|
||||
machine->initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
boot_info.initrd_base = initrd_base;
|
||||
boot_info.initrd_size = initrd_size;
|
||||
}
|
||||
|
||||
if (machine->kernel_cmdline) {
|
||||
len = strlen(machine->kernel_cmdline);
|
||||
bdloc -= ((len + 255) & ~255);
|
||||
cpu_physical_memory_write(bdloc, machine->kernel_cmdline, len + 1);
|
||||
boot_info.cmdline_base = bdloc;
|
||||
boot_info.cmdline_size = bdloc + len;
|
||||
}
|
||||
|
||||
/* Install our custom reset handler to start from Linux */
|
||||
qemu_register_reset(main_cpu_reset, cpu);
|
||||
env->load_info = &boot_info;
|
||||
}
|
||||
|
||||
static void ref405ep_init(MachineState *machine)
|
||||
{
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
const char *bios_name = machine->firmware ?: BIOS_FILENAME;
|
||||
const char *kernel_filename = machine->kernel_filename;
|
||||
const char *kernel_cmdline = machine->kernel_cmdline;
|
||||
const char *initrd_filename = machine->initrd_filename;
|
||||
char *filename;
|
||||
ppc4xx_bd_info_t bd;
|
||||
CPUPPCState *env;
|
||||
PowerPCCPU *cpu;
|
||||
DeviceState *dev;
|
||||
SysBusDevice *s;
|
||||
MemoryRegion *bios;
|
||||
MemoryRegion *sram = g_new(MemoryRegion, 1);
|
||||
ram_addr_t bdloc;
|
||||
MemoryRegion *ram_memories = g_new(MemoryRegion, 2);
|
||||
hwaddr ram_bases[2], ram_sizes[2];
|
||||
target_ulong sram_size;
|
||||
long bios_size;
|
||||
//int phy_addr = 0;
|
||||
//static int phy_addr = 1;
|
||||
target_ulong kernel_base, initrd_base;
|
||||
long kernel_size, initrd_size;
|
||||
int linux_boot;
|
||||
int len;
|
||||
DriveInfo *dinfo;
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
DeviceState *uicdev;
|
||||
|
||||
|
@ -180,132 +250,80 @@ static void ref405ep_init(MachineState *machine)
|
|||
memory_region_init(&ram_memories[1], NULL, "ef405ep.ram1", 0);
|
||||
ram_bases[1] = 0x00000000;
|
||||
ram_sizes[1] = 0x00000000;
|
||||
env = ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes,
|
||||
|
||||
cpu = ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes,
|
||||
33333333, &uicdev, kernel_filename == NULL ? 0 : 1);
|
||||
|
||||
/* allocate SRAM */
|
||||
sram_size = 512 * KiB;
|
||||
memory_region_init_ram(sram, NULL, "ef405ep.sram", sram_size,
|
||||
memory_region_init_ram(sram, NULL, "ef405ep.sram", PPC405EP_SRAM_SIZE,
|
||||
&error_fatal);
|
||||
memory_region_add_subregion(sysmem, 0xFFF00000, sram);
|
||||
memory_region_add_subregion(sysmem, PPC405EP_SRAM_BASE, sram);
|
||||
|
||||
/* allocate and load BIOS */
|
||||
#ifdef USE_FLASH_BIOS
|
||||
dinfo = drive_get(IF_PFLASH, 0, 0);
|
||||
if (dinfo) {
|
||||
bios_size = 8 * MiB;
|
||||
pflash_cfi02_register((uint32_t)(-bios_size),
|
||||
"ef405ep.bios", bios_size,
|
||||
blk_by_legacy_dinfo(dinfo),
|
||||
64 * KiB, 1,
|
||||
2, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA,
|
||||
1);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
bios = g_new(MemoryRegion, 1);
|
||||
if (machine->firmware) {
|
||||
MemoryRegion *bios = g_new(MemoryRegion, 1);
|
||||
g_autofree char *filename;
|
||||
long bios_size;
|
||||
|
||||
memory_region_init_rom(bios, NULL, "ef405ep.bios", BIOS_SIZE,
|
||||
&error_fatal);
|
||||
|
||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
|
||||
if (filename) {
|
||||
bios_size = load_image_size(filename,
|
||||
memory_region_get_ram_ptr(bios),
|
||||
BIOS_SIZE);
|
||||
g_free(filename);
|
||||
if (bios_size < 0) {
|
||||
error_report("Could not load PowerPC BIOS '%s'", bios_name);
|
||||
exit(1);
|
||||
}
|
||||
bios_size = (bios_size + 0xfff) & ~0xfff;
|
||||
memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios);
|
||||
} else if (!qtest_enabled() || kernel_filename != NULL) {
|
||||
error_report("Could not load PowerPC BIOS '%s'", bios_name);
|
||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, machine->firmware);
|
||||
if (!filename) {
|
||||
error_report("Could not find firmware '%s'", machine->firmware);
|
||||
exit(1);
|
||||
} else {
|
||||
/* Avoid an uninitialized variable warning */
|
||||
bios_size = -1;
|
||||
}
|
||||
|
||||
bios_size = load_image_size(filename,
|
||||
memory_region_get_ram_ptr(bios),
|
||||
BIOS_SIZE);
|
||||
if (bios_size < 0) {
|
||||
error_report("Could not load PowerPC BIOS '%s'", machine->firmware);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
bios_size = (bios_size + 0xfff) & ~0xfff;
|
||||
memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios);
|
||||
}
|
||||
|
||||
/* Register FPGA */
|
||||
ref405ep_fpga_init(sysmem, 0xF0300000);
|
||||
ref405ep_fpga_init(sysmem, PPC405EP_FPGA_BASE);
|
||||
/* Register NVRAM */
|
||||
dev = qdev_new("sysbus-m48t08");
|
||||
qdev_prop_set_int32(dev, "base-year", 1968);
|
||||
s = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize_and_unref(s, &error_fatal);
|
||||
sysbus_mmio_map(s, 0, 0xF0000000);
|
||||
/* Load kernel */
|
||||
linux_boot = (kernel_filename != NULL);
|
||||
if (linux_boot) {
|
||||
memset(&bd, 0, sizeof(bd));
|
||||
bd.bi_memstart = 0x00000000;
|
||||
bd.bi_memsize = machine->ram_size;
|
||||
bd.bi_flashstart = -bios_size;
|
||||
bd.bi_flashsize = -bios_size;
|
||||
bd.bi_flashoffset = 0;
|
||||
bd.bi_sramstart = 0xFFF00000;
|
||||
bd.bi_sramsize = sram_size;
|
||||
bd.bi_bootflags = 0;
|
||||
bd.bi_intfreq = 133333333;
|
||||
bd.bi_busfreq = 33333333;
|
||||
bd.bi_baudrate = 115200;
|
||||
bd.bi_s_version[0] = 'Q';
|
||||
bd.bi_s_version[1] = 'M';
|
||||
bd.bi_s_version[2] = 'U';
|
||||
bd.bi_s_version[3] = '\0';
|
||||
bd.bi_r_version[0] = 'Q';
|
||||
bd.bi_r_version[1] = 'E';
|
||||
bd.bi_r_version[2] = 'M';
|
||||
bd.bi_r_version[3] = 'U';
|
||||
bd.bi_r_version[4] = '\0';
|
||||
bd.bi_procfreq = 133333333;
|
||||
bd.bi_plb_busfreq = 33333333;
|
||||
bd.bi_pci_busfreq = 33333333;
|
||||
bd.bi_opbfreq = 33333333;
|
||||
bdloc = ppc405_set_bootinfo(env, &bd, 0x00000001);
|
||||
env->gpr[3] = bdloc;
|
||||
sysbus_mmio_map(s, 0, PPC405EP_NVRAM_BASE);
|
||||
|
||||
/* Load kernel and initrd using U-Boot images */
|
||||
if (kernel_filename && machine->firmware) {
|
||||
target_ulong kernel_base, initrd_base;
|
||||
long kernel_size, initrd_size;
|
||||
|
||||
kernel_base = KERNEL_LOAD_ADDR;
|
||||
/* now we can load the kernel */
|
||||
kernel_size = load_image_targphys(kernel_filename, kernel_base,
|
||||
machine->ram_size - kernel_base);
|
||||
if (kernel_size < 0) {
|
||||
error_report("could not load kernel '%s'", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
printf("Load kernel size %ld at " TARGET_FMT_lx,
|
||||
kernel_size, kernel_base);
|
||||
|
||||
/* load initrd */
|
||||
if (initrd_filename) {
|
||||
if (machine->initrd_filename) {
|
||||
initrd_base = INITRD_LOAD_ADDR;
|
||||
initrd_size = load_image_targphys(initrd_filename, initrd_base,
|
||||
initrd_size = load_image_targphys(machine->initrd_filename,
|
||||
initrd_base,
|
||||
machine->ram_size - initrd_base);
|
||||
if (initrd_size < 0) {
|
||||
error_report("could not load initial ram disk '%s'",
|
||||
initrd_filename);
|
||||
machine->initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
}
|
||||
env->gpr[4] = initrd_base;
|
||||
env->gpr[5] = initrd_size;
|
||||
if (kernel_cmdline != NULL) {
|
||||
len = strlen(kernel_cmdline);
|
||||
bdloc -= ((len + 255) & ~255);
|
||||
cpu_physical_memory_write(bdloc, kernel_cmdline, len + 1);
|
||||
env->gpr[6] = bdloc;
|
||||
env->gpr[7] = bdloc + len;
|
||||
} else {
|
||||
env->gpr[6] = 0;
|
||||
env->gpr[7] = 0;
|
||||
}
|
||||
env->nip = KERNEL_LOAD_ADDR;
|
||||
} else {
|
||||
kernel_base = 0;
|
||||
kernel_size = 0;
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
bdloc = 0;
|
||||
|
||||
/* Load ELF kernel and rootfs.cpio */
|
||||
} else if (kernel_filename && !machine->firmware) {
|
||||
boot_from_kernel(machine, cpu);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -547,6 +565,7 @@ static void taihu_class_init(ObjectClass *oc, void *data)
|
|||
mc->init = taihu_405ep_init;
|
||||
mc->default_ram_size = 0x08000000;
|
||||
mc->default_ram_id = "taihu_405ep.ram";
|
||||
mc->deprecation_reason = "incomplete, use 'ref405ep' instead";
|
||||
}
|
||||
|
||||
static const TypeInfo taihu_type = {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/log.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/ppc/ppc.h"
|
||||
#include "hw/i2c/ppc4xx_i2c.h"
|
||||
|
@ -38,18 +39,37 @@
|
|||
#include "hw/intc/ppc-uic.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qapi/error.h"
|
||||
#include "trace.h"
|
||||
|
||||
//#define DEBUG_OPBA
|
||||
//#define DEBUG_SDRAM
|
||||
//#define DEBUG_GPIO
|
||||
//#define DEBUG_SERIAL
|
||||
//#define DEBUG_OCM
|
||||
//#define DEBUG_GPT
|
||||
//#define DEBUG_CLOCKS
|
||||
//#define DEBUG_CLOCKS_LL
|
||||
static void ppc405_set_default_bootinfo(ppc4xx_bd_info_t *bd,
|
||||
ram_addr_t ram_size)
|
||||
{
|
||||
memset(bd, 0, sizeof(*bd));
|
||||
|
||||
ram_addr_t ppc405_set_bootinfo (CPUPPCState *env, ppc4xx_bd_info_t *bd,
|
||||
uint32_t flags)
|
||||
bd->bi_memstart = PPC405EP_SDRAM_BASE;
|
||||
bd->bi_memsize = ram_size;
|
||||
bd->bi_sramstart = PPC405EP_SRAM_BASE;
|
||||
bd->bi_sramsize = PPC405EP_SRAM_SIZE;
|
||||
bd->bi_bootflags = 0;
|
||||
bd->bi_intfreq = 133333333;
|
||||
bd->bi_busfreq = 33333333;
|
||||
bd->bi_baudrate = 115200;
|
||||
bd->bi_s_version[0] = 'Q';
|
||||
bd->bi_s_version[1] = 'M';
|
||||
bd->bi_s_version[2] = 'U';
|
||||
bd->bi_s_version[3] = '\0';
|
||||
bd->bi_r_version[0] = 'Q';
|
||||
bd->bi_r_version[1] = 'E';
|
||||
bd->bi_r_version[2] = 'M';
|
||||
bd->bi_r_version[3] = 'U';
|
||||
bd->bi_r_version[4] = '\0';
|
||||
bd->bi_procfreq = 133333333;
|
||||
bd->bi_plb_busfreq = 33333333;
|
||||
bd->bi_pci_busfreq = 33333333;
|
||||
bd->bi_opbfreq = 33333333;
|
||||
}
|
||||
|
||||
static ram_addr_t __ppc405_set_bootinfo(CPUPPCState *env, ppc4xx_bd_info_t *bd)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
ram_addr_t bdloc;
|
||||
|
@ -82,15 +102,15 @@ ram_addr_t ppc405_set_bootinfo (CPUPPCState *env, ppc4xx_bd_info_t *bd,
|
|||
for (i = 0; i < 32; i++) {
|
||||
stb_phys(cs->as, bdloc + 0x3C + i, bd->bi_r_version[i]);
|
||||
}
|
||||
stl_be_phys(cs->as, bdloc + 0x5C, bd->bi_plb_busfreq);
|
||||
stl_be_phys(cs->as, bdloc + 0x60, bd->bi_pci_busfreq);
|
||||
stl_be_phys(cs->as, bdloc + 0x5C, bd->bi_procfreq);
|
||||
stl_be_phys(cs->as, bdloc + 0x60, bd->bi_plb_busfreq);
|
||||
stl_be_phys(cs->as, bdloc + 0x64, bd->bi_pci_busfreq);
|
||||
for (i = 0; i < 6; i++) {
|
||||
stb_phys(cs->as, bdloc + 0x64 + i, bd->bi_pci_enetaddr[i]);
|
||||
stb_phys(cs->as, bdloc + 0x68 + i, bd->bi_pci_enetaddr[i]);
|
||||
}
|
||||
n = 0x6A;
|
||||
if (flags & 0x00000001) {
|
||||
for (i = 0; i < 6; i++)
|
||||
stb_phys(cs->as, bdloc + n++, bd->bi_pci_enetaddr2[i]);
|
||||
n = 0x70; /* includes 2 bytes hole */
|
||||
for (i = 0; i < 6; i++) {
|
||||
stb_phys(cs->as, bdloc + n++, bd->bi_pci_enetaddr2[i]);
|
||||
}
|
||||
stl_be_phys(cs->as, bdloc + n, bd->bi_opbfreq);
|
||||
n += 4;
|
||||
|
@ -102,6 +122,17 @@ ram_addr_t ppc405_set_bootinfo (CPUPPCState *env, ppc4xx_bd_info_t *bd,
|
|||
return bdloc;
|
||||
}
|
||||
|
||||
ram_addr_t ppc405_set_bootinfo(CPUPPCState *env, ram_addr_t ram_size)
|
||||
{
|
||||
ppc4xx_bd_info_t bd;
|
||||
|
||||
memset(&bd, 0, sizeof(bd));
|
||||
|
||||
ppc405_set_default_bootinfo(&bd, ram_size);
|
||||
|
||||
return __ppc405_set_bootinfo(env, &bd);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Shared peripherals */
|
||||
|
||||
|
@ -287,13 +318,9 @@ struct ppc4xx_opba_t {
|
|||
|
||||
static uint64_t opba_readb(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
ppc4xx_opba_t *opba;
|
||||
ppc4xx_opba_t *opba = opaque;
|
||||
uint32_t ret;
|
||||
|
||||
#ifdef DEBUG_OPBA
|
||||
printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
|
||||
#endif
|
||||
opba = opaque;
|
||||
switch (addr) {
|
||||
case 0x00:
|
||||
ret = opba->cr;
|
||||
|
@ -306,19 +333,17 @@ static uint64_t opba_readb(void *opaque, hwaddr addr, unsigned size)
|
|||
break;
|
||||
}
|
||||
|
||||
trace_opba_readb(addr, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void opba_writeb(void *opaque, hwaddr addr, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
ppc4xx_opba_t *opba;
|
||||
ppc4xx_opba_t *opba = opaque;
|
||||
|
||||
trace_opba_writeb(addr, value);
|
||||
|
||||
#ifdef DEBUG_OPBA
|
||||
printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
|
||||
value);
|
||||
#endif
|
||||
opba = opaque;
|
||||
switch (addr) {
|
||||
case 0x00:
|
||||
opba->cr = value & 0xF8;
|
||||
|
@ -353,10 +378,9 @@ static void ppc4xx_opba_init(hwaddr base)
|
|||
{
|
||||
ppc4xx_opba_t *opba;
|
||||
|
||||
trace_opba_init(base);
|
||||
|
||||
opba = g_malloc0(sizeof(ppc4xx_opba_t));
|
||||
#ifdef DEBUG_OPBA
|
||||
printf("%s: offset " TARGET_FMT_plx "\n", __func__, base);
|
||||
#endif
|
||||
memory_region_init_io(&opba->io, NULL, &opba_ops, opba, "opba", 0x002);
|
||||
memory_region_add_subregion(get_system_memory(), base, &opba->io);
|
||||
qemu_register_reset(ppc4xx_opba_reset, opba);
|
||||
|
@ -707,20 +731,14 @@ struct ppc405_gpio_t {
|
|||
|
||||
static uint64_t ppc405_gpio_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
#ifdef DEBUG_GPIO
|
||||
printf("%s: addr " TARGET_FMT_plx " size %d\n", __func__, addr, size);
|
||||
#endif
|
||||
|
||||
trace_ppc405_gpio_read(addr, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ppc405_gpio_write(void *opaque, hwaddr addr, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
#ifdef DEBUG_GPIO
|
||||
printf("%s: addr " TARGET_FMT_plx " size %d val %08" PRIx32 "\n",
|
||||
__func__, addr, size, value);
|
||||
#endif
|
||||
trace_ppc405_gpio_write(addr, size, value);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ppc405_gpio_ops = {
|
||||
|
@ -737,10 +755,9 @@ static void ppc405_gpio_init(hwaddr base)
|
|||
{
|
||||
ppc405_gpio_t *gpio;
|
||||
|
||||
trace_ppc405_gpio_init(base);
|
||||
|
||||
gpio = g_malloc0(sizeof(ppc405_gpio_t));
|
||||
#ifdef DEBUG_GPIO
|
||||
printf("%s: offset " TARGET_FMT_plx "\n", __func__, base);
|
||||
#endif
|
||||
memory_region_init_io(&gpio->io, NULL, &ppc405_gpio_ops, gpio, "pgio", 0x038);
|
||||
memory_region_add_subregion(get_system_memory(), base, &gpio->io);
|
||||
qemu_register_reset(&ppc405_gpio_reset, gpio);
|
||||
|
@ -770,25 +787,19 @@ static void ocm_update_mappings (ppc405_ocm_t *ocm,
|
|||
uint32_t isarc, uint32_t isacntl,
|
||||
uint32_t dsarc, uint32_t dsacntl)
|
||||
{
|
||||
#ifdef DEBUG_OCM
|
||||
printf("OCM update ISA %08" PRIx32 " %08" PRIx32 " (%08" PRIx32
|
||||
" %08" PRIx32 ") DSA %08" PRIx32 " %08" PRIx32
|
||||
" (%08" PRIx32 " %08" PRIx32 ")\n",
|
||||
isarc, isacntl, dsarc, dsacntl,
|
||||
ocm->isarc, ocm->isacntl, ocm->dsarc, ocm->dsacntl);
|
||||
#endif
|
||||
trace_ocm_update_mappings(isarc, isacntl, dsarc, dsacntl, ocm->isarc,
|
||||
ocm->isacntl, ocm->dsarc, ocm->dsacntl);
|
||||
|
||||
if (ocm->isarc != isarc ||
|
||||
(ocm->isacntl & 0x80000000) != (isacntl & 0x80000000)) {
|
||||
if (ocm->isacntl & 0x80000000) {
|
||||
/* Unmap previously assigned memory region */
|
||||
printf("OCM unmap ISA %08" PRIx32 "\n", ocm->isarc);
|
||||
trace_ocm_unmap("ISA", ocm->isarc);
|
||||
memory_region_del_subregion(get_system_memory(), &ocm->isarc_ram);
|
||||
}
|
||||
if (isacntl & 0x80000000) {
|
||||
/* Map new instruction memory region */
|
||||
#ifdef DEBUG_OCM
|
||||
printf("OCM map ISA %08" PRIx32 "\n", isarc);
|
||||
#endif
|
||||
trace_ocm_map("ISA", isarc);
|
||||
memory_region_add_subregion(get_system_memory(), isarc,
|
||||
&ocm->isarc_ram);
|
||||
}
|
||||
|
@ -799,9 +810,7 @@ static void ocm_update_mappings (ppc405_ocm_t *ocm,
|
|||
/* Beware not to unmap the region we just mapped */
|
||||
if (!(isacntl & 0x80000000) || ocm->dsarc != isarc) {
|
||||
/* Unmap previously assigned memory region */
|
||||
#ifdef DEBUG_OCM
|
||||
printf("OCM unmap DSA %08" PRIx32 "\n", ocm->dsarc);
|
||||
#endif
|
||||
trace_ocm_unmap("DSA", ocm->dsarc);
|
||||
memory_region_del_subregion(get_system_memory(),
|
||||
&ocm->dsarc_ram);
|
||||
}
|
||||
|
@ -810,9 +819,7 @@ static void ocm_update_mappings (ppc405_ocm_t *ocm,
|
|||
/* Beware not to remap the region we just mapped */
|
||||
if (!(isacntl & 0x80000000) || dsarc != isarc) {
|
||||
/* Map new data memory region */
|
||||
#ifdef DEBUG_OCM
|
||||
printf("OCM map DSA %08" PRIx32 "\n", dsarc);
|
||||
#endif
|
||||
trace_ocm_map("DSA", dsarc);
|
||||
memory_region_add_subregion(get_system_memory(), dsarc,
|
||||
&ocm->dsarc_ram);
|
||||
}
|
||||
|
@ -988,14 +995,12 @@ static void ppc4xx_gpt_compute_timer (ppc4xx_gpt_t *gpt)
|
|||
|
||||
static uint64_t ppc4xx_gpt_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
ppc4xx_gpt_t *gpt;
|
||||
ppc4xx_gpt_t *gpt = opaque;
|
||||
uint32_t ret;
|
||||
int idx;
|
||||
|
||||
#ifdef DEBUG_GPT
|
||||
printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
|
||||
#endif
|
||||
gpt = opaque;
|
||||
trace_ppc4xx_gpt_read(addr, size);
|
||||
|
||||
switch (addr) {
|
||||
case 0x00:
|
||||
/* Time base counter */
|
||||
|
@ -1044,14 +1049,11 @@ static uint64_t ppc4xx_gpt_read(void *opaque, hwaddr addr, unsigned size)
|
|||
static void ppc4xx_gpt_write(void *opaque, hwaddr addr, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
ppc4xx_gpt_t *gpt;
|
||||
ppc4xx_gpt_t *gpt = opaque;
|
||||
int idx;
|
||||
|
||||
#ifdef DEBUG_I2C
|
||||
printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
|
||||
value);
|
||||
#endif
|
||||
gpt = opaque;
|
||||
trace_ppc4xx_gpt_write(addr, size, value);
|
||||
|
||||
switch (addr) {
|
||||
case 0x00:
|
||||
/* Time base counter */
|
||||
|
@ -1144,14 +1146,13 @@ static void ppc4xx_gpt_init(hwaddr base, qemu_irq irqs[5])
|
|||
ppc4xx_gpt_t *gpt;
|
||||
int i;
|
||||
|
||||
trace_ppc4xx_gpt_init(base);
|
||||
|
||||
gpt = g_malloc0(sizeof(ppc4xx_gpt_t));
|
||||
for (i = 0; i < 5; i++) {
|
||||
gpt->irqs[i] = irqs[i];
|
||||
}
|
||||
gpt->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &ppc4xx_gpt_cb, gpt);
|
||||
#ifdef DEBUG_GPT
|
||||
printf("%s: offset " TARGET_FMT_plx "\n", __func__, base);
|
||||
#endif
|
||||
memory_region_init_io(&gpt->iomem, NULL, &gpt_ops, gpt, "gpt", 0x0d4);
|
||||
memory_region_add_subregion(get_system_memory(), base, &gpt->iomem);
|
||||
qemu_register_reset(ppc4xx_gpt_reset, gpt);
|
||||
|
@ -1215,17 +1216,14 @@ static void ppc405ep_compute_clocks (ppc405ep_cpc_t *cpc)
|
|||
VCO_out = 0;
|
||||
if ((cpc->pllmr[1] & 0x80000000) && !(cpc->pllmr[1] & 0x40000000)) {
|
||||
M = (((cpc->pllmr[1] >> 20) - 1) & 0xF) + 1; /* FBMUL */
|
||||
#ifdef DEBUG_CLOCKS_LL
|
||||
printf("FBMUL %01" PRIx32 " %d\n", (cpc->pllmr[1] >> 20) & 0xF, M);
|
||||
#endif
|
||||
trace_ppc405ep_clocks_compute("FBMUL", (cpc->pllmr[1] >> 20) & 0xF, M);
|
||||
D = 8 - ((cpc->pllmr[1] >> 16) & 0x7); /* FWDA */
|
||||
#ifdef DEBUG_CLOCKS_LL
|
||||
printf("FWDA %01" PRIx32 " %d\n", (cpc->pllmr[1] >> 16) & 0x7, D);
|
||||
#endif
|
||||
trace_ppc405ep_clocks_compute("FWDA", (cpc->pllmr[1] >> 16) & 0x7, D);
|
||||
VCO_out = (uint64_t)cpc->sysclk * M * D;
|
||||
if (VCO_out < 500000000UL || VCO_out > 1000000000UL) {
|
||||
/* Error - unlock the PLL */
|
||||
printf("VCO out of range %" PRIu64 "\n", VCO_out);
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "VCO out of range %" PRIu64 "\n",
|
||||
VCO_out);
|
||||
#if 0
|
||||
cpc->pllmr[1] &= ~0x80000000;
|
||||
goto pll_bypass;
|
||||
|
@ -1246,54 +1244,43 @@ static void ppc405ep_compute_clocks (ppc405ep_cpc_t *cpc)
|
|||
}
|
||||
/* Now, compute all other clocks */
|
||||
D = ((cpc->pllmr[0] >> 20) & 0x3) + 1; /* CCDV */
|
||||
#ifdef DEBUG_CLOCKS_LL
|
||||
printf("CCDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 20) & 0x3, D);
|
||||
#endif
|
||||
trace_ppc405ep_clocks_compute("CCDV", (cpc->pllmr[0] >> 20) & 0x3, D);
|
||||
CPU_clk = PLL_out / D;
|
||||
D = ((cpc->pllmr[0] >> 16) & 0x3) + 1; /* CBDV */
|
||||
#ifdef DEBUG_CLOCKS_LL
|
||||
printf("CBDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 16) & 0x3, D);
|
||||
#endif
|
||||
trace_ppc405ep_clocks_compute("CBDV", (cpc->pllmr[0] >> 16) & 0x3, D);
|
||||
PLB_clk = CPU_clk / D;
|
||||
D = ((cpc->pllmr[0] >> 12) & 0x3) + 1; /* OPDV */
|
||||
#ifdef DEBUG_CLOCKS_LL
|
||||
printf("OPDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 12) & 0x3, D);
|
||||
#endif
|
||||
trace_ppc405ep_clocks_compute("OPDV", (cpc->pllmr[0] >> 12) & 0x3, D);
|
||||
OPB_clk = PLB_clk / D;
|
||||
D = ((cpc->pllmr[0] >> 8) & 0x3) + 2; /* EPDV */
|
||||
#ifdef DEBUG_CLOCKS_LL
|
||||
printf("EPDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 8) & 0x3, D);
|
||||
#endif
|
||||
trace_ppc405ep_clocks_compute("EPDV", (cpc->pllmr[0] >> 8) & 0x3, D);
|
||||
EBC_clk = PLB_clk / D;
|
||||
D = ((cpc->pllmr[0] >> 4) & 0x3) + 1; /* MPDV */
|
||||
#ifdef DEBUG_CLOCKS_LL
|
||||
printf("MPDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 4) & 0x3, D);
|
||||
#endif
|
||||
trace_ppc405ep_clocks_compute("MPDV", (cpc->pllmr[0] >> 4) & 0x3, D);
|
||||
MAL_clk = PLB_clk / D;
|
||||
D = (cpc->pllmr[0] & 0x3) + 1; /* PPDV */
|
||||
#ifdef DEBUG_CLOCKS_LL
|
||||
printf("PPDV %01" PRIx32 " %d\n", cpc->pllmr[0] & 0x3, D);
|
||||
#endif
|
||||
trace_ppc405ep_clocks_compute("PPDV", cpc->pllmr[0] & 0x3, D);
|
||||
PCI_clk = PLB_clk / D;
|
||||
D = ((cpc->ucr - 1) & 0x7F) + 1; /* U0DIV */
|
||||
#ifdef DEBUG_CLOCKS_LL
|
||||
printf("U0DIV %01" PRIx32 " %d\n", cpc->ucr & 0x7F, D);
|
||||
#endif
|
||||
trace_ppc405ep_clocks_compute("U0DIV", cpc->ucr & 0x7F, D);
|
||||
UART0_clk = PLL_out / D;
|
||||
D = (((cpc->ucr >> 8) - 1) & 0x7F) + 1; /* U1DIV */
|
||||
#ifdef DEBUG_CLOCKS_LL
|
||||
printf("U1DIV %01" PRIx32 " %d\n", (cpc->ucr >> 8) & 0x7F, D);
|
||||
#endif
|
||||
trace_ppc405ep_clocks_compute("U1DIV", (cpc->ucr >> 8) & 0x7F, D);
|
||||
UART1_clk = PLL_out / D;
|
||||
#ifdef DEBUG_CLOCKS
|
||||
printf("Setup PPC405EP clocks - sysclk %" PRIu32 " VCO %" PRIu64
|
||||
" PLL out %" PRIu64 " Hz\n", cpc->sysclk, VCO_out, PLL_out);
|
||||
printf("CPU %" PRIu32 " PLB %" PRIu32 " OPB %" PRIu32 " EBC %" PRIu32
|
||||
" MAL %" PRIu32 " PCI %" PRIu32 " UART0 %" PRIu32
|
||||
" UART1 %" PRIu32 "\n",
|
||||
CPU_clk, PLB_clk, OPB_clk, EBC_clk, MAL_clk, PCI_clk,
|
||||
UART0_clk, UART1_clk);
|
||||
#endif
|
||||
|
||||
if (trace_event_get_state_backends(TRACE_PPC405EP_CLOCKS_SETUP)) {
|
||||
g_autofree char *trace = g_strdup_printf(
|
||||
"Setup PPC405EP clocks - sysclk %" PRIu32 " VCO %" PRIu64
|
||||
" PLL out %" PRIu64 " Hz\n"
|
||||
"CPU %" PRIu32 " PLB %" PRIu32 " OPB %" PRIu32 " EBC %" PRIu32
|
||||
" MAL %" PRIu32 " PCI %" PRIu32 " UART0 %" PRIu32
|
||||
" UART1 %" PRIu32 "\n",
|
||||
cpc->sysclk, VCO_out, PLL_out,
|
||||
CPU_clk, PLB_clk, OPB_clk, EBC_clk, MAL_clk, PCI_clk,
|
||||
UART0_clk, UART1_clk);
|
||||
trace_ppc405ep_clocks_setup(trace);
|
||||
}
|
||||
|
||||
/* Setup CPU clocks */
|
||||
clk_setup(&cpc->clk_setup[PPC405EP_CPU_CLK], CPU_clk);
|
||||
/* Setup PLB clock */
|
||||
|
@ -1395,9 +1382,9 @@ static void ppc405ep_cpc_reset (void *opaque)
|
|||
|
||||
cpc->boot = 0x00000010; /* Boot from PCI - IIC EEPROM disabled */
|
||||
cpc->epctl = 0x00000000;
|
||||
cpc->pllmr[0] = 0x00011010;
|
||||
cpc->pllmr[1] = 0x40000000;
|
||||
cpc->ucr = 0x00000000;
|
||||
cpc->pllmr[0] = 0x00021002;
|
||||
cpc->pllmr[1] = 0x80a552be;
|
||||
cpc->ucr = 0x00004646;
|
||||
cpc->srr = 0x00040000;
|
||||
cpc->pci = 0x00000000;
|
||||
cpc->er = 0x00000000;
|
||||
|
@ -1444,7 +1431,7 @@ static void ppc405ep_cpc_init (CPUPPCState *env, clk_setup_t clk_setup[8],
|
|||
#endif
|
||||
}
|
||||
|
||||
CPUPPCState *ppc405ep_init(MemoryRegion *address_space_mem,
|
||||
PowerPCCPU *ppc405ep_init(MemoryRegion *address_space_mem,
|
||||
MemoryRegion ram_memories[2],
|
||||
hwaddr ram_bases[2],
|
||||
hwaddr ram_sizes[2],
|
||||
|
@ -1543,5 +1530,5 @@ CPUPPCState *ppc405ep_init(MemoryRegion *address_space_mem,
|
|||
/* CPU control */
|
||||
ppc405ep_cpc_init(env, clk_setup, sysclk);
|
||||
|
||||
return env;
|
||||
return cpu;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "target/ppc/kvm_ppc.h"
|
||||
#include "hw/ppc/ppc.h"
|
||||
#include "target/ppc/mmu-hash64.h"
|
||||
#include "target/ppc/power8-pmu.h"
|
||||
#include "sysemu/numa.h"
|
||||
#include "sysemu/reset.h"
|
||||
#include "sysemu/hw_accel.h"
|
||||
|
|
|
@ -119,6 +119,9 @@ ppc_irq_set_state(const char *name, uint32_t level) "\"%s\" level %d"
|
|||
ppc_irq_reset(const char *name) "%s"
|
||||
ppc_irq_cpu(const char *action) "%s"
|
||||
|
||||
ppc_dcr_read(uint32_t addr, uint32_t val) "DRCN[0x%x] -> 0x%x"
|
||||
ppc_dcr_write(uint32_t addr, uint32_t val) "DRCN[0x%x] <- 0x%x"
|
||||
|
||||
# prep_systemio.c
|
||||
prep_systemio_read(uint32_t addr, uint32_t val) "read addr=0x%x val=0x%x"
|
||||
prep_systemio_write(uint32_t addr, uint32_t val) "write addr=0x%x val=0x%x"
|
||||
|
@ -141,3 +144,23 @@ ppc440_pcix_update_pim(int idx, uint64_t size, uint64_t la) "Added window %d of
|
|||
ppc440_pcix_update_pom(int idx, uint32_t size, uint64_t la, uint64_t pcia) "Added window %d of size=0x%x from CPU=0x%" PRIx64 " to PCI=0x%" PRIx64
|
||||
ppc440_pcix_reg_read(uint64_t addr, uint32_t val) "addr 0x%" PRIx64 " = 0x%" PRIx32
|
||||
ppc440_pcix_reg_write(uint64_t addr, uint32_t val, uint32_t size) "addr 0x%" PRIx64 " = 0x%" PRIx32 " size 0x%" PRIx32
|
||||
|
||||
# ppc405_boards.c
|
||||
opba_readb(uint64_t addr, uint32_t val) "addr 0x%" PRIx64 " = 0x%" PRIx32
|
||||
opba_writeb(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " = 0x%" PRIx64
|
||||
opba_init(uint64_t addr) "offet 0x%" PRIx64
|
||||
|
||||
ppc405_gpio_read(uint64_t addr, uint32_t size) "addr 0x%" PRIx64 " size %d"
|
||||
ppc405_gpio_write(uint64_t addr, uint32_t size, uint64_t val) "addr 0x%" PRIx64 " size %d = 0x%" PRIx64
|
||||
ppc405_gpio_init(uint64_t addr) "offet 0x%" PRIx64
|
||||
|
||||
ocm_update_mappings(uint32_t isarc, uint32_t isacntl, uint32_t dsarc, uint32_t dsacntl, uint32_t ocm_isarc, uint32_t ocm_isacntl, uint32_t ocm_dsarc, uint32_t ocm_dsacntl) "OCM update ISA 0x%08" PRIx32 " 0x%08" PRIx32 " (0x%08" PRIx32" 0x%08" PRIx32 ") DSA 0x%08" PRIx32 " 0x%08" PRIx32" (0x%08" PRIx32 " 0x%08" PRIx32 ")"
|
||||
ocm_map(const char* prefix, uint32_t isarc) "OCM map %s 0x%08" PRIx32
|
||||
ocm_unmap(const char* prefix, uint32_t isarc) "OCM unmap %s 0x%08" PRIx32
|
||||
|
||||
ppc4xx_gpt_read(uint64_t addr, uint32_t size) "addr 0x%" PRIx64 " size %d"
|
||||
ppc4xx_gpt_write(uint64_t addr, uint32_t size, uint64_t val) "addr 0x%" PRIx64 " size %d = 0x%" PRIx64
|
||||
ppc4xx_gpt_init(uint64_t addr) "offet 0x%" PRIx64
|
||||
|
||||
ppc405ep_clocks_compute(const char *param, uint32_t param2, uint32_t val) "%s 0x%1" PRIx32 " %d"
|
||||
ppc405ep_clocks_setup(const char *trace) "%s"
|
||||
|
|
|
@ -145,13 +145,20 @@ typedef enum __attribute__((__packed__)) {
|
|||
*/
|
||||
|
||||
enum {
|
||||
float_flag_invalid = 1,
|
||||
float_flag_divbyzero = 4,
|
||||
float_flag_overflow = 8,
|
||||
float_flag_underflow = 16,
|
||||
float_flag_inexact = 32,
|
||||
float_flag_input_denormal = 64,
|
||||
float_flag_output_denormal = 128
|
||||
float_flag_invalid = 0x0001,
|
||||
float_flag_divbyzero = 0x0002,
|
||||
float_flag_overflow = 0x0004,
|
||||
float_flag_underflow = 0x0008,
|
||||
float_flag_inexact = 0x0010,
|
||||
float_flag_input_denormal = 0x0020,
|
||||
float_flag_output_denormal = 0x0040,
|
||||
float_flag_invalid_isi = 0x0080, /* inf - inf */
|
||||
float_flag_invalid_imz = 0x0100, /* inf * 0 */
|
||||
float_flag_invalid_idi = 0x0200, /* inf / inf */
|
||||
float_flag_invalid_zdz = 0x0400, /* 0 / 0 */
|
||||
float_flag_invalid_sqrt = 0x0800, /* sqrt(-x) */
|
||||
float_flag_invalid_cvti = 0x1000, /* non-nan to integer */
|
||||
float_flag_invalid_snan = 0x2000, /* any operand was snan */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -171,8 +178,8 @@ typedef enum __attribute__((__packed__)) {
|
|||
*/
|
||||
|
||||
typedef struct float_status {
|
||||
uint16_t float_exception_flags;
|
||||
FloatRoundMode float_rounding_mode;
|
||||
uint8_t float_exception_flags;
|
||||
FloatX80RoundPrec floatx80_rounding_precision;
|
||||
bool tininess_before_rounding;
|
||||
/* should denormalised results go to zero and set the inexact flag? */
|
||||
|
|
|
@ -100,7 +100,7 @@ typedef enum {
|
|||
| Routine to raise any or all of the software IEC/IEEE floating-point
|
||||
| exception flags.
|
||||
*----------------------------------------------------------------------------*/
|
||||
static inline void float_raise(uint8_t flags, float_status *status)
|
||||
static inline void float_raise(uint16_t flags, float_status *status)
|
||||
{
|
||||
status->float_exception_flags |= flags;
|
||||
}
|
||||
|
@ -908,6 +908,18 @@ static inline bool float64_unordered_quiet(float64 a, float64 b,
|
|||
*----------------------------------------------------------------------------*/
|
||||
float64 float64_default_nan(float_status *status);
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE double-precision operations, rounding to single precision,
|
||||
| returning a result in double precision, with only one rounding step.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
float64 float64r32_add(float64, float64, float_status *status);
|
||||
float64 float64r32_sub(float64, float64, float_status *status);
|
||||
float64 float64r32_mul(float64, float64, float_status *status);
|
||||
float64 float64r32_div(float64, float64, float_status *status);
|
||||
float64 float64r32_muladd(float64, float64, float64, int, float_status *status);
|
||||
float64 float64r32_sqrt(float64, float_status *status);
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE extended double-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "qom/object.h"
|
||||
|
||||
typedef struct PnvPHB3 PnvPHB3;
|
||||
typedef struct PnvChip PnvChip;
|
||||
|
||||
/*
|
||||
* PHB3 XICS Source for MSIs
|
||||
|
@ -157,6 +158,8 @@ struct PnvPHB3 {
|
|||
PnvPHB3RootPort root;
|
||||
|
||||
QLIST_HEAD(, PnvPhb3DMASpace) dma_spaces;
|
||||
|
||||
PnvChip *chip;
|
||||
};
|
||||
|
||||
uint64_t pnv_phb3_reg_read(void *opaque, hwaddr off, unsigned size);
|
||||
|
|
|
@ -205,6 +205,8 @@ struct PnvPhb4PecState {
|
|||
#define PHB4_PEC_MAX_STACKS 3
|
||||
uint32_t num_stacks;
|
||||
PnvPhb4PecStack stacks[PHB4_PEC_MAX_STACKS];
|
||||
|
||||
PnvChip *chip;
|
||||
};
|
||||
|
||||
|
||||
|
@ -219,6 +221,9 @@ struct PnvPhb4PecClass {
|
|||
int compat_size;
|
||||
const char *stk_compat;
|
||||
int stk_compat_size;
|
||||
uint64_t version;
|
||||
uint64_t device_id;
|
||||
const uint32_t *num_stacks;
|
||||
};
|
||||
|
||||
#endif /* PCI_HOST_PNV_PHB4_H */
|
||||
|
|
|
@ -53,6 +53,7 @@ struct PnvChip {
|
|||
PnvCore **cores;
|
||||
|
||||
uint32_t num_phbs;
|
||||
uint32_t num_pecs;
|
||||
|
||||
MemoryRegion xscom_mmio;
|
||||
MemoryRegion xscom;
|
||||
|
@ -136,6 +137,7 @@ struct PnvChipClass {
|
|||
uint64_t chip_cfam_id;
|
||||
uint64_t cores_mask;
|
||||
uint32_t num_phbs;
|
||||
uint32_t num_pecs;
|
||||
|
||||
DeviceRealize parent_realize;
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
|
||||
implementation for certain IBM POWER hardware. The sources are at
|
||||
https://github.com/aik/SLOF, and the image currently in qemu is
|
||||
built from git tag qemu-slof-20210711.
|
||||
built from git tag qemu-slof-20211112.
|
||||
|
||||
- VOF (Virtual Open Firmware) is a minimalistic firmware to work with
|
||||
-machine pseries,x-vof=on. When enabled, the firmware acts as a slim shim and
|
||||
|
|
BIN
pc-bios/slof.bin
BIN
pc-bios/slof.bin
Binary file not shown.
|
@ -1 +1 @@
|
|||
Subproject commit dd0dcaa1c1085c159ddab709c7f274b3917be8bd
|
||||
Subproject commit a6906b024c6cca5a86496f51eb4bfee3a0c36148
|
|
@ -67,40 +67,6 @@
|
|||
POWERPC_DEF_SVR(_name, _desc, _pvr, POWERPC_SVR_NONE, _type)
|
||||
|
||||
/* Embedded PowerPC */
|
||||
/* PowerPC 401 family */
|
||||
POWERPC_DEF("401", CPU_POWERPC_401, 401,
|
||||
"Generic PowerPC 401")
|
||||
/* PowerPC 401 cores */
|
||||
POWERPC_DEF("401a1", CPU_POWERPC_401A1, 401,
|
||||
"PowerPC 401A1")
|
||||
POWERPC_DEF("401b2", CPU_POWERPC_401B2, 401x2,
|
||||
"PowerPC 401B2")
|
||||
POWERPC_DEF("401c2", CPU_POWERPC_401C2, 401x2,
|
||||
"PowerPC 401C2")
|
||||
POWERPC_DEF("401d2", CPU_POWERPC_401D2, 401x2,
|
||||
"PowerPC 401D2")
|
||||
POWERPC_DEF("401e2", CPU_POWERPC_401E2, 401x2,
|
||||
"PowerPC 401E2")
|
||||
POWERPC_DEF("401f2", CPU_POWERPC_401F2, 401x2,
|
||||
"PowerPC 401F2")
|
||||
/* XXX: to be checked */
|
||||
POWERPC_DEF("401g2", CPU_POWERPC_401G2, 401x2,
|
||||
"PowerPC 401G2")
|
||||
/* PowerPC 401 microcontrollers */
|
||||
POWERPC_DEF("iop480", CPU_POWERPC_IOP480, IOP480,
|
||||
"IOP480 (401 microcontroller)")
|
||||
POWERPC_DEF("cobra", CPU_POWERPC_COBRA, 401,
|
||||
"IBM Processor for Network Resources")
|
||||
/* PowerPC 403 family */
|
||||
/* PowerPC 403 microcontrollers */
|
||||
POWERPC_DEF("403ga", CPU_POWERPC_403GA, 403,
|
||||
"PowerPC 403 GA")
|
||||
POWERPC_DEF("403gb", CPU_POWERPC_403GB, 403,
|
||||
"PowerPC 403 GB")
|
||||
POWERPC_DEF("403gc", CPU_POWERPC_403GC, 403,
|
||||
"PowerPC 403 GC")
|
||||
POWERPC_DEF("403gcx", CPU_POWERPC_403GCX, 403GCX,
|
||||
"PowerPC 403 GCX")
|
||||
/* PowerPC 405 family */
|
||||
/* PowerPC 405 cores */
|
||||
POWERPC_DEF("405d2", CPU_POWERPC_405D2, 405,
|
||||
|
|
|
@ -38,27 +38,8 @@ extern PowerPCCPUAlias ppc_cpu_aliases[];
|
|||
/*****************************************************************************/
|
||||
/* PVR definitions for most known PowerPC */
|
||||
enum {
|
||||
/* PowerPC 401 family */
|
||||
/* Generic PowerPC 401 */
|
||||
#define CPU_POWERPC_401 CPU_POWERPC_401G2
|
||||
/* PowerPC 401 cores */
|
||||
CPU_POWERPC_401A1 = 0x00210000,
|
||||
CPU_POWERPC_401B2 = 0x00220000,
|
||||
CPU_POWERPC_401C2 = 0x00230000,
|
||||
CPU_POWERPC_401D2 = 0x00240000,
|
||||
CPU_POWERPC_401E2 = 0x00250000,
|
||||
CPU_POWERPC_401F2 = 0x00260000,
|
||||
CPU_POWERPC_401G2 = 0x00270000,
|
||||
/* PowerPC 401 microcontrolers */
|
||||
#define CPU_POWERPC_IOP480 CPU_POWERPC_401B2
|
||||
/* IBM Processor for Network Resources */
|
||||
CPU_POWERPC_COBRA = 0x10100000, /* XXX: 405 ? */
|
||||
/* PowerPC 403 family */
|
||||
/* PowerPC 403 microcontrollers */
|
||||
CPU_POWERPC_403GA = 0x00200011,
|
||||
CPU_POWERPC_403GB = 0x00200100,
|
||||
CPU_POWERPC_403GC = 0x00200200,
|
||||
CPU_POWERPC_403GCX = 0x00201400,
|
||||
/* PowerPC 405 family */
|
||||
/* PowerPC 405 cores */
|
||||
CPU_POWERPC_405D2 = 0x20010000,
|
||||
|
|
|
@ -45,12 +45,14 @@ enum powerpc_mmu_t {
|
|||
POWERPC_MMU_32B = 0x00000001,
|
||||
/* PowerPC 6xx MMU with software TLB */
|
||||
POWERPC_MMU_SOFT_6xx = 0x00000002,
|
||||
/* PowerPC 74xx MMU with software TLB */
|
||||
/*
|
||||
* PowerPC 74xx MMU with software TLB (this has been
|
||||
* disabled, see git history for more information.
|
||||
* keywords: tlbld tlbli TLBMISS PTEHI PTELO)
|
||||
*/
|
||||
POWERPC_MMU_SOFT_74xx = 0x00000003,
|
||||
/* PowerPC 4xx MMU with software TLB */
|
||||
POWERPC_MMU_SOFT_4xx = 0x00000004,
|
||||
/* PowerPC 4xx MMU with software TLB and zones protections */
|
||||
POWERPC_MMU_SOFT_4xx_Z = 0x00000005,
|
||||
/* PowerPC MMU in real mode only */
|
||||
POWERPC_MMU_REAL = 0x00000006,
|
||||
/* Freescale MPC8xx MMU model */
|
||||
|
@ -94,8 +96,6 @@ enum powerpc_excp_t {
|
|||
POWERPC_EXCP_602,
|
||||
/* PowerPC 603 exception model */
|
||||
POWERPC_EXCP_603,
|
||||
/* PowerPC 603e exception model */
|
||||
POWERPC_EXCP_603E,
|
||||
/* PowerPC G2 exception model */
|
||||
POWERPC_EXCP_G2,
|
||||
/* PowerPC 604 exception model */
|
||||
|
@ -147,8 +147,6 @@ enum powerpc_input_t {
|
|||
PPC_FLAGS_INPUT_POWER7,
|
||||
/* PowerPC POWER9 bus */
|
||||
PPC_FLAGS_INPUT_POWER9,
|
||||
/* PowerPC 401 bus */
|
||||
PPC_FLAGS_INPUT_401,
|
||||
/* Freescale RCPU bus */
|
||||
PPC_FLAGS_INPUT_RCPU,
|
||||
};
|
||||
|
|
|
@ -112,7 +112,7 @@ static inline void fpscr_set_rounding_mode(CPUPPCState *env)
|
|||
|
||||
void ppc_store_fpscr(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
val &= ~(FP_VX | FP_FEX);
|
||||
val &= FPSCR_MTFS_MASK;
|
||||
if (val & FPSCR_IX) {
|
||||
val |= FP_VX;
|
||||
}
|
||||
|
|
|
@ -296,6 +296,16 @@ typedef struct ppc_v3_pate_t {
|
|||
uint64_t dw1;
|
||||
} ppc_v3_pate_t;
|
||||
|
||||
/* PMU related structs and defines */
|
||||
#define PMU_COUNTERS_NUM 6
|
||||
typedef enum {
|
||||
PMU_EVENT_INVALID = 0,
|
||||
PMU_EVENT_INACTIVE,
|
||||
PMU_EVENT_CYCLES,
|
||||
PMU_EVENT_INSTRUCTIONS,
|
||||
PMU_EVENT_INSN_RUN_LATCH,
|
||||
} PMUEventType;
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Machine state register bits definition */
|
||||
#define MSR_SF 63 /* Sixty-four-bit mode hflags */
|
||||
|
@ -351,6 +361,11 @@ typedef struct ppc_v3_pate_t {
|
|||
#define MMCR0_FCECE PPC_BIT(38) /* FC on Enabled Cond or Event */
|
||||
#define MMCR0_PMCC0 PPC_BIT(44) /* PMC Control bit 0 */
|
||||
#define MMCR0_PMCC1 PPC_BIT(45) /* PMC Control bit 1 */
|
||||
#define MMCR0_PMCC PPC_BITMASK(44, 45) /* PMC Control */
|
||||
#define MMCR0_FC14 PPC_BIT(58) /* PMC Freeze Counters 1-4 bit */
|
||||
#define MMCR0_FC56 PPC_BIT(59) /* PMC Freeze Counters 5-6 bit */
|
||||
#define MMCR0_PMC1CE PPC_BIT(48) /* MMCR0 PMC1 Condition Enabled */
|
||||
#define MMCR0_PMCjCE PPC_BIT(49) /* MMCR0 PMCj Condition Enabled */
|
||||
/* MMCR0 userspace r/w mask */
|
||||
#define MMCR0_UREG_MASK (MMCR0_FC | MMCR0_PMAO | MMCR0_PMAE)
|
||||
/* MMCR2 userspace r/w mask */
|
||||
|
@ -363,6 +378,33 @@ typedef struct ppc_v3_pate_t {
|
|||
#define MMCR2_UREG_MASK (MMCR2_FC1P0 | MMCR2_FC2P0 | MMCR2_FC3P0 | \
|
||||
MMCR2_FC4P0 | MMCR2_FC5P0 | MMCR2_FC6P0)
|
||||
|
||||
#define MMCR1_EVT_SIZE 8
|
||||
/* extract64() does a right shift before extracting */
|
||||
#define MMCR1_PMC1SEL_START 32
|
||||
#define MMCR1_PMC1EVT_EXTR (64 - MMCR1_PMC1SEL_START - MMCR1_EVT_SIZE)
|
||||
#define MMCR1_PMC2SEL_START 40
|
||||
#define MMCR1_PMC2EVT_EXTR (64 - MMCR1_PMC2SEL_START - MMCR1_EVT_SIZE)
|
||||
#define MMCR1_PMC3SEL_START 48
|
||||
#define MMCR1_PMC3EVT_EXTR (64 - MMCR1_PMC3SEL_START - MMCR1_EVT_SIZE)
|
||||
#define MMCR1_PMC4SEL_START 56
|
||||
#define MMCR1_PMC4EVT_EXTR (64 - MMCR1_PMC4SEL_START - MMCR1_EVT_SIZE)
|
||||
|
||||
/* PMU uses CTRL_RUN to sample PM_RUN_INST_CMPL */
|
||||
#define CTRL_RUN PPC_BIT(63)
|
||||
|
||||
/* EBB/BESCR bits */
|
||||
/* Global Enable */
|
||||
#define BESCR_GE PPC_BIT(0)
|
||||
/* External Event-based Exception Enable */
|
||||
#define BESCR_EE PPC_BIT(30)
|
||||
/* Performance Monitor Event-based Exception Enable */
|
||||
#define BESCR_PME PPC_BIT(31)
|
||||
/* External Event-based Exception Occurred */
|
||||
#define BESCR_EEO PPC_BIT(62)
|
||||
/* Performance Monitor Event-based Exception Occurred */
|
||||
#define BESCR_PMEO PPC_BIT(63)
|
||||
#define BESCR_INVALID PPC_BITMASK(32, 33)
|
||||
|
||||
/* LPCR bits */
|
||||
#define LPCR_VPM0 PPC_BIT(0)
|
||||
#define LPCR_VPM1 PPC_BIT(1)
|
||||
|
@ -630,6 +672,7 @@ enum {
|
|||
HFLAGS_PR = 14, /* MSR_PR */
|
||||
HFLAGS_PMCC0 = 15, /* MMCR0 PMCC bit 0 */
|
||||
HFLAGS_PMCC1 = 16, /* MMCR0 PMCC bit 1 */
|
||||
HFLAGS_INSN_CNT = 17, /* PMU instruction count enabled */
|
||||
HFLAGS_VSX = 23, /* MSR_VSX if cpu has VSX */
|
||||
HFLAGS_VR = 25, /* MSR_VR if cpu has VRE */
|
||||
|
||||
|
@ -759,6 +802,10 @@ enum {
|
|||
FP_VXZDZ | FP_VXIMZ | FP_VXVC | FP_VXSOFT | \
|
||||
FP_VXSQRT | FP_VXCVI)
|
||||
|
||||
/* FPSCR bits that can be set by mtfsf, mtfsfi and mtfsb1 */
|
||||
#define FPSCR_MTFS_MASK (~(MAKE_64BIT_MASK(36, 28) | PPC_BIT(28) | \
|
||||
FP_FEX | FP_VX | PPC_BIT(52)))
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Vector status and control register */
|
||||
#define VSCR_NJ 16 /* Vector non-java */
|
||||
|
@ -1191,6 +1238,18 @@ struct CPUPPCState {
|
|||
uint32_t tm_vscr;
|
||||
uint64_t tm_dscr;
|
||||
uint64_t tm_tar;
|
||||
|
||||
/*
|
||||
* Timers used to fire performance monitor alerts
|
||||
* when counting cycles.
|
||||
*/
|
||||
QEMUTimer *pmu_cyc_overflow_timers[PMU_COUNTERS_NUM];
|
||||
|
||||
/*
|
||||
* PMU base time value used by the PMU to calculate
|
||||
* running cycles.
|
||||
*/
|
||||
uint64_t pmu_base_time;
|
||||
};
|
||||
|
||||
#define SET_FIT_PERIOD(a_, b_, c_, d_) \
|
||||
|
@ -2138,8 +2197,6 @@ enum {
|
|||
PPC_SEGMENT = 0x0000020000000000ULL,
|
||||
/* PowerPC 6xx TLB management instructions */
|
||||
PPC_6xx_TLB = 0x0000040000000000ULL,
|
||||
/* PowerPC 74xx TLB management instructions */
|
||||
PPC_74xx_TLB = 0x0000080000000000ULL,
|
||||
/* PowerPC 40x TLB management instructions */
|
||||
PPC_40x_TLB = 0x0000100000000000ULL,
|
||||
/* segment register access instructions for PowerPC 64 "bridge" */
|
||||
|
@ -2196,7 +2253,7 @@ enum {
|
|||
| PPC_CACHE_DCBZ \
|
||||
| PPC_CACHE_DCBA | PPC_CACHE_LOCK \
|
||||
| PPC_EXTERN | PPC_SEGMENT | PPC_6xx_TLB \
|
||||
| PPC_74xx_TLB | PPC_40x_TLB | PPC_SEGMENT_64B \
|
||||
| PPC_40x_TLB | PPC_SEGMENT_64B \
|
||||
| PPC_SLBI | PPC_WRTEE | PPC_40x_EXCP \
|
||||
| PPC_405_MAC | PPC_440_SPEC | PPC_BOOKE \
|
||||
| PPC_MFAPIDI | PPC_TLBIVA | PPC_TLBIVAX \
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "helper_regs.h"
|
||||
#include "internal.h"
|
||||
#include "spr_tcg.h"
|
||||
#include "power8-pmu.h"
|
||||
|
||||
/* #define PPC_DEBUG_SPR */
|
||||
/* #define USE_APPLE_GDB */
|
||||
|
@ -945,31 +946,6 @@ static void register_l3_ctrl(CPUPPCState *env)
|
|||
0x00000000);
|
||||
}
|
||||
|
||||
static void register_74xx_soft_tlb(CPUPPCState *env, int nb_tlbs, int nb_ways)
|
||||
{
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
env->nb_tlb = nb_tlbs;
|
||||
env->nb_ways = nb_ways;
|
||||
env->id_tlbs = 1;
|
||||
env->tlb_type = TLB_6XX;
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_PTEHI, "PTEHI",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_PTELO, "PTELO",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_TLBMISS, "TLBMISS",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void register_usprg3_sprs(CPUPPCState *env)
|
||||
{
|
||||
spr_register(env, SPR_USPRG3, "USPRG3",
|
||||
|
@ -1578,169 +1554,6 @@ static void register_405_sprs(CPUPPCState *env)
|
|||
register_usprgh_sprs(env);
|
||||
}
|
||||
|
||||
/* SPR shared between PowerPC 401 & 403 implementations */
|
||||
static void register_401_403_sprs(CPUPPCState *env)
|
||||
{
|
||||
/* Time base */
|
||||
spr_register(env, SPR_403_VTBL, "TBL",
|
||||
&spr_read_tbl, SPR_NOACCESS,
|
||||
&spr_read_tbl, SPR_NOACCESS,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_403_TBL, "TBL",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, &spr_write_tbl,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_403_VTBU, "TBU",
|
||||
&spr_read_tbu, SPR_NOACCESS,
|
||||
&spr_read_tbu, SPR_NOACCESS,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_403_TBU, "TBU",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, &spr_write_tbu,
|
||||
0x00000000);
|
||||
/* Debug */
|
||||
/* not emulated, as QEMU do not emulate caches */
|
||||
spr_register(env, SPR_403_CDBCR, "CDBCR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
}
|
||||
|
||||
/* SPR specific to PowerPC 401 implementation */
|
||||
static void register_401_sprs(CPUPPCState *env)
|
||||
{
|
||||
/* Debug interface */
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_40x_DBCR0, "DBCR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_40x_dbcr0,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_40x_DBSR, "DBSR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_clear,
|
||||
/* Last reset was system reset */
|
||||
0x00000300);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_40x_DAC1, "DAC",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_40x_IAC1, "IAC",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* Storage control */
|
||||
/* XXX: TODO: not implemented */
|
||||
spr_register(env, SPR_405_SLER, "SLER",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_40x_sler,
|
||||
0x00000000);
|
||||
/* not emulated, as QEMU never does speculative access */
|
||||
spr_register(env, SPR_40x_SGR, "SGR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0xFFFFFFFF);
|
||||
/* not emulated, as QEMU do not emulate caches */
|
||||
spr_register(env, SPR_40x_DCWR, "DCWR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
}
|
||||
|
||||
static void register_401x2_sprs(CPUPPCState *env)
|
||||
{
|
||||
register_401_sprs(env);
|
||||
spr_register(env, SPR_40x_PID, "PID",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_40x_ZPR, "ZPR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
}
|
||||
|
||||
/* SPR specific to PowerPC 403 implementation */
|
||||
static void register_403_sprs(CPUPPCState *env)
|
||||
{
|
||||
/* Debug interface */
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_40x_DBCR0, "DBCR0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_40x_dbcr0,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_40x_DBSR, "DBSR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_clear,
|
||||
/* Last reset was system reset */
|
||||
0x00000300);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_40x_DAC1, "DAC1",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_40x_DAC2, "DAC2",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_40x_IAC1, "IAC1",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_40x_IAC2, "IAC2",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
}
|
||||
|
||||
static void register_403_real_sprs(CPUPPCState *env)
|
||||
{
|
||||
spr_register(env, SPR_403_PBL1, "PBL1",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_403_pbr, &spr_write_403_pbr,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_403_PBU1, "PBU1",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_403_pbr, &spr_write_403_pbr,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_403_PBL2, "PBL2",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_403_pbr, &spr_write_403_pbr,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_403_PBU2, "PBU2",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_403_pbr, &spr_write_403_pbr,
|
||||
0x00000000);
|
||||
}
|
||||
|
||||
static void register_403_mmu_sprs(CPUPPCState *env)
|
||||
{
|
||||
/* MMU */
|
||||
spr_register(env, SPR_40x_PID, "PID",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_40x_ZPR, "ZPR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
}
|
||||
|
||||
/* SPR specific to PowerPC compression coprocessor extension */
|
||||
static void register_compress_sprs(CPUPPCState *env)
|
||||
{
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_401_SKR, "SKR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
}
|
||||
|
||||
static void register_5xx_8xx_sprs(CPUPPCState *env)
|
||||
{
|
||||
|
@ -2128,26 +1941,6 @@ static void register_8xx_sprs(CPUPPCState *env)
|
|||
|
||||
/*****************************************************************************/
|
||||
/* Exception vectors models */
|
||||
static void init_excp_4xx_real(CPUPPCState *env)
|
||||
{
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
env->excp_vectors[POWERPC_EXCP_CRITICAL] = 0x00000100;
|
||||
env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
|
||||
env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
|
||||
env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
|
||||
env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
|
||||
env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
|
||||
env->excp_vectors[POWERPC_EXCP_PIT] = 0x00001000;
|
||||
env->excp_vectors[POWERPC_EXCP_FIT] = 0x00001010;
|
||||
env->excp_vectors[POWERPC_EXCP_WDT] = 0x00001020;
|
||||
env->excp_vectors[POWERPC_EXCP_DEBUG] = 0x00002000;
|
||||
env->ivor_mask = 0x0000FFF0UL;
|
||||
env->ivpr_mask = 0xFFFF0000UL;
|
||||
/* Hardware reset vector */
|
||||
env->hreset_vector = 0xFFFFFFFCUL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void init_excp_4xx_softmmu(CPUPPCState *env)
|
||||
{
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
|
@ -2180,7 +1973,7 @@ static void init_excp_MPC5xx(CPUPPCState *env)
|
|||
env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
|
||||
env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
|
||||
env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
|
||||
env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000900;
|
||||
env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800;
|
||||
env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
|
||||
env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
|
||||
env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
|
||||
|
@ -2207,7 +2000,7 @@ static void init_excp_MPC8xx(CPUPPCState *env)
|
|||
env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
|
||||
env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
|
||||
env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
|
||||
env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000900;
|
||||
env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800;
|
||||
env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
|
||||
env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
|
||||
env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
|
||||
|
@ -2273,8 +2066,14 @@ static void init_excp_e200(CPUPPCState *env, target_ulong ivpr_mask)
|
|||
env->excp_vectors[POWERPC_EXCP_DTLB] = 0x00000000;
|
||||
env->excp_vectors[POWERPC_EXCP_ITLB] = 0x00000000;
|
||||
env->excp_vectors[POWERPC_EXCP_DEBUG] = 0x00000000;
|
||||
/*
|
||||
* These two are the same IVOR as POWERPC_EXCP_VPU and
|
||||
* POWERPC_EXCP_VPUA. We deal with that when dispatching at
|
||||
* powerpc_excp().
|
||||
*/
|
||||
env->excp_vectors[POWERPC_EXCP_SPEU] = 0x00000000;
|
||||
env->excp_vectors[POWERPC_EXCP_EFPDI] = 0x00000000;
|
||||
|
||||
env->excp_vectors[POWERPC_EXCP_EFPRI] = 0x00000000;
|
||||
env->ivor_mask = 0x0000FFF7UL;
|
||||
env->ivpr_mask = ivpr_mask;
|
||||
|
@ -2537,9 +2336,6 @@ static void init_excp_7450(CPUPPCState *env)
|
|||
env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
|
||||
env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00;
|
||||
env->excp_vectors[POWERPC_EXCP_VPU] = 0x00000F20;
|
||||
env->excp_vectors[POWERPC_EXCP_IFTLB] = 0x00001000;
|
||||
env->excp_vectors[POWERPC_EXCP_DLTLB] = 0x00001100;
|
||||
env->excp_vectors[POWERPC_EXCP_DSTLB] = 0x00001200;
|
||||
env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300;
|
||||
env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400;
|
||||
env->excp_vectors[POWERPC_EXCP_VPUA] = 0x00001600;
|
||||
|
@ -2690,335 +2486,6 @@ static int check_pow_hid0_74xx(CPUPPCState *env)
|
|||
\
|
||||
static void glue(glue(ppc_, _name), _cpu_family_class_init)
|
||||
|
||||
static void init_proc_401(CPUPPCState *env)
|
||||
{
|
||||
register_40x_sprs(env);
|
||||
register_401_403_sprs(env);
|
||||
register_401_sprs(env);
|
||||
init_excp_4xx_real(env);
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
/* Allocate hardware IRQ controller */
|
||||
ppc40x_irq_init(env_archcpu(env));
|
||||
|
||||
SET_FIT_PERIOD(12, 16, 20, 24);
|
||||
SET_WDT_PERIOD(16, 20, 24, 28);
|
||||
}
|
||||
|
||||
POWERPC_FAMILY(401)(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||
|
||||
dc->desc = "PowerPC 401";
|
||||
pcc->init_proc = init_proc_401;
|
||||
pcc->check_pow = check_pow_nocheck;
|
||||
pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING |
|
||||
PPC_WRTEE | PPC_DCR |
|
||||
PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT |
|
||||
PPC_CACHE_DCBZ |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_4xx_COMMON | PPC_40x_EXCP;
|
||||
pcc->msr_mask = (1ull << MSR_KEY) |
|
||||
(1ull << MSR_POW) |
|
||||
(1ull << MSR_CE) |
|
||||
(1ull << MSR_ILE) |
|
||||
(1ull << MSR_EE) |
|
||||
(1ull << MSR_PR) |
|
||||
(1ull << MSR_ME) |
|
||||
(1ull << MSR_DE) |
|
||||
(1ull << MSR_LE);
|
||||
pcc->mmu_model = POWERPC_MMU_REAL;
|
||||
pcc->excp_model = POWERPC_EXCP_40x;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_401;
|
||||
pcc->bfd_mach = bfd_mach_ppc_403;
|
||||
pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DE |
|
||||
POWERPC_FLAG_BUS_CLK;
|
||||
}
|
||||
|
||||
static void init_proc_401x2(CPUPPCState *env)
|
||||
{
|
||||
register_40x_sprs(env);
|
||||
register_401_403_sprs(env);
|
||||
register_401x2_sprs(env);
|
||||
register_compress_sprs(env);
|
||||
/* Memory management */
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
env->nb_tlb = 64;
|
||||
env->nb_ways = 1;
|
||||
env->id_tlbs = 0;
|
||||
env->tlb_type = TLB_EMB;
|
||||
#endif
|
||||
init_excp_4xx_softmmu(env);
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
/* Allocate hardware IRQ controller */
|
||||
ppc40x_irq_init(env_archcpu(env));
|
||||
|
||||
SET_FIT_PERIOD(12, 16, 20, 24);
|
||||
SET_WDT_PERIOD(16, 20, 24, 28);
|
||||
}
|
||||
|
||||
POWERPC_FAMILY(401x2)(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||
|
||||
dc->desc = "PowerPC 401x2";
|
||||
pcc->init_proc = init_proc_401x2;
|
||||
pcc->check_pow = check_pow_nocheck;
|
||||
pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB |
|
||||
PPC_DCR | PPC_WRTEE |
|
||||
PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT |
|
||||
PPC_CACHE_DCBZ | PPC_CACHE_DCBA |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC |
|
||||
PPC_4xx_COMMON | PPC_40x_EXCP;
|
||||
pcc->msr_mask = (1ull << 20) |
|
||||
(1ull << MSR_KEY) |
|
||||
(1ull << MSR_POW) |
|
||||
(1ull << MSR_CE) |
|
||||
(1ull << MSR_ILE) |
|
||||
(1ull << MSR_EE) |
|
||||
(1ull << MSR_PR) |
|
||||
(1ull << MSR_ME) |
|
||||
(1ull << MSR_DE) |
|
||||
(1ull << MSR_IR) |
|
||||
(1ull << MSR_DR) |
|
||||
(1ull << MSR_LE);
|
||||
pcc->mmu_model = POWERPC_MMU_SOFT_4xx_Z;
|
||||
pcc->excp_model = POWERPC_EXCP_40x;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_401;
|
||||
pcc->bfd_mach = bfd_mach_ppc_403;
|
||||
pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DE |
|
||||
POWERPC_FLAG_BUS_CLK;
|
||||
}
|
||||
|
||||
static void init_proc_401x3(CPUPPCState *env)
|
||||
{
|
||||
register_40x_sprs(env);
|
||||
register_401_403_sprs(env);
|
||||
register_401_sprs(env);
|
||||
register_401x2_sprs(env);
|
||||
register_compress_sprs(env);
|
||||
init_excp_4xx_softmmu(env);
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
/* Allocate hardware IRQ controller */
|
||||
ppc40x_irq_init(env_archcpu(env));
|
||||
|
||||
SET_FIT_PERIOD(12, 16, 20, 24);
|
||||
SET_WDT_PERIOD(16, 20, 24, 28);
|
||||
}
|
||||
|
||||
POWERPC_FAMILY(401x3)(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||
|
||||
dc->desc = "PowerPC 401x3";
|
||||
pcc->init_proc = init_proc_401x3;
|
||||
pcc->check_pow = check_pow_nocheck;
|
||||
pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB |
|
||||
PPC_DCR | PPC_WRTEE |
|
||||
PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT |
|
||||
PPC_CACHE_DCBZ | PPC_CACHE_DCBA |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC |
|
||||
PPC_4xx_COMMON | PPC_40x_EXCP;
|
||||
pcc->msr_mask = (1ull << 20) |
|
||||
(1ull << MSR_KEY) |
|
||||
(1ull << MSR_POW) |
|
||||
(1ull << MSR_CE) |
|
||||
(1ull << MSR_ILE) |
|
||||
(1ull << MSR_EE) |
|
||||
(1ull << MSR_PR) |
|
||||
(1ull << MSR_ME) |
|
||||
(1ull << MSR_DWE) |
|
||||
(1ull << MSR_DE) |
|
||||
(1ull << MSR_IR) |
|
||||
(1ull << MSR_DR) |
|
||||
(1ull << MSR_LE);
|
||||
pcc->mmu_model = POWERPC_MMU_SOFT_4xx_Z;
|
||||
pcc->excp_model = POWERPC_EXCP_40x;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_401;
|
||||
pcc->bfd_mach = bfd_mach_ppc_403;
|
||||
pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DE |
|
||||
POWERPC_FLAG_BUS_CLK;
|
||||
}
|
||||
|
||||
static void init_proc_IOP480(CPUPPCState *env)
|
||||
{
|
||||
register_40x_sprs(env);
|
||||
register_401_403_sprs(env);
|
||||
register_401x2_sprs(env);
|
||||
register_compress_sprs(env);
|
||||
/* Memory management */
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
env->nb_tlb = 64;
|
||||
env->nb_ways = 1;
|
||||
env->id_tlbs = 0;
|
||||
env->tlb_type = TLB_EMB;
|
||||
#endif
|
||||
init_excp_4xx_softmmu(env);
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
/* Allocate hardware IRQ controller */
|
||||
ppc40x_irq_init(env_archcpu(env));
|
||||
|
||||
SET_FIT_PERIOD(8, 12, 16, 20);
|
||||
SET_WDT_PERIOD(16, 20, 24, 28);
|
||||
}
|
||||
|
||||
POWERPC_FAMILY(IOP480)(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||
|
||||
dc->desc = "IOP480";
|
||||
pcc->init_proc = init_proc_IOP480;
|
||||
pcc->check_pow = check_pow_nocheck;
|
||||
pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING |
|
||||
PPC_DCR | PPC_WRTEE |
|
||||
PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT |
|
||||
PPC_CACHE_DCBZ | PPC_CACHE_DCBA |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC |
|
||||
PPC_4xx_COMMON | PPC_40x_EXCP;
|
||||
pcc->msr_mask = (1ull << 20) |
|
||||
(1ull << MSR_KEY) |
|
||||
(1ull << MSR_POW) |
|
||||
(1ull << MSR_CE) |
|
||||
(1ull << MSR_ILE) |
|
||||
(1ull << MSR_EE) |
|
||||
(1ull << MSR_PR) |
|
||||
(1ull << MSR_ME) |
|
||||
(1ull << MSR_DE) |
|
||||
(1ull << MSR_IR) |
|
||||
(1ull << MSR_DR) |
|
||||
(1ull << MSR_LE);
|
||||
pcc->mmu_model = POWERPC_MMU_SOFT_4xx_Z;
|
||||
pcc->excp_model = POWERPC_EXCP_40x;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_401;
|
||||
pcc->bfd_mach = bfd_mach_ppc_403;
|
||||
pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DE |
|
||||
POWERPC_FLAG_BUS_CLK;
|
||||
}
|
||||
|
||||
static void init_proc_403(CPUPPCState *env)
|
||||
{
|
||||
register_40x_sprs(env);
|
||||
register_401_403_sprs(env);
|
||||
register_403_sprs(env);
|
||||
register_403_real_sprs(env);
|
||||
init_excp_4xx_real(env);
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
/* Allocate hardware IRQ controller */
|
||||
ppc40x_irq_init(env_archcpu(env));
|
||||
|
||||
SET_FIT_PERIOD(8, 12, 16, 20);
|
||||
SET_WDT_PERIOD(16, 20, 24, 28);
|
||||
}
|
||||
|
||||
POWERPC_FAMILY(403)(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||
|
||||
dc->desc = "PowerPC 403";
|
||||
pcc->init_proc = init_proc_403;
|
||||
pcc->check_pow = check_pow_nocheck;
|
||||
pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING |
|
||||
PPC_DCR | PPC_WRTEE |
|
||||
PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT |
|
||||
PPC_CACHE_DCBZ |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_4xx_COMMON | PPC_40x_EXCP;
|
||||
pcc->msr_mask = (1ull << MSR_POW) |
|
||||
(1ull << MSR_CE) |
|
||||
(1ull << MSR_ILE) |
|
||||
(1ull << MSR_EE) |
|
||||
(1ull << MSR_PR) |
|
||||
(1ull << MSR_ME) |
|
||||
(1ull << MSR_PE) |
|
||||
(1ull << MSR_PX) |
|
||||
(1ull << MSR_LE);
|
||||
pcc->mmu_model = POWERPC_MMU_REAL;
|
||||
pcc->excp_model = POWERPC_EXCP_40x;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_401;
|
||||
pcc->bfd_mach = bfd_mach_ppc_403;
|
||||
pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_PX |
|
||||
POWERPC_FLAG_BUS_CLK;
|
||||
}
|
||||
|
||||
static void init_proc_403GCX(CPUPPCState *env)
|
||||
{
|
||||
register_40x_sprs(env);
|
||||
register_401_403_sprs(env);
|
||||
register_403_sprs(env);
|
||||
register_403_real_sprs(env);
|
||||
register_403_mmu_sprs(env);
|
||||
/* Bus access control */
|
||||
/* not emulated, as QEMU never does speculative access */
|
||||
spr_register(env, SPR_40x_SGR, "SGR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0xFFFFFFFF);
|
||||
/* not emulated, as QEMU do not emulate caches */
|
||||
spr_register(env, SPR_40x_DCWR, "DCWR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* Memory management */
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
env->nb_tlb = 64;
|
||||
env->nb_ways = 1;
|
||||
env->id_tlbs = 0;
|
||||
env->tlb_type = TLB_EMB;
|
||||
#endif
|
||||
init_excp_4xx_softmmu(env);
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
/* Allocate hardware IRQ controller */
|
||||
ppc40x_irq_init(env_archcpu(env));
|
||||
|
||||
SET_FIT_PERIOD(8, 12, 16, 20);
|
||||
SET_WDT_PERIOD(16, 20, 24, 28);
|
||||
}
|
||||
|
||||
POWERPC_FAMILY(403GCX)(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||
|
||||
dc->desc = "PowerPC 403 GCX";
|
||||
pcc->init_proc = init_proc_403GCX;
|
||||
pcc->check_pow = check_pow_nocheck;
|
||||
pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING |
|
||||
PPC_DCR | PPC_WRTEE |
|
||||
PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT |
|
||||
PPC_CACHE_DCBZ |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC |
|
||||
PPC_4xx_COMMON | PPC_40x_EXCP;
|
||||
pcc->msr_mask = (1ull << MSR_POW) |
|
||||
(1ull << MSR_CE) |
|
||||
(1ull << MSR_ILE) |
|
||||
(1ull << MSR_EE) |
|
||||
(1ull << MSR_PR) |
|
||||
(1ull << MSR_ME) |
|
||||
(1ull << MSR_PE) |
|
||||
(1ull << MSR_PX) |
|
||||
(1ull << MSR_LE);
|
||||
pcc->mmu_model = POWERPC_MMU_SOFT_4xx_Z;
|
||||
pcc->excp_model = POWERPC_EXCP_40x;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_401;
|
||||
pcc->bfd_mach = bfd_mach_ppc_403;
|
||||
pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_PX |
|
||||
POWERPC_FLAG_BUS_CLK;
|
||||
}
|
||||
|
||||
static void init_proc_405(CPUPPCState *env)
|
||||
{
|
||||
/* Time base */
|
||||
|
@ -4607,6 +4074,7 @@ POWERPC_FAMILY(601v)(ObjectClass *oc, void *data)
|
|||
(1ull << MSR_IR) |
|
||||
(1ull << MSR_DR);
|
||||
pcc->mmu_model = POWERPC_MMU_601;
|
||||
pcc->excp_model = POWERPC_EXCP_601;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_601;
|
||||
pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK | POWERPC_FLAG_HID0_LE;
|
||||
|
@ -4749,41 +4217,13 @@ POWERPC_FAMILY(603)(ObjectClass *oc, void *data)
|
|||
POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK;
|
||||
}
|
||||
|
||||
static void init_proc_603E(CPUPPCState *env)
|
||||
{
|
||||
register_ne_601_sprs(env);
|
||||
register_sdr1_sprs(env);
|
||||
register_603_sprs(env);
|
||||
/* Time base */
|
||||
register_tbl(env);
|
||||
/* hardware implementation registers */
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_HID0, "HID0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_HID1, "HID1",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* Memory management */
|
||||
register_low_BATs(env);
|
||||
register_6xx_7xx_soft_tlb(env, 64, 2);
|
||||
init_excp_603(env);
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
/* Allocate hardware IRQ controller */
|
||||
ppc6xx_irq_init(env_archcpu(env));
|
||||
}
|
||||
|
||||
POWERPC_FAMILY(603E)(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||
|
||||
dc->desc = "PowerPC 603e";
|
||||
pcc->init_proc = init_proc_603E;
|
||||
pcc->init_proc = init_proc_603;
|
||||
pcc->check_pow = check_pow_hid0;
|
||||
pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB |
|
||||
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
|
||||
|
@ -4809,7 +4249,7 @@ POWERPC_FAMILY(603E)(ObjectClass *oc, void *data)
|
|||
(1ull << MSR_RI) |
|
||||
(1ull << MSR_LE);
|
||||
pcc->mmu_model = POWERPC_MMU_SOFT_6xx;
|
||||
pcc->excp_model = POWERPC_EXCP_603E;
|
||||
pcc->excp_model = POWERPC_EXCP_603;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_ec603e;
|
||||
pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE |
|
||||
|
@ -5932,7 +5372,6 @@ static void init_proc_7440(CPUPPCState *env)
|
|||
0x00000000);
|
||||
/* Memory management */
|
||||
register_low_BATs(env);
|
||||
register_74xx_soft_tlb(env, 128, 2);
|
||||
init_excp_7450(env);
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
|
@ -5956,7 +5395,7 @@ POWERPC_FAMILY(7440)(ObjectClass *oc, void *data)
|
|||
PPC_CACHE_DCBA | PPC_CACHE_DCBZ |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
|
||||
PPC_MEM_TLBIA | PPC_74xx_TLB |
|
||||
PPC_MEM_TLBIA |
|
||||
PPC_SEGMENT | PPC_EXTERN |
|
||||
PPC_ALTIVEC;
|
||||
pcc->msr_mask = (1ull << MSR_VR) |
|
||||
|
@ -5976,7 +5415,7 @@ POWERPC_FAMILY(7440)(ObjectClass *oc, void *data)
|
|||
(1ull << MSR_PMM) |
|
||||
(1ull << MSR_RI) |
|
||||
(1ull << MSR_LE);
|
||||
pcc->mmu_model = POWERPC_MMU_SOFT_74xx;
|
||||
pcc->mmu_model = POWERPC_MMU_32B;
|
||||
pcc->excp_model = POWERPC_EXCP_74xx;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_7400;
|
||||
|
@ -6067,7 +5506,6 @@ static void init_proc_7450(CPUPPCState *env)
|
|||
0x00000000);
|
||||
/* Memory management */
|
||||
register_low_BATs(env);
|
||||
register_74xx_soft_tlb(env, 128, 2);
|
||||
init_excp_7450(env);
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
|
@ -6091,7 +5529,7 @@ POWERPC_FAMILY(7450)(ObjectClass *oc, void *data)
|
|||
PPC_CACHE_DCBA | PPC_CACHE_DCBZ |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
|
||||
PPC_MEM_TLBIA | PPC_74xx_TLB |
|
||||
PPC_MEM_TLBIA |
|
||||
PPC_SEGMENT | PPC_EXTERN |
|
||||
PPC_ALTIVEC;
|
||||
pcc->msr_mask = (1ull << MSR_VR) |
|
||||
|
@ -6111,7 +5549,7 @@ POWERPC_FAMILY(7450)(ObjectClass *oc, void *data)
|
|||
(1ull << MSR_PMM) |
|
||||
(1ull << MSR_RI) |
|
||||
(1ull << MSR_LE);
|
||||
pcc->mmu_model = POWERPC_MMU_SOFT_74xx;
|
||||
pcc->mmu_model = POWERPC_MMU_32B;
|
||||
pcc->excp_model = POWERPC_EXCP_74xx;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_7400;
|
||||
|
@ -6205,7 +5643,6 @@ static void init_proc_7445(CPUPPCState *env)
|
|||
/* Memory management */
|
||||
register_low_BATs(env);
|
||||
register_high_BATs(env);
|
||||
register_74xx_soft_tlb(env, 128, 2);
|
||||
init_excp_7450(env);
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
|
@ -6229,7 +5666,7 @@ POWERPC_FAMILY(7445)(ObjectClass *oc, void *data)
|
|||
PPC_CACHE_DCBA | PPC_CACHE_DCBZ |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
|
||||
PPC_MEM_TLBIA | PPC_74xx_TLB |
|
||||
PPC_MEM_TLBIA |
|
||||
PPC_SEGMENT | PPC_EXTERN |
|
||||
PPC_ALTIVEC;
|
||||
pcc->msr_mask = (1ull << MSR_VR) |
|
||||
|
@ -6249,7 +5686,7 @@ POWERPC_FAMILY(7445)(ObjectClass *oc, void *data)
|
|||
(1ull << MSR_PMM) |
|
||||
(1ull << MSR_RI) |
|
||||
(1ull << MSR_LE);
|
||||
pcc->mmu_model = POWERPC_MMU_SOFT_74xx;
|
||||
pcc->mmu_model = POWERPC_MMU_32B;
|
||||
pcc->excp_model = POWERPC_EXCP_74xx;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_7400;
|
||||
|
@ -6345,7 +5782,6 @@ static void init_proc_7455(CPUPPCState *env)
|
|||
/* Memory management */
|
||||
register_low_BATs(env);
|
||||
register_high_BATs(env);
|
||||
register_74xx_soft_tlb(env, 128, 2);
|
||||
init_excp_7450(env);
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
|
@ -6369,7 +5805,7 @@ POWERPC_FAMILY(7455)(ObjectClass *oc, void *data)
|
|||
PPC_CACHE_DCBA | PPC_CACHE_DCBZ |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
|
||||
PPC_MEM_TLBIA | PPC_74xx_TLB |
|
||||
PPC_MEM_TLBIA |
|
||||
PPC_SEGMENT | PPC_EXTERN |
|
||||
PPC_ALTIVEC;
|
||||
pcc->msr_mask = (1ull << MSR_VR) |
|
||||
|
@ -6389,7 +5825,7 @@ POWERPC_FAMILY(7455)(ObjectClass *oc, void *data)
|
|||
(1ull << MSR_PMM) |
|
||||
(1ull << MSR_RI) |
|
||||
(1ull << MSR_LE);
|
||||
pcc->mmu_model = POWERPC_MMU_SOFT_74xx;
|
||||
pcc->mmu_model = POWERPC_MMU_32B;
|
||||
pcc->excp_model = POWERPC_EXCP_74xx;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_7400;
|
||||
|
@ -6509,7 +5945,6 @@ static void init_proc_7457(CPUPPCState *env)
|
|||
/* Memory management */
|
||||
register_low_BATs(env);
|
||||
register_high_BATs(env);
|
||||
register_74xx_soft_tlb(env, 128, 2);
|
||||
init_excp_7450(env);
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
|
@ -6533,7 +5968,7 @@ POWERPC_FAMILY(7457)(ObjectClass *oc, void *data)
|
|||
PPC_CACHE_DCBA | PPC_CACHE_DCBZ |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
|
||||
PPC_MEM_TLBIA | PPC_74xx_TLB |
|
||||
PPC_MEM_TLBIA |
|
||||
PPC_SEGMENT | PPC_EXTERN |
|
||||
PPC_ALTIVEC;
|
||||
pcc->msr_mask = (1ull << MSR_VR) |
|
||||
|
@ -6553,7 +5988,7 @@ POWERPC_FAMILY(7457)(ObjectClass *oc, void *data)
|
|||
(1ull << MSR_PMM) |
|
||||
(1ull << MSR_RI) |
|
||||
(1ull << MSR_LE);
|
||||
pcc->mmu_model = POWERPC_MMU_SOFT_74xx;
|
||||
pcc->mmu_model = POWERPC_MMU_32B;
|
||||
pcc->excp_model = POWERPC_EXCP_74xx;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_7400;
|
||||
|
@ -6648,7 +6083,6 @@ static void init_proc_e600(CPUPPCState *env)
|
|||
/* Memory management */
|
||||
register_low_BATs(env);
|
||||
register_high_BATs(env);
|
||||
register_74xx_soft_tlb(env, 128, 2);
|
||||
init_excp_7450(env);
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
|
@ -6672,7 +6106,7 @@ POWERPC_FAMILY(e600)(ObjectClass *oc, void *data)
|
|||
PPC_CACHE_DCBA | PPC_CACHE_DCBZ |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
|
||||
PPC_MEM_TLBIA | PPC_74xx_TLB |
|
||||
PPC_MEM_TLBIA |
|
||||
PPC_SEGMENT | PPC_EXTERN |
|
||||
PPC_ALTIVEC;
|
||||
pcc->insns_flags2 = PPC_NONE;
|
||||
|
@ -6748,7 +6182,7 @@ static void register_book3s_ctrl_sprs(CPUPPCState *env)
|
|||
{
|
||||
spr_register(env, SPR_CTRL, "SPR_CTRL",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, &spr_write_generic,
|
||||
SPR_NOACCESS, &spr_write_CTRL,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_UCTRL, "SPR_UCTRL",
|
||||
&spr_read_ureg, SPR_NOACCESS,
|
||||
|
@ -6820,11 +6254,11 @@ static void register_book3s_pmu_sup_sprs(CPUPPCState *env)
|
|||
{
|
||||
spr_register_kvm(env, SPR_POWER_MMCR0, "MMCR0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
KVM_REG_PPC_MMCR0, 0x00000000);
|
||||
&spr_read_generic, &spr_write_MMCR0,
|
||||
KVM_REG_PPC_MMCR0, 0x80000000);
|
||||
spr_register_kvm(env, SPR_POWER_MMCR1, "MMCR1",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
&spr_read_generic, &spr_write_MMCR1,
|
||||
KVM_REG_PPC_MMCR1, 0x00000000);
|
||||
spr_register_kvm(env, SPR_POWER_MMCRA, "MMCRA",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
|
@ -6832,27 +6266,27 @@ static void register_book3s_pmu_sup_sprs(CPUPPCState *env)
|
|||
KVM_REG_PPC_MMCRA, 0x00000000);
|
||||
spr_register_kvm(env, SPR_POWER_PMC1, "PMC1",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
&spr_read_PMC, &spr_write_PMC,
|
||||
KVM_REG_PPC_PMC1, 0x00000000);
|
||||
spr_register_kvm(env, SPR_POWER_PMC2, "PMC2",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
&spr_read_PMC, &spr_write_PMC,
|
||||
KVM_REG_PPC_PMC2, 0x00000000);
|
||||
spr_register_kvm(env, SPR_POWER_PMC3, "PMC3",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
&spr_read_PMC, &spr_write_PMC,
|
||||
KVM_REG_PPC_PMC3, 0x00000000);
|
||||
spr_register_kvm(env, SPR_POWER_PMC4, "PMC4",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
&spr_read_PMC, &spr_write_PMC,
|
||||
KVM_REG_PPC_PMC4, 0x00000000);
|
||||
spr_register_kvm(env, SPR_POWER_PMC5, "PMC5",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
&spr_read_PMC, &spr_write_PMC,
|
||||
KVM_REG_PPC_PMC5, 0x00000000);
|
||||
spr_register_kvm(env, SPR_POWER_PMC6, "PMC6",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
&spr_read_PMC, &spr_write_PMC,
|
||||
KVM_REG_PPC_PMC6, 0x00000000);
|
||||
spr_register_kvm(env, SPR_POWER_SIAR, "SIAR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
|
@ -6869,7 +6303,7 @@ static void register_book3s_pmu_user_sprs(CPUPPCState *env)
|
|||
spr_register(env, SPR_POWER_UMMCR0, "UMMCR0",
|
||||
&spr_read_MMCR0_ureg, &spr_write_MMCR0_ureg,
|
||||
&spr_read_ureg, &spr_write_ureg,
|
||||
0x00000000);
|
||||
0x80000000);
|
||||
spr_register(env, SPR_POWER_UMMCR1, "UMMCR1",
|
||||
&spr_read_ureg, SPR_NOACCESS,
|
||||
&spr_read_ureg, &spr_write_ureg,
|
||||
|
@ -7377,6 +6811,20 @@ static void register_power9_mmu_sprs(CPUPPCState *env)
|
|||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize PMU counter overflow timers for Power8 and
|
||||
* newer Power chips when using TCG.
|
||||
*/
|
||||
static void init_tcg_pmu_power8(CPUPPCState *env)
|
||||
{
|
||||
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
|
||||
/* Init PMU overflow timers */
|
||||
if (!kvm_enabled()) {
|
||||
cpu_ppc_pmu_init(env);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void init_proc_book3s_common(CPUPPCState *env)
|
||||
{
|
||||
register_ne_601_sprs(env);
|
||||
|
@ -7694,6 +7142,9 @@ static void init_proc_POWER8(CPUPPCState *env)
|
|||
register_sdr1_sprs(env);
|
||||
register_book3s_207_dbg_sprs(env);
|
||||
|
||||
/* Common TCG PMU */
|
||||
init_tcg_pmu_power8(env);
|
||||
|
||||
/* POWER8 Specific Registers */
|
||||
register_book3s_ids_sprs(env);
|
||||
register_rmor_sprs(env);
|
||||
|
@ -7888,6 +7339,9 @@ static void init_proc_POWER9(CPUPPCState *env)
|
|||
init_proc_book3s_common(env);
|
||||
register_book3s_207_dbg_sprs(env);
|
||||
|
||||
/* Common TCG PMU */
|
||||
init_tcg_pmu_power8(env);
|
||||
|
||||
/* POWER8 Specific Registers */
|
||||
register_book3s_ids_sprs(env);
|
||||
register_amr_sprs(env);
|
||||
|
@ -8104,6 +7558,9 @@ static void init_proc_POWER10(CPUPPCState *env)
|
|||
init_proc_book3s_common(env);
|
||||
register_book3s_207_dbg_sprs(env);
|
||||
|
||||
/* Common TCG PMU */
|
||||
init_tcg_pmu_power8(env);
|
||||
|
||||
/* POWER8 Specific Registers */
|
||||
register_book3s_ids_sprs(env);
|
||||
register_amr_sprs(env);
|
||||
|
@ -9247,7 +8704,6 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags)
|
|||
case POWERPC_MMU_32B:
|
||||
case POWERPC_MMU_601:
|
||||
case POWERPC_MMU_SOFT_6xx:
|
||||
case POWERPC_MMU_SOFT_74xx:
|
||||
#if defined(TARGET_PPC64)
|
||||
case POWERPC_MMU_64B:
|
||||
case POWERPC_MMU_2_03:
|
||||
|
|
|
@ -344,6 +344,16 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
|||
excp = POWERPC_EXCP_PROGRAM;
|
||||
}
|
||||
|
||||
#ifdef TARGET_PPC64
|
||||
/*
|
||||
* SPEU and VPU share the same IVOR but they exist in different
|
||||
* processors. SPEU is e500v1/2 only and VPU is e6500 only.
|
||||
*/
|
||||
if (excp_model == POWERPC_EXCP_BOOKE && excp == POWERPC_EXCP_VPU) {
|
||||
excp = POWERPC_EXCP_SPEU;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (excp) {
|
||||
case POWERPC_EXCP_NONE:
|
||||
/* Should never happen */
|
||||
|
@ -454,15 +464,13 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
|||
break;
|
||||
}
|
||||
case POWERPC_EXCP_ALIGN: /* Alignment exception */
|
||||
/* Get rS/rD and rA from faulting opcode */
|
||||
/*
|
||||
* Get rS/rD and rA from faulting opcode.
|
||||
* Note: We will only invoke ALIGN for atomic operations,
|
||||
* so all instructions are X-form.
|
||||
* Note: the opcode fields will not be set properly for a
|
||||
* direct store load/store, but nobody cares as nobody
|
||||
* actually uses direct store segments.
|
||||
*/
|
||||
{
|
||||
uint32_t insn = cpu_ldl_code(env, env->nip);
|
||||
env->spr[SPR_DSISR] |= (insn & 0x03FF0000) >> 16;
|
||||
}
|
||||
env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16;
|
||||
break;
|
||||
case POWERPC_EXCP_PROGRAM: /* Program exception */
|
||||
switch (env->error_code & ~0xF) {
|
||||
|
@ -569,7 +577,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
|||
cpu_abort(cs, "Debug exception triggered on unsupported model\n");
|
||||
}
|
||||
break;
|
||||
case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */
|
||||
case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */
|
||||
env->spr[SPR_BOOKE_ESR] = ESR_SPV;
|
||||
break;
|
||||
case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */
|
||||
|
@ -672,7 +680,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
|||
switch (excp_model) {
|
||||
case POWERPC_EXCP_602:
|
||||
case POWERPC_EXCP_603:
|
||||
case POWERPC_EXCP_603E:
|
||||
case POWERPC_EXCP_G2:
|
||||
/* Swap temporary saved registers with GPRs */
|
||||
if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) {
|
||||
|
@ -714,35 +721,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
|||
/* Set way using a LRU mechanism */
|
||||
msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17;
|
||||
break;
|
||||
case POWERPC_EXCP_74xx:
|
||||
#if defined(DEBUG_SOFTWARE_TLB)
|
||||
if (qemu_log_enabled()) {
|
||||
const char *es;
|
||||
target_ulong *miss, *cmp;
|
||||
int en;
|
||||
|
||||
if (excp == POWERPC_EXCP_IFTLB) {
|
||||
es = "I";
|
||||
en = 'I';
|
||||
miss = &env->spr[SPR_TLBMISS];
|
||||
cmp = &env->spr[SPR_PTEHI];
|
||||
} else {
|
||||
if (excp == POWERPC_EXCP_DLTLB) {
|
||||
es = "DL";
|
||||
} else {
|
||||
es = "DS";
|
||||
}
|
||||
en = 'D';
|
||||
miss = &env->spr[SPR_TLBMISS];
|
||||
cmp = &env->spr[SPR_PTEHI];
|
||||
}
|
||||
qemu_log("74xx %sTLB miss: %cM " TARGET_FMT_lx " %cC "
|
||||
TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp,
|
||||
env->error_code);
|
||||
}
|
||||
#endif
|
||||
msr |= env->error_code; /* key bit */
|
||||
break;
|
||||
default:
|
||||
cpu_abort(cs, "Invalid TLB miss exception\n");
|
||||
break;
|
||||
|
@ -1250,6 +1228,37 @@ void helper_hrfid(CPUPPCState *env)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
|
||||
void helper_rfebb(CPUPPCState *env, target_ulong s)
|
||||
{
|
||||
target_ulong msr = env->msr;
|
||||
|
||||
/*
|
||||
* Handling of BESCR bits 32:33 according to PowerISA v3.1:
|
||||
*
|
||||
* "If BESCR 32:33 != 0b00 the instruction is treated as if
|
||||
* the instruction form were invalid."
|
||||
*/
|
||||
if (env->spr[SPR_BESCR] & BESCR_INVALID) {
|
||||
raise_exception_err(env, POWERPC_EXCP_PROGRAM,
|
||||
POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL);
|
||||
}
|
||||
|
||||
env->nip = env->spr[SPR_EBBRR];
|
||||
|
||||
/* Switching to 32-bit ? Crop the nip */
|
||||
if (!msr_is_64bit(env, msr)) {
|
||||
env->nip = (uint32_t)env->spr[SPR_EBBRR];
|
||||
}
|
||||
|
||||
if (s) {
|
||||
env->spr[SPR_BESCR] |= BESCR_GE;
|
||||
} else {
|
||||
env->spr[SPR_BESCR] &= ~BESCR_GE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Embedded PowerPC specific helpers */
|
||||
void helper_40x_rfci(CPUPPCState *env)
|
||||
|
@ -1461,10 +1470,14 @@ void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr,
|
|||
int mmu_idx, uintptr_t retaddr)
|
||||
{
|
||||
CPUPPCState *env = cs->env_ptr;
|
||||
uint32_t insn;
|
||||
|
||||
/* Restore state and reload the insn we executed, for filling in DSISR. */
|
||||
cpu_restore_state(cs, retaddr, true);
|
||||
insn = cpu_ldl_code(env, env->nip);
|
||||
|
||||
switch (env->mmu_model) {
|
||||
case POWERPC_MMU_SOFT_4xx:
|
||||
case POWERPC_MMU_SOFT_4xx_Z:
|
||||
env->spr[SPR_40x_DEAR] = vaddr;
|
||||
break;
|
||||
case POWERPC_MMU_BOOKE:
|
||||
|
@ -1477,8 +1490,8 @@ void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr,
|
|||
}
|
||||
|
||||
cs->exception_index = POWERPC_EXCP_ALIGN;
|
||||
env->error_code = 0;
|
||||
cpu_loop_exit_restore(cs, retaddr);
|
||||
env->error_code = insn & 0x03FF0000;
|
||||
cpu_loop_exit(cs);
|
||||
}
|
||||
#endif /* CONFIG_TCG */
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
|
|
@ -414,6 +414,54 @@ void helper_store_fpscr(CPUPPCState *env, uint64_t val, uint32_t nibbles)
|
|||
ppc_store_fpscr(env, val);
|
||||
}
|
||||
|
||||
void helper_fpscr_check_status(CPUPPCState *env)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
target_ulong fpscr = env->fpscr;
|
||||
int error = 0;
|
||||
|
||||
if ((fpscr & FP_OX) && (fpscr & FP_OE)) {
|
||||
error = POWERPC_EXCP_FP_OX;
|
||||
} else if ((fpscr & FP_UX) && (fpscr & FP_UE)) {
|
||||
error = POWERPC_EXCP_FP_UX;
|
||||
} else if ((fpscr & FP_XX) && (fpscr & FP_XE)) {
|
||||
error = POWERPC_EXCP_FP_XX;
|
||||
} else if ((fpscr & FP_ZX) && (fpscr & FP_ZE)) {
|
||||
error = POWERPC_EXCP_FP_ZX;
|
||||
} else if (fpscr & FP_VE) {
|
||||
if (fpscr & FP_VXSOFT) {
|
||||
error = POWERPC_EXCP_FP_VXSOFT;
|
||||
} else if (fpscr & FP_VXSNAN) {
|
||||
error = POWERPC_EXCP_FP_VXSNAN;
|
||||
} else if (fpscr & FP_VXISI) {
|
||||
error = POWERPC_EXCP_FP_VXISI;
|
||||
} else if (fpscr & FP_VXIDI) {
|
||||
error = POWERPC_EXCP_FP_VXIDI;
|
||||
} else if (fpscr & FP_VXZDZ) {
|
||||
error = POWERPC_EXCP_FP_VXZDZ;
|
||||
} else if (fpscr & FP_VXIMZ) {
|
||||
error = POWERPC_EXCP_FP_VXIMZ;
|
||||
} else if (fpscr & FP_VXVC) {
|
||||
error = POWERPC_EXCP_FP_VXVC;
|
||||
} else if (fpscr & FP_VXSQRT) {
|
||||
error = POWERPC_EXCP_FP_VXSQRT;
|
||||
} else if (fpscr & FP_VXCVI) {
|
||||
error = POWERPC_EXCP_FP_VXCVI;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
cs->exception_index = POWERPC_EXCP_PROGRAM;
|
||||
env->error_code = error | POWERPC_EXCP_FP;
|
||||
/* Deferred floating-point exception after target FPSCR update */
|
||||
if (fp_exceptions_enabled(env)) {
|
||||
raise_exception_err_ra(env, cs->exception_index,
|
||||
env->error_code, GETPC());
|
||||
}
|
||||
}
|
||||
|
||||
static void do_float_check_status(CPUPPCState *env, uintptr_t raddr)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
|
@ -450,13 +498,12 @@ void helper_reset_fpstatus(CPUPPCState *env)
|
|||
set_float_exception_flags(0, &env->fp_status);
|
||||
}
|
||||
|
||||
static void float_invalid_op_addsub(CPUPPCState *env, bool set_fpcc,
|
||||
uintptr_t retaddr, int classes)
|
||||
static void float_invalid_op_addsub(CPUPPCState *env, int flags,
|
||||
bool set_fpcc, uintptr_t retaddr)
|
||||
{
|
||||
if ((classes & ~is_neg) == is_inf) {
|
||||
/* Magnitude subtraction of infinities */
|
||||
if (flags & float_flag_invalid_isi) {
|
||||
float_invalid_op_vxisi(env, set_fpcc, retaddr);
|
||||
} else if (classes & is_snan) {
|
||||
} else if (flags & float_flag_invalid_snan) {
|
||||
float_invalid_op_vxsnan(env, retaddr);
|
||||
}
|
||||
}
|
||||
|
@ -465,39 +512,58 @@ static void float_invalid_op_addsub(CPUPPCState *env, bool set_fpcc,
|
|||
float64 helper_fadd(CPUPPCState *env, float64 arg1, float64 arg2)
|
||||
{
|
||||
float64 ret = float64_add(arg1, arg2, &env->fp_status);
|
||||
int status = get_float_exception_flags(&env->fp_status);
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
if (unlikely(status & float_flag_invalid)) {
|
||||
float_invalid_op_addsub(env, 1, GETPC(),
|
||||
float64_classify(arg1) |
|
||||
float64_classify(arg2));
|
||||
if (unlikely(flags & float_flag_invalid)) {
|
||||
float_invalid_op_addsub(env, flags, 1, GETPC());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* fadds - fadds. */
|
||||
float64 helper_fadds(CPUPPCState *env, float64 arg1, float64 arg2)
|
||||
{
|
||||
float64 ret = float64r32_add(arg1, arg2, &env->fp_status);
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
if (unlikely(flags & float_flag_invalid)) {
|
||||
float_invalid_op_addsub(env, flags, 1, GETPC());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* fsub - fsub. */
|
||||
float64 helper_fsub(CPUPPCState *env, float64 arg1, float64 arg2)
|
||||
{
|
||||
float64 ret = float64_sub(arg1, arg2, &env->fp_status);
|
||||
int status = get_float_exception_flags(&env->fp_status);
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
if (unlikely(status & float_flag_invalid)) {
|
||||
float_invalid_op_addsub(env, 1, GETPC(),
|
||||
float64_classify(arg1) |
|
||||
float64_classify(arg2));
|
||||
if (unlikely(flags & float_flag_invalid)) {
|
||||
float_invalid_op_addsub(env, flags, 1, GETPC());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void float_invalid_op_mul(CPUPPCState *env, bool set_fprc,
|
||||
uintptr_t retaddr, int classes)
|
||||
/* fsubs - fsubs. */
|
||||
float64 helper_fsubs(CPUPPCState *env, float64 arg1, float64 arg2)
|
||||
{
|
||||
if ((classes & (is_zero | is_inf)) == (is_zero | is_inf)) {
|
||||
/* Multiplication of zero by infinity */
|
||||
float64 ret = float64r32_sub(arg1, arg2, &env->fp_status);
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
if (unlikely(flags & float_flag_invalid)) {
|
||||
float_invalid_op_addsub(env, flags, 1, GETPC());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void float_invalid_op_mul(CPUPPCState *env, int flags,
|
||||
bool set_fprc, uintptr_t retaddr)
|
||||
{
|
||||
if (flags & float_flag_invalid_imz) {
|
||||
float_invalid_op_vximz(env, set_fprc, retaddr);
|
||||
} else if (classes & is_snan) {
|
||||
} else if (flags & float_flag_invalid_snan) {
|
||||
float_invalid_op_vxsnan(env, retaddr);
|
||||
}
|
||||
}
|
||||
|
@ -506,28 +572,35 @@ static void float_invalid_op_mul(CPUPPCState *env, bool set_fprc,
|
|||
float64 helper_fmul(CPUPPCState *env, float64 arg1, float64 arg2)
|
||||
{
|
||||
float64 ret = float64_mul(arg1, arg2, &env->fp_status);
|
||||
int status = get_float_exception_flags(&env->fp_status);
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
if (unlikely(status & float_flag_invalid)) {
|
||||
float_invalid_op_mul(env, 1, GETPC(),
|
||||
float64_classify(arg1) |
|
||||
float64_classify(arg2));
|
||||
if (unlikely(flags & float_flag_invalid)) {
|
||||
float_invalid_op_mul(env, flags, 1, GETPC());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void float_invalid_op_div(CPUPPCState *env, bool set_fprc,
|
||||
uintptr_t retaddr, int classes)
|
||||
/* fmuls - fmuls. */
|
||||
float64 helper_fmuls(CPUPPCState *env, float64 arg1, float64 arg2)
|
||||
{
|
||||
classes &= ~is_neg;
|
||||
if (classes == is_inf) {
|
||||
/* Division of infinity by infinity */
|
||||
float64 ret = float64r32_mul(arg1, arg2, &env->fp_status);
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
if (unlikely(flags & float_flag_invalid)) {
|
||||
float_invalid_op_mul(env, flags, 1, GETPC());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void float_invalid_op_div(CPUPPCState *env, int flags,
|
||||
bool set_fprc, uintptr_t retaddr)
|
||||
{
|
||||
if (flags & float_flag_invalid_idi) {
|
||||
float_invalid_op_vxidi(env, set_fprc, retaddr);
|
||||
} else if (classes == is_zero) {
|
||||
/* Division of zero by zero */
|
||||
} else if (flags & float_flag_invalid_zdz) {
|
||||
float_invalid_op_vxzdz(env, set_fprc, retaddr);
|
||||
} else if (classes & is_snan) {
|
||||
} else if (flags & float_flag_invalid_snan) {
|
||||
float_invalid_op_vxsnan(env, retaddr);
|
||||
}
|
||||
}
|
||||
|
@ -536,43 +609,57 @@ static void float_invalid_op_div(CPUPPCState *env, bool set_fprc,
|
|||
float64 helper_fdiv(CPUPPCState *env, float64 arg1, float64 arg2)
|
||||
{
|
||||
float64 ret = float64_div(arg1, arg2, &env->fp_status);
|
||||
int status = get_float_exception_flags(&env->fp_status);
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
if (unlikely(status)) {
|
||||
if (status & float_flag_invalid) {
|
||||
float_invalid_op_div(env, 1, GETPC(),
|
||||
float64_classify(arg1) |
|
||||
float64_classify(arg2));
|
||||
}
|
||||
if (status & float_flag_divbyzero) {
|
||||
float_zero_divide_excp(env, GETPC());
|
||||
}
|
||||
if (unlikely(flags & float_flag_invalid)) {
|
||||
float_invalid_op_div(env, flags, 1, GETPC());
|
||||
}
|
||||
if (unlikely(flags & float_flag_divbyzero)) {
|
||||
float_zero_divide_excp(env, GETPC());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void float_invalid_cvt(CPUPPCState *env, bool set_fprc,
|
||||
uintptr_t retaddr, int class1)
|
||||
/* fdivs - fdivs. */
|
||||
float64 helper_fdivs(CPUPPCState *env, float64 arg1, float64 arg2)
|
||||
{
|
||||
float_invalid_op_vxcvi(env, set_fprc, retaddr);
|
||||
if (class1 & is_snan) {
|
||||
float_invalid_op_vxsnan(env, retaddr);
|
||||
float64 ret = float64r32_div(arg1, arg2, &env->fp_status);
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
if (unlikely(flags & float_flag_invalid)) {
|
||||
float_invalid_op_div(env, flags, 1, GETPC());
|
||||
}
|
||||
if (unlikely(flags & float_flag_divbyzero)) {
|
||||
float_zero_divide_excp(env, GETPC());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint64_t float_invalid_cvt(CPUPPCState *env, int flags,
|
||||
uint64_t ret, uint64_t ret_nan,
|
||||
bool set_fprc, uintptr_t retaddr)
|
||||
{
|
||||
/*
|
||||
* VXCVI is different from most in that it sets two exception bits,
|
||||
* VXCVI and VXSNAN for an SNaN input.
|
||||
*/
|
||||
if (flags & float_flag_invalid_snan) {
|
||||
env->fpscr |= FP_VXSNAN;
|
||||
}
|
||||
float_invalid_op_vxcvi(env, set_fprc, retaddr);
|
||||
|
||||
return flags & float_flag_invalid_cvti ? ret : ret_nan;
|
||||
}
|
||||
|
||||
#define FPU_FCTI(op, cvt, nanval) \
|
||||
uint64_t helper_##op(CPUPPCState *env, float64 arg) \
|
||||
{ \
|
||||
uint64_t ret = float64_to_##cvt(arg, &env->fp_status); \
|
||||
int status = get_float_exception_flags(&env->fp_status); \
|
||||
\
|
||||
if (unlikely(status)) { \
|
||||
if (status & float_flag_invalid) { \
|
||||
float_invalid_cvt(env, 1, GETPC(), float64_classify(arg)); \
|
||||
ret = nanval; \
|
||||
} \
|
||||
do_float_check_status(env, GETPC()); \
|
||||
int flags = get_float_exception_flags(&env->fp_status); \
|
||||
if (unlikely(flags & float_flag_invalid)) { \
|
||||
ret = float_invalid_cvt(env, flags, ret, nanval, 1, GETPC()); \
|
||||
} \
|
||||
return ret; \
|
||||
}
|
||||
|
@ -606,32 +693,26 @@ FPU_FCFI(fcfids, int64_to_float32, 1)
|
|||
FPU_FCFI(fcfidu, uint64_to_float64, 0)
|
||||
FPU_FCFI(fcfidus, uint64_to_float32, 1)
|
||||
|
||||
static inline uint64_t do_fri(CPUPPCState *env, uint64_t arg,
|
||||
int rounding_mode)
|
||||
static uint64_t do_fri(CPUPPCState *env, uint64_t arg,
|
||||
FloatRoundMode rounding_mode)
|
||||
{
|
||||
CPU_DoubleU farg;
|
||||
FloatRoundMode old_rounding_mode = get_float_rounding_mode(&env->fp_status);
|
||||
int flags;
|
||||
|
||||
farg.ll = arg;
|
||||
set_float_rounding_mode(rounding_mode, &env->fp_status);
|
||||
arg = float64_round_to_int(arg, &env->fp_status);
|
||||
set_float_rounding_mode(old_rounding_mode, &env->fp_status);
|
||||
|
||||
if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) {
|
||||
/* sNaN round */
|
||||
flags = get_float_exception_flags(&env->fp_status);
|
||||
if (flags & float_flag_invalid_snan) {
|
||||
float_invalid_op_vxsnan(env, GETPC());
|
||||
farg.ll = arg | 0x0008000000000000ULL;
|
||||
} else {
|
||||
int inexact = get_float_exception_flags(&env->fp_status) &
|
||||
float_flag_inexact;
|
||||
set_float_rounding_mode(rounding_mode, &env->fp_status);
|
||||
farg.ll = float64_round_to_int(farg.d, &env->fp_status);
|
||||
set_float_rounding_mode(old_rounding_mode, &env->fp_status);
|
||||
|
||||
/* fri* does not set FPSCR[XX] */
|
||||
if (!inexact) {
|
||||
env->fp_status.float_exception_flags &= ~float_flag_inexact;
|
||||
}
|
||||
}
|
||||
|
||||
/* fri* does not set FPSCR[XX] */
|
||||
set_float_exception_flags(flags & ~float_flag_inexact, &env->fp_status);
|
||||
do_float_check_status(env, GETPC());
|
||||
return farg.ll;
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
uint64_t helper_frin(CPUPPCState *env, uint64_t arg)
|
||||
|
@ -654,57 +735,48 @@ uint64_t helper_frim(CPUPPCState *env, uint64_t arg)
|
|||
return do_fri(env, arg, float_round_down);
|
||||
}
|
||||
|
||||
#define FPU_MADDSUB_UPDATE(NAME, TP) \
|
||||
static void NAME(CPUPPCState *env, TP arg1, TP arg2, TP arg3, \
|
||||
unsigned int madd_flags, uintptr_t retaddr) \
|
||||
{ \
|
||||
if (TP##_is_signaling_nan(arg1, &env->fp_status) || \
|
||||
TP##_is_signaling_nan(arg2, &env->fp_status) || \
|
||||
TP##_is_signaling_nan(arg3, &env->fp_status)) { \
|
||||
/* sNaN operation */ \
|
||||
float_invalid_op_vxsnan(env, retaddr); \
|
||||
} \
|
||||
if ((TP##_is_infinity(arg1) && TP##_is_zero(arg2)) || \
|
||||
(TP##_is_zero(arg1) && TP##_is_infinity(arg2))) { \
|
||||
/* Multiplication of zero by infinity */ \
|
||||
float_invalid_op_vximz(env, 1, retaddr); \
|
||||
} \
|
||||
if ((TP##_is_infinity(arg1) || TP##_is_infinity(arg2)) && \
|
||||
TP##_is_infinity(arg3)) { \
|
||||
uint8_t aSign, bSign, cSign; \
|
||||
\
|
||||
aSign = TP##_is_neg(arg1); \
|
||||
bSign = TP##_is_neg(arg2); \
|
||||
cSign = TP##_is_neg(arg3); \
|
||||
if (madd_flags & float_muladd_negate_c) { \
|
||||
cSign ^= 1; \
|
||||
} \
|
||||
if (aSign ^ bSign ^ cSign) { \
|
||||
float_invalid_op_vxisi(env, 1, retaddr); \
|
||||
} \
|
||||
} \
|
||||
static void float_invalid_op_madd(CPUPPCState *env, int flags,
|
||||
bool set_fpcc, uintptr_t retaddr)
|
||||
{
|
||||
if (flags & float_flag_invalid_imz) {
|
||||
float_invalid_op_vximz(env, set_fpcc, retaddr);
|
||||
} else {
|
||||
float_invalid_op_addsub(env, flags, set_fpcc, retaddr);
|
||||
}
|
||||
}
|
||||
FPU_MADDSUB_UPDATE(float32_maddsub_update_excp, float32)
|
||||
FPU_MADDSUB_UPDATE(float64_maddsub_update_excp, float64)
|
||||
|
||||
#define FPU_FMADD(op, madd_flags) \
|
||||
uint64_t helper_##op(CPUPPCState *env, uint64_t arg1, \
|
||||
uint64_t arg2, uint64_t arg3) \
|
||||
{ \
|
||||
uint32_t flags; \
|
||||
float64 ret = float64_muladd(arg1, arg2, arg3, madd_flags, \
|
||||
&env->fp_status); \
|
||||
flags = get_float_exception_flags(&env->fp_status); \
|
||||
if (flags) { \
|
||||
if (flags & float_flag_invalid) { \
|
||||
float64_maddsub_update_excp(env, arg1, arg2, arg3, \
|
||||
madd_flags, GETPC()); \
|
||||
} \
|
||||
do_float_check_status(env, GETPC()); \
|
||||
} \
|
||||
return ret; \
|
||||
static float64 do_fmadd(CPUPPCState *env, float64 a, float64 b,
|
||||
float64 c, int madd_flags, uintptr_t retaddr)
|
||||
{
|
||||
float64 ret = float64_muladd(a, b, c, madd_flags, &env->fp_status);
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
if (unlikely(flags & float_flag_invalid)) {
|
||||
float_invalid_op_madd(env, flags, 1, retaddr);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint64_t do_fmadds(CPUPPCState *env, float64 a, float64 b,
|
||||
float64 c, int madd_flags, uintptr_t retaddr)
|
||||
{
|
||||
float64 ret = float64r32_muladd(a, b, c, madd_flags, &env->fp_status);
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
if (unlikely(flags & float_flag_invalid)) {
|
||||
float_invalid_op_madd(env, flags, 1, retaddr);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define FPU_FMADD(op, madd_flags) \
|
||||
uint64_t helper_##op(CPUPPCState *env, uint64_t arg1, \
|
||||
uint64_t arg2, uint64_t arg3) \
|
||||
{ return do_fmadd(env, arg1, arg2, arg3, madd_flags, GETPC()); } \
|
||||
uint64_t helper_##op##s(CPUPPCState *env, uint64_t arg1, \
|
||||
uint64_t arg2, uint64_t arg3) \
|
||||
{ return do_fmadds(env, arg1, arg2, arg3, madd_flags, GETPC()); }
|
||||
|
||||
#define MADD_FLGS 0
|
||||
#define MSUB_FLGS float_muladd_negate_c
|
||||
#define NMADD_FLGS float_muladd_negate_result
|
||||
|
@ -716,62 +788,71 @@ FPU_FMADD(fmsub, MSUB_FLGS)
|
|||
FPU_FMADD(fnmsub, NMSUB_FLGS)
|
||||
|
||||
/* frsp - frsp. */
|
||||
static uint64_t do_frsp(CPUPPCState *env, uint64_t arg, uintptr_t retaddr)
|
||||
{
|
||||
float32 f32 = float64_to_float32(arg, &env->fp_status);
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
if (unlikely(flags & float_flag_invalid_snan)) {
|
||||
float_invalid_op_vxsnan(env, retaddr);
|
||||
}
|
||||
return helper_todouble(f32);
|
||||
}
|
||||
|
||||
uint64_t helper_frsp(CPUPPCState *env, uint64_t arg)
|
||||
{
|
||||
CPU_DoubleU farg;
|
||||
float32 f32;
|
||||
return do_frsp(env, arg, GETPC());
|
||||
}
|
||||
|
||||
farg.ll = arg;
|
||||
|
||||
if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) {
|
||||
float_invalid_op_vxsnan(env, GETPC());
|
||||
static void float_invalid_op_sqrt(CPUPPCState *env, int flags,
|
||||
bool set_fpcc, uintptr_t retaddr)
|
||||
{
|
||||
if (unlikely(flags & float_flag_invalid_sqrt)) {
|
||||
float_invalid_op_vxsqrt(env, set_fpcc, retaddr);
|
||||
} else if (unlikely(flags & float_flag_invalid_snan)) {
|
||||
float_invalid_op_vxsnan(env, retaddr);
|
||||
}
|
||||
f32 = float64_to_float32(farg.d, &env->fp_status);
|
||||
farg.d = float32_to_float64(f32, &env->fp_status);
|
||||
|
||||
return farg.ll;
|
||||
}
|
||||
|
||||
/* fsqrt - fsqrt. */
|
||||
float64 helper_fsqrt(CPUPPCState *env, float64 arg)
|
||||
{
|
||||
float64 ret = float64_sqrt(arg, &env->fp_status);
|
||||
int status = get_float_exception_flags(&env->fp_status);
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
if (unlikely(status & float_flag_invalid)) {
|
||||
if (unlikely(float64_is_any_nan(arg))) {
|
||||
if (unlikely(float64_is_signaling_nan(arg, &env->fp_status))) {
|
||||
/* sNaN square root */
|
||||
float_invalid_op_vxsnan(env, GETPC());
|
||||
}
|
||||
} else {
|
||||
/* Square root of a negative nonzero number */
|
||||
float_invalid_op_vxsqrt(env, 1, GETPC());
|
||||
}
|
||||
if (unlikely(flags & float_flag_invalid)) {
|
||||
float_invalid_op_sqrt(env, flags, 1, GETPC());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* fsqrts - fsqrts. */
|
||||
float64 helper_fsqrts(CPUPPCState *env, float64 arg)
|
||||
{
|
||||
float64 ret = float64r32_sqrt(arg, &env->fp_status);
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
if (unlikely(flags & float_flag_invalid)) {
|
||||
float_invalid_op_sqrt(env, flags, 1, GETPC());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* fre - fre. */
|
||||
float64 helper_fre(CPUPPCState *env, float64 arg)
|
||||
{
|
||||
/* "Estimate" the reciprocal with actual division. */
|
||||
float64 ret = float64_div(float64_one, arg, &env->fp_status);
|
||||
int status = get_float_exception_flags(&env->fp_status);
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
if (unlikely(status)) {
|
||||
if (status & float_flag_invalid) {
|
||||
if (float64_is_signaling_nan(arg, &env->fp_status)) {
|
||||
/* sNaN reciprocal */
|
||||
float_invalid_op_vxsnan(env, GETPC());
|
||||
}
|
||||
}
|
||||
if (status & float_flag_divbyzero) {
|
||||
float_zero_divide_excp(env, GETPC());
|
||||
/* For FPSCR.ZE == 0, the result is 1/2. */
|
||||
ret = float64_set_sign(float64_half, float64_is_neg(arg));
|
||||
}
|
||||
if (unlikely(flags & float_flag_invalid_snan)) {
|
||||
float_invalid_op_vxsnan(env, GETPC());
|
||||
}
|
||||
if (unlikely(flags & float_flag_divbyzero)) {
|
||||
float_zero_divide_excp(env, GETPC());
|
||||
/* For FPSCR.ZE == 0, the result is 1/2. */
|
||||
ret = float64_set_sign(float64_half, float64_is_neg(arg));
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -780,20 +861,20 @@ float64 helper_fre(CPUPPCState *env, float64 arg)
|
|||
/* fres - fres. */
|
||||
uint64_t helper_fres(CPUPPCState *env, uint64_t arg)
|
||||
{
|
||||
CPU_DoubleU farg;
|
||||
float32 f32;
|
||||
/* "Estimate" the reciprocal with actual division. */
|
||||
float64 ret = float64r32_div(float64_one, arg, &env->fp_status);
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
farg.ll = arg;
|
||||
|
||||
if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) {
|
||||
/* sNaN reciprocal */
|
||||
if (unlikely(flags & float_flag_invalid_snan)) {
|
||||
float_invalid_op_vxsnan(env, GETPC());
|
||||
}
|
||||
farg.d = float64_div(float64_one, farg.d, &env->fp_status);
|
||||
f32 = float64_to_float32(farg.d, &env->fp_status);
|
||||
farg.d = float32_to_float64(f32, &env->fp_status);
|
||||
if (unlikely(flags & float_flag_divbyzero)) {
|
||||
float_zero_divide_excp(env, GETPC());
|
||||
/* For FPSCR.ZE == 0, the result is 1/2. */
|
||||
ret = float64_set_sign(float64_half, float64_is_neg(arg));
|
||||
}
|
||||
|
||||
return farg.ll;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* frsqrte - frsqrte. */
|
||||
|
@ -802,22 +883,33 @@ float64 helper_frsqrte(CPUPPCState *env, float64 arg)
|
|||
/* "Estimate" the reciprocal with actual division. */
|
||||
float64 rets = float64_sqrt(arg, &env->fp_status);
|
||||
float64 retd = float64_div(float64_one, rets, &env->fp_status);
|
||||
int status = get_float_exception_flags(&env->fp_status);
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
if (unlikely(status)) {
|
||||
if (status & float_flag_invalid) {
|
||||
if (float64_is_signaling_nan(arg, &env->fp_status)) {
|
||||
/* sNaN reciprocal */
|
||||
float_invalid_op_vxsnan(env, GETPC());
|
||||
} else {
|
||||
/* Square root of a negative nonzero number */
|
||||
float_invalid_op_vxsqrt(env, 1, GETPC());
|
||||
}
|
||||
}
|
||||
if (status & float_flag_divbyzero) {
|
||||
/* Reciprocal of (square root of) zero. */
|
||||
float_zero_divide_excp(env, GETPC());
|
||||
}
|
||||
if (unlikely(flags & float_flag_invalid)) {
|
||||
float_invalid_op_sqrt(env, flags, 1, GETPC());
|
||||
}
|
||||
if (unlikely(flags & float_flag_divbyzero)) {
|
||||
/* Reciprocal of (square root of) zero. */
|
||||
float_zero_divide_excp(env, GETPC());
|
||||
}
|
||||
|
||||
return retd;
|
||||
}
|
||||
|
||||
/* frsqrtes - frsqrtes. */
|
||||
float64 helper_frsqrtes(CPUPPCState *env, float64 arg)
|
||||
{
|
||||
/* "Estimate" the reciprocal with actual division. */
|
||||
float64 rets = float64_sqrt(arg, &env->fp_status);
|
||||
float64 retd = float64r32_div(float64_one, rets, &env->fp_status);
|
||||
int flags = get_float_exception_flags(&env->fp_status);
|
||||
|
||||
if (unlikely(flags & float_flag_invalid)) {
|
||||
float_invalid_op_sqrt(env, flags, 1, GETPC());
|
||||
}
|
||||
if (unlikely(flags & float_flag_divbyzero)) {
|
||||
/* Reciprocal of (square root of) zero. */
|
||||
float_zero_divide_excp(env, GETPC());
|
||||
}
|
||||
|
||||
return retd;
|
||||
|
@ -1616,13 +1708,12 @@ void helper_##name(CPUPPCState *env, ppc_vsr_t *xt, \
|
|||
env->fp_status.float_exception_flags |= tstat.float_exception_flags; \
|
||||
\
|
||||
if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \
|
||||
float_invalid_op_addsub(env, sfprf, GETPC(), \
|
||||
tp##_classify(xa->fld) | \
|
||||
tp##_classify(xb->fld)); \
|
||||
float_invalid_op_addsub(env, tstat.float_exception_flags, \
|
||||
sfprf, GETPC()); \
|
||||
} \
|
||||
\
|
||||
if (r2sp) { \
|
||||
t.fld = helper_frsp(env, t.fld); \
|
||||
t.fld = do_frsp(env, t.fld, GETPC()); \
|
||||
} \
|
||||
\
|
||||
if (sfprf) { \
|
||||
|
@ -1660,9 +1751,7 @@ void helper_xsaddqp(CPUPPCState *env, uint32_t opcode,
|
|||
env->fp_status.float_exception_flags |= tstat.float_exception_flags;
|
||||
|
||||
if (unlikely(tstat.float_exception_flags & float_flag_invalid)) {
|
||||
float_invalid_op_addsub(env, 1, GETPC(),
|
||||
float128_classify(xa->f128) |
|
||||
float128_classify(xb->f128));
|
||||
float_invalid_op_addsub(env, tstat.float_exception_flags, 1, GETPC());
|
||||
}
|
||||
|
||||
helper_compute_fprf_float128(env, t.f128);
|
||||
|
@ -1695,13 +1784,12 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \
|
|||
env->fp_status.float_exception_flags |= tstat.float_exception_flags; \
|
||||
\
|
||||
if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \
|
||||
float_invalid_op_mul(env, sfprf, GETPC(), \
|
||||
tp##_classify(xa->fld) | \
|
||||
tp##_classify(xb->fld)); \
|
||||
float_invalid_op_mul(env, tstat.float_exception_flags, \
|
||||
sfprf, GETPC()); \
|
||||
} \
|
||||
\
|
||||
if (r2sp) { \
|
||||
t.fld = helper_frsp(env, t.fld); \
|
||||
t.fld = do_frsp(env, t.fld, GETPC()); \
|
||||
} \
|
||||
\
|
||||
if (sfprf) { \
|
||||
|
@ -1735,9 +1823,7 @@ void helper_xsmulqp(CPUPPCState *env, uint32_t opcode,
|
|||
env->fp_status.float_exception_flags |= tstat.float_exception_flags;
|
||||
|
||||
if (unlikely(tstat.float_exception_flags & float_flag_invalid)) {
|
||||
float_invalid_op_mul(env, 1, GETPC(),
|
||||
float128_classify(xa->f128) |
|
||||
float128_classify(xb->f128));
|
||||
float_invalid_op_mul(env, tstat.float_exception_flags, 1, GETPC());
|
||||
}
|
||||
helper_compute_fprf_float128(env, t.f128);
|
||||
|
||||
|
@ -1769,16 +1855,15 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \
|
|||
env->fp_status.float_exception_flags |= tstat.float_exception_flags; \
|
||||
\
|
||||
if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \
|
||||
float_invalid_op_div(env, sfprf, GETPC(), \
|
||||
tp##_classify(xa->fld) | \
|
||||
tp##_classify(xb->fld)); \
|
||||
float_invalid_op_div(env, tstat.float_exception_flags, \
|
||||
sfprf, GETPC()); \
|
||||
} \
|
||||
if (unlikely(tstat.float_exception_flags & float_flag_divbyzero)) { \
|
||||
float_zero_divide_excp(env, GETPC()); \
|
||||
} \
|
||||
\
|
||||
if (r2sp) { \
|
||||
t.fld = helper_frsp(env, t.fld); \
|
||||
t.fld = do_frsp(env, t.fld, GETPC()); \
|
||||
} \
|
||||
\
|
||||
if (sfprf) { \
|
||||
|
@ -1812,9 +1897,7 @@ void helper_xsdivqp(CPUPPCState *env, uint32_t opcode,
|
|||
env->fp_status.float_exception_flags |= tstat.float_exception_flags;
|
||||
|
||||
if (unlikely(tstat.float_exception_flags & float_flag_invalid)) {
|
||||
float_invalid_op_div(env, 1, GETPC(),
|
||||
float128_classify(xa->f128) |
|
||||
float128_classify(xb->f128));
|
||||
float_invalid_op_div(env, tstat.float_exception_flags, 1, GETPC());
|
||||
}
|
||||
if (unlikely(tstat.float_exception_flags & float_flag_divbyzero)) {
|
||||
float_zero_divide_excp(env, GETPC());
|
||||
|
@ -1848,7 +1931,7 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \
|
|||
t.fld = tp##_div(tp##_one, xb->fld, &env->fp_status); \
|
||||
\
|
||||
if (r2sp) { \
|
||||
t.fld = helper_frsp(env, t.fld); \
|
||||
t.fld = do_frsp(env, t.fld, GETPC()); \
|
||||
} \
|
||||
\
|
||||
if (sfprf) { \
|
||||
|
@ -1888,15 +1971,12 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \
|
|||
env->fp_status.float_exception_flags |= tstat.float_exception_flags; \
|
||||
\
|
||||
if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \
|
||||
if (tp##_is_neg(xb->fld) && !tp##_is_zero(xb->fld)) { \
|
||||
float_invalid_op_vxsqrt(env, sfprf, GETPC()); \
|
||||
} else if (tp##_is_signaling_nan(xb->fld, &tstat)) { \
|
||||
float_invalid_op_vxsnan(env, GETPC()); \
|
||||
} \
|
||||
float_invalid_op_sqrt(env, tstat.float_exception_flags, \
|
||||
sfprf, GETPC()); \
|
||||
} \
|
||||
\
|
||||
if (r2sp) { \
|
||||
t.fld = helper_frsp(env, t.fld); \
|
||||
t.fld = do_frsp(env, t.fld, GETPC()); \
|
||||
} \
|
||||
\
|
||||
if (sfprf) { \
|
||||
|
@ -1935,17 +2015,12 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \
|
|||
t.fld = tp##_sqrt(xb->fld, &tstat); \
|
||||
t.fld = tp##_div(tp##_one, t.fld, &tstat); \
|
||||
env->fp_status.float_exception_flags |= tstat.float_exception_flags; \
|
||||
\
|
||||
if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \
|
||||
if (tp##_is_neg(xb->fld) && !tp##_is_zero(xb->fld)) { \
|
||||
float_invalid_op_vxsqrt(env, sfprf, GETPC()); \
|
||||
} else if (tp##_is_signaling_nan(xb->fld, &tstat)) { \
|
||||
float_invalid_op_vxsnan(env, GETPC()); \
|
||||
} \
|
||||
float_invalid_op_sqrt(env, tstat.float_exception_flags, \
|
||||
sfprf, GETPC()); \
|
||||
} \
|
||||
\
|
||||
if (r2sp) { \
|
||||
t.fld = helper_frsp(env, t.fld); \
|
||||
t.fld = do_frsp(env, t.fld, GETPC()); \
|
||||
} \
|
||||
\
|
||||
if (sfprf) { \
|
||||
|
@ -2111,12 +2186,12 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \
|
|||
env->fp_status.float_exception_flags |= tstat.float_exception_flags; \
|
||||
\
|
||||
if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \
|
||||
tp##_maddsub_update_excp(env, xa->fld, b->fld, \
|
||||
c->fld, maddflgs, GETPC()); \
|
||||
float_invalid_op_madd(env, tstat.float_exception_flags, \
|
||||
sfprf, GETPC()); \
|
||||
} \
|
||||
\
|
||||
if (r2sp) { \
|
||||
t.fld = helper_frsp(env, t.fld); \
|
||||
t.fld = do_frsp(env, t.fld, GETPC()); \
|
||||
} \
|
||||
\
|
||||
if (sfprf) { \
|
||||
|
@ -2420,7 +2495,7 @@ VSX_MAX_MIN(xvmindp, minnum, 2, float64, VsrD(i))
|
|||
VSX_MAX_MIN(xvminsp, minnum, 4, float32, VsrW(i))
|
||||
|
||||
#define VSX_MAX_MINC(name, max) \
|
||||
void helper_##name(CPUPPCState *env, uint32_t opcode, \
|
||||
void helper_##name(CPUPPCState *env, \
|
||||
ppc_vsr_t *xt, ppc_vsr_t *xa, ppc_vsr_t *xb) \
|
||||
{ \
|
||||
ppc_vsr_t t = *xt; \
|
||||
|
@ -2455,7 +2530,7 @@ VSX_MAX_MINC(xsmaxcdp, 1);
|
|||
VSX_MAX_MINC(xsmincdp, 0);
|
||||
|
||||
#define VSX_MAX_MINJ(name, max) \
|
||||
void helper_##name(CPUPPCState *env, uint32_t opcode, \
|
||||
void helper_##name(CPUPPCState *env, \
|
||||
ppc_vsr_t *xt, ppc_vsr_t *xa, ppc_vsr_t *xb) \
|
||||
{ \
|
||||
ppc_vsr_t t = *xt; \
|
||||
|
@ -2676,18 +2751,14 @@ VSX_CVT_FP_TO_FP_HP(xscvhpdp, 1, float16, float64, VsrH(3), VsrD(0), 1)
|
|||
VSX_CVT_FP_TO_FP_HP(xvcvsphp, 4, float32, float16, VsrW(i), VsrH(2 * i + 1), 0)
|
||||
VSX_CVT_FP_TO_FP_HP(xvcvhpsp, 4, float16, float32, VsrH(2 * i + 1), VsrW(i), 0)
|
||||
|
||||
/*
|
||||
* xscvqpdp isn't using VSX_CVT_FP_TO_FP() because xscvqpdpo will be
|
||||
* added to this later.
|
||||
*/
|
||||
void helper_xscvqpdp(CPUPPCState *env, uint32_t opcode,
|
||||
ppc_vsr_t *xt, ppc_vsr_t *xb)
|
||||
void helper_XSCVQPDP(CPUPPCState *env, uint32_t ro, ppc_vsr_t *xt,
|
||||
ppc_vsr_t *xb)
|
||||
{
|
||||
ppc_vsr_t t = { };
|
||||
float_status tstat;
|
||||
|
||||
tstat = env->fp_status;
|
||||
if (unlikely(Rc(opcode) != 0)) {
|
||||
if (ro != 0) {
|
||||
tstat.float_rounding_mode = float_round_to_odd;
|
||||
}
|
||||
|
||||
|
@ -2773,8 +2844,7 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \
|
|||
t.tfld = stp##_to_##ttp##_round_to_zero(xb->sfld, &env->fp_status); \
|
||||
flags = env->fp_status.float_exception_flags; \
|
||||
if (unlikely(flags & float_flag_invalid)) { \
|
||||
float_invalid_cvt(env, 0, GETPC(), stp##_classify(xb->sfld)); \
|
||||
t.tfld = rnan; \
|
||||
t.tfld = float_invalid_cvt(env, flags, t.tfld, rnan, 0, GETPC());\
|
||||
} \
|
||||
all_flags |= flags; \
|
||||
} \
|
||||
|
@ -2816,11 +2886,12 @@ void helper_##op(CPUPPCState *env, uint32_t opcode, \
|
|||
ppc_vsr_t *xt, ppc_vsr_t *xb) \
|
||||
{ \
|
||||
ppc_vsr_t t = { }; \
|
||||
int flags; \
|
||||
\
|
||||
t.tfld = stp##_to_##ttp##_round_to_zero(xb->sfld, &env->fp_status); \
|
||||
if (env->fp_status.float_exception_flags & float_flag_invalid) { \
|
||||
float_invalid_cvt(env, 0, GETPC(), stp##_classify(xb->sfld)); \
|
||||
t.tfld = rnan; \
|
||||
flags = get_float_exception_flags(&env->fp_status); \
|
||||
if (flags & float_flag_invalid) { \
|
||||
t.tfld = float_invalid_cvt(env, flags, t.tfld, rnan, 0, GETPC()); \
|
||||
} \
|
||||
\
|
||||
*xt = t; \
|
||||
|
@ -2855,7 +2926,7 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \
|
|||
for (i = 0; i < nels; i++) { \
|
||||
t.tfld = stp##_to_##ttp(xb->sfld, &env->fp_status); \
|
||||
if (r2sp) { \
|
||||
t.tfld = helper_frsp(env, t.tfld); \
|
||||
t.tfld = do_frsp(env, t.tfld, GETPC()); \
|
||||
} \
|
||||
if (sfprf) { \
|
||||
helper_compute_fprf_float64(env, t.tfld); \
|
||||
|
@ -2980,7 +3051,7 @@ uint64_t helper_xsrsp(CPUPPCState *env, uint64_t xb)
|
|||
{
|
||||
helper_reset_fpstatus(env);
|
||||
|
||||
uint64_t xt = helper_frsp(env, xb);
|
||||
uint64_t xt = do_frsp(env, xb, GETPC());
|
||||
|
||||
helper_compute_fprf_float64(env, xt);
|
||||
do_float_check_status(env, GETPC());
|
||||
|
@ -3088,26 +3159,25 @@ void helper_xststdcsp(CPUPPCState *env, uint32_t opcode, ppc_vsr_t *xb)
|
|||
{
|
||||
uint32_t dcmx, sign, exp;
|
||||
uint32_t cc, match = 0, not_sp = 0;
|
||||
float64 arg = xb->VsrD(0);
|
||||
float64 arg_sp;
|
||||
|
||||
dcmx = DCMX(opcode);
|
||||
exp = (xb->VsrD(0) >> 52) & 0x7FF;
|
||||
exp = (arg >> 52) & 0x7FF;
|
||||
sign = float64_is_neg(arg);
|
||||
|
||||
sign = float64_is_neg(xb->VsrD(0));
|
||||
if (float64_is_any_nan(xb->VsrD(0))) {
|
||||
if (float64_is_any_nan(arg)) {
|
||||
match = extract32(dcmx, 6, 1);
|
||||
} else if (float64_is_infinity(xb->VsrD(0))) {
|
||||
} else if (float64_is_infinity(arg)) {
|
||||
match = extract32(dcmx, 4 + !sign, 1);
|
||||
} else if (float64_is_zero(xb->VsrD(0))) {
|
||||
} else if (float64_is_zero(arg)) {
|
||||
match = extract32(dcmx, 2 + !sign, 1);
|
||||
} else if (float64_is_zero_or_denormal(xb->VsrD(0)) ||
|
||||
(exp > 0 && exp < 0x381)) {
|
||||
} else if (float64_is_zero_or_denormal(arg) || (exp > 0 && exp < 0x381)) {
|
||||
match = extract32(dcmx, 0 + !sign, 1);
|
||||
}
|
||||
|
||||
not_sp = !float64_eq(xb->VsrD(0),
|
||||
float32_to_float64(
|
||||
float64_to_float32(xb->VsrD(0), &env->fp_status),
|
||||
&env->fp_status), &env->fp_status);
|
||||
arg_sp = helper_todouble(helper_tosingle(arg));
|
||||
not_sp = arg != arg_sp;
|
||||
|
||||
cc = sign << CRF_LT_BIT | match << CRF_EQ_BIT | not_sp << CRF_SO_BIT;
|
||||
env->fpscr &= ~FP_FPCC;
|
||||
|
@ -3156,11 +3226,8 @@ void helper_xsrqpi(CPUPPCState *env, uint32_t opcode,
|
|||
t.f128 = float128_round_to_int(xb->f128, &tstat);
|
||||
env->fp_status.float_exception_flags |= tstat.float_exception_flags;
|
||||
|
||||
if (unlikely(tstat.float_exception_flags & float_flag_invalid)) {
|
||||
if (float128_is_signaling_nan(xb->f128, &tstat)) {
|
||||
float_invalid_op_vxsnan(env, GETPC());
|
||||
t.f128 = float128_snan_to_qnan(t.f128);
|
||||
}
|
||||
if (unlikely(tstat.float_exception_flags & float_flag_invalid_snan)) {
|
||||
float_invalid_op_vxsnan(env, GETPC());
|
||||
}
|
||||
|
||||
if (ex == 0 && (tstat.float_exception_flags & float_flag_inexact)) {
|
||||
|
@ -3214,11 +3281,9 @@ void helper_xsrqpxp(CPUPPCState *env, uint32_t opcode,
|
|||
t.f128 = floatx80_to_float128(round_res, &tstat);
|
||||
env->fp_status.float_exception_flags |= tstat.float_exception_flags;
|
||||
|
||||
if (unlikely(tstat.float_exception_flags & float_flag_invalid)) {
|
||||
if (float128_is_signaling_nan(xb->f128, &tstat)) {
|
||||
float_invalid_op_vxsnan(env, GETPC());
|
||||
t.f128 = float128_snan_to_qnan(t.f128);
|
||||
}
|
||||
if (unlikely(tstat.float_exception_flags & float_flag_invalid_snan)) {
|
||||
float_invalid_op_vxsnan(env, GETPC());
|
||||
t.f128 = float128_snan_to_qnan(t.f128);
|
||||
}
|
||||
|
||||
helper_compute_fprf_float128(env, t.f128);
|
||||
|
@ -3244,15 +3309,7 @@ void helper_xssqrtqp(CPUPPCState *env, uint32_t opcode,
|
|||
env->fp_status.float_exception_flags |= tstat.float_exception_flags;
|
||||
|
||||
if (unlikely(tstat.float_exception_flags & float_flag_invalid)) {
|
||||
if (float128_is_signaling_nan(xb->f128, &tstat)) {
|
||||
float_invalid_op_vxsnan(env, GETPC());
|
||||
t.f128 = float128_snan_to_qnan(xb->f128);
|
||||
} else if (float128_is_quiet_nan(xb->f128, &tstat)) {
|
||||
t.f128 = xb->f128;
|
||||
} else if (float128_is_neg(xb->f128) && !float128_is_zero(xb->f128)) {
|
||||
float_invalid_op_vxsqrt(env, 1, GETPC());
|
||||
t.f128 = float128_default_nan(&env->fp_status);
|
||||
}
|
||||
float_invalid_op_sqrt(env, tstat.float_exception_flags, 1, GETPC());
|
||||
}
|
||||
|
||||
helper_compute_fprf_float128(env, t.f128);
|
||||
|
@ -3278,9 +3335,7 @@ void helper_xssubqp(CPUPPCState *env, uint32_t opcode,
|
|||
env->fp_status.float_exception_flags |= tstat.float_exception_flags;
|
||||
|
||||
if (unlikely(tstat.float_exception_flags & float_flag_invalid)) {
|
||||
float_invalid_op_addsub(env, 1, GETPC(),
|
||||
float128_classify(xa->f128) |
|
||||
float128_classify(xb->f128));
|
||||
float_invalid_op_addsub(env, tstat.float_exception_flags, 1, GETPC());
|
||||
}
|
||||
|
||||
helper_compute_fprf_float128(env, t.f128);
|
||||
|
|
|
@ -18,8 +18,14 @@ DEF_HELPER_2(pminsn, void, env, i32)
|
|||
DEF_HELPER_1(rfid, void, env)
|
||||
DEF_HELPER_1(rfscv, void, env)
|
||||
DEF_HELPER_1(hrfid, void, env)
|
||||
DEF_HELPER_2(rfebb, void, env, tl)
|
||||
DEF_HELPER_2(store_lpcr, void, env, tl)
|
||||
DEF_HELPER_2(store_pcr, void, env, tl)
|
||||
DEF_HELPER_2(store_mmcr0, void, env, tl)
|
||||
DEF_HELPER_2(store_mmcr1, void, env, tl)
|
||||
DEF_HELPER_3(store_pmc, void, env, i32, i64)
|
||||
DEF_HELPER_2(read_pmc, tl, env, i32)
|
||||
DEF_HELPER_2(insns_inc, void, env, i32)
|
||||
#endif
|
||||
DEF_HELPER_1(check_tlb_flush_local, void, env)
|
||||
DEF_HELPER_1(check_tlb_flush_global, void, env)
|
||||
|
@ -63,6 +69,7 @@ DEF_HELPER_FLAGS_1(cntlzw32, TCG_CALL_NO_RWG_SE, i32, i32)
|
|||
DEF_HELPER_FLAGS_2(brinc, TCG_CALL_NO_RWG_SE, tl, tl, tl)
|
||||
|
||||
DEF_HELPER_1(float_check_status, void, env)
|
||||
DEF_HELPER_1(fpscr_check_status, void, env)
|
||||
DEF_HELPER_1(reset_fpstatus, void, env)
|
||||
DEF_HELPER_2(compute_fprf_float64, void, env, i64)
|
||||
DEF_HELPER_3(store_fpscr, void, env, i64, i32)
|
||||
|
@ -93,17 +100,27 @@ DEF_HELPER_2(frip, i64, env, i64)
|
|||
DEF_HELPER_2(frim, i64, env, i64)
|
||||
|
||||
DEF_HELPER_3(fadd, f64, env, f64, f64)
|
||||
DEF_HELPER_3(fadds, f64, env, f64, f64)
|
||||
DEF_HELPER_3(fsub, f64, env, f64, f64)
|
||||
DEF_HELPER_3(fsubs, f64, env, f64, f64)
|
||||
DEF_HELPER_3(fmul, f64, env, f64, f64)
|
||||
DEF_HELPER_3(fmuls, f64, env, f64, f64)
|
||||
DEF_HELPER_3(fdiv, f64, env, f64, f64)
|
||||
DEF_HELPER_3(fdivs, f64, env, f64, f64)
|
||||
DEF_HELPER_4(fmadd, i64, env, i64, i64, i64)
|
||||
DEF_HELPER_4(fmsub, i64, env, i64, i64, i64)
|
||||
DEF_HELPER_4(fnmadd, i64, env, i64, i64, i64)
|
||||
DEF_HELPER_4(fnmsub, i64, env, i64, i64, i64)
|
||||
DEF_HELPER_4(fmadds, i64, env, i64, i64, i64)
|
||||
DEF_HELPER_4(fmsubs, i64, env, i64, i64, i64)
|
||||
DEF_HELPER_4(fnmadds, i64, env, i64, i64, i64)
|
||||
DEF_HELPER_4(fnmsubs, i64, env, i64, i64, i64)
|
||||
DEF_HELPER_2(fsqrt, f64, env, f64)
|
||||
DEF_HELPER_2(fsqrts, f64, env, f64)
|
||||
DEF_HELPER_2(fre, i64, env, i64)
|
||||
DEF_HELPER_2(fres, i64, env, i64)
|
||||
DEF_HELPER_2(frsqrte, i64, env, i64)
|
||||
DEF_HELPER_2(frsqrtes, i64, env, i64)
|
||||
DEF_HELPER_4(fsel, i64, env, i64, i64, i64)
|
||||
|
||||
DEF_HELPER_FLAGS_2(ftdiv, TCG_CALL_NO_RWG_SE, i32, i64, i64)
|
||||
|
@ -392,15 +409,15 @@ DEF_HELPER_4(xscmpoqp, void, env, i32, vsr, vsr)
|
|||
DEF_HELPER_4(xscmpuqp, void, env, i32, vsr, vsr)
|
||||
DEF_HELPER_4(xsmaxdp, void, env, vsr, vsr, vsr)
|
||||
DEF_HELPER_4(xsmindp, void, env, vsr, vsr, vsr)
|
||||
DEF_HELPER_5(xsmaxcdp, void, env, i32, vsr, vsr, vsr)
|
||||
DEF_HELPER_5(xsmincdp, void, env, i32, vsr, vsr, vsr)
|
||||
DEF_HELPER_5(xsmaxjdp, void, env, i32, vsr, vsr, vsr)
|
||||
DEF_HELPER_5(xsminjdp, void, env, i32, vsr, vsr, vsr)
|
||||
DEF_HELPER_4(xsmaxcdp, void, env, vsr, vsr, vsr)
|
||||
DEF_HELPER_4(xsmincdp, void, env, vsr, vsr, vsr)
|
||||
DEF_HELPER_4(xsmaxjdp, void, env, vsr, vsr, vsr)
|
||||
DEF_HELPER_4(xsminjdp, void, env, vsr, vsr, vsr)
|
||||
DEF_HELPER_3(xscvdphp, void, env, vsr, vsr)
|
||||
DEF_HELPER_4(xscvdpqp, void, env, i32, vsr, vsr)
|
||||
DEF_HELPER_3(xscvdpsp, void, env, vsr, vsr)
|
||||
DEF_HELPER_2(xscvdpspn, i64, env, i64)
|
||||
DEF_HELPER_4(xscvqpdp, void, env, i32, vsr, vsr)
|
||||
DEF_HELPER_4(XSCVQPDP, void, env, i32, vsr, vsr)
|
||||
DEF_HELPER_4(xscvqpsdz, void, env, i32, vsr, vsr)
|
||||
DEF_HELPER_4(xscvqpswz, void, env, i32, vsr, vsr)
|
||||
DEF_HELPER_4(xscvqpudz, void, env, i32, vsr, vsr)
|
||||
|
@ -614,8 +631,6 @@ DEF_HELPER_2(booke_set_eplc, void, env, tl)
|
|||
DEF_HELPER_2(booke_set_epsc, void, env, tl)
|
||||
DEF_HELPER_2(6xx_tlbd, void, env, tl)
|
||||
DEF_HELPER_2(6xx_tlbi, void, env, tl)
|
||||
DEF_HELPER_2(74xx_tlbd, void, env, tl)
|
||||
DEF_HELPER_2(74xx_tlbi, void, env, tl)
|
||||
DEF_HELPER_FLAGS_1(tlbia, TCG_CALL_NO_RWG, void, env)
|
||||
DEF_HELPER_FLAGS_2(tlbie, TCG_CALL_NO_RWG, void, env, tl)
|
||||
DEF_HELPER_FLAGS_2(tlbiva, TCG_CALL_NO_RWG, void, env, tl)
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "exec/exec-all.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "helper_regs.h"
|
||||
#include "power8-pmu.h"
|
||||
|
||||
/* Swap temporary saved registers with GPRs */
|
||||
void hreg_swap_gpr_tgpr(CPUPPCState *env)
|
||||
|
@ -121,6 +122,12 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env)
|
|||
hflags |= 1 << HFLAGS_HV;
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
if (pmu_insn_cnt_enabled(env)) {
|
||||
hflags |= 1 << HFLAGS_INSN_CNT;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is our encoding for server processors. The architecture
|
||||
* specifies that there is no such thing as userspace with
|
||||
|
|
|
@ -40,6 +40,10 @@
|
|||
%ds_rtp 22:4 !function=times_2
|
||||
@DS_rtp ...... ....0 ra:5 .............. .. &D rt=%ds_rtp si=%ds_si
|
||||
|
||||
&DX_b vrt b
|
||||
%dx_b 6:10 16:5 0:1
|
||||
@DX_b ...... vrt:5 ..... .......... ..... . &DX_b b=%dx_b
|
||||
|
||||
&DX rt d
|
||||
%dx_d 6:s10 16:5 0:1
|
||||
@DX ...... rt:5 ..... .......... ..... . &DX d=%dx_d
|
||||
|
@ -56,6 +60,9 @@
|
|||
&VX_uim4 vrt uim vrb
|
||||
@VX_uim4 ...... vrt:5 . uim:4 vrb:5 ........... &VX_uim4
|
||||
|
||||
&VX_tb vrt vrb
|
||||
@VX_tb ...... vrt:5 ..... vrb:5 ........... &VX_tb
|
||||
|
||||
&X rt ra rb
|
||||
@X ...... rt:5 ra:5 rb:5 .......... . &X
|
||||
|
||||
|
@ -123,10 +130,14 @@
|
|||
&X_vrt_frbp vrt frbp
|
||||
@X_vrt_frbp ...... vrt:5 ..... ....0 .......... . &X_vrt_frbp frbp=%x_frbp
|
||||
|
||||
%xx_xt 0:1 21:5
|
||||
%xx_xb 1:1 11:5
|
||||
%xx_xa 2:1 16:5
|
||||
&XX2 xt xb uim:uint8_t
|
||||
%xx2_xt 0:1 21:5
|
||||
%xx2_xb 1:1 11:5
|
||||
@XX2 ...... ..... ... uim:2 ..... ......... .. &XX2 xt=%xx2_xt xb=%xx2_xb
|
||||
@XX2 ...... ..... ... uim:2 ..... ......... .. &XX2 xt=%xx_xt xb=%xx_xb
|
||||
|
||||
&XX3 xt xa xb
|
||||
@XX3 ...... ..... ..... ..... ........ ... &XX3 xt=%xx_xt xa=%xx_xa xb=%xx_xb
|
||||
|
||||
&Z22_bf_fra bf fra dm
|
||||
@Z22_bf_fra ...... bf:3 .. fra:5 dm:6 ......... . &Z22_bf_fra
|
||||
|
@ -408,6 +419,27 @@ VINSWVRX 000100 ..... ..... ..... 00110001111 @VX
|
|||
VSLDBI 000100 ..... ..... ..... 00 ... 010110 @VN
|
||||
VSRDBI 000100 ..... ..... ..... 01 ... 010110 @VN
|
||||
|
||||
## Vector Mask Manipulation Instructions
|
||||
|
||||
MTVSRBM 000100 ..... 10000 ..... 11001000010 @VX_tb
|
||||
MTVSRHM 000100 ..... 10001 ..... 11001000010 @VX_tb
|
||||
MTVSRWM 000100 ..... 10010 ..... 11001000010 @VX_tb
|
||||
MTVSRDM 000100 ..... 10011 ..... 11001000010 @VX_tb
|
||||
MTVSRQM 000100 ..... 10100 ..... 11001000010 @VX_tb
|
||||
MTVSRBMI 000100 ..... ..... .......... 01010 . @DX_b
|
||||
|
||||
VEXPANDBM 000100 ..... 00000 ..... 11001000010 @VX_tb
|
||||
VEXPANDHM 000100 ..... 00001 ..... 11001000010 @VX_tb
|
||||
VEXPANDWM 000100 ..... 00010 ..... 11001000010 @VX_tb
|
||||
VEXPANDDM 000100 ..... 00011 ..... 11001000010 @VX_tb
|
||||
VEXPANDQM 000100 ..... 00100 ..... 11001000010 @VX_tb
|
||||
|
||||
VEXTRACTBM 000100 ..... 01000 ..... 11001000010 @VX_tb
|
||||
VEXTRACTHM 000100 ..... 01001 ..... 11001000010 @VX_tb
|
||||
VEXTRACTWM 000100 ..... 01010 ..... 11001000010 @VX_tb
|
||||
VEXTRACTDM 000100 ..... 01011 ..... 11001000010 @VX_tb
|
||||
VEXTRACTQM 000100 ..... 01100 ..... 11001000010 @VX_tb
|
||||
|
||||
# VSX Load/Store Instructions
|
||||
|
||||
LXV 111101 ..... ..... ............ . 001 @DQ_TSX
|
||||
|
@ -427,3 +459,19 @@ XXSPLTW 111100 ..... ---.. ..... 010100100 . . @XX2
|
|||
## VSX Vector Load Special Value Instruction
|
||||
|
||||
LXVKQ 111100 ..... 11111 ..... 0101101000 . @X_uim5
|
||||
|
||||
## VSX Comparison Instructions
|
||||
|
||||
XSMAXCDP 111100 ..... ..... ..... 10000000 ... @XX3
|
||||
XSMINCDP 111100 ..... ..... ..... 10001000 ... @XX3
|
||||
XSMAXJDP 111100 ..... ..... ..... 10010000 ... @XX3
|
||||
XSMINJDP 111100 ..... ..... ..... 10011000 ... @XX3
|
||||
|
||||
## VSX Binary Floating-Point Convert Instructions
|
||||
|
||||
XSCVQPDP 111111 ..... 10100 ..... 1101000100 . @X_tb_rc
|
||||
|
||||
### rfebb
|
||||
&XL_s s:uint8_t
|
||||
@XL_s ......-------------- s:1 .......... - &XL_s
|
||||
RFEBB 010011-------------- . 0010010010 - @XL_s
|
||||
|
|
|
@ -51,6 +51,7 @@ ppc_softmmu_ss.add(when: 'TARGET_PPC64', if_true: files(
|
|||
'mmu-book3s-v3.c',
|
||||
'mmu-hash64.c',
|
||||
'mmu-radix64.c',
|
||||
'power8-pmu.c',
|
||||
))
|
||||
|
||||
target_arch += {'ppc': ppc_ss}
|
||||
|
|
|
@ -1147,7 +1147,6 @@ void dump_mmu(CPUPPCState *env)
|
|||
mmubooke206_dump_mmu(env);
|
||||
break;
|
||||
case POWERPC_MMU_SOFT_6xx:
|
||||
case POWERPC_MMU_SOFT_74xx:
|
||||
mmu6xx_dump_mmu(env);
|
||||
break;
|
||||
#if defined(TARGET_PPC64)
|
||||
|
@ -1174,53 +1173,23 @@ void dump_mmu(CPUPPCState *env)
|
|||
static int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr,
|
||||
MMUAccessType access_type)
|
||||
{
|
||||
int in_plb, ret;
|
||||
|
||||
ctx->raddr = eaddr;
|
||||
ctx->prot = PAGE_READ | PAGE_EXEC;
|
||||
ret = 0;
|
||||
|
||||
switch (env->mmu_model) {
|
||||
case POWERPC_MMU_SOFT_6xx:
|
||||
case POWERPC_MMU_SOFT_74xx:
|
||||
case POWERPC_MMU_SOFT_4xx:
|
||||
case POWERPC_MMU_REAL:
|
||||
case POWERPC_MMU_BOOKE:
|
||||
ctx->prot |= PAGE_WRITE;
|
||||
break;
|
||||
|
||||
case POWERPC_MMU_SOFT_4xx_Z:
|
||||
if (unlikely(msr_pe != 0)) {
|
||||
/*
|
||||
* 403 family add some particular protections, using
|
||||
* PBL/PBU registers for accesses with no translation.
|
||||
*/
|
||||
in_plb =
|
||||
/* Check PLB validity */
|
||||
(env->pb[0] < env->pb[1] &&
|
||||
/* and address in plb area */
|
||||
eaddr >= env->pb[0] && eaddr < env->pb[1]) ||
|
||||
(env->pb[2] < env->pb[3] &&
|
||||
eaddr >= env->pb[2] && eaddr < env->pb[3]) ? 1 : 0;
|
||||
if (in_plb ^ msr_px) {
|
||||
/* Access in protected area */
|
||||
if (access_type == MMU_DATA_STORE) {
|
||||
/* Access is not allowed */
|
||||
ret = -2;
|
||||
}
|
||||
} else {
|
||||
/* Read-write access is allowed */
|
||||
ctx->prot |= PAGE_WRITE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Caller's checks mean we should never get here for other models */
|
||||
abort();
|
||||
return -1;
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx,
|
||||
|
@ -1234,7 +1203,6 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx,
|
|||
|
||||
switch (env->mmu_model) {
|
||||
case POWERPC_MMU_SOFT_6xx:
|
||||
case POWERPC_MMU_SOFT_74xx:
|
||||
if (real_mode) {
|
||||
ret = check_physical(env, ctx, eaddr, access_type);
|
||||
} else {
|
||||
|
@ -1250,7 +1218,6 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx,
|
|||
break;
|
||||
|
||||
case POWERPC_MMU_SOFT_4xx:
|
||||
case POWERPC_MMU_SOFT_4xx_Z:
|
||||
if (real_mode) {
|
||||
ret = check_physical(env, ctx, eaddr, access_type);
|
||||
} else {
|
||||
|
@ -1383,11 +1350,7 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr,
|
|||
env->spr[SPR_IMISS] = eaddr;
|
||||
env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem;
|
||||
goto tlb_miss;
|
||||
case POWERPC_MMU_SOFT_74xx:
|
||||
cs->exception_index = POWERPC_EXCP_IFTLB;
|
||||
goto tlb_miss_74xx;
|
||||
case POWERPC_MMU_SOFT_4xx:
|
||||
case POWERPC_MMU_SOFT_4xx_Z:
|
||||
cs->exception_index = POWERPC_EXCP_ITLB;
|
||||
env->error_code = 0;
|
||||
env->spr[SPR_40x_DEAR] = eaddr;
|
||||
|
@ -1454,21 +1417,7 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr,
|
|||
env->spr[SPR_HASH2] = ppc_hash32_hpt_base(cpu) +
|
||||
get_pteg_offset32(cpu, ctx.hash[1]);
|
||||
break;
|
||||
case POWERPC_MMU_SOFT_74xx:
|
||||
if (access_type == MMU_DATA_STORE) {
|
||||
cs->exception_index = POWERPC_EXCP_DSTLB;
|
||||
} else {
|
||||
cs->exception_index = POWERPC_EXCP_DLTLB;
|
||||
}
|
||||
tlb_miss_74xx:
|
||||
/* Implement LRU algorithm */
|
||||
env->error_code = ctx.key << 19;
|
||||
env->spr[SPR_TLBMISS] = (eaddr & ~((target_ulong)0x3)) |
|
||||
((env->last_way + 1) & (env->nb_ways - 1));
|
||||
env->spr[SPR_PTEHI] = 0x80000000 | ctx.ptem;
|
||||
break;
|
||||
case POWERPC_MMU_SOFT_4xx:
|
||||
case POWERPC_MMU_SOFT_4xx_Z:
|
||||
cs->exception_index = POWERPC_EXCP_DTLB;
|
||||
env->error_code = 0;
|
||||
env->spr[SPR_40x_DEAR] = eaddr;
|
||||
|
@ -1501,8 +1450,7 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr,
|
|||
/* Access rights violation */
|
||||
cs->exception_index = POWERPC_EXCP_DSI;
|
||||
env->error_code = 0;
|
||||
if (env->mmu_model == POWERPC_MMU_SOFT_4xx
|
||||
|| env->mmu_model == POWERPC_MMU_SOFT_4xx_Z) {
|
||||
if (env->mmu_model == POWERPC_MMU_SOFT_4xx) {
|
||||
env->spr[SPR_40x_DEAR] = eaddr;
|
||||
if (access_type == MMU_DATA_STORE) {
|
||||
env->spr[SPR_40x_ESR] |= 0x00800000;
|
||||
|
|
|
@ -385,11 +385,9 @@ void ppc_tlb_invalidate_all(CPUPPCState *env)
|
|||
#endif /* defined(TARGET_PPC64) */
|
||||
switch (env->mmu_model) {
|
||||
case POWERPC_MMU_SOFT_6xx:
|
||||
case POWERPC_MMU_SOFT_74xx:
|
||||
ppc6xx_tlb_invalidate_all(env);
|
||||
break;
|
||||
case POWERPC_MMU_SOFT_4xx:
|
||||
case POWERPC_MMU_SOFT_4xx_Z:
|
||||
ppc4xx_tlb_invalidate_all(env);
|
||||
break;
|
||||
case POWERPC_MMU_REAL:
|
||||
|
@ -434,7 +432,6 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr)
|
|||
#endif /* defined(TARGET_PPC64) */
|
||||
switch (env->mmu_model) {
|
||||
case POWERPC_MMU_SOFT_6xx:
|
||||
case POWERPC_MMU_SOFT_74xx:
|
||||
ppc6xx_tlb_invalidate_virt(env, addr, 0);
|
||||
if (env->id_tlbs == 1) {
|
||||
ppc6xx_tlb_invalidate_virt(env, addr, 1);
|
||||
|
@ -571,35 +568,6 @@ void helper_6xx_tlbi(CPUPPCState *env, target_ulong EPN)
|
|||
do_6xx_tlb(env, EPN, 1);
|
||||
}
|
||||
|
||||
/* PowerPC 74xx software TLB load instructions helpers */
|
||||
static void do_74xx_tlb(CPUPPCState *env, target_ulong new_EPN, int is_code)
|
||||
{
|
||||
target_ulong RPN, CMP, EPN;
|
||||
int way;
|
||||
|
||||
RPN = env->spr[SPR_PTELO];
|
||||
CMP = env->spr[SPR_PTEHI];
|
||||
EPN = env->spr[SPR_TLBMISS] & ~0x3;
|
||||
way = env->spr[SPR_TLBMISS] & 0x3;
|
||||
(void)EPN; /* avoid a compiler warning */
|
||||
LOG_SWTLB("%s: EPN " TARGET_FMT_lx " " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx
|
||||
" PTE1 " TARGET_FMT_lx " way %d\n", __func__, new_EPN, EPN, CMP,
|
||||
RPN, way);
|
||||
/* Store this TLB */
|
||||
ppc6xx_tlb_store(env, (uint32_t)(new_EPN & TARGET_PAGE_MASK),
|
||||
way, is_code, CMP, RPN);
|
||||
}
|
||||
|
||||
void helper_74xx_tlbd(CPUPPCState *env, target_ulong EPN)
|
||||
{
|
||||
do_74xx_tlb(env, EPN, 0);
|
||||
}
|
||||
|
||||
void helper_74xx_tlbi(CPUPPCState *env, target_ulong EPN)
|
||||
{
|
||||
do_74xx_tlb(env, EPN, 1);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* PowerPC 601 specific instructions (POWER bridge) */
|
||||
|
||||
|
|
|
@ -104,6 +104,23 @@ void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn)
|
|||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
static void write_MMCR0_common(DisasContext *ctx, TCGv val)
|
||||
{
|
||||
/*
|
||||
* helper_store_mmcr0 will make clock based operations that
|
||||
* will cause 'bad icount read' errors if we do not execute
|
||||
* gen_icount_io_start() beforehand.
|
||||
*/
|
||||
gen_icount_io_start(ctx);
|
||||
gen_helper_store_mmcr0(cpu_env, val);
|
||||
|
||||
/*
|
||||
* End the translation block because MMCR0 writes can change
|
||||
* ctx->pmu_insn_cnt.
|
||||
*/
|
||||
ctx->base.is_jmp = DISAS_EXIT_UPDATE;
|
||||
}
|
||||
|
||||
void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
TCGv masked_gprn;
|
||||
|
@ -119,7 +136,7 @@ void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn)
|
|||
*/
|
||||
masked_gprn = masked_gprn_for_spr_write(gprn, SPR_POWER_MMCR0,
|
||||
MMCR0_UREG_MASK);
|
||||
gen_store_spr(SPR_POWER_MMCR0, masked_gprn);
|
||||
write_MMCR0_common(ctx, masked_gprn);
|
||||
|
||||
tcg_temp_free(masked_gprn);
|
||||
}
|
||||
|
@ -170,13 +187,23 @@ void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn)
|
|||
tcg_temp_free(masked_gprn);
|
||||
}
|
||||
|
||||
void spr_read_PMC(DisasContext *ctx, int gprn, int sprn)
|
||||
{
|
||||
TCGv_i32 t_sprn = tcg_const_i32(sprn);
|
||||
|
||||
gen_icount_io_start(ctx);
|
||||
gen_helper_read_pmc(cpu_gpr[gprn], cpu_env, t_sprn);
|
||||
|
||||
tcg_temp_free_i32(t_sprn);
|
||||
}
|
||||
|
||||
void spr_read_PMC14_ureg(DisasContext *ctx, int gprn, int sprn)
|
||||
{
|
||||
if (!spr_groupA_read_allowed(ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
spr_read_ureg(ctx, gprn, sprn);
|
||||
spr_read_PMC(ctx, gprn, sprn + 0x10);
|
||||
}
|
||||
|
||||
void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn)
|
||||
|
@ -195,13 +222,23 @@ void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn)
|
|||
spr_read_PMC14_ureg(ctx, gprn, sprn);
|
||||
}
|
||||
|
||||
void spr_write_PMC(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
TCGv_i32 t_sprn = tcg_const_i32(sprn);
|
||||
|
||||
gen_icount_io_start(ctx);
|
||||
gen_helper_store_pmc(cpu_env, t_sprn, cpu_gpr[gprn]);
|
||||
|
||||
tcg_temp_free_i32(t_sprn);
|
||||
}
|
||||
|
||||
void spr_write_PMC14_ureg(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
if (!spr_groupA_write_allowed(ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
spr_write_ureg(ctx, sprn, gprn);
|
||||
spr_write_PMC(ctx, sprn + 0x10, gprn);
|
||||
}
|
||||
|
||||
void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn)
|
||||
|
@ -219,6 +256,17 @@ void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn)
|
|||
/* The remaining steps are similar to PMCs 1-4 userspace write */
|
||||
spr_write_PMC14_ureg(ctx, sprn, gprn);
|
||||
}
|
||||
|
||||
void spr_write_MMCR0(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
write_MMCR0_common(ctx, cpu_gpr[gprn]);
|
||||
}
|
||||
|
||||
void spr_write_MMCR1(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
gen_icount_io_start(ctx);
|
||||
gen_helper_store_mmcr1(cpu_env, cpu_gpr[gprn]);
|
||||
}
|
||||
#else
|
||||
void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn)
|
||||
{
|
||||
|
@ -259,4 +307,19 @@ void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn)
|
|||
{
|
||||
spr_noaccess(ctx, gprn, sprn);
|
||||
}
|
||||
|
||||
void spr_write_MMCR0(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
spr_write_generic(ctx, sprn, gprn);
|
||||
}
|
||||
|
||||
void spr_write_MMCR1(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
spr_write_generic(ctx, sprn, gprn);
|
||||
}
|
||||
|
||||
void spr_write_PMC(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
spr_write_generic(ctx, sprn, gprn);
|
||||
}
|
||||
#endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */
|
||||
|
|
|
@ -0,0 +1,350 @@
|
|||
/*
|
||||
* PMU emulation helpers for TCG IBM POWER chips
|
||||
*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* Authors:
|
||||
* Daniel Henrique Barboza <danielhb413@gmail.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "power8-pmu.h"
|
||||
#include "cpu.h"
|
||||
#include "helper_regs.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "hw/ppc/ppc.h"
|
||||
|
||||
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
|
||||
|
||||
#define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL
|
||||
|
||||
static bool pmc_is_inactive(CPUPPCState *env, int sprn)
|
||||
{
|
||||
if (env->spr[SPR_POWER_MMCR0] & MMCR0_FC) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sprn < SPR_POWER_PMC5) {
|
||||
return env->spr[SPR_POWER_MMCR0] & MMCR0_FC14;
|
||||
}
|
||||
|
||||
return env->spr[SPR_POWER_MMCR0] & MMCR0_FC56;
|
||||
}
|
||||
|
||||
static bool pmc_has_overflow_enabled(CPUPPCState *env, int sprn)
|
||||
{
|
||||
if (sprn == SPR_POWER_PMC1) {
|
||||
return env->spr[SPR_POWER_MMCR0] & MMCR0_PMC1CE;
|
||||
}
|
||||
|
||||
return env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE;
|
||||
}
|
||||
|
||||
/*
|
||||
* For PMCs 1-4, IBM POWER chips has support for an implementation
|
||||
* dependent event, 0x1E, that enables cycle counting. The Linux kernel
|
||||
* makes extensive use of 0x1E, so let's also support it.
|
||||
*
|
||||
* Likewise, event 0x2 is an implementation-dependent event that IBM
|
||||
* POWER chips implement (at least since POWER8) that is equivalent to
|
||||
* PM_INST_CMPL. Let's support this event on PMCs 1-4 as well.
|
||||
*/
|
||||
static PMUEventType pmc_get_event(CPUPPCState *env, int sprn)
|
||||
{
|
||||
uint8_t mmcr1_evt_extr[] = { MMCR1_PMC1EVT_EXTR, MMCR1_PMC2EVT_EXTR,
|
||||
MMCR1_PMC3EVT_EXTR, MMCR1_PMC4EVT_EXTR };
|
||||
PMUEventType evt_type = PMU_EVENT_INVALID;
|
||||
uint8_t pmcsel;
|
||||
int i;
|
||||
|
||||
if (pmc_is_inactive(env, sprn)) {
|
||||
return PMU_EVENT_INACTIVE;
|
||||
}
|
||||
|
||||
if (sprn == SPR_POWER_PMC5) {
|
||||
return PMU_EVENT_INSTRUCTIONS;
|
||||
}
|
||||
|
||||
if (sprn == SPR_POWER_PMC6) {
|
||||
return PMU_EVENT_CYCLES;
|
||||
}
|
||||
|
||||
i = sprn - SPR_POWER_PMC1;
|
||||
pmcsel = extract64(env->spr[SPR_POWER_MMCR1], mmcr1_evt_extr[i],
|
||||
MMCR1_EVT_SIZE);
|
||||
|
||||
switch (pmcsel) {
|
||||
case 0x2:
|
||||
evt_type = PMU_EVENT_INSTRUCTIONS;
|
||||
break;
|
||||
case 0x1E:
|
||||
evt_type = PMU_EVENT_CYCLES;
|
||||
break;
|
||||
case 0xF0:
|
||||
/*
|
||||
* PMC1SEL = 0xF0 is the architected PowerISA v3.1
|
||||
* event that counts cycles using PMC1.
|
||||
*/
|
||||
if (sprn == SPR_POWER_PMC1) {
|
||||
evt_type = PMU_EVENT_CYCLES;
|
||||
}
|
||||
break;
|
||||
case 0xFA:
|
||||
/*
|
||||
* PMC4SEL = 0xFA is the "instructions completed
|
||||
* with run latch set" event.
|
||||
*/
|
||||
if (sprn == SPR_POWER_PMC4) {
|
||||
evt_type = PMU_EVENT_INSN_RUN_LATCH;
|
||||
}
|
||||
break;
|
||||
case 0xFE:
|
||||
/*
|
||||
* PMC1SEL = 0xFE is the architected PowerISA v3.1
|
||||
* event to sample instructions using PMC1.
|
||||
*/
|
||||
if (sprn == SPR_POWER_PMC1) {
|
||||
evt_type = PMU_EVENT_INSTRUCTIONS;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return evt_type;
|
||||
}
|
||||
|
||||
bool pmu_insn_cnt_enabled(CPUPPCState *env)
|
||||
{
|
||||
int sprn;
|
||||
|
||||
for (sprn = SPR_POWER_PMC1; sprn <= SPR_POWER_PMC5; sprn++) {
|
||||
if (pmc_get_event(env, sprn) == PMU_EVENT_INSTRUCTIONS ||
|
||||
pmc_get_event(env, sprn) == PMU_EVENT_INSN_RUN_LATCH) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool pmu_increment_insns(CPUPPCState *env, uint32_t num_insns)
|
||||
{
|
||||
bool overflow_triggered = false;
|
||||
int sprn;
|
||||
|
||||
/* PMC6 never counts instructions */
|
||||
for (sprn = SPR_POWER_PMC1; sprn <= SPR_POWER_PMC5; sprn++) {
|
||||
PMUEventType evt_type = pmc_get_event(env, sprn);
|
||||
bool insn_event = evt_type == PMU_EVENT_INSTRUCTIONS ||
|
||||
evt_type == PMU_EVENT_INSN_RUN_LATCH;
|
||||
|
||||
if (pmc_is_inactive(env, sprn) || !insn_event) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (evt_type == PMU_EVENT_INSTRUCTIONS) {
|
||||
env->spr[sprn] += num_insns;
|
||||
}
|
||||
|
||||
if (evt_type == PMU_EVENT_INSN_RUN_LATCH &&
|
||||
env->spr[SPR_CTRL] & CTRL_RUN) {
|
||||
env->spr[sprn] += num_insns;
|
||||
}
|
||||
|
||||
if (env->spr[sprn] >= PMC_COUNTER_NEGATIVE_VAL &&
|
||||
pmc_has_overflow_enabled(env, sprn)) {
|
||||
|
||||
overflow_triggered = true;
|
||||
|
||||
/*
|
||||
* The real PMU will always trigger a counter overflow with
|
||||
* PMC_COUNTER_NEGATIVE_VAL. We don't have an easy way to
|
||||
* do that since we're counting block of instructions at
|
||||
* the end of each translation block, and we're probably
|
||||
* passing this value at this point.
|
||||
*
|
||||
* Let's write PMC_COUNTER_NEGATIVE_VAL to the overflowed
|
||||
* counter to simulate what the real hardware would do.
|
||||
*/
|
||||
env->spr[sprn] = PMC_COUNTER_NEGATIVE_VAL;
|
||||
}
|
||||
}
|
||||
|
||||
return overflow_triggered;
|
||||
}
|
||||
|
||||
static void pmu_update_cycles(CPUPPCState *env)
|
||||
{
|
||||
uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
uint64_t time_delta = now - env->pmu_base_time;
|
||||
int sprn;
|
||||
|
||||
for (sprn = SPR_POWER_PMC1; sprn <= SPR_POWER_PMC6; sprn++) {
|
||||
if (pmc_get_event(env, sprn) != PMU_EVENT_CYCLES) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* The pseries and powernv clock runs at 1Ghz, meaning
|
||||
* that 1 nanosec equals 1 cycle.
|
||||
*/
|
||||
env->spr[sprn] += time_delta;
|
||||
}
|
||||
|
||||
/* Update base_time for future calculations */
|
||||
env->pmu_base_time = now;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to retrieve the cycle overflow timer of the
|
||||
* 'sprn' counter.
|
||||
*/
|
||||
static QEMUTimer *get_cyc_overflow_timer(CPUPPCState *env, int sprn)
|
||||
{
|
||||
return env->pmu_cyc_overflow_timers[sprn - SPR_POWER_PMC1];
|
||||
}
|
||||
|
||||
static void pmc_update_overflow_timer(CPUPPCState *env, int sprn)
|
||||
{
|
||||
QEMUTimer *pmc_overflow_timer = get_cyc_overflow_timer(env, sprn);
|
||||
int64_t timeout;
|
||||
|
||||
/*
|
||||
* PMC5 does not have an overflow timer and this pointer
|
||||
* will be NULL.
|
||||
*/
|
||||
if (!pmc_overflow_timer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pmc_get_event(env, sprn) != PMU_EVENT_CYCLES ||
|
||||
!pmc_has_overflow_enabled(env, sprn)) {
|
||||
/* Overflow timer is not needed for this counter */
|
||||
timer_del(pmc_overflow_timer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (env->spr[sprn] >= PMC_COUNTER_NEGATIVE_VAL) {
|
||||
timeout = 0;
|
||||
} else {
|
||||
timeout = PMC_COUNTER_NEGATIVE_VAL - env->spr[sprn];
|
||||
}
|
||||
|
||||
/*
|
||||
* Use timer_mod_anticipate() because an overflow timer might
|
||||
* be already running for this PMC.
|
||||
*/
|
||||
timer_mod_anticipate(pmc_overflow_timer, env->pmu_base_time + timeout);
|
||||
}
|
||||
|
||||
static void pmu_update_overflow_timers(CPUPPCState *env)
|
||||
{
|
||||
int sprn;
|
||||
|
||||
/*
|
||||
* Scroll through all PMCs and start counter overflow timers for
|
||||
* PM_CYC events, if needed.
|
||||
*/
|
||||
for (sprn = SPR_POWER_PMC1; sprn <= SPR_POWER_PMC6; sprn++) {
|
||||
pmc_update_overflow_timer(env, sprn);
|
||||
}
|
||||
}
|
||||
|
||||
void helper_store_mmcr0(CPUPPCState *env, target_ulong value)
|
||||
{
|
||||
pmu_update_cycles(env);
|
||||
|
||||
env->spr[SPR_POWER_MMCR0] = value;
|
||||
|
||||
/* MMCR0 writes can change HFLAGS_PMCCCLEAR and HFLAGS_INSN_CNT */
|
||||
hreg_compute_hflags(env);
|
||||
|
||||
/* Update cycle overflow timers with the current MMCR0 state */
|
||||
pmu_update_overflow_timers(env);
|
||||
}
|
||||
|
||||
void helper_store_mmcr1(CPUPPCState *env, uint64_t value)
|
||||
{
|
||||
pmu_update_cycles(env);
|
||||
|
||||
env->spr[SPR_POWER_MMCR1] = value;
|
||||
|
||||
/* MMCR1 writes can change HFLAGS_INSN_CNT */
|
||||
hreg_compute_hflags(env);
|
||||
}
|
||||
|
||||
target_ulong helper_read_pmc(CPUPPCState *env, uint32_t sprn)
|
||||
{
|
||||
pmu_update_cycles(env);
|
||||
|
||||
return env->spr[sprn];
|
||||
}
|
||||
|
||||
void helper_store_pmc(CPUPPCState *env, uint32_t sprn, uint64_t value)
|
||||
{
|
||||
pmu_update_cycles(env);
|
||||
|
||||
env->spr[sprn] = value;
|
||||
|
||||
pmc_update_overflow_timer(env, sprn);
|
||||
}
|
||||
|
||||
static void fire_PMC_interrupt(PowerPCCPU *cpu)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
if (!(env->spr[SPR_POWER_MMCR0] & MMCR0_EBE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* PMC interrupt not implemented yet */
|
||||
return;
|
||||
}
|
||||
|
||||
/* This helper assumes that the PMC is running. */
|
||||
void helper_insns_inc(CPUPPCState *env, uint32_t num_insns)
|
||||
{
|
||||
bool overflow_triggered;
|
||||
PowerPCCPU *cpu;
|
||||
|
||||
overflow_triggered = pmu_increment_insns(env, num_insns);
|
||||
|
||||
if (overflow_triggered) {
|
||||
cpu = env_archcpu(env);
|
||||
fire_PMC_interrupt(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
static void cpu_ppc_pmu_timer_cb(void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
|
||||
fire_PMC_interrupt(cpu);
|
||||
}
|
||||
|
||||
void cpu_ppc_pmu_init(CPUPPCState *env)
|
||||
{
|
||||
PowerPCCPU *cpu = env_archcpu(env);
|
||||
int i, sprn;
|
||||
|
||||
for (sprn = SPR_POWER_PMC1; sprn <= SPR_POWER_PMC6; sprn++) {
|
||||
if (sprn == SPR_POWER_PMC5) {
|
||||
continue;
|
||||
}
|
||||
|
||||
i = sprn - SPR_POWER_PMC1;
|
||||
|
||||
env->pmu_cyc_overflow_timers[i] = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
&cpu_ppc_pmu_timer_cb,
|
||||
cpu);
|
||||
}
|
||||
}
|
||||
#endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* PMU emulation helpers for TCG IBM POWER chips
|
||||
*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* Authors:
|
||||
* Daniel Henrique Barboza <danielhb413@gmail.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef POWER8_PMU
|
||||
#define POWER8_PMU
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
|
||||
void cpu_ppc_pmu_init(CPUPPCState *env);
|
||||
bool pmu_insn_cnt_enabled(CPUPPCState *env);
|
||||
|
||||
#endif
|
|
@ -25,6 +25,10 @@
|
|||
void spr_noaccess(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_read_generic(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_write_generic(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_write_MMCR0(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_write_MMCR1(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_write_PMC(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_write_CTRL(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_read_xer(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_write_xer(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_read_lr(DisasContext *ctx, int gprn, int sprn);
|
||||
|
@ -34,6 +38,7 @@ void spr_write_ctr(DisasContext *ctx, int sprn, int gprn);
|
|||
void spr_read_ureg(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_read_MMCR2_ureg(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_read_PMC(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_read_PMC14_ureg(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_read_tbl(DisasContext *ctx, int gprn, int sprn);
|
||||
|
|
|
@ -177,6 +177,7 @@ struct DisasContext {
|
|||
bool hr;
|
||||
bool mmcr0_pmcc0;
|
||||
bool mmcr0_pmcc1;
|
||||
bool pmu_insn_cnt;
|
||||
ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
|
||||
int singlestep_enabled;
|
||||
uint32_t flags;
|
||||
|
@ -402,6 +403,18 @@ void spr_write_generic(DisasContext *ctx, int sprn, int gprn)
|
|||
spr_store_dump_spr(sprn);
|
||||
}
|
||||
|
||||
void spr_write_CTRL(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
spr_write_generic(ctx, sprn, gprn);
|
||||
|
||||
/*
|
||||
* SPR_CTRL writes must force a new translation block,
|
||||
* allowing the PMU to calculate the run latch events with
|
||||
* more accuracy.
|
||||
*/
|
||||
ctx->base.is_jmp = DISAS_EXIT_UPDATE;
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
void spr_write_generic32(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
|
@ -4170,6 +4183,49 @@ static inline void gen_update_cfar(DisasContext *ctx, target_ulong nip)
|
|||
#endif
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
static void pmu_count_insns(DisasContext *ctx)
|
||||
{
|
||||
/*
|
||||
* Do not bother calling the helper if the PMU isn't counting
|
||||
* instructions.
|
||||
*/
|
||||
if (!ctx->pmu_insn_cnt) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
/*
|
||||
* The PMU insns_inc() helper stops the internal PMU timer if a
|
||||
* counter overflows happens. In that case, if the guest is
|
||||
* running with icount and we do not handle it beforehand,
|
||||
* the helper can trigger a 'bad icount read'.
|
||||
*/
|
||||
gen_icount_io_start(ctx);
|
||||
|
||||
gen_helper_insns_inc(cpu_env, tcg_constant_i32(ctx->base.num_insns));
|
||||
#else
|
||||
/*
|
||||
* User mode can read (but not write) PMC5 and start/stop
|
||||
* the PMU via MMCR0_FC. In this case just increment
|
||||
* PMC5 with base.num_insns.
|
||||
*/
|
||||
TCGv t0 = tcg_temp_new();
|
||||
|
||||
gen_load_spr(t0, SPR_POWER_PMC5);
|
||||
tcg_gen_addi_tl(t0, t0, ctx->base.num_insns);
|
||||
gen_store_spr(SPR_POWER_PMC5, t0);
|
||||
|
||||
tcg_temp_free(t0);
|
||||
#endif /* #if !defined(CONFIG_USER_ONLY) */
|
||||
}
|
||||
#else
|
||||
static void pmu_count_insns(DisasContext *ctx)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif /* #if defined(TARGET_PPC64) */
|
||||
|
||||
static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest)
|
||||
{
|
||||
return translator_use_goto_tb(&ctx->base, dest);
|
||||
|
@ -4180,6 +4236,14 @@ static void gen_lookup_and_goto_ptr(DisasContext *ctx)
|
|||
if (unlikely(ctx->singlestep_enabled)) {
|
||||
gen_debug_exception(ctx);
|
||||
} else {
|
||||
/*
|
||||
* tcg_gen_lookup_and_goto_ptr will exit the TB if
|
||||
* CF_NO_GOTO_PTR is set. Count insns now.
|
||||
*/
|
||||
if (ctx->base.tb->flags & CF_NO_GOTO_PTR) {
|
||||
pmu_count_insns(ctx);
|
||||
}
|
||||
|
||||
tcg_gen_lookup_and_goto_ptr();
|
||||
}
|
||||
}
|
||||
|
@ -4191,6 +4255,7 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
|
|||
dest = (uint32_t) dest;
|
||||
}
|
||||
if (use_goto_tb(ctx, dest)) {
|
||||
pmu_count_insns(ctx);
|
||||
tcg_gen_goto_tb(n);
|
||||
tcg_gen_movi_tl(cpu_nip, dest & ~3);
|
||||
tcg_gen_exit_tb(ctx->base.tb, n);
|
||||
|
@ -6252,30 +6317,6 @@ static void gen_tlbli_6xx(DisasContext *ctx)
|
|||
#endif /* defined(CONFIG_USER_ONLY) */
|
||||
}
|
||||
|
||||
/* 74xx TLB management */
|
||||
|
||||
/* tlbld */
|
||||
static void gen_tlbld_74xx(DisasContext *ctx)
|
||||
{
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
GEN_PRIV;
|
||||
#else
|
||||
CHK_SV;
|
||||
gen_helper_74xx_tlbd(cpu_env, cpu_gpr[rB(ctx->opcode)]);
|
||||
#endif /* defined(CONFIG_USER_ONLY) */
|
||||
}
|
||||
|
||||
/* tlbli */
|
||||
static void gen_tlbli_74xx(DisasContext *ctx)
|
||||
{
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
GEN_PRIV;
|
||||
#else
|
||||
CHK_SV;
|
||||
gen_helper_74xx_tlbi(cpu_env, cpu_gpr[rB(ctx->opcode)]);
|
||||
#endif /* defined(CONFIG_USER_ONLY) */
|
||||
}
|
||||
|
||||
/* POWER instructions not in PowerPC 601 */
|
||||
|
||||
/* clf */
|
||||
|
@ -7420,6 +7461,8 @@ static bool resolve_PLS_D(DisasContext *ctx, arg_D *d, arg_PLS_D *a)
|
|||
|
||||
#include "translate/spe-impl.c.inc"
|
||||
|
||||
#include "translate/branch-impl.c.inc"
|
||||
|
||||
/* Handles lfdp, lxsd, lxssp */
|
||||
static void gen_dform39(DisasContext *ctx)
|
||||
{
|
||||
|
@ -7735,8 +7778,6 @@ GEN_HANDLER(esa, 0x1F, 0x14, 0x12, 0x03FFF801, PPC_602_SPEC),
|
|||
GEN_HANDLER(mfrom, 0x1F, 0x09, 0x08, 0x03E0F801, PPC_602_SPEC),
|
||||
GEN_HANDLER2(tlbld_6xx, "tlbld", 0x1F, 0x12, 0x1E, 0x03FF0001, PPC_6xx_TLB),
|
||||
GEN_HANDLER2(tlbli_6xx, "tlbli", 0x1F, 0x12, 0x1F, 0x03FF0001, PPC_6xx_TLB),
|
||||
GEN_HANDLER2(tlbld_74xx, "tlbld", 0x1F, 0x12, 0x1E, 0x03FF0001, PPC_74xx_TLB),
|
||||
GEN_HANDLER2(tlbli_74xx, "tlbli", 0x1F, 0x12, 0x1F, 0x03FF0001, PPC_74xx_TLB),
|
||||
GEN_HANDLER(clf, 0x1F, 0x16, 0x03, 0x03E00000, PPC_POWER),
|
||||
GEN_HANDLER(cli, 0x1F, 0x16, 0x0F, 0x03E00000, PPC_POWER),
|
||||
GEN_HANDLER(dclst, 0x1F, 0x16, 0x13, 0x03E00000, PPC_POWER),
|
||||
|
@ -8458,6 +8499,7 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
|
|||
ctx->hr = (hflags >> HFLAGS_HR) & 1;
|
||||
ctx->mmcr0_pmcc0 = (hflags >> HFLAGS_PMCC0) & 1;
|
||||
ctx->mmcr0_pmcc1 = (hflags >> HFLAGS_PMCC1) & 1;
|
||||
ctx->pmu_insn_cnt = (hflags >> HFLAGS_INSN_CNT) & 1;
|
||||
|
||||
ctx->singlestep_enabled = 0;
|
||||
if ((hflags >> HFLAGS_SE) & 1) {
|
||||
|
@ -8564,6 +8606,7 @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
|
|||
switch (is_jmp) {
|
||||
case DISAS_TOO_MANY:
|
||||
if (use_goto_tb(ctx, nip)) {
|
||||
pmu_count_insns(ctx);
|
||||
tcg_gen_goto_tb(0);
|
||||
gen_update_nip(ctx, nip);
|
||||
tcg_gen_exit_tb(ctx->base.tb, 0);
|
||||
|
@ -8574,6 +8617,14 @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
|
|||
gen_update_nip(ctx, nip);
|
||||
/* fall through */
|
||||
case DISAS_CHAIN:
|
||||
/*
|
||||
* tcg_gen_lookup_and_goto_ptr will exit the TB if
|
||||
* CF_NO_GOTO_PTR is set. Count insns now.
|
||||
*/
|
||||
if (ctx->base.tb->flags & CF_NO_GOTO_PTR) {
|
||||
pmu_count_insns(ctx);
|
||||
}
|
||||
|
||||
tcg_gen_lookup_and_goto_ptr();
|
||||
break;
|
||||
|
||||
|
@ -8581,6 +8632,7 @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
|
|||
gen_update_nip(ctx, nip);
|
||||
/* fall through */
|
||||
case DISAS_EXIT:
|
||||
pmu_count_insns(ctx);
|
||||
tcg_gen_exit_tb(NULL, 0);
|
||||
break;
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Power ISA decode for branch instructions
|
||||
*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* Authors:
|
||||
* Daniel Henrique Barboza <danielhb413@gmail.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
|
||||
|
||||
static bool trans_RFEBB(DisasContext *ctx, arg_XL_s *arg)
|
||||
{
|
||||
REQUIRE_INSNS_FLAGS2(ctx, ISA207S);
|
||||
|
||||
gen_icount_io_start(ctx);
|
||||
gen_update_cfar(ctx, ctx->cia);
|
||||
gen_helper_rfebb(cpu_env, cpu_gpr[arg->s]);
|
||||
|
||||
ctx->base.is_jmp = DISAS_CHAIN;
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
static bool trans_RFEBB(DisasContext *ctx, arg_XL_s *arg)
|
||||
{
|
||||
gen_invalid(ctx);
|
||||
return true;
|
||||
}
|
||||
#endif
|
|
@ -31,7 +31,7 @@ static void gen_set_cr1_from_fpscr(DisasContext *ctx)
|
|||
#endif
|
||||
|
||||
/*** Floating-Point arithmetic ***/
|
||||
#define _GEN_FLOAT_ACB(name, op, op1, op2, isfloat, set_fprf, type) \
|
||||
#define _GEN_FLOAT_ACB(name, op1, op2, set_fprf, type) \
|
||||
static void gen_f##name(DisasContext *ctx) \
|
||||
{ \
|
||||
TCGv_i64 t0; \
|
||||
|
@ -50,10 +50,7 @@ static void gen_f##name(DisasContext *ctx) \
|
|||
get_fpr(t0, rA(ctx->opcode)); \
|
||||
get_fpr(t1, rC(ctx->opcode)); \
|
||||
get_fpr(t2, rB(ctx->opcode)); \
|
||||
gen_helper_f##op(t3, cpu_env, t0, t1, t2); \
|
||||
if (isfloat) { \
|
||||
gen_helper_frsp(t3, cpu_env, t3); \
|
||||
} \
|
||||
gen_helper_f##name(t3, cpu_env, t0, t1, t2); \
|
||||
set_fpr(rD(ctx->opcode), t3); \
|
||||
if (set_fprf) { \
|
||||
gen_compute_fprf_float64(t3); \
|
||||
|
@ -68,10 +65,10 @@ static void gen_f##name(DisasContext *ctx) \
|
|||
}
|
||||
|
||||
#define GEN_FLOAT_ACB(name, op2, set_fprf, type) \
|
||||
_GEN_FLOAT_ACB(name, name, 0x3F, op2, 0, set_fprf, type); \
|
||||
_GEN_FLOAT_ACB(name##s, name, 0x3B, op2, 1, set_fprf, type);
|
||||
_GEN_FLOAT_ACB(name, 0x3F, op2, set_fprf, type); \
|
||||
_GEN_FLOAT_ACB(name##s, 0x3B, op2, set_fprf, type);
|
||||
|
||||
#define _GEN_FLOAT_AB(name, op, op1, op2, inval, isfloat, set_fprf, type) \
|
||||
#define _GEN_FLOAT_AB(name, op1, op2, inval, set_fprf, type) \
|
||||
static void gen_f##name(DisasContext *ctx) \
|
||||
{ \
|
||||
TCGv_i64 t0; \
|
||||
|
@ -87,10 +84,7 @@ static void gen_f##name(DisasContext *ctx) \
|
|||
gen_reset_fpstatus(); \
|
||||
get_fpr(t0, rA(ctx->opcode)); \
|
||||
get_fpr(t1, rB(ctx->opcode)); \
|
||||
gen_helper_f##op(t2, cpu_env, t0, t1); \
|
||||
if (isfloat) { \
|
||||
gen_helper_frsp(t2, cpu_env, t2); \
|
||||
} \
|
||||
gen_helper_f##name(t2, cpu_env, t0, t1); \
|
||||
set_fpr(rD(ctx->opcode), t2); \
|
||||
if (set_fprf) { \
|
||||
gen_compute_fprf_float64(t2); \
|
||||
|
@ -103,10 +97,10 @@ static void gen_f##name(DisasContext *ctx) \
|
|||
tcg_temp_free_i64(t2); \
|
||||
}
|
||||
#define GEN_FLOAT_AB(name, op2, inval, set_fprf, type) \
|
||||
_GEN_FLOAT_AB(name, name, 0x3F, op2, inval, 0, set_fprf, type); \
|
||||
_GEN_FLOAT_AB(name##s, name, 0x3B, op2, inval, 1, set_fprf, type);
|
||||
_GEN_FLOAT_AB(name, 0x3F, op2, inval, set_fprf, type); \
|
||||
_GEN_FLOAT_AB(name##s, 0x3B, op2, inval, set_fprf, type);
|
||||
|
||||
#define _GEN_FLOAT_AC(name, op, op1, op2, inval, isfloat, set_fprf, type) \
|
||||
#define _GEN_FLOAT_AC(name, op1, op2, inval, set_fprf, type) \
|
||||
static void gen_f##name(DisasContext *ctx) \
|
||||
{ \
|
||||
TCGv_i64 t0; \
|
||||
|
@ -122,10 +116,7 @@ static void gen_f##name(DisasContext *ctx) \
|
|||
gen_reset_fpstatus(); \
|
||||
get_fpr(t0, rA(ctx->opcode)); \
|
||||
get_fpr(t1, rC(ctx->opcode)); \
|
||||
gen_helper_f##op(t2, cpu_env, t0, t1); \
|
||||
if (isfloat) { \
|
||||
gen_helper_frsp(t2, cpu_env, t2); \
|
||||
} \
|
||||
gen_helper_f##name(t2, cpu_env, t0, t1); \
|
||||
set_fpr(rD(ctx->opcode), t2); \
|
||||
if (set_fprf) { \
|
||||
gen_compute_fprf_float64(t2); \
|
||||
|
@ -138,8 +129,8 @@ static void gen_f##name(DisasContext *ctx) \
|
|||
tcg_temp_free_i64(t2); \
|
||||
}
|
||||
#define GEN_FLOAT_AC(name, op2, inval, set_fprf, type) \
|
||||
_GEN_FLOAT_AC(name, name, 0x3F, op2, inval, 0, set_fprf, type); \
|
||||
_GEN_FLOAT_AC(name##s, name, 0x3B, op2, inval, 1, set_fprf, type);
|
||||
_GEN_FLOAT_AC(name, 0x3F, op2, inval, set_fprf, type); \
|
||||
_GEN_FLOAT_AC(name##s, 0x3B, op2, inval, set_fprf, type);
|
||||
|
||||
#define GEN_FLOAT_B(name, op2, op3, set_fprf, type) \
|
||||
static void gen_f##name(DisasContext *ctx) \
|
||||
|
@ -157,8 +148,9 @@ static void gen_f##name(DisasContext *ctx) \
|
|||
gen_helper_f##name(t1, cpu_env, t0); \
|
||||
set_fpr(rD(ctx->opcode), t1); \
|
||||
if (set_fprf) { \
|
||||
gen_compute_fprf_float64(t1); \
|
||||
gen_helper_compute_fprf_float64(cpu_env, t1); \
|
||||
} \
|
||||
gen_helper_float_check_status(cpu_env); \
|
||||
if (unlikely(Rc(ctx->opcode) != 0)) { \
|
||||
gen_set_cr1_from_fpscr(ctx); \
|
||||
} \
|
||||
|
@ -220,8 +212,7 @@ static void gen_frsqrtes(DisasContext *ctx)
|
|||
t1 = tcg_temp_new_i64();
|
||||
gen_reset_fpstatus();
|
||||
get_fpr(t0, rB(ctx->opcode));
|
||||
gen_helper_frsqrte(t1, cpu_env, t0);
|
||||
gen_helper_frsp(t1, cpu_env, t1);
|
||||
gen_helper_frsqrtes(t1, cpu_env, t0);
|
||||
set_fpr(rD(ctx->opcode), t1);
|
||||
gen_compute_fprf_float64(t1);
|
||||
if (unlikely(Rc(ctx->opcode) != 0)) {
|
||||
|
@ -232,7 +223,7 @@ static void gen_frsqrtes(DisasContext *ctx)
|
|||
}
|
||||
|
||||
/* fsel */
|
||||
_GEN_FLOAT_ACB(sel, sel, 0x3F, 0x17, 0, 0, PPC_FLOAT_FSEL);
|
||||
_GEN_FLOAT_ACB(sel, 0x3F, 0x17, 0, PPC_FLOAT_FSEL);
|
||||
/* fsub - fsubs */
|
||||
GEN_FLOAT_AB(sub, 0x14, 0x000007C0, 1, PPC_FLOAT);
|
||||
/* Optional: */
|
||||
|
@ -272,8 +263,7 @@ static void gen_fsqrts(DisasContext *ctx)
|
|||
t1 = tcg_temp_new_i64();
|
||||
gen_reset_fpstatus();
|
||||
get_fpr(t0, rB(ctx->opcode));
|
||||
gen_helper_fsqrt(t1, cpu_env, t0);
|
||||
gen_helper_frsp(t1, cpu_env, t1);
|
||||
gen_helper_fsqrts(t1, cpu_env, t0);
|
||||
set_fpr(rD(ctx->opcode), t1);
|
||||
gen_compute_fprf_float64(t1);
|
||||
if (unlikely(Rc(ctx->opcode) != 0)) {
|
||||
|
@ -769,7 +759,6 @@ static void gen_mtfsb1(DisasContext *ctx)
|
|||
return;
|
||||
}
|
||||
crb = 31 - crbD(ctx->opcode);
|
||||
gen_reset_fpstatus();
|
||||
/* XXX: we pretend we can only do IEEE floating-point computations */
|
||||
if (likely(crb != FPSCR_FEX && crb != FPSCR_VX && crb != FPSCR_NI)) {
|
||||
TCGv_i32 t0;
|
||||
|
@ -782,7 +771,7 @@ static void gen_mtfsb1(DisasContext *ctx)
|
|||
tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX);
|
||||
}
|
||||
/* We can raise a deferred exception */
|
||||
gen_helper_float_check_status(cpu_env);
|
||||
gen_helper_fpscr_check_status(cpu_env);
|
||||
}
|
||||
|
||||
/* mtfsf */
|
||||
|
@ -803,7 +792,6 @@ static void gen_mtfsf(DisasContext *ctx)
|
|||
gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
|
||||
return;
|
||||
}
|
||||
gen_reset_fpstatus();
|
||||
if (l) {
|
||||
t0 = tcg_const_i32((ctx->insns_flags2 & PPC2_ISA205) ? 0xffff : 0xff);
|
||||
} else {
|
||||
|
@ -818,7 +806,7 @@ static void gen_mtfsf(DisasContext *ctx)
|
|||
tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX);
|
||||
}
|
||||
/* We can raise a deferred exception */
|
||||
gen_helper_float_check_status(cpu_env);
|
||||
gen_helper_fpscr_check_status(cpu_env);
|
||||
tcg_temp_free_i64(t1);
|
||||
}
|
||||
|
||||
|
@ -840,7 +828,6 @@ static void gen_mtfsfi(DisasContext *ctx)
|
|||
return;
|
||||
}
|
||||
sh = (8 * w) + 7 - bf;
|
||||
gen_reset_fpstatus();
|
||||
t0 = tcg_const_i64(((uint64_t)FPIMM(ctx->opcode)) << (4 * sh));
|
||||
t1 = tcg_const_i32(1 << sh);
|
||||
gen_helper_store_fpscr(cpu_env, t0, t1);
|
||||
|
@ -851,7 +838,7 @@ static void gen_mtfsfi(DisasContext *ctx)
|
|||
tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX);
|
||||
}
|
||||
/* We can raise a deferred exception */
|
||||
gen_helper_float_check_status(cpu_env);
|
||||
gen_helper_fpscr_check_status(cpu_env);
|
||||
}
|
||||
|
||||
static void gen_qemu_ld32fs(DisasContext *ctx, TCGv_i64 dest, TCGv addr)
|
||||
|
|
|
@ -1491,6 +1491,237 @@ static bool trans_VSRDBI(DisasContext *ctx, arg_VN *a)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool do_vexpand(DisasContext *ctx, arg_VX_tb *a, unsigned vece)
|
||||
{
|
||||
REQUIRE_INSNS_FLAGS2(ctx, ISA310);
|
||||
REQUIRE_VECTOR(ctx);
|
||||
|
||||
tcg_gen_gvec_sari(vece, avr_full_offset(a->vrt), avr_full_offset(a->vrb),
|
||||
(8 << vece) - 1, 16, 16);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(VEXPANDBM, do_vexpand, MO_8)
|
||||
TRANS(VEXPANDHM, do_vexpand, MO_16)
|
||||
TRANS(VEXPANDWM, do_vexpand, MO_32)
|
||||
TRANS(VEXPANDDM, do_vexpand, MO_64)
|
||||
|
||||
static bool trans_VEXPANDQM(DisasContext *ctx, arg_VX_tb *a)
|
||||
{
|
||||
TCGv_i64 tmp;
|
||||
|
||||
REQUIRE_INSNS_FLAGS2(ctx, ISA310);
|
||||
REQUIRE_VECTOR(ctx);
|
||||
|
||||
tmp = tcg_temp_new_i64();
|
||||
|
||||
get_avr64(tmp, a->vrb, true);
|
||||
tcg_gen_sari_i64(tmp, tmp, 63);
|
||||
set_avr64(a->vrt, tmp, false);
|
||||
set_avr64(a->vrt, tmp, true);
|
||||
|
||||
tcg_temp_free_i64(tmp);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool do_vextractm(DisasContext *ctx, arg_VX_tb *a, unsigned vece)
|
||||
{
|
||||
const uint64_t elem_width = 8 << vece, elem_count_half = 8 >> vece,
|
||||
mask = dup_const(vece, 1 << (elem_width - 1));
|
||||
uint64_t i, j;
|
||||
TCGv_i64 lo, hi, t0, t1;
|
||||
|
||||
REQUIRE_INSNS_FLAGS2(ctx, ISA310);
|
||||
REQUIRE_VECTOR(ctx);
|
||||
|
||||
hi = tcg_temp_new_i64();
|
||||
lo = tcg_temp_new_i64();
|
||||
t0 = tcg_temp_new_i64();
|
||||
t1 = tcg_temp_new_i64();
|
||||
|
||||
get_avr64(lo, a->vrb, false);
|
||||
get_avr64(hi, a->vrb, true);
|
||||
|
||||
tcg_gen_andi_i64(lo, lo, mask);
|
||||
tcg_gen_andi_i64(hi, hi, mask);
|
||||
|
||||
/*
|
||||
* Gather the most significant bit of each element in the highest element
|
||||
* element. E.g. for bytes:
|
||||
* aXXXXXXXbXXXXXXXcXXXXXXXdXXXXXXXeXXXXXXXfXXXXXXXgXXXXXXXhXXXXXXX
|
||||
* & dup(1 << (elem_width - 1))
|
||||
* a0000000b0000000c0000000d0000000e0000000f0000000g0000000h0000000
|
||||
* << 32 - 4
|
||||
* 0000e0000000f0000000g0000000h00000000000000000000000000000000000
|
||||
* |
|
||||
* a000e000b000f000c000g000d000h000e0000000f0000000g0000000h0000000
|
||||
* << 16 - 2
|
||||
* 00c000g000d000h000e0000000f0000000g0000000h000000000000000000000
|
||||
* |
|
||||
* a0c0e0g0b0d0f0h0c0e0g000d0f0h000e0g00000f0h00000g0000000h0000000
|
||||
* << 8 - 1
|
||||
* 0b0d0f0h0c0e0g000d0f0h000e0g00000f0h00000g0000000h00000000000000
|
||||
* |
|
||||
* abcdefghbcdefgh0cdefgh00defgh000efgh0000fgh00000gh000000h0000000
|
||||
*/
|
||||
for (i = elem_count_half / 2, j = 32; i > 0; i >>= 1, j >>= 1) {
|
||||
tcg_gen_shli_i64(t0, hi, j - i);
|
||||
tcg_gen_shli_i64(t1, lo, j - i);
|
||||
tcg_gen_or_i64(hi, hi, t0);
|
||||
tcg_gen_or_i64(lo, lo, t1);
|
||||
}
|
||||
|
||||
tcg_gen_shri_i64(hi, hi, 64 - elem_count_half);
|
||||
tcg_gen_extract2_i64(lo, lo, hi, 64 - elem_count_half);
|
||||
tcg_gen_trunc_i64_tl(cpu_gpr[a->vrt], lo);
|
||||
|
||||
tcg_temp_free_i64(hi);
|
||||
tcg_temp_free_i64(lo);
|
||||
tcg_temp_free_i64(t0);
|
||||
tcg_temp_free_i64(t1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(VEXTRACTBM, do_vextractm, MO_8)
|
||||
TRANS(VEXTRACTHM, do_vextractm, MO_16)
|
||||
TRANS(VEXTRACTWM, do_vextractm, MO_32)
|
||||
TRANS(VEXTRACTDM, do_vextractm, MO_64)
|
||||
|
||||
static bool trans_VEXTRACTQM(DisasContext *ctx, arg_VX_tb *a)
|
||||
{
|
||||
TCGv_i64 tmp;
|
||||
|
||||
REQUIRE_INSNS_FLAGS2(ctx, ISA310);
|
||||
REQUIRE_VECTOR(ctx);
|
||||
|
||||
tmp = tcg_temp_new_i64();
|
||||
|
||||
get_avr64(tmp, a->vrb, true);
|
||||
tcg_gen_shri_i64(tmp, tmp, 63);
|
||||
tcg_gen_trunc_i64_tl(cpu_gpr[a->vrt], tmp);
|
||||
|
||||
tcg_temp_free_i64(tmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool do_mtvsrm(DisasContext *ctx, arg_VX_tb *a, unsigned vece)
|
||||
{
|
||||
const uint64_t elem_width = 8 << vece, elem_count_half = 8 >> vece;
|
||||
uint64_t c;
|
||||
int i, j;
|
||||
TCGv_i64 hi, lo, t0, t1;
|
||||
|
||||
REQUIRE_INSNS_FLAGS2(ctx, ISA310);
|
||||
REQUIRE_VECTOR(ctx);
|
||||
|
||||
hi = tcg_temp_new_i64();
|
||||
lo = tcg_temp_new_i64();
|
||||
t0 = tcg_temp_new_i64();
|
||||
t1 = tcg_temp_new_i64();
|
||||
|
||||
tcg_gen_extu_tl_i64(t0, cpu_gpr[a->vrb]);
|
||||
tcg_gen_extract_i64(hi, t0, elem_count_half, elem_count_half);
|
||||
tcg_gen_extract_i64(lo, t0, 0, elem_count_half);
|
||||
|
||||
/*
|
||||
* Spread the bits into their respective elements.
|
||||
* E.g. for bytes:
|
||||
* 00000000000000000000000000000000000000000000000000000000abcdefgh
|
||||
* << 32 - 4
|
||||
* 0000000000000000000000000000abcdefgh0000000000000000000000000000
|
||||
* |
|
||||
* 0000000000000000000000000000abcdefgh00000000000000000000abcdefgh
|
||||
* << 16 - 2
|
||||
* 00000000000000abcdefgh00000000000000000000abcdefgh00000000000000
|
||||
* |
|
||||
* 00000000000000abcdefgh000000abcdefgh000000abcdefgh000000abcdefgh
|
||||
* << 8 - 1
|
||||
* 0000000abcdefgh000000abcdefgh000000abcdefgh000000abcdefgh0000000
|
||||
* |
|
||||
* 0000000abcdefgXbcdefgXbcdefgXbcdefgXbcdefgXbcdefgXbcdefgXbcdefgh
|
||||
* & dup(1)
|
||||
* 0000000a0000000b0000000c0000000d0000000e0000000f0000000g0000000h
|
||||
* * 0xff
|
||||
* aaaaaaaabbbbbbbbccccccccddddddddeeeeeeeeffffffffgggggggghhhhhhhh
|
||||
*/
|
||||
for (i = elem_count_half / 2, j = 32; i > 0; i >>= 1, j >>= 1) {
|
||||
tcg_gen_shli_i64(t0, hi, j - i);
|
||||
tcg_gen_shli_i64(t1, lo, j - i);
|
||||
tcg_gen_or_i64(hi, hi, t0);
|
||||
tcg_gen_or_i64(lo, lo, t1);
|
||||
}
|
||||
|
||||
c = dup_const(vece, 1);
|
||||
tcg_gen_andi_i64(hi, hi, c);
|
||||
tcg_gen_andi_i64(lo, lo, c);
|
||||
|
||||
c = MAKE_64BIT_MASK(0, elem_width);
|
||||
tcg_gen_muli_i64(hi, hi, c);
|
||||
tcg_gen_muli_i64(lo, lo, c);
|
||||
|
||||
set_avr64(a->vrt, lo, false);
|
||||
set_avr64(a->vrt, hi, true);
|
||||
|
||||
tcg_temp_free_i64(hi);
|
||||
tcg_temp_free_i64(lo);
|
||||
tcg_temp_free_i64(t0);
|
||||
tcg_temp_free_i64(t1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(MTVSRBM, do_mtvsrm, MO_8)
|
||||
TRANS(MTVSRHM, do_mtvsrm, MO_16)
|
||||
TRANS(MTVSRWM, do_mtvsrm, MO_32)
|
||||
TRANS(MTVSRDM, do_mtvsrm, MO_64)
|
||||
|
||||
static bool trans_MTVSRQM(DisasContext *ctx, arg_VX_tb *a)
|
||||
{
|
||||
TCGv_i64 tmp;
|
||||
|
||||
REQUIRE_INSNS_FLAGS2(ctx, ISA310);
|
||||
REQUIRE_VECTOR(ctx);
|
||||
|
||||
tmp = tcg_temp_new_i64();
|
||||
|
||||
tcg_gen_ext_tl_i64(tmp, cpu_gpr[a->vrb]);
|
||||
tcg_gen_sextract_i64(tmp, tmp, 0, 1);
|
||||
set_avr64(a->vrt, tmp, false);
|
||||
set_avr64(a->vrt, tmp, true);
|
||||
|
||||
tcg_temp_free_i64(tmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_MTVSRBMI(DisasContext *ctx, arg_DX_b *a)
|
||||
{
|
||||
const uint64_t mask = dup_const(MO_8, 1);
|
||||
uint64_t hi, lo;
|
||||
|
||||
REQUIRE_INSNS_FLAGS2(ctx, ISA310);
|
||||
REQUIRE_VECTOR(ctx);
|
||||
|
||||
hi = extract16(a->b, 8, 8);
|
||||
lo = extract16(a->b, 0, 8);
|
||||
|
||||
for (int i = 4, j = 32; i > 0; i >>= 1, j >>= 1) {
|
||||
hi |= hi << (j - i);
|
||||
lo |= lo << (j - i);
|
||||
}
|
||||
|
||||
hi = (hi & mask) * 0xFF;
|
||||
lo = (lo & mask) * 0xFF;
|
||||
|
||||
set_avr64(a->vrt, tcg_constant_i64(hi), true);
|
||||
set_avr64(a->vrt, tcg_constant_i64(lo), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define GEN_VAFORM_PAIRED(name0, name1, opc2) \
|
||||
static void glue(gen_, name0##_##name1)(DisasContext *ctx) \
|
||||
{ \
|
||||
|
|
|
@ -904,21 +904,24 @@ VSX_CMP(xvcmpgesp, 0x0C, 0x0A, 0, PPC2_VSX)
|
|||
VSX_CMP(xvcmpgtsp, 0x0C, 0x09, 0, PPC2_VSX)
|
||||
VSX_CMP(xvcmpnesp, 0x0C, 0x0B, 0, PPC2_VSX)
|
||||
|
||||
static void gen_xscvqpdp(DisasContext *ctx)
|
||||
static bool trans_XSCVQPDP(DisasContext *ctx, arg_X_tb_rc *a)
|
||||
{
|
||||
TCGv_i32 opc;
|
||||
TCGv_i32 ro;
|
||||
TCGv_ptr xt, xb;
|
||||
if (unlikely(!ctx->vsx_enabled)) {
|
||||
gen_exception(ctx, POWERPC_EXCP_VSXU);
|
||||
return;
|
||||
}
|
||||
opc = tcg_const_i32(ctx->opcode);
|
||||
xt = gen_vsr_ptr(xT(ctx->opcode));
|
||||
xb = gen_vsr_ptr(xB(ctx->opcode));
|
||||
gen_helper_xscvqpdp(cpu_env, opc, xt, xb);
|
||||
tcg_temp_free_i32(opc);
|
||||
|
||||
REQUIRE_INSNS_FLAGS2(ctx, ISA300);
|
||||
REQUIRE_VSX(ctx);
|
||||
|
||||
ro = tcg_const_i32(a->rc);
|
||||
|
||||
xt = gen_avr_ptr(a->rt);
|
||||
xb = gen_avr_ptr(a->rb);
|
||||
gen_helper_XSCVQPDP(cpu_env, ro, xt, xb);
|
||||
tcg_temp_free_i32(ro);
|
||||
tcg_temp_free_ptr(xt);
|
||||
tcg_temp_free_ptr(xb);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define GEN_VSX_HELPER_2(name, op1, op2, inval, type) \
|
||||
|
@ -1098,10 +1101,6 @@ GEN_VSX_HELPER_R2_AB(xscmpoqp, 0x04, 0x04, 0, PPC2_VSX)
|
|||
GEN_VSX_HELPER_R2_AB(xscmpuqp, 0x04, 0x14, 0, PPC2_VSX)
|
||||
GEN_VSX_HELPER_X3(xsmaxdp, 0x00, 0x14, 0, PPC2_VSX)
|
||||
GEN_VSX_HELPER_X3(xsmindp, 0x00, 0x15, 0, PPC2_VSX)
|
||||
GEN_VSX_HELPER_R3(xsmaxcdp, 0x00, 0x10, 0, PPC2_ISA300)
|
||||
GEN_VSX_HELPER_R3(xsmincdp, 0x00, 0x11, 0, PPC2_ISA300)
|
||||
GEN_VSX_HELPER_R3(xsmaxjdp, 0x00, 0x12, 0, PPC2_ISA300)
|
||||
GEN_VSX_HELPER_R3(xsminjdp, 0x00, 0x12, 0, PPC2_ISA300)
|
||||
GEN_VSX_HELPER_X2(xscvdphp, 0x16, 0x15, 0x11, PPC2_ISA300)
|
||||
GEN_VSX_HELPER_X2(xscvdpsp, 0x12, 0x10, 0, PPC2_VSX)
|
||||
GEN_VSX_HELPER_R2(xscvdpqp, 0x04, 0x1A, 0x16, PPC2_ISA300)
|
||||
|
@ -2185,6 +2184,32 @@ TRANS(XXBLENDVH, do_xxblendv, MO_16)
|
|||
TRANS(XXBLENDVW, do_xxblendv, MO_32)
|
||||
TRANS(XXBLENDVD, do_xxblendv, MO_64)
|
||||
|
||||
static bool do_xsmaxmincjdp(DisasContext *ctx, arg_XX3 *a,
|
||||
void (*helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr))
|
||||
{
|
||||
TCGv_ptr xt, xa, xb;
|
||||
|
||||
REQUIRE_INSNS_FLAGS2(ctx, ISA300);
|
||||
REQUIRE_VSX(ctx);
|
||||
|
||||
xt = gen_vsr_ptr(a->xt);
|
||||
xa = gen_vsr_ptr(a->xa);
|
||||
xb = gen_vsr_ptr(a->xb);
|
||||
|
||||
helper(cpu_env, xt, xa, xb);
|
||||
|
||||
tcg_temp_free_ptr(xt);
|
||||
tcg_temp_free_ptr(xa);
|
||||
tcg_temp_free_ptr(xb);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(XSMAXCDP, do_xsmaxmincjdp, gen_helper_xsmaxcdp)
|
||||
TRANS(XSMINCDP, do_xsmaxmincjdp, gen_helper_xsmincdp)
|
||||
TRANS(XSMAXJDP, do_xsmaxmincjdp, gen_helper_xsmaxjdp)
|
||||
TRANS(XSMINJDP, do_xsmaxmincjdp, gen_helper_xsminjdp)
|
||||
|
||||
#undef GEN_XX2FORM
|
||||
#undef GEN_XX3FORM
|
||||
#undef GEN_XX2IFORM
|
||||
|
|
|
@ -133,7 +133,6 @@ GEN_VSX_XFORM_300_EO(xsnabsqp, 0x04, 0x19, 0x08, 0x00000001),
|
|||
GEN_VSX_XFORM_300_EO(xsnegqp, 0x04, 0x19, 0x10, 0x00000001),
|
||||
GEN_VSX_XFORM_300(xscpsgnqp, 0x04, 0x03, 0x00000001),
|
||||
GEN_VSX_XFORM_300_EO(xscvdpqp, 0x04, 0x1A, 0x16, 0x00000001),
|
||||
GEN_VSX_XFORM_300_EO(xscvqpdp, 0x04, 0x1A, 0x14, 0x0),
|
||||
GEN_VSX_XFORM_300_EO(xscvqpsdz, 0x04, 0x1A, 0x19, 0x00000001),
|
||||
GEN_VSX_XFORM_300_EO(xscvqpswz, 0x04, 0x1A, 0x09, 0x00000001),
|
||||
GEN_VSX_XFORM_300_EO(xscvqpudz, 0x04, 0x1A, 0x11, 0x00000001),
|
||||
|
@ -207,10 +206,6 @@ GEN_VSX_XFORM_300(xscmpoqp, 0x04, 0x04, 0x00600001),
|
|||
GEN_VSX_XFORM_300(xscmpuqp, 0x04, 0x14, 0x00600001),
|
||||
GEN_XX3FORM(xsmaxdp, 0x00, 0x14, PPC2_VSX),
|
||||
GEN_XX3FORM(xsmindp, 0x00, 0x15, PPC2_VSX),
|
||||
GEN_XX3FORM(xsmaxcdp, 0x00, 0x10, PPC2_ISA300),
|
||||
GEN_XX3FORM(xsmincdp, 0x00, 0x11, PPC2_ISA300),
|
||||
GEN_XX3FORM(xsmaxjdp, 0x00, 0x12, PPC2_ISA300),
|
||||
GEN_XX3FORM(xsminjdp, 0x00, 0x13, PPC2_ISA300),
|
||||
GEN_XX2FORM_EO(xscvdphp, 0x16, 0x15, 0x11, PPC2_ISA300),
|
||||
GEN_XX2FORM(xscvdpsp, 0x12, 0x10, PPC2_VSX),
|
||||
GEN_XX2FORM(xscvdpspn, 0x16, 0x10, PPC2_VSX207),
|
||||
|
|
|
@ -463,7 +463,6 @@ static gchar *mktempshm(int size, int *fd)
|
|||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret, fd;
|
||||
const char *arch = qtest_get_arch();
|
||||
gchar dir[] = "/tmp/ivshmem-test.XXXXXX";
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
@ -488,9 +487,7 @@ int main(int argc, char **argv)
|
|||
qtest_add_func("/ivshmem/memdev", test_ivshmem_memdev);
|
||||
if (g_test_slow()) {
|
||||
qtest_add_func("/ivshmem/pair", test_ivshmem_pair);
|
||||
if (strcmp(arch, "ppc64") != 0) {
|
||||
qtest_add_func("/ivshmem/server", test_ivshmem_server);
|
||||
}
|
||||
qtest_add_func("/ivshmem/server", test_ivshmem_server);
|
||||
}
|
||||
|
||||
out:
|
||||
|
|
|
@ -11,6 +11,7 @@ endif
|
|||
bcdsub: CFLAGS += -mpower8-vector
|
||||
|
||||
PPC64_TESTS += byte_reverse
|
||||
PPC64_TESTS += mtfsf
|
||||
ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_POWER10),)
|
||||
run-byte_reverse: QEMU_OPTS+=-cpu POWER10
|
||||
run-plugin-byte_reverse-with-%: QEMU_OPTS+=-cpu POWER10
|
||||
|
|
|
@ -16,6 +16,7 @@ byte_reverse: CFLAGS += -mcpu=power10
|
|||
run-byte_reverse: QEMU_OPTS+=-cpu POWER10
|
||||
run-plugin-byte_reverse-with-%: QEMU_OPTS+=-cpu POWER10
|
||||
|
||||
PPC64LE_TESTS += mtfsf
|
||||
PPC64LE_TESTS += signal_save_restore_xer
|
||||
|
||||
TESTS += $(PPC64LE_TESTS)
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#define FPSCR_VE 7 /* Floating-point invalid operation exception enable */
|
||||
#define FPSCR_VXSOFT 10 /* Floating-point invalid operation exception (soft) */
|
||||
#define FPSCR_FI 17 /* Floating-point fraction inexact */
|
||||
|
||||
#define FP_VE (1ull << FPSCR_VE)
|
||||
#define FP_VXSOFT (1ull << FPSCR_VXSOFT)
|
||||
#define FP_FI (1ull << FPSCR_FI)
|
||||
|
||||
void sigfpe_handler(int sig, siginfo_t *si, void *ucontext)
|
||||
{
|
||||
if (si->si_code == FPE_FLTINV) {
|
||||
exit(0);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
union {
|
||||
double d;
|
||||
long long ll;
|
||||
} fpscr;
|
||||
|
||||
struct sigaction sa = {
|
||||
.sa_sigaction = sigfpe_handler,
|
||||
.sa_flags = SA_SIGINFO
|
||||
};
|
||||
|
||||
/*
|
||||
* Enable the MSR bits F0 and F1 to enable exceptions.
|
||||
* This shouldn't be needed in linux-user as these bits are enabled by
|
||||
* default, but this allows to execute either in a VM or a real machine
|
||||
* to compare the behaviors.
|
||||
*/
|
||||
prctl(PR_SET_FPEXC, PR_FP_EXC_PRECISE);
|
||||
|
||||
/* First test if the FI bit is being set correctly */
|
||||
fpscr.ll = FP_FI;
|
||||
__builtin_mtfsf(0b11111111, fpscr.d);
|
||||
fpscr.d = __builtin_mffs();
|
||||
assert((fpscr.ll & FP_FI) != 0);
|
||||
|
||||
/* Then test if the deferred exception is being called correctly */
|
||||
sigaction(SIGFPE, &sa, NULL);
|
||||
|
||||
/*
|
||||
* Although the VXSOFT exception has been chosen, based on test in a Power9
|
||||
* any combination of exception bit + its enabling bit should work.
|
||||
* But if a different exception is chosen si_code check should
|
||||
* change accordingly.
|
||||
*/
|
||||
fpscr.ll = FP_VE | FP_VXSOFT;
|
||||
__builtin_mtfsf(0b11111111, fpscr.d);
|
||||
|
||||
return 1;
|
||||
}
|
Loading…
Reference in New Issue