ppc patch queue 2020-02093
This pull request supersedes ppc-for-5.0-20200131. The only changes are one extra patch to suppress some irritating warnings during tests under TCG, and an extra Tested-by in one of the other patches. Here's the next batch of patches for ppc and associated machine types. Highlights includes: * Remove the deprecated "prep" machine type and its OpenHackware firmware * Add TCG emulation of the msgsndp etc. supervisor privileged doorbell instructions * Allow "pnv" machine type to run Hostboot style firmwares * Add a virtual TPM device for spapr machines * Implement devices for POWER8 PHB3 and POWER9 PHB4 host bridges for the pnv machine type * Use faster Spectre mitigation by default for POWER9 DD2.3 machines * Introduce Firmware Assisted NMI dump facility for spapr machines * Fix a performance regression with load/store multiple instructions in TCG as well as some other assorted cleanups and fixes. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEdfRlhq5hpmzETofcbDjKyiDZs5IFAl43k9AACgkQbDjKyiDZ s5KKihAAucn4VbO5qbdruLdPSe4pfPZQ8cM7DuYEQQdd2GCgdSJcSKrCJ1ufSrBn 8FA6zpIMWQchv8Rkuyk7o8DGts5P+9sN8x160sQrMWpKzWZyTkA7IJX45BzHZz1M Y8AHs9SlkvEz6yqHlFyP1GtQH0jFWFOAYD/TvvTpAoqTPJ3wcIZixoZvexSj2a2L ayP/S/Ler+5vxVYjjNipXKxghNup2ln2vewC+5Q4HghPglafkJpdJ8dQvol1DwLl gigs75HY8BLHx0JSi+GBcAu8qOOqLM5vVoXoorWTySfo1FTuhNQYYT+I9X8ecgLL 2Vf/zPJRimk/h9fnHS8VUXRM9ckTq9Pva5d8Zk2/eVvnGwWAG1YKF3CkZO0dEtD5 tuqDsq2UD4j64QDssCu14V8jXVXGJUZYspgGKnnBih8xJhE9XNbOt2TgeZGoClE3 0EcLYElcGLJsMj3jozZt1U02ypZVa2QaIG2LC8//Qp2wR9wUxULdRWVaz9L5bZ1/ SeWT7qQZwrQdrLzTsIJxLycs/WR3VtXAJWc52Jy6l8XgCyprP/HfNOs1iQnuHVjl eCFTM1hTGEnBSh2g4lBG46sMz9YhZ/CQttLfu9Xr968ZDpUnCAdXjf/pW/48VYrJ xlOGjCaMvQL/pjWquBynhTeTUu7kaoByDCxO/FIo2Ns+SjVyIYQ= =ZOd2 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-5.0-20200203' into staging ppc patch queue 2020-02093 This pull request supersedes ppc-for-5.0-20200131. The only changes are one extra patch to suppress some irritating warnings during tests under TCG, and an extra Tested-by in one of the other patches. Here's the next batch of patches for ppc and associated machine types. Highlights includes: * Remove the deprecated "prep" machine type and its OpenHackware firmware * Add TCG emulation of the msgsndp etc. supervisor privileged doorbell instructions * Allow "pnv" machine type to run Hostboot style firmwares * Add a virtual TPM device for spapr machines * Implement devices for POWER8 PHB3 and POWER9 PHB4 host bridges for the pnv machine type * Use faster Spectre mitigation by default for POWER9 DD2.3 machines * Introduce Firmware Assisted NMI dump facility for spapr machines * Fix a performance regression with load/store multiple instructions in TCG as well as some other assorted cleanups and fixes. # gpg: Signature made Mon 03 Feb 2020 03:30:24 GMT # gpg: using RSA key 75F46586AE61A66CC44E87DC6C38CACA20D9B392 # gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" [full] # gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>" [full] # gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" [full] # gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>" [unknown] # Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392 * remotes/dgibson/tags/ppc-for-5.0-20200203: (35 commits) tests: Silence various warnings with pseries target/ppc: Use probe_write for DCBZ target/ppc: Remove redundant mask in DCBZ target/ppc: Use probe_access for LMW, STMW target/ppc: Use probe_access for LSW, STSW ppc: spapr: Activate the FWNMI functionality migration: Include migration support for machine check handling ppc: spapr: Handle "ibm,nmi-register" and "ibm,nmi-interlock" RTAS calls target/ppc: Build rtas error log upon an MCE target/ppc: Handle NMI guest exit ppc: spapr: Introduce FWNMI capability Wrapper function to wait on condition for the main loop mutex target/ppc/cpu.h: Put macro parameter in parentheses spapr: Enable DD2.3 accelerated count cache flush in pseries-5.0 machine ppc/pnv: change the PowerNV machine devices to be non user creatable ppc/pnv: Add models for POWER8 PHB3 PCIe Host bridge ppc/pnv: Add models for POWER9 PHB4 PCIe Host bridge docs/specs/tpm: reST-ify TPM documentation hw/ppc/Kconfig: Enable TPM_SPAPR as part of PSERIES config tpm_spapr: Support suspend and resume ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
035b21977c
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -10,9 +10,6 @@
|
|||
[submodule "roms/openbios"]
|
||||
path = roms/openbios
|
||||
url = https://git.qemu.org/git/openbios.git
|
||||
[submodule "roms/openhackware"]
|
||||
path = roms/openhackware
|
||||
url = https://git.qemu.org/git/openhackware.git
|
||||
[submodule "roms/qemu-palcode"]
|
||||
path = roms/qemu-palcode
|
||||
url = https://git.qemu.org/git/qemu-palcode.git
|
||||
|
|
|
@ -1103,7 +1103,6 @@ F: hw/dma/i82374.c
|
|||
F: hw/rtc/m48t59-isa.c
|
||||
F: include/hw/isa/pc87312.h
|
||||
F: include/hw/rtc/m48t59.h
|
||||
F: pc-bios/ppc_rom.bin
|
||||
F: tests/acceptance/ppc_prep_40p.py
|
||||
|
||||
sPAPR
|
||||
|
|
2
Makefile
2
Makefile
|
@ -784,7 +784,7 @@ ifdef INSTALL_BLOBS
|
|||
BLOBS=bios.bin bios-256k.bin bios-microvm.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
||||
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
|
||||
vgabios-ramfb.bin vgabios-bochs-display.bin vgabios-ati.bin \
|
||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
||||
openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
||||
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
||||
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
||||
efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \
|
||||
|
|
5
cpus.c
5
cpus.c
|
@ -1839,6 +1839,11 @@ void qemu_mutex_unlock_iothread(void)
|
|||
qemu_mutex_unlock(&qemu_global_mutex);
|
||||
}
|
||||
|
||||
void qemu_cond_wait_iothread(QemuCond *cond)
|
||||
{
|
||||
qemu_cond_wait(cond, &qemu_global_mutex);
|
||||
}
|
||||
|
||||
static bool all_vcpus_paused(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
|
|
|
@ -27,8 +27,7 @@
|
|||
#
|
||||
# @openfirmware: The interface is defined by the (historical) IEEE
|
||||
# 1275-1994 standard. Examples for firmware projects that
|
||||
# provide this interface are: OpenBIOS, OpenHackWare,
|
||||
# SLOF.
|
||||
# provide this interface are: OpenBIOS and SLOF.
|
||||
#
|
||||
# @uboot: Firmware interface defined by the U-Boot project.
|
||||
#
|
||||
|
|
|
@ -13,3 +13,4 @@ Contents:
|
|||
ppc-xive
|
||||
ppc-spapr-xive
|
||||
acpi_hw_reduced_hotplug
|
||||
tpm
|
||||
|
|
503
docs/specs/tpm.rst
Normal file
503
docs/specs/tpm.rst
Normal file
|
@ -0,0 +1,503 @@
|
|||
===============
|
||||
QEMU TPM Device
|
||||
===============
|
||||
|
||||
Guest-side hardware interface
|
||||
=============================
|
||||
|
||||
TIS interface
|
||||
-------------
|
||||
|
||||
The QEMU TPM emulation implements a TPM TIS hardware interface
|
||||
following the Trusted Computing Group's specification "TCG PC Client
|
||||
Specific TPM Interface Specification (TIS)", Specification Version
|
||||
1.3, 21 March 2013. (see the `TIS specification`_, or a later version
|
||||
of it).
|
||||
|
||||
The TIS interface makes a memory mapped IO region in the area
|
||||
0xfed40000-0xfed44fff available to the guest operating system.
|
||||
|
||||
QEMU files related to TPM TIS interface:
|
||||
- ``hw/tpm/tpm_tis.c``
|
||||
- ``hw/tpm/tpm_tis.h``
|
||||
|
||||
CRB interface
|
||||
-------------
|
||||
|
||||
QEMU also implements a TPM CRB interface following the Trusted
|
||||
Computing Group's specification "TCG PC Client Platform TPM Profile
|
||||
(PTP) Specification", Family "2.0", Level 00 Revision 01.03 v22, May
|
||||
22, 2017. (see the `CRB specification`_, or a later version of it)
|
||||
|
||||
The CRB interface makes a memory mapped IO region in the area
|
||||
0xfed40000-0xfed40fff (1 locality) available to the guest
|
||||
operating system.
|
||||
|
||||
QEMU files related to TPM CRB interface:
|
||||
- ``hw/tpm/tpm_crb.c``
|
||||
|
||||
SPAPR interface
|
||||
---------------
|
||||
|
||||
pSeries (ppc64) machines offer a tpm-spapr device model.
|
||||
|
||||
QEMU files related to the SPAPR interface:
|
||||
- ``hw/tpm/tpm_spapr.c``
|
||||
|
||||
fw_cfg interface
|
||||
================
|
||||
|
||||
The bios/firmware may read the ``"etc/tpm/config"`` fw_cfg entry for
|
||||
configuring the guest appropriately.
|
||||
|
||||
The entry of 6 bytes has the following content, in little-endian:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define TPM_VERSION_UNSPEC 0
|
||||
#define TPM_VERSION_1_2 1
|
||||
#define TPM_VERSION_2_0 2
|
||||
|
||||
#define TPM_PPI_VERSION_NONE 0
|
||||
#define TPM_PPI_VERSION_1_30 1
|
||||
|
||||
struct FwCfgTPMConfig {
|
||||
uint32_t tpmppi_address; /* PPI memory location */
|
||||
uint8_t tpm_version; /* TPM version */
|
||||
uint8_t tpmppi_version; /* PPI version */
|
||||
};
|
||||
|
||||
ACPI interface
|
||||
==============
|
||||
|
||||
The TPM device is defined with ACPI ID "PNP0C31". QEMU builds a SSDT
|
||||
and passes it into the guest through the fw_cfg device. The device
|
||||
description contains the base address of the TIS interface 0xfed40000
|
||||
and the size of the MMIO area (0x5000). In case a TPM2 is used by
|
||||
QEMU, a TPM2 ACPI table is also provided. The device is described to
|
||||
be used in polling mode rather than interrupt mode primarily because
|
||||
no unused IRQ could be found.
|
||||
|
||||
To support measurement logs to be written by the firmware,
|
||||
e.g. SeaBIOS, a TCPA table is implemented. This table provides a 64kb
|
||||
buffer where the firmware can write its log into. For TPM 2 only a
|
||||
more recent version of the TPM2 table provides support for
|
||||
measurements logs and a TCPA table does not need to be created.
|
||||
|
||||
The TCPA and TPM2 ACPI tables follow the Trusted Computing Group
|
||||
specification "TCG ACPI Specification" Family "1.2" and "2.0", Level
|
||||
00 Revision 00.37. (see the `ACPI specification`_, or a later version
|
||||
of it)
|
||||
|
||||
ACPI PPI Interface
|
||||
------------------
|
||||
|
||||
QEMU supports the Physical Presence Interface (PPI) for TPM 1.2 and
|
||||
TPM 2. This interface requires ACPI and firmware support. (see the
|
||||
`PPI specification`_)
|
||||
|
||||
PPI enables a system administrator (root) to request a modification to
|
||||
the TPM upon reboot. The PPI specification defines the operation
|
||||
requests and the actions the firmware has to take. The system
|
||||
administrator passes the operation request number to the firmware
|
||||
through an ACPI interface which writes this number to a memory
|
||||
location that the firmware knows. Upon reboot, the firmware finds the
|
||||
number and sends commands to the TPM. The firmware writes the TPM
|
||||
result code and the operation request number to a memory location that
|
||||
ACPI can read from and pass the result on to the administrator.
|
||||
|
||||
The PPI specification defines a set of mandatory and optional
|
||||
operations for the firmware to implement. The ACPI interface also
|
||||
allows an administrator to list the supported operations. In QEMU the
|
||||
ACPI code is generated by QEMU, yet the firmware needs to implement
|
||||
support on a per-operations basis, and different firmwares may support
|
||||
a different subset. Therefore, QEMU introduces the virtual memory
|
||||
device for PPI where the firmware can indicate which operations it
|
||||
supports and ACPI can enable the ones that are supported and disable
|
||||
all others. This interface lies in main memory and has the following
|
||||
layout:
|
||||
|
||||
+-------------+--------+--------+-------------------------------------------+
|
||||
| Field | Length | Offset | Description |
|
||||
+=============+========+========+===========================================+
|
||||
| ``func`` | 0x100 | 0x000 | Firmware sets values for each supported |
|
||||
| | | | operation. See defined values below. |
|
||||
+-------------+--------+--------+-------------------------------------------+
|
||||
| ``ppin`` | 0x1 | 0x100 | SMI interrupt to use. Set by firmware. |
|
||||
| | | | Not supported. |
|
||||
+-------------+--------+--------+-------------------------------------------+
|
||||
| ``ppip`` | 0x4 | 0x101 | ACPI function index to pass to SMM code. |
|
||||
| | | | Set by ACPI. Not supported. |
|
||||
+-------------+--------+--------+-------------------------------------------+
|
||||
| ``pprp`` | 0x4 | 0x105 | Result of last executed operation. Set by |
|
||||
| | | | firmware. See function index 5 for values.|
|
||||
+-------------+--------+--------+-------------------------------------------+
|
||||
| ``pprq`` | 0x4 | 0x109 | Operation request number to execute. See |
|
||||
| | | | 'Physical Presence Interface Operation |
|
||||
| | | | Summary' tables in specs. Set by ACPI. |
|
||||
+-------------+--------+--------+-------------------------------------------+
|
||||
| ``pprm`` | 0x4 | 0x10d | Operation request optional parameter. |
|
||||
| | | | Values depend on operation. Set by ACPI. |
|
||||
+-------------+--------+--------+-------------------------------------------+
|
||||
| ``lppr`` | 0x4 | 0x111 | Last executed operation request number. |
|
||||
| | | | Copied from pprq field by firmware. |
|
||||
+-------------+--------+--------+-------------------------------------------+
|
||||
| ``fret`` | 0x4 | 0x115 | Result code from SMM function. |
|
||||
| | | | Not supported. |
|
||||
+-------------+--------+--------+-------------------------------------------+
|
||||
| ``res1`` | 0x40 | 0x119 | Reserved for future use |
|
||||
+-------------+--------+--------+-------------------------------------------+
|
||||
|``next_step``| 0x1 | 0x159 | Operation to execute after reboot by |
|
||||
| | | | firmware. Used by firmware. |
|
||||
+-------------+--------+--------+-------------------------------------------+
|
||||
| ``movv`` | 0x1 | 0x15a | Memory overwrite variable |
|
||||
+-------------+--------+--------+-------------------------------------------+
|
||||
|
||||
The following values are supported for the ``func`` field. They
|
||||
correspond to the values used by ACPI function index 8.
|
||||
|
||||
+----------+-------------------------------------------------------------+
|
||||
| Value | Description |
|
||||
+==========+=============================================================+
|
||||
| 0 | Operation is not implemented. |
|
||||
+----------+-------------------------------------------------------------+
|
||||
| 1 | Operation is only accessible through firmware. |
|
||||
+----------+-------------------------------------------------------------+
|
||||
| 2 | Operation is blocked for OS by firmware configuration. |
|
||||
+----------+-------------------------------------------------------------+
|
||||
| 3 | Operation is allowed and physically present user required. |
|
||||
+----------+-------------------------------------------------------------+
|
||||
| 4 | Operation is allowed and physically present user is not |
|
||||
| | required. |
|
||||
+----------+-------------------------------------------------------------+
|
||||
|
||||
The location of the table is given by the fw_cfg ``tpmppi_address``
|
||||
field. The PPI memory region size is 0x400 (``TPM_PPI_ADDR_SIZE``) to
|
||||
leave enough room for future updates.
|
||||
|
||||
QEMU files related to TPM ACPI tables:
|
||||
- ``hw/i386/acpi-build.c``
|
||||
- ``include/hw/acpi/tpm.h``
|
||||
|
||||
TPM backend devices
|
||||
===================
|
||||
|
||||
The TPM implementation is split into two parts, frontend and
|
||||
backend. The frontend part is the hardware interface, such as the TPM
|
||||
TIS interface described earlier, and the other part is the TPM backend
|
||||
interface. The backend interfaces implement the interaction with a TPM
|
||||
device, which may be a physical or an emulated device. The split
|
||||
between the front- and backend devices allows a frontend to be
|
||||
connected with any available backend. This enables the TIS interface
|
||||
to be used with the passthrough backend or the swtpm backend.
|
||||
|
||||
QEMU files related to TPM backends:
|
||||
- ``backends/tpm.c``
|
||||
- ``include/sysemu/tpm_backend.h``
|
||||
- ``include/sysemu/tpm_backend_int.h``
|
||||
|
||||
The QEMU TPM passthrough device
|
||||
-------------------------------
|
||||
|
||||
In case QEMU is run on Linux as the host operating system it is
|
||||
possible to make the hardware TPM device available to a single QEMU
|
||||
guest. In this case the user must make sure that no other program is
|
||||
using the device, e.g., /dev/tpm0, before trying to start QEMU with
|
||||
it.
|
||||
|
||||
The passthrough driver uses the host's TPM device for sending TPM
|
||||
commands and receiving responses from. Besides that it accesses the
|
||||
TPM device's sysfs entry for support of command cancellation. Since
|
||||
none of the state of a hardware TPM can be migrated between hosts,
|
||||
virtual machine migration is disabled when the TPM passthrough driver
|
||||
is used.
|
||||
|
||||
Since the host's TPM device will already be initialized by the host's
|
||||
firmware, certain commands, e.g. ``TPM_Startup()``, sent by the
|
||||
virtual firmware for device initialization, will fail. In this case
|
||||
the firmware should not use the TPM.
|
||||
|
||||
Sharing the device with the host is generally not a recommended usage
|
||||
scenario for a TPM device. The primary reason for this is that two
|
||||
operating systems can then access the device's single set of
|
||||
resources, such as platform configuration registers
|
||||
(PCRs). Applications or kernel security subsystems, such as the Linux
|
||||
Integrity Measurement Architecture (IMA), are not expecting to share
|
||||
PCRs.
|
||||
|
||||
QEMU files related to the TPM passthrough device:
|
||||
- ``hw/tpm/tpm_passthrough.c``
|
||||
- ``hw/tpm/tpm_util.c``
|
||||
- ``hw/tpm/tpm_util.h``
|
||||
|
||||
|
||||
Command line to start QEMU with the TPM passthrough device using the host's
|
||||
hardware TPM ``/dev/tpm0``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
qemu-system-x86_64 -display sdl -accel kvm \
|
||||
-m 1024 -boot d -bios bios-256k.bin -boot menu=on \
|
||||
-tpmdev passthrough,id=tpm0,path=/dev/tpm0 \
|
||||
-device tpm-tis,tpmdev=tpm0 test.img
|
||||
|
||||
|
||||
The following commands should result in similar output inside the VM
|
||||
with a Linux kernel that either has the TPM TIS driver built-in or
|
||||
available as a module:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# dmesg | grep -i tpm
|
||||
[ 0.711310] tpm_tis 00:06: 1.2 TPM (device=id 0x1, rev-id 1)
|
||||
|
||||
# dmesg | grep TCPA
|
||||
[ 0.000000] ACPI: TCPA 0x0000000003FFD191C 000032 (v02 BOCHS \
|
||||
BXPCTCPA 0000001 BXPC 00000001)
|
||||
|
||||
# ls -l /dev/tpm*
|
||||
crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0
|
||||
|
||||
# find /sys/devices/ | grep pcrs$ | xargs cat
|
||||
PCR-00: 35 4E 3B CE 23 9F 38 59 ...
|
||||
...
|
||||
PCR-23: 00 00 00 00 00 00 00 00 ...
|
||||
|
||||
The QEMU TPM emulator device
|
||||
----------------------------
|
||||
|
||||
The TPM emulator device uses an external TPM emulator called 'swtpm'
|
||||
for sending TPM commands to and receiving responses from. The swtpm
|
||||
program must have been started before trying to access it through the
|
||||
TPM emulator with QEMU.
|
||||
|
||||
The TPM emulator implements a command channel for transferring TPM
|
||||
commands and responses as well as a control channel over which control
|
||||
commands can be sent. (see the `SWTPM protocol`_ specification)
|
||||
|
||||
The control channel serves the purpose of resetting, initializing, and
|
||||
migrating the TPM state, among other things.
|
||||
|
||||
The swtpm program behaves like a hardware TPM and therefore needs to
|
||||
be initialized by the firmware running inside the QEMU virtual
|
||||
machine. One necessary step for initializing the device is to send
|
||||
the TPM_Startup command to it. SeaBIOS, for example, has been
|
||||
instrumented to initialize a TPM 1.2 or TPM 2 device using this
|
||||
command.
|
||||
|
||||
QEMU files related to the TPM emulator device:
|
||||
- ``hw/tpm/tpm_emulator.c``
|
||||
- ``hw/tpm/tpm_util.c``
|
||||
- ``hw/tpm/tpm_util.h``
|
||||
|
||||
The following commands start the swtpm with a UnixIO control channel over
|
||||
a socket interface. They do not need to be run as root.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
mkdir /tmp/mytpm1
|
||||
swtpm socket --tpmstate dir=/tmp/mytpm1 \
|
||||
--ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \
|
||||
--log level=20
|
||||
|
||||
Command line to start QEMU with the TPM emulator device communicating
|
||||
with the swtpm (x86):
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
qemu-system-x86_64 -display sdl -accel kvm \
|
||||
-m 1024 -boot d -bios bios-256k.bin -boot menu=on \
|
||||
-chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
|
||||
-tpmdev emulator,id=tpm0,chardev=chrtpm \
|
||||
-device tpm-tis,tpmdev=tpm0 test.img
|
||||
|
||||
In case a pSeries machine is emulated, use the following command line:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
qemu-system-ppc64 -display sdl -machine pseries,accel=kvm \
|
||||
-m 1024 -bios slof.bin -boot menu=on \
|
||||
-nodefaults -device VGA -device pci-ohci -device usb-kbd \
|
||||
-chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
|
||||
-tpmdev emulator,id=tpm0,chardev=chrtpm \
|
||||
-device tpm-spapr,tpmdev=tpm0 \
|
||||
-device spapr-vscsi,id=scsi0,reg=0x00002000 \
|
||||
-device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,id=virtio-disk0 \
|
||||
-drive file=test.img,format=raw,if=none,id=drive-virtio-disk0
|
||||
|
||||
In case SeaBIOS is used as firmware, it should show the TPM menu item
|
||||
after entering the menu with 'ESC'.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
Select boot device:
|
||||
1. DVD/CD [ata1-0: QEMU DVD-ROM ATAPI-4 DVD/CD]
|
||||
[...]
|
||||
5. Legacy option rom
|
||||
|
||||
t. TPM Configuration
|
||||
|
||||
The following commands should result in similar output inside the VM
|
||||
with a Linux kernel that either has the TPM TIS driver built-in or
|
||||
available as a module:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# dmesg | grep -i tpm
|
||||
[ 0.711310] tpm_tis 00:06: 1.2 TPM (device=id 0x1, rev-id 1)
|
||||
|
||||
# dmesg | grep TCPA
|
||||
[ 0.000000] ACPI: TCPA 0x0000000003FFD191C 000032 (v02 BOCHS \
|
||||
BXPCTCPA 0000001 BXPC 00000001)
|
||||
|
||||
# ls -l /dev/tpm*
|
||||
crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0
|
||||
|
||||
# find /sys/devices/ | grep pcrs$ | xargs cat
|
||||
PCR-00: 35 4E 3B CE 23 9F 38 59 ...
|
||||
...
|
||||
PCR-23: 00 00 00 00 00 00 00 00 ...
|
||||
|
||||
Migration with the TPM emulator
|
||||
===============================
|
||||
|
||||
The TPM emulator supports the following types of virtual machine
|
||||
migration:
|
||||
|
||||
- VM save / restore (migration into a file)
|
||||
- Network migration
|
||||
- Snapshotting (migration into storage like QoW2 or QED)
|
||||
|
||||
The following command sequences can be used to test VM save / restore.
|
||||
|
||||
In a 1st terminal start an instance of a swtpm using the following command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
mkdir /tmp/mytpm1
|
||||
swtpm socket --tpmstate dir=/tmp/mytpm1 \
|
||||
--ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \
|
||||
--log level=20 --tpm2
|
||||
|
||||
In a 2nd terminal start the VM:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
qemu-system-x86_64 -display sdl -accel kvm \
|
||||
-m 1024 -boot d -bios bios-256k.bin -boot menu=on \
|
||||
-chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
|
||||
-tpmdev emulator,id=tpm0,chardev=chrtpm \
|
||||
-device tpm-tis,tpmdev=tpm0 \
|
||||
-monitor stdio \
|
||||
test.img
|
||||
|
||||
Verify that the attached TPM is working as expected using applications
|
||||
inside the VM.
|
||||
|
||||
To store the state of the VM use the following command in the QEMU
|
||||
monitor in the 2nd terminal:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
(qemu) migrate "exec:cat > testvm.bin"
|
||||
(qemu) quit
|
||||
|
||||
At this point a file called ``testvm.bin`` should exists and the swtpm
|
||||
and QEMU processes should have ended.
|
||||
|
||||
To test 'VM restore' you have to start the swtpm with the same
|
||||
parameters as before. If previously a TPM 2 [--tpm2] was saved, --tpm2
|
||||
must now be passed again on the command line.
|
||||
|
||||
In the 1st terminal restart the swtpm with the same command line as
|
||||
before:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
swtpm socket --tpmstate dir=/tmp/mytpm1 \
|
||||
--ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \
|
||||
--log level=20 --tpm2
|
||||
|
||||
In the 2nd terminal restore the state of the VM using the additional
|
||||
'-incoming' option.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
qemu-system-x86_64 -display sdl -accel kvm \
|
||||
-m 1024 -boot d -bios bios-256k.bin -boot menu=on \
|
||||
-chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
|
||||
-tpmdev emulator,id=tpm0,chardev=chrtpm \
|
||||
-device tpm-tis,tpmdev=tpm0 \
|
||||
-incoming "exec:cat < testvm.bin" \
|
||||
test.img
|
||||
|
||||
Troubleshooting migration
|
||||
-------------------------
|
||||
|
||||
There are several reasons why migration may fail. In case of problems,
|
||||
please ensure that the command lines adhere to the following rules
|
||||
and, if possible, that identical versions of QEMU and swtpm are used
|
||||
at all times.
|
||||
|
||||
VM save and restore:
|
||||
|
||||
- QEMU command line parameters should be identical apart from the
|
||||
'-incoming' option on VM restore
|
||||
|
||||
- swtpm command line parameters should be identical
|
||||
|
||||
VM migration to 'localhost':
|
||||
|
||||
- QEMU command line parameters should be identical apart from the
|
||||
'-incoming' option on the destination side
|
||||
|
||||
- swtpm command line parameters should point to two different
|
||||
directories on the source and destination swtpm (--tpmstate dir=...)
|
||||
(especially if different versions of libtpms were to be used on the
|
||||
same machine).
|
||||
|
||||
VM migration across the network:
|
||||
|
||||
- QEMU command line parameters should be identical apart from the
|
||||
'-incoming' option on the destination side
|
||||
|
||||
- swtpm command line parameters should be identical
|
||||
|
||||
VM Snapshotting:
|
||||
- QEMU command line parameters should be identical
|
||||
|
||||
- swtpm command line parameters should be identical
|
||||
|
||||
|
||||
Besides that, migration failure reasons on the swtpm level may include
|
||||
the following:
|
||||
|
||||
- the versions of the swtpm on the source and destination sides are
|
||||
incompatible
|
||||
|
||||
- downgrading of TPM state may not be supported
|
||||
|
||||
- the source and destination libtpms were compiled with different
|
||||
compile-time options and the destination side refuses to accept the
|
||||
state
|
||||
|
||||
- different migration keys are used on the source and destination side
|
||||
and the destination side cannot decrypt the migrated state
|
||||
(swtpm ... --migration-key ... )
|
||||
|
||||
|
||||
.. _TIS specification:
|
||||
https://trustedcomputinggroup.org/pc-client-work-group-pc-client-specific-tpm-interface-specification-tis/
|
||||
|
||||
.. _CRB specification:
|
||||
https://trustedcomputinggroup.org/resource/pc-client-platform-tpm-profile-ptp-specification/
|
||||
|
||||
|
||||
.. _ACPI specification:
|
||||
https://trustedcomputinggroup.org/tcg-acpi-specification/
|
||||
|
||||
.. _PPI specification:
|
||||
https://trustedcomputinggroup.org/resource/tcg-physical-presence-interface-specification/
|
||||
|
||||
.. _SWTPM protocol:
|
||||
https://github.com/stefanberger/swtpm/blob/master/man/man3/swtpm_ioctls.pod
|
|
@ -1,427 +0,0 @@
|
|||
QEMU TPM Device
|
||||
===============
|
||||
|
||||
= Guest-side Hardware Interface =
|
||||
|
||||
The QEMU TPM emulation implements a TPM TIS hardware interface following the
|
||||
Trusted Computing Group's specification "TCG PC Client Specific TPM Interface
|
||||
Specification (TIS)", Specification Version 1.3, 21 March 2013. This
|
||||
specification, or a later version of it, can be accessed from the following
|
||||
URL:
|
||||
|
||||
https://trustedcomputinggroup.org/pc-client-work-group-pc-client-specific-tpm-interface-specification-tis/
|
||||
|
||||
The TIS interface makes a memory mapped IO region in the area 0xfed40000 -
|
||||
0xfed44fff available to the guest operating system.
|
||||
|
||||
|
||||
QEMU files related to TPM TIS interface:
|
||||
- hw/tpm/tpm_tis.c
|
||||
- hw/tpm/tpm_tis.h
|
||||
|
||||
|
||||
QEMU also implements a TPM CRB interface following the Trusted Computing
|
||||
Group's specification "TCG PC Client Platform TPM Profile (PTP)
|
||||
Specification", Family "2.0", Level 00 Revision 01.03 v22, May 22, 2017.
|
||||
This specification, or a later version of it, can be accessed from the
|
||||
following URL:
|
||||
|
||||
https://trustedcomputinggroup.org/resource/pc-client-platform-tpm-profile-ptp-specification/
|
||||
|
||||
The CRB interface makes a memory mapped IO region in the area 0xfed40000 -
|
||||
0xfed40fff (1 locality) available to the guest operating system.
|
||||
|
||||
QEMU files related to TPM CRB interface:
|
||||
- hw/tpm/tpm_crb.c
|
||||
|
||||
= fw_cfg interface =
|
||||
|
||||
The bios/firmware may read the "etc/tpm/config" fw_cfg entry for
|
||||
configuring the guest appropriately.
|
||||
|
||||
The entry of 6 bytes has the following content, in little-endian:
|
||||
|
||||
#define TPM_VERSION_UNSPEC 0
|
||||
#define TPM_VERSION_1_2 1
|
||||
#define TPM_VERSION_2_0 2
|
||||
|
||||
#define TPM_PPI_VERSION_NONE 0
|
||||
#define TPM_PPI_VERSION_1_30 1
|
||||
|
||||
struct FwCfgTPMConfig {
|
||||
uint32_t tpmppi_address; /* PPI memory location */
|
||||
uint8_t tpm_version; /* TPM version */
|
||||
uint8_t tpmppi_version; /* PPI version */
|
||||
};
|
||||
|
||||
= ACPI Interface =
|
||||
|
||||
The TPM device is defined with ACPI ID "PNP0C31". QEMU builds a SSDT and passes
|
||||
it into the guest through the fw_cfg device. The device description contains
|
||||
the base address of the TIS interface 0xfed40000 and the size of the MMIO area
|
||||
(0x5000). In case a TPM2 is used by QEMU, a TPM2 ACPI table is also provided.
|
||||
The device is described to be used in polling mode rather than interrupt mode
|
||||
primarily because no unused IRQ could be found.
|
||||
|
||||
To support measurement logs to be written by the firmware, e.g. SeaBIOS, a TCPA
|
||||
table is implemented. This table provides a 64kb buffer where the firmware can
|
||||
write its log into. For TPM 2 only a more recent version of the TPM2 table
|
||||
provides support for measurements logs and a TCPA table does not need to be
|
||||
created.
|
||||
|
||||
The TCPA and TPM2 ACPI tables follow the Trusted Computing Group specification
|
||||
"TCG ACPI Specification" Family "1.2" and "2.0", Level 00 Revision 00.37. This
|
||||
specification, or a later version of it, can be accessed from the following
|
||||
URL:
|
||||
|
||||
https://trustedcomputinggroup.org/tcg-acpi-specification/
|
||||
|
||||
== ACPI PPI Interface ==
|
||||
|
||||
QEMU supports the Physical Presence Interface (PPI) for TPM 1.2 and TPM 2. This
|
||||
interface requires ACPI and firmware support. The specification can be found at
|
||||
the following URL:
|
||||
|
||||
https://trustedcomputinggroup.org/resource/tcg-physical-presence-interface-specification/
|
||||
|
||||
PPI enables a system administrator (root) to request a modification to the
|
||||
TPM upon reboot. The PPI specification defines the operation requests and the
|
||||
actions the firmware has to take. The system administrator passes the operation
|
||||
request number to the firmware through an ACPI interface which writes this
|
||||
number to a memory location that the firmware knows. Upon reboot, the firmware
|
||||
finds the number and sends commands to the TPM. The firmware writes the TPM
|
||||
result code and the operation request number to a memory location that ACPI can
|
||||
read from and pass the result on to the administrator.
|
||||
|
||||
The PPI specification defines a set of mandatory and optional operations for
|
||||
the firmware to implement. The ACPI interface also allows an administrator to
|
||||
list the supported operations. In QEMU the ACPI code is generated by QEMU, yet
|
||||
the firmware needs to implement support on a per-operations basis, and
|
||||
different firmwares may support a different subset. Therefore, QEMU introduces
|
||||
the virtual memory device for PPI where the firmware can indicate which
|
||||
operations it supports and ACPI can enable the ones that are supported and
|
||||
disable all others. This interface lies in main memory and has the following
|
||||
layout:
|
||||
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| Field | Length | Offset | Description |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| func | 0x100 | 0x000 | Firmware sets values for each supported |
|
||||
| | | | operation. See defined values below. |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| ppin | 0x1 | 0x100 | SMI interrupt to use. Set by firmware. |
|
||||
| | | | Not supported. |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| ppip | 0x4 | 0x101 | ACPI function index to pass to SMM code. |
|
||||
| | | | Set by ACPI. Not supported. |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| pprp | 0x4 | 0x105 | Result of last executed operation. Set by |
|
||||
| | | | firmware. See function index 5 for values.|
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| pprq | 0x4 | 0x109 | Operation request number to execute. See |
|
||||
| | | | 'Physical Presence Interface Operation |
|
||||
| | | | Summary' tables in specs. Set by ACPI. |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| pprm | 0x4 | 0x10d | Operation request optional parameter. |
|
||||
| | | | Values depend on operation. Set by ACPI. |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| lppr | 0x4 | 0x111 | Last executed operation request number. |
|
||||
| | | | Copied from pprq field by firmware. |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| fret | 0x4 | 0x115 | Result code from SMM function. |
|
||||
| | | | Not supported. |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| res1 | 0x40 | 0x119 | Reserved for future use |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| next_step| 0x1 | 0x159 | Operation to execute after reboot by |
|
||||
| | | | firmware. Used by firmware. |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| movv | 0x1 | 0x15a | Memory overwrite variable |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
|
||||
The following values are supported for the 'func' field. They correspond
|
||||
to the values used by ACPI function index 8.
|
||||
|
||||
+----------+-------------------------------------------------------------+
|
||||
| value | Description |
|
||||
+----------+-------------------------------------------------------------+
|
||||
| 0 | Operation is not implemented. |
|
||||
+----------+-------------------------------------------------------------+
|
||||
| 1 | Operation is only accessible through firmware. |
|
||||
+----------+-------------------------------------------------------------+
|
||||
| 2 | Operation is blocked for OS by firmware configuration. |
|
||||
+----------+-------------------------------------------------------------+
|
||||
| 3 | Operation is allowed and physically present user required. |
|
||||
+----------+-------------------------------------------------------------+
|
||||
| 4 | Operation is allowed and physically present user is not |
|
||||
| | required. |
|
||||
+----------+-------------------------------------------------------------+
|
||||
|
||||
The location of the table is given by the fw_cfg tpmppi_address field.
|
||||
The PPI memory region size is 0x400 (TPM_PPI_ADDR_SIZE) to leave
|
||||
enough room for future updates.
|
||||
|
||||
|
||||
QEMU files related to TPM ACPI tables:
|
||||
- hw/i386/acpi-build.c
|
||||
- include/hw/acpi/tpm.h
|
||||
|
||||
|
||||
= TPM backend devices =
|
||||
|
||||
The TPM implementation is split into two parts, frontend and backend. The
|
||||
frontend part is the hardware interface, such as the TPM TIS interface
|
||||
described earlier, and the other part is the TPM backend interface. The backend
|
||||
interfaces implement the interaction with a TPM device, which may be a physical
|
||||
or an emulated device. The split between the front- and backend devices allows
|
||||
a frontend to be connected with any available backend. This enables the TIS
|
||||
interface to be used with the passthrough backend or the (future) swtpm backend.
|
||||
|
||||
|
||||
QEMU files related to TPM backends:
|
||||
- backends/tpm.c
|
||||
- include/sysemu/tpm_backend.h
|
||||
- include/sysemu/tpm_backend_int.h
|
||||
|
||||
|
||||
== The QEMU TPM passthrough device ==
|
||||
|
||||
In case QEMU is run on Linux as the host operating system it is possible to
|
||||
make the hardware TPM device available to a single QEMU guest. In this case the
|
||||
user must make sure that no other program is using the device, e.g., /dev/tpm0,
|
||||
before trying to start QEMU with it.
|
||||
|
||||
The passthrough driver uses the host's TPM device for sending TPM commands
|
||||
and receiving responses from. Besides that it accesses the TPM device's sysfs
|
||||
entry for support of command cancellation. Since none of the state of a
|
||||
hardware TPM can be migrated between hosts, virtual machine migration is
|
||||
disabled when the TPM passthrough driver is used.
|
||||
|
||||
Since the host's TPM device will already be initialized by the host's firmware,
|
||||
certain commands, e.g. TPM_Startup(), sent by the virtual firmware for device
|
||||
initialization, will fail. In this case the firmware should not use the TPM.
|
||||
|
||||
Sharing the device with the host is generally not a recommended usage scenario
|
||||
for a TPM device. The primary reason for this is that two operating systems can
|
||||
then access the device's single set of resources, such as platform configuration
|
||||
registers (PCRs). Applications or kernel security subsystems, such as the
|
||||
Linux Integrity Measurement Architecture (IMA), are not expecting to share PCRs.
|
||||
|
||||
|
||||
QEMU files related to the TPM passthrough device:
|
||||
- hw/tpm/tpm_passthrough.c
|
||||
- hw/tpm/tpm_util.c
|
||||
- hw/tpm/tpm_util.h
|
||||
|
||||
|
||||
Command line to start QEMU with the TPM passthrough device using the host's
|
||||
hardware TPM /dev/tpm0:
|
||||
|
||||
qemu-system-x86_64 -display sdl -accel kvm \
|
||||
-m 1024 -boot d -bios bios-256k.bin -boot menu=on \
|
||||
-tpmdev passthrough,id=tpm0,path=/dev/tpm0 \
|
||||
-device tpm-tis,tpmdev=tpm0 test.img
|
||||
|
||||
The following commands should result in similar output inside the VM with a
|
||||
Linux kernel that either has the TPM TIS driver built-in or available as a
|
||||
module:
|
||||
|
||||
#> dmesg | grep -i tpm
|
||||
[ 0.711310] tpm_tis 00:06: 1.2 TPM (device=id 0x1, rev-id 1)
|
||||
|
||||
#> dmesg | grep TCPA
|
||||
[ 0.000000] ACPI: TCPA 0x0000000003FFD191C 000032 (v02 BOCHS \
|
||||
BXPCTCPA 0000001 BXPC 00000001)
|
||||
|
||||
#> ls -l /dev/tpm*
|
||||
crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0
|
||||
|
||||
#> find /sys/devices/ | grep pcrs$ | xargs cat
|
||||
PCR-00: 35 4E 3B CE 23 9F 38 59 ...
|
||||
...
|
||||
PCR-23: 00 00 00 00 00 00 00 00 ...
|
||||
|
||||
|
||||
== The QEMU TPM emulator device ==
|
||||
|
||||
The TPM emulator device uses an external TPM emulator called 'swtpm' for
|
||||
sending TPM commands to and receiving responses from. The swtpm program
|
||||
must have been started before trying to access it through the TPM emulator
|
||||
with QEMU.
|
||||
|
||||
The TPM emulator implements a command channel for transferring TPM commands
|
||||
and responses as well as a control channel over which control commands can
|
||||
be sent. The specification for the control channel can be found here:
|
||||
|
||||
https://github.com/stefanberger/swtpm/blob/master/man/man3/swtpm_ioctls.pod
|
||||
|
||||
|
||||
The control channel serves the purpose of resetting, initializing, and
|
||||
migrating the TPM state, among other things.
|
||||
|
||||
The swtpm program behaves like a hardware TPM and therefore needs to be
|
||||
initialized by the firmware running inside the QEMU virtual machine.
|
||||
One necessary step for initializing the device is to send the TPM_Startup
|
||||
command to it. SeaBIOS, for example, has been instrumented to initialize
|
||||
a TPM 1.2 or TPM 2 device using this command.
|
||||
|
||||
|
||||
QEMU files related to the TPM emulator device:
|
||||
- hw/tpm/tpm_emulator.c
|
||||
- hw/tpm/tpm_util.c
|
||||
- hw/tpm/tpm_util.h
|
||||
|
||||
|
||||
The following commands start the swtpm with a UnixIO control channel over
|
||||
a socket interface. They do not need to be run as root.
|
||||
|
||||
mkdir /tmp/mytpm1
|
||||
swtpm socket --tpmstate dir=/tmp/mytpm1 \
|
||||
--ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \
|
||||
--log level=20
|
||||
|
||||
Command line to start QEMU with the TPM emulator device communicating with
|
||||
the swtpm:
|
||||
|
||||
qemu-system-x86_64 -display sdl -accel kvm \
|
||||
-m 1024 -boot d -bios bios-256k.bin -boot menu=on \
|
||||
-chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
|
||||
-tpmdev emulator,id=tpm0,chardev=chrtpm \
|
||||
-device tpm-tis,tpmdev=tpm0 test.img
|
||||
|
||||
|
||||
In case SeaBIOS is used as firmware, it should show the TPM menu item
|
||||
after entering the menu with 'ESC'.
|
||||
|
||||
Select boot device:
|
||||
1. DVD/CD [ata1-0: QEMU DVD-ROM ATAPI-4 DVD/CD]
|
||||
[...]
|
||||
5. Legacy option rom
|
||||
|
||||
t. TPM Configuration
|
||||
|
||||
|
||||
The following commands should result in similar output inside the VM with a
|
||||
Linux kernel that either has the TPM TIS driver built-in or available as a
|
||||
module:
|
||||
|
||||
#> dmesg | grep -i tpm
|
||||
[ 0.711310] tpm_tis 00:06: 1.2 TPM (device=id 0x1, rev-id 1)
|
||||
|
||||
#> dmesg | grep TCPA
|
||||
[ 0.000000] ACPI: TCPA 0x0000000003FFD191C 000032 (v02 BOCHS \
|
||||
BXPCTCPA 0000001 BXPC 00000001)
|
||||
|
||||
#> ls -l /dev/tpm*
|
||||
crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0
|
||||
|
||||
#> find /sys/devices/ | grep pcrs$ | xargs cat
|
||||
PCR-00: 35 4E 3B CE 23 9F 38 59 ...
|
||||
...
|
||||
PCR-23: 00 00 00 00 00 00 00 00 ...
|
||||
|
||||
|
||||
=== Migration with the TPM emulator ===
|
||||
|
||||
The TPM emulator supports the following types of virtual machine migration:
|
||||
|
||||
- VM save / restore (migration into a file)
|
||||
- Network migration
|
||||
- Snapshotting (migration into storage like QoW2 or QED)
|
||||
|
||||
The following command sequences can be used to test VM save / restore.
|
||||
|
||||
|
||||
In a 1st terminal start an instance of a swtpm using the following command:
|
||||
|
||||
mkdir /tmp/mytpm1
|
||||
swtpm socket --tpmstate dir=/tmp/mytpm1 \
|
||||
--ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \
|
||||
--log level=20 --tpm2
|
||||
|
||||
In a 2nd terminal start the VM:
|
||||
|
||||
qemu-system-x86_64 -display sdl -accel kvm \
|
||||
-m 1024 -boot d -bios bios-256k.bin -boot menu=on \
|
||||
-chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
|
||||
-tpmdev emulator,id=tpm0,chardev=chrtpm \
|
||||
-device tpm-tis,tpmdev=tpm0 \
|
||||
-monitor stdio \
|
||||
test.img
|
||||
|
||||
Verify that the attached TPM is working as expected using applications inside
|
||||
the VM.
|
||||
|
||||
To store the state of the VM use the following command in the QEMU monitor in
|
||||
the 2nd terminal:
|
||||
|
||||
(qemu) migrate "exec:cat > testvm.bin"
|
||||
(qemu) quit
|
||||
|
||||
At this point a file called 'testvm.bin' should exists and the swtpm and QEMU
|
||||
processes should have ended.
|
||||
|
||||
To test 'VM restore' you have to start the swtpm with the same parameters
|
||||
as before. If previously a TPM 2 [--tpm2] was saved, --tpm2 must now be
|
||||
passed again on the command line.
|
||||
|
||||
In the 1st terminal restart the swtpm with the same command line as before:
|
||||
|
||||
swtpm socket --tpmstate dir=/tmp/mytpm1 \
|
||||
--ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \
|
||||
--log level=20 --tpm2
|
||||
|
||||
In the 2nd terminal restore the state of the VM using the additional
|
||||
'-incoming' option.
|
||||
|
||||
qemu-system-x86_64 -display sdl -accel kvm \
|
||||
-m 1024 -boot d -bios bios-256k.bin -boot menu=on \
|
||||
-chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
|
||||
-tpmdev emulator,id=tpm0,chardev=chrtpm \
|
||||
-device tpm-tis,tpmdev=tpm0 \
|
||||
-incoming "exec:cat < testvm.bin" \
|
||||
test.img
|
||||
|
||||
|
||||
Troubleshooting migration:
|
||||
|
||||
There are several reasons why migration may fail. In case of problems,
|
||||
please ensure that the command lines adhere to the following rules and,
|
||||
if possible, that identical versions of QEMU and swtpm are used at all
|
||||
times.
|
||||
|
||||
VM save and restore:
|
||||
- QEMU command line parameters should be identical apart from the
|
||||
'-incoming' option on VM restore
|
||||
- swtpm command line parameters should be identical
|
||||
|
||||
VM migration to 'localhost':
|
||||
- QEMU command line parameters should be identical apart from the
|
||||
'-incoming' option on the destination side
|
||||
- swtpm command line parameters should point to two different
|
||||
directories on the source and destination swtpm (--tpmstate dir=...)
|
||||
(especially if different versions of libtpms were to be used on the
|
||||
same machine).
|
||||
|
||||
VM migration across the network:
|
||||
- QEMU command line parameters should be identical apart from the
|
||||
'-incoming' option on the destination side
|
||||
- swtpm command line parameters should be identical
|
||||
|
||||
VM Snapshotting:
|
||||
- QEMU command line parameters should be identical
|
||||
- swtpm command line parameters should be identical
|
||||
|
||||
|
||||
Besides that, migration failure reasons on the swtpm level may include
|
||||
the following:
|
||||
|
||||
- the versions of the swtpm on the source and destination sides are
|
||||
incompatible
|
||||
- downgrading of TPM state may not be supported
|
||||
- the source and destination libtpms were compiled with different
|
||||
compile-time options and the destination side refuses to accept the
|
||||
state
|
||||
- different migration keys are used on the source and destination side
|
||||
and the destination side cannot decrypt the migrated state
|
||||
(swtpm ... --migration-key ... )
|
|
@ -217,7 +217,7 @@ void icp_eoi(ICPState *icp, uint32_t xirr)
|
|||
}
|
||||
}
|
||||
|
||||
static void icp_irq(ICSState *ics, int server, int nr, uint8_t priority)
|
||||
void icp_irq(ICSState *ics, int server, int nr, uint8_t priority)
|
||||
{
|
||||
ICPState *icp = xics_icp_get(ics->xics, server);
|
||||
|
||||
|
@ -512,8 +512,14 @@ void ics_write_xive(ICSState *ics, int srcno, int server,
|
|||
|
||||
static void ics_reject(ICSState *ics, uint32_t nr)
|
||||
{
|
||||
ICSStateClass *isc = ICS_GET_CLASS(ics);
|
||||
ICSIRQState *irq = ics->irqs + nr - ics->offset;
|
||||
|
||||
if (isc->reject) {
|
||||
isc->reject(ics, nr);
|
||||
return;
|
||||
}
|
||||
|
||||
trace_xics_ics_reject(nr, nr - ics->offset);
|
||||
if (irq->flags & XICS_FLAGS_IRQ_MSI) {
|
||||
irq->status |= XICS_STATUS_REJECTED;
|
||||
|
@ -524,8 +530,14 @@ static void ics_reject(ICSState *ics, uint32_t nr)
|
|||
|
||||
void ics_resend(ICSState *ics)
|
||||
{
|
||||
ICSStateClass *isc = ICS_GET_CLASS(ics);
|
||||
int i;
|
||||
|
||||
if (isc->resend) {
|
||||
isc->resend(ics);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < ics->nr_irqs; i++) {
|
||||
/* FIXME: filter by server#? */
|
||||
if (ics->irqs[i].flags & XICS_FLAGS_IRQ_LSI) {
|
||||
|
|
|
@ -20,3 +20,5 @@ common-obj-$(CONFIG_PCI_EXPRESS_GENERIC_BRIDGE) += gpex.o
|
|||
common-obj-$(CONFIG_PCI_EXPRESS_XILINX) += xilinx-pcie.o
|
||||
|
||||
common-obj-$(CONFIG_PCI_EXPRESS_DESIGNWARE) += designware.o
|
||||
obj-$(CONFIG_POWERNV) += pnv_phb4.o pnv_phb4_pec.o
|
||||
obj-$(CONFIG_POWERNV) += pnv_phb3.o pnv_phb3_msi.o pnv_phb3_pbcq.o
|
||||
|
|
1197
hw/pci-host/pnv_phb3.c
Normal file
1197
hw/pci-host/pnv_phb3.c
Normal file
File diff suppressed because it is too large
Load diff
349
hw/pci-host/pnv_phb3_msi.c
Normal file
349
hw/pci-host/pnv_phb3_msi.c
Normal file
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
* QEMU PowerPC PowerNV (POWER8) PHB3 model
|
||||
*
|
||||
* Copyright (c) 2014-2020, IBM Corporation.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See the
|
||||
* COPYING file in the top-level directory.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/pci-host/pnv_phb3_regs.h"
|
||||
#include "hw/pci-host/pnv_phb3.h"
|
||||
#include "hw/ppc/pnv.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "sysemu/reset.h"
|
||||
|
||||
static uint64_t phb3_msi_ive_addr(PnvPHB3 *phb, int srcno)
|
||||
{
|
||||
uint64_t ivtbar = phb->regs[PHB_IVT_BAR >> 3];
|
||||
uint64_t phbctl = phb->regs[PHB_CONTROL >> 3];
|
||||
|
||||
if (!(ivtbar & PHB_IVT_BAR_ENABLE)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "Failed access to disable IVT BAR !");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (srcno >= (ivtbar & PHB_IVT_LENGTH_MASK)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "MSI out of bounds (%d vs 0x%"PRIx64")",
|
||||
srcno, (uint64_t) (ivtbar & PHB_IVT_LENGTH_MASK));
|
||||
return 0;
|
||||
}
|
||||
|
||||
ivtbar &= PHB_IVT_BASE_ADDRESS_MASK;
|
||||
|
||||
if (phbctl & PHB_CTRL_IVE_128_BYTES) {
|
||||
return ivtbar + 128 * srcno;
|
||||
} else {
|
||||
return ivtbar + 16 * srcno;
|
||||
}
|
||||
}
|
||||
|
||||
static bool phb3_msi_read_ive(PnvPHB3 *phb, int srcno, uint64_t *out_ive)
|
||||
{
|
||||
uint64_t ive_addr, ive;
|
||||
|
||||
ive_addr = phb3_msi_ive_addr(phb, srcno);
|
||||
if (!ive_addr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dma_memory_read(&address_space_memory, ive_addr, &ive, sizeof(ive))) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "Failed to read IVE at 0x%" PRIx64,
|
||||
ive_addr);
|
||||
return false;
|
||||
}
|
||||
*out_ive = be64_to_cpu(ive);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void phb3_msi_set_p(Phb3MsiState *msi, int srcno, uint8_t gen)
|
||||
{
|
||||
uint64_t ive_addr;
|
||||
uint8_t p = 0x01 | (gen << 1);
|
||||
|
||||
ive_addr = phb3_msi_ive_addr(msi->phb, srcno);
|
||||
if (!ive_addr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dma_memory_write(&address_space_memory, ive_addr + 4, &p, 1)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"Failed to write IVE (set P) at 0x%" PRIx64, ive_addr);
|
||||
}
|
||||
}
|
||||
|
||||
static void phb3_msi_set_q(Phb3MsiState *msi, int srcno)
|
||||
{
|
||||
uint64_t ive_addr;
|
||||
uint8_t q = 0x01;
|
||||
|
||||
ive_addr = phb3_msi_ive_addr(msi->phb, srcno);
|
||||
if (!ive_addr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dma_memory_write(&address_space_memory, ive_addr + 5, &q, 1)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"Failed to write IVE (set Q) at 0x%" PRIx64, ive_addr);
|
||||
}
|
||||
}
|
||||
|
||||
static void phb3_msi_try_send(Phb3MsiState *msi, int srcno, bool force)
|
||||
{
|
||||
ICSState *ics = ICS(msi);
|
||||
uint64_t ive;
|
||||
uint64_t server, prio, pq, gen;
|
||||
|
||||
if (!phb3_msi_read_ive(msi->phb, srcno, &ive)) {
|
||||
return;
|
||||
}
|
||||
|
||||
server = GETFIELD(IODA2_IVT_SERVER, ive);
|
||||
prio = GETFIELD(IODA2_IVT_PRIORITY, ive);
|
||||
if (!force) {
|
||||
pq = GETFIELD(IODA2_IVT_Q, ive) | (GETFIELD(IODA2_IVT_P, ive) << 1);
|
||||
} else {
|
||||
pq = 0;
|
||||
}
|
||||
gen = GETFIELD(IODA2_IVT_GEN, ive);
|
||||
|
||||
/*
|
||||
* The low order 2 bits are the link pointer (Type II interrupts).
|
||||
* Shift back to get a valid IRQ server.
|
||||
*/
|
||||
server >>= 2;
|
||||
|
||||
switch (pq) {
|
||||
case 0: /* 00 */
|
||||
if (prio == 0xff) {
|
||||
/* Masked, set Q */
|
||||
phb3_msi_set_q(msi, srcno);
|
||||
} else {
|
||||
/* Enabled, set P and send */
|
||||
phb3_msi_set_p(msi, srcno, gen);
|
||||
icp_irq(ics, server, srcno + ics->offset, prio);
|
||||
}
|
||||
break;
|
||||
case 2: /* 10 */
|
||||
/* Already pending, set Q */
|
||||
phb3_msi_set_q(msi, srcno);
|
||||
break;
|
||||
case 1: /* 01 */
|
||||
case 3: /* 11 */
|
||||
default:
|
||||
/* Just drop stuff if Q already set */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void phb3_msi_set_irq(void *opaque, int srcno, int val)
|
||||
{
|
||||
Phb3MsiState *msi = PHB3_MSI(opaque);
|
||||
|
||||
if (val) {
|
||||
phb3_msi_try_send(msi, srcno, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void pnv_phb3_msi_send(Phb3MsiState *msi, uint64_t addr, uint16_t data,
|
||||
int32_t dev_pe)
|
||||
{
|
||||
ICSState *ics = ICS(msi);
|
||||
uint64_t ive;
|
||||
uint16_t pe;
|
||||
uint32_t src = ((addr >> 4) & 0xffff) | (data & 0x1f);
|
||||
|
||||
if (src >= ics->nr_irqs) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "MSI %d out of bounds", src);
|
||||
return;
|
||||
}
|
||||
if (dev_pe >= 0) {
|
||||
if (!phb3_msi_read_ive(msi->phb, src, &ive)) {
|
||||
return;
|
||||
}
|
||||
pe = GETFIELD(IODA2_IVT_PE, ive);
|
||||
if (pe != dev_pe) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"MSI %d send by PE#%d but assigned to PE#%d",
|
||||
src, dev_pe, pe);
|
||||
return;
|
||||
}
|
||||
}
|
||||
qemu_irq_pulse(msi->qirqs[src]);
|
||||
}
|
||||
|
||||
void pnv_phb3_msi_ffi(Phb3MsiState *msi, uint64_t val)
|
||||
{
|
||||
/* Emit interrupt */
|
||||
pnv_phb3_msi_send(msi, val, 0, -1);
|
||||
|
||||
/* Clear FFI lock */
|
||||
msi->phb->regs[PHB_FFI_LOCK >> 3] = 0;
|
||||
}
|
||||
|
||||
static void phb3_msi_reject(ICSState *ics, uint32_t nr)
|
||||
{
|
||||
Phb3MsiState *msi = PHB3_MSI(ics);
|
||||
unsigned int srcno = nr - ics->offset;
|
||||
unsigned int idx = srcno >> 6;
|
||||
unsigned int bit = 1ull << (srcno & 0x3f);
|
||||
|
||||
assert(srcno < PHB3_MAX_MSI);
|
||||
|
||||
msi->rba[idx] |= bit;
|
||||
msi->rba_sum |= (1u << idx);
|
||||
}
|
||||
|
||||
static void phb3_msi_resend(ICSState *ics)
|
||||
{
|
||||
Phb3MsiState *msi = PHB3_MSI(ics);
|
||||
unsigned int i, j;
|
||||
|
||||
if (msi->rba_sum == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
if ((msi->rba_sum & (1u << i)) == 0) {
|
||||
continue;
|
||||
}
|
||||
msi->rba_sum &= ~(1u << i);
|
||||
for (j = 0; j < 64; j++) {
|
||||
if ((msi->rba[i] & (1ull << j)) == 0) {
|
||||
continue;
|
||||
}
|
||||
msi->rba[i] &= ~(1u << j);
|
||||
phb3_msi_try_send(msi, i * 64 + j, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void phb3_msi_reset(DeviceState *dev)
|
||||
{
|
||||
Phb3MsiState *msi = PHB3_MSI(dev);
|
||||
ICSStateClass *icsc = ICS_GET_CLASS(dev);
|
||||
|
||||
icsc->parent_reset(dev);
|
||||
|
||||
memset(msi->rba, 0, sizeof(msi->rba));
|
||||
msi->rba_sum = 0;
|
||||
}
|
||||
|
||||
static void phb3_msi_reset_handler(void *dev)
|
||||
{
|
||||
phb3_msi_reset(dev);
|
||||
}
|
||||
|
||||
void pnv_phb3_msi_update_config(Phb3MsiState *msi, uint32_t base,
|
||||
uint32_t count)
|
||||
{
|
||||
ICSState *ics = ICS(msi);
|
||||
|
||||
if (count > PHB3_MAX_MSI) {
|
||||
count = PHB3_MAX_MSI;
|
||||
}
|
||||
ics->nr_irqs = count;
|
||||
ics->offset = base;
|
||||
}
|
||||
|
||||
static void phb3_msi_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
Phb3MsiState *msi = PHB3_MSI(dev);
|
||||
ICSState *ics = ICS(msi);
|
||||
ICSStateClass *icsc = ICS_GET_CLASS(ics);
|
||||
Error *local_err = NULL;
|
||||
|
||||
assert(msi->phb);
|
||||
|
||||
icsc->parent_realize(dev, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
msi->qirqs = qemu_allocate_irqs(phb3_msi_set_irq, msi, ics->nr_irqs);
|
||||
|
||||
qemu_register_reset(phb3_msi_reset_handler, dev);
|
||||
}
|
||||
|
||||
static void phb3_msi_instance_init(Object *obj)
|
||||
{
|
||||
Phb3MsiState *msi = PHB3_MSI(obj);
|
||||
ICSState *ics = ICS(obj);
|
||||
|
||||
object_property_add_link(obj, "phb", TYPE_PNV_PHB3,
|
||||
(Object **)&msi->phb,
|
||||
object_property_allow_set_link,
|
||||
OBJ_PROP_LINK_STRONG,
|
||||
&error_abort);
|
||||
|
||||
/* Will be overriden later */
|
||||
ics->offset = 0;
|
||||
}
|
||||
|
||||
static void phb3_msi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
ICSStateClass *isc = ICS_CLASS(klass);
|
||||
|
||||
device_class_set_parent_realize(dc, phb3_msi_realize,
|
||||
&isc->parent_realize);
|
||||
device_class_set_parent_reset(dc, phb3_msi_reset,
|
||||
&isc->parent_reset);
|
||||
|
||||
isc->reject = phb3_msi_reject;
|
||||
isc->resend = phb3_msi_resend;
|
||||
}
|
||||
|
||||
static const TypeInfo phb3_msi_info = {
|
||||
.name = TYPE_PHB3_MSI,
|
||||
.parent = TYPE_ICS,
|
||||
.instance_size = sizeof(Phb3MsiState),
|
||||
.class_init = phb3_msi_class_init,
|
||||
.class_size = sizeof(ICSStateClass),
|
||||
.instance_init = phb3_msi_instance_init,
|
||||
};
|
||||
|
||||
static void pnv_phb3_msi_register_types(void)
|
||||
{
|
||||
type_register_static(&phb3_msi_info);
|
||||
}
|
||||
|
||||
type_init(pnv_phb3_msi_register_types);
|
||||
|
||||
void pnv_phb3_msi_pic_print_info(Phb3MsiState *msi, Monitor *mon)
|
||||
{
|
||||
ICSState *ics = ICS(msi);
|
||||
int i;
|
||||
|
||||
monitor_printf(mon, "ICS %4x..%4x %p\n",
|
||||
ics->offset, ics->offset + ics->nr_irqs - 1, ics);
|
||||
|
||||
for (i = 0; i < ics->nr_irqs; i++) {
|
||||
uint64_t ive;
|
||||
|
||||
if (!phb3_msi_read_ive(msi->phb, i, &ive)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GETFIELD(IODA2_IVT_PRIORITY, ive) == 0xff) {
|
||||
continue;
|
||||
}
|
||||
|
||||
monitor_printf(mon, " %4x %c%c server=%04x prio=%02x gen=%d\n",
|
||||
ics->offset + i,
|
||||
GETFIELD(IODA2_IVT_P, ive) ? 'P' : '-',
|
||||
GETFIELD(IODA2_IVT_Q, ive) ? 'Q' : '-',
|
||||
(uint32_t) GETFIELD(IODA2_IVT_SERVER, ive) >> 2,
|
||||
(uint32_t) GETFIELD(IODA2_IVT_PRIORITY, ive),
|
||||
(uint32_t) GETFIELD(IODA2_IVT_GEN, ive));
|
||||
}
|
||||
}
|
358
hw/pci-host/pnv_phb3_pbcq.c
Normal file
358
hw/pci-host/pnv_phb3_pbcq.c
Normal file
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
* QEMU PowerPC PowerNV (POWER8) PHB3 model
|
||||
*
|
||||
* Copyright (c) 2014-2020, IBM Corporation.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See the
|
||||
* COPYING file in the top-level directory.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/log.h"
|
||||
#include "target/ppc/cpu.h"
|
||||
#include "hw/ppc/fdt.h"
|
||||
#include "hw/pci-host/pnv_phb3_regs.h"
|
||||
#include "hw/pci-host/pnv_phb3.h"
|
||||
#include "hw/ppc/pnv.h"
|
||||
#include "hw/ppc/pnv_xscom.h"
|
||||
#include "hw/pci/pci_bridge.h"
|
||||
#include "hw/pci/pci_bus.h"
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
#define phb3_pbcq_error(pbcq, fmt, ...) \
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "phb3_pbcq[%d:%d]: " fmt "\n", \
|
||||
(pbcq)->phb->chip_id, (pbcq)->phb->phb_id, ## __VA_ARGS__)
|
||||
|
||||
static uint64_t pnv_pbcq_nest_xscom_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvPBCQState *pbcq = PNV_PBCQ(opaque);
|
||||
uint32_t offset = addr >> 3;
|
||||
|
||||
return pbcq->nest_regs[offset];
|
||||
}
|
||||
|
||||
static uint64_t pnv_pbcq_pci_xscom_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvPBCQState *pbcq = PNV_PBCQ(opaque);
|
||||
uint32_t offset = addr >> 3;
|
||||
|
||||
return pbcq->pci_regs[offset];
|
||||
}
|
||||
|
||||
static uint64_t pnv_pbcq_spci_xscom_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvPBCQState *pbcq = PNV_PBCQ(opaque);
|
||||
uint32_t offset = addr >> 3;
|
||||
|
||||
if (offset == PBCQ_SPCI_ASB_DATA) {
|
||||
return pnv_phb3_reg_read(pbcq->phb,
|
||||
pbcq->spci_regs[PBCQ_SPCI_ASB_ADDR], 8);
|
||||
}
|
||||
return pbcq->spci_regs[offset];
|
||||
}
|
||||
|
||||
static void pnv_pbcq_update_map(PnvPBCQState *pbcq)
|
||||
{
|
||||
uint64_t bar_en = pbcq->nest_regs[PBCQ_NEST_BAR_EN];
|
||||
uint64_t bar, mask, size;
|
||||
|
||||
/*
|
||||
* NOTE: This will really not work well if those are remapped
|
||||
* after the PHB has created its sub regions. We could do better
|
||||
* if we had a way to resize regions but we don't really care
|
||||
* that much in practice as the stuff below really only happens
|
||||
* once early during boot
|
||||
*/
|
||||
|
||||
/* Handle unmaps */
|
||||
if (memory_region_is_mapped(&pbcq->mmbar0) &&
|
||||
!(bar_en & PBCQ_NEST_BAR_EN_MMIO0)) {
|
||||
memory_region_del_subregion(get_system_memory(), &pbcq->mmbar0);
|
||||
}
|
||||
if (memory_region_is_mapped(&pbcq->mmbar1) &&
|
||||
!(bar_en & PBCQ_NEST_BAR_EN_MMIO1)) {
|
||||
memory_region_del_subregion(get_system_memory(), &pbcq->mmbar1);
|
||||
}
|
||||
if (memory_region_is_mapped(&pbcq->phbbar) &&
|
||||
!(bar_en & PBCQ_NEST_BAR_EN_PHB)) {
|
||||
memory_region_del_subregion(get_system_memory(), &pbcq->phbbar);
|
||||
}
|
||||
|
||||
/* Update PHB */
|
||||
pnv_phb3_update_regions(pbcq->phb);
|
||||
|
||||
/* Handle maps */
|
||||
if (!memory_region_is_mapped(&pbcq->mmbar0) &&
|
||||
(bar_en & PBCQ_NEST_BAR_EN_MMIO0)) {
|
||||
bar = pbcq->nest_regs[PBCQ_NEST_MMIO_BAR0] >> 14;
|
||||
mask = pbcq->nest_regs[PBCQ_NEST_MMIO_MASK0];
|
||||
size = ((~mask) >> 14) + 1;
|
||||
memory_region_init(&pbcq->mmbar0, OBJECT(pbcq), "pbcq-mmio0", size);
|
||||
memory_region_add_subregion(get_system_memory(), bar, &pbcq->mmbar0);
|
||||
pbcq->mmio0_base = bar;
|
||||
pbcq->mmio0_size = size;
|
||||
}
|
||||
if (!memory_region_is_mapped(&pbcq->mmbar1) &&
|
||||
(bar_en & PBCQ_NEST_BAR_EN_MMIO1)) {
|
||||
bar = pbcq->nest_regs[PBCQ_NEST_MMIO_BAR1] >> 14;
|
||||
mask = pbcq->nest_regs[PBCQ_NEST_MMIO_MASK1];
|
||||
size = ((~mask) >> 14) + 1;
|
||||
memory_region_init(&pbcq->mmbar1, OBJECT(pbcq), "pbcq-mmio1", size);
|
||||
memory_region_add_subregion(get_system_memory(), bar, &pbcq->mmbar1);
|
||||
pbcq->mmio1_base = bar;
|
||||
pbcq->mmio1_size = size;
|
||||
}
|
||||
if (!memory_region_is_mapped(&pbcq->phbbar)
|
||||
&& (bar_en & PBCQ_NEST_BAR_EN_PHB)) {
|
||||
bar = pbcq->nest_regs[PBCQ_NEST_PHB_BAR] >> 14;
|
||||
size = 0x1000;
|
||||
memory_region_init(&pbcq->phbbar, OBJECT(pbcq), "pbcq-phb", size);
|
||||
memory_region_add_subregion(get_system_memory(), bar, &pbcq->phbbar);
|
||||
}
|
||||
|
||||
/* Update PHB */
|
||||
pnv_phb3_update_regions(pbcq->phb);
|
||||
}
|
||||
|
||||
static void pnv_pbcq_nest_xscom_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
PnvPBCQState *pbcq = PNV_PBCQ(opaque);
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
switch (reg) {
|
||||
case PBCQ_NEST_MMIO_BAR0:
|
||||
case PBCQ_NEST_MMIO_BAR1:
|
||||
case PBCQ_NEST_MMIO_MASK0:
|
||||
case PBCQ_NEST_MMIO_MASK1:
|
||||
if (pbcq->nest_regs[PBCQ_NEST_BAR_EN] &
|
||||
(PBCQ_NEST_BAR_EN_MMIO0 |
|
||||
PBCQ_NEST_BAR_EN_MMIO1)) {
|
||||
phb3_pbcq_error(pbcq, "Changing enabled BAR unsupported");
|
||||
}
|
||||
pbcq->nest_regs[reg] = val & 0xffffffffc0000000ull;
|
||||
break;
|
||||
case PBCQ_NEST_PHB_BAR:
|
||||
if (pbcq->nest_regs[PBCQ_NEST_BAR_EN] & PBCQ_NEST_BAR_EN_PHB) {
|
||||
phb3_pbcq_error(pbcq, "Changing enabled BAR unsupported");
|
||||
}
|
||||
pbcq->nest_regs[reg] = val & 0xfffffffffc000000ull;
|
||||
break;
|
||||
case PBCQ_NEST_BAR_EN:
|
||||
pbcq->nest_regs[reg] = val & 0xf800000000000000ull;
|
||||
pnv_pbcq_update_map(pbcq);
|
||||
pnv_phb3_remap_irqs(pbcq->phb);
|
||||
break;
|
||||
case PBCQ_NEST_IRSN_COMPARE:
|
||||
case PBCQ_NEST_IRSN_MASK:
|
||||
pbcq->nest_regs[reg] = val & PBCQ_NEST_IRSN_COMP;
|
||||
pnv_phb3_remap_irqs(pbcq->phb);
|
||||
break;
|
||||
case PBCQ_NEST_LSI_SRC_ID:
|
||||
pbcq->nest_regs[reg] = val & PBCQ_NEST_LSI_SRC;
|
||||
pnv_phb3_remap_irqs(pbcq->phb);
|
||||
break;
|
||||
default:
|
||||
phb3_pbcq_error(pbcq, "%s @0x%"HWADDR_PRIx"=%"PRIx64, __func__,
|
||||
addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void pnv_pbcq_pci_xscom_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
PnvPBCQState *pbcq = PNV_PBCQ(opaque);
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
switch (reg) {
|
||||
case PBCQ_PCI_BAR2:
|
||||
pbcq->pci_regs[reg] = val & 0xfffffffffc000000ull;
|
||||
pnv_pbcq_update_map(pbcq);
|
||||
default:
|
||||
phb3_pbcq_error(pbcq, "%s @0x%"HWADDR_PRIx"=%"PRIx64, __func__,
|
||||
addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void pnv_pbcq_spci_xscom_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
PnvPBCQState *pbcq = PNV_PBCQ(opaque);
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
switch (reg) {
|
||||
case PBCQ_SPCI_ASB_ADDR:
|
||||
pbcq->spci_regs[reg] = val & 0xfff;
|
||||
break;
|
||||
case PBCQ_SPCI_ASB_STATUS:
|
||||
pbcq->spci_regs[reg] &= ~val;
|
||||
break;
|
||||
case PBCQ_SPCI_ASB_DATA:
|
||||
pnv_phb3_reg_write(pbcq->phb, pbcq->spci_regs[PBCQ_SPCI_ASB_ADDR],
|
||||
val, 8);
|
||||
break;
|
||||
case PBCQ_SPCI_AIB_CAPP_EN:
|
||||
case PBCQ_SPCI_CAPP_SEC_TMR:
|
||||
break;
|
||||
default:
|
||||
phb3_pbcq_error(pbcq, "%s @0x%"HWADDR_PRIx"=%"PRIx64, __func__,
|
||||
addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pnv_pbcq_nest_xscom_ops = {
|
||||
.read = pnv_pbcq_nest_xscom_read,
|
||||
.write = pnv_pbcq_nest_xscom_write,
|
||||
.valid.min_access_size = 8,
|
||||
.valid.max_access_size = 8,
|
||||
.impl.min_access_size = 8,
|
||||
.impl.max_access_size = 8,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps pnv_pbcq_pci_xscom_ops = {
|
||||
.read = pnv_pbcq_pci_xscom_read,
|
||||
.write = pnv_pbcq_pci_xscom_write,
|
||||
.valid.min_access_size = 8,
|
||||
.valid.max_access_size = 8,
|
||||
.impl.min_access_size = 8,
|
||||
.impl.max_access_size = 8,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps pnv_pbcq_spci_xscom_ops = {
|
||||
.read = pnv_pbcq_spci_xscom_read,
|
||||
.write = pnv_pbcq_spci_xscom_write,
|
||||
.valid.min_access_size = 8,
|
||||
.valid.max_access_size = 8,
|
||||
.impl.min_access_size = 8,
|
||||
.impl.max_access_size = 8,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static void pnv_pbcq_default_bars(PnvPBCQState *pbcq)
|
||||
{
|
||||
uint64_t mm0, mm1, reg;
|
||||
PnvPHB3 *phb = pbcq->phb;
|
||||
|
||||
mm0 = 0x3d00000000000ull + 0x4000000000ull * phb->chip_id +
|
||||
0x1000000000ull * phb->phb_id;
|
||||
mm1 = 0x3ff8000000000ull + 0x0200000000ull * phb->chip_id +
|
||||
0x0080000000ull * phb->phb_id;
|
||||
reg = 0x3fffe40000000ull + 0x0000400000ull * phb->chip_id +
|
||||
0x0000100000ull * phb->phb_id;
|
||||
|
||||
pbcq->nest_regs[PBCQ_NEST_MMIO_BAR0] = mm0 << 14;
|
||||
pbcq->nest_regs[PBCQ_NEST_MMIO_BAR1] = mm1 << 14;
|
||||
pbcq->nest_regs[PBCQ_NEST_PHB_BAR] = reg << 14;
|
||||
pbcq->nest_regs[PBCQ_NEST_MMIO_MASK0] = 0x3fff000000000ull << 14;
|
||||
pbcq->nest_regs[PBCQ_NEST_MMIO_MASK1] = 0x3ffff80000000ull << 14;
|
||||
pbcq->pci_regs[PBCQ_PCI_BAR2] = reg << 14;
|
||||
}
|
||||
|
||||
static void pnv_pbcq_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvPBCQState *pbcq = PNV_PBCQ(dev);
|
||||
PnvPHB3 *phb;
|
||||
char name[32];
|
||||
|
||||
assert(pbcq->phb);
|
||||
phb = pbcq->phb;
|
||||
|
||||
/* TODO: Fix OPAL to do that: establish default BAR values */
|
||||
pnv_pbcq_default_bars(pbcq);
|
||||
|
||||
/* Initialize the XSCOM region for the PBCQ registers */
|
||||
snprintf(name, sizeof(name), "xscom-pbcq-nest-%d.%d",
|
||||
phb->chip_id, phb->phb_id);
|
||||
pnv_xscom_region_init(&pbcq->xscom_nest_regs, OBJECT(dev),
|
||||
&pnv_pbcq_nest_xscom_ops, pbcq, name,
|
||||
PNV_XSCOM_PBCQ_NEST_SIZE);
|
||||
snprintf(name, sizeof(name), "xscom-pbcq-pci-%d.%d",
|
||||
phb->chip_id, phb->phb_id);
|
||||
pnv_xscom_region_init(&pbcq->xscom_pci_regs, OBJECT(dev),
|
||||
&pnv_pbcq_pci_xscom_ops, pbcq, name,
|
||||
PNV_XSCOM_PBCQ_PCI_SIZE);
|
||||
snprintf(name, sizeof(name), "xscom-pbcq-spci-%d.%d",
|
||||
phb->chip_id, phb->phb_id);
|
||||
pnv_xscom_region_init(&pbcq->xscom_spci_regs, OBJECT(dev),
|
||||
&pnv_pbcq_spci_xscom_ops, pbcq, name,
|
||||
PNV_XSCOM_PBCQ_SPCI_SIZE);
|
||||
}
|
||||
|
||||
static int pnv_pbcq_dt_xscom(PnvXScomInterface *dev, void *fdt,
|
||||
int xscom_offset)
|
||||
{
|
||||
const char compat[] = "ibm,power8-pbcq";
|
||||
PnvPHB3 *phb = PNV_PBCQ(dev)->phb;
|
||||
char *name;
|
||||
int offset;
|
||||
uint32_t lpc_pcba = PNV_XSCOM_PBCQ_NEST_BASE + 0x400 * phb->phb_id;
|
||||
uint32_t reg[] = {
|
||||
cpu_to_be32(lpc_pcba),
|
||||
cpu_to_be32(PNV_XSCOM_PBCQ_NEST_SIZE),
|
||||
cpu_to_be32(PNV_XSCOM_PBCQ_PCI_BASE + 0x400 * phb->phb_id),
|
||||
cpu_to_be32(PNV_XSCOM_PBCQ_PCI_SIZE),
|
||||
cpu_to_be32(PNV_XSCOM_PBCQ_SPCI_BASE + 0x040 * phb->phb_id),
|
||||
cpu_to_be32(PNV_XSCOM_PBCQ_SPCI_SIZE)
|
||||
};
|
||||
|
||||
name = g_strdup_printf("pbcq@%x", lpc_pcba);
|
||||
offset = fdt_add_subnode(fdt, xscom_offset, name);
|
||||
_FDT(offset);
|
||||
g_free(name);
|
||||
|
||||
_FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
|
||||
|
||||
_FDT((fdt_setprop_cell(fdt, offset, "ibm,phb-index", phb->phb_id)));
|
||||
_FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", phb->chip_id)));
|
||||
_FDT((fdt_setprop(fdt, offset, "compatible", compat,
|
||||
sizeof(compat))));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void phb3_pbcq_instance_init(Object *obj)
|
||||
{
|
||||
PnvPBCQState *pbcq = PNV_PBCQ(obj);
|
||||
|
||||
object_property_add_link(obj, "phb", TYPE_PNV_PHB3,
|
||||
(Object **)&pbcq->phb,
|
||||
object_property_allow_set_link,
|
||||
OBJ_PROP_LINK_STRONG,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static void pnv_pbcq_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
|
||||
|
||||
xdc->dt_xscom = pnv_pbcq_dt_xscom;
|
||||
|
||||
dc->realize = pnv_pbcq_realize;
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_pbcq_type_info = {
|
||||
.name = TYPE_PNV_PBCQ,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(PnvPBCQState),
|
||||
.instance_init = phb3_pbcq_instance_init,
|
||||
.class_init = pnv_pbcq_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_PNV_XSCOM_INTERFACE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void pnv_pbcq_register_types(void)
|
||||
{
|
||||
type_register_static(&pnv_pbcq_type_info);
|
||||
}
|
||||
|
||||
type_init(pnv_pbcq_register_types)
|
1439
hw/pci-host/pnv_phb4.c
Normal file
1439
hw/pci-host/pnv_phb4.c
Normal file
File diff suppressed because it is too large
Load diff
595
hw/pci-host/pnv_phb4_pec.c
Normal file
595
hw/pci-host/pnv_phb4_pec.c
Normal file
|
@ -0,0 +1,595 @@
|
|||
/*
|
||||
* QEMU PowerPC PowerNV (POWER9) PHB4 model
|
||||
*
|
||||
* Copyright (c) 2018-2020, IBM Corporation.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See the
|
||||
* COPYING file in the top-level directory.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/log.h"
|
||||
#include "target/ppc/cpu.h"
|
||||
#include "hw/ppc/fdt.h"
|
||||
#include "hw/pci-host/pnv_phb4_regs.h"
|
||||
#include "hw/pci-host/pnv_phb4.h"
|
||||
#include "hw/ppc/pnv_xscom.h"
|
||||
#include "hw/pci/pci_bridge.h"
|
||||
#include "hw/pci/pci_bus.h"
|
||||
#include "hw/ppc/pnv.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
#define phb_pec_error(pec, fmt, ...) \
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "phb4_pec[%d:%d]: " fmt "\n", \
|
||||
(pec)->chip_id, (pec)->index, ## __VA_ARGS__)
|
||||
|
||||
|
||||
static uint64_t pnv_pec_nest_xscom_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque);
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
/* TODO: add list of allowed registers and error out if not */
|
||||
return pec->nest_regs[reg];
|
||||
}
|
||||
|
||||
static void pnv_pec_nest_xscom_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque);
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
switch (reg) {
|
||||
case PEC_NEST_PBCQ_HW_CONFIG:
|
||||
case PEC_NEST_DROP_PRIO_CTRL:
|
||||
case PEC_NEST_PBCQ_ERR_INJECT:
|
||||
case PEC_NEST_PCI_NEST_CLK_TRACE_CTL:
|
||||
case PEC_NEST_PBCQ_PMON_CTRL:
|
||||
case PEC_NEST_PBCQ_PBUS_ADDR_EXT:
|
||||
case PEC_NEST_PBCQ_PRED_VEC_TIMEOUT:
|
||||
case PEC_NEST_CAPP_CTRL:
|
||||
case PEC_NEST_PBCQ_READ_STK_OVR:
|
||||
case PEC_NEST_PBCQ_WRITE_STK_OVR:
|
||||
case PEC_NEST_PBCQ_STORE_STK_OVR:
|
||||
case PEC_NEST_PBCQ_RETRY_BKOFF_CTRL:
|
||||
pec->nest_regs[reg] = val;
|
||||
break;
|
||||
default:
|
||||
phb_pec_error(pec, "%s @0x%"HWADDR_PRIx"=%"PRIx64"\n", __func__,
|
||||
addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pnv_pec_nest_xscom_ops = {
|
||||
.read = pnv_pec_nest_xscom_read,
|
||||
.write = pnv_pec_nest_xscom_write,
|
||||
.valid.min_access_size = 8,
|
||||
.valid.max_access_size = 8,
|
||||
.impl.min_access_size = 8,
|
||||
.impl.max_access_size = 8,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static uint64_t pnv_pec_pci_xscom_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque);
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
/* TODO: add list of allowed registers and error out if not */
|
||||
return pec->pci_regs[reg];
|
||||
}
|
||||
|
||||
static void pnv_pec_pci_xscom_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque);
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
switch (reg) {
|
||||
case PEC_PCI_PBAIB_HW_CONFIG:
|
||||
case PEC_PCI_PBAIB_READ_STK_OVR:
|
||||
pec->pci_regs[reg] = val;
|
||||
break;
|
||||
default:
|
||||
phb_pec_error(pec, "%s @0x%"HWADDR_PRIx"=%"PRIx64"\n", __func__,
|
||||
addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pnv_pec_pci_xscom_ops = {
|
||||
.read = pnv_pec_pci_xscom_read,
|
||||
.write = pnv_pec_pci_xscom_write,
|
||||
.valid.min_access_size = 8,
|
||||
.valid.max_access_size = 8,
|
||||
.impl.min_access_size = 8,
|
||||
.impl.max_access_size = 8,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static uint64_t pnv_pec_stk_nest_xscom_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
/* TODO: add list of allowed registers and error out if not */
|
||||
return stack->nest_regs[reg];
|
||||
}
|
||||
|
||||
static void pnv_pec_stk_update_map(PnvPhb4PecStack *stack)
|
||||
{
|
||||
PnvPhb4PecState *pec = stack->pec;
|
||||
MemoryRegion *sysmem = pec->system_memory;
|
||||
uint64_t bar_en = stack->nest_regs[PEC_NEST_STK_BAR_EN];
|
||||
uint64_t bar, mask, size;
|
||||
char name[64];
|
||||
|
||||
/*
|
||||
* NOTE: This will really not work well if those are remapped
|
||||
* after the PHB has created its sub regions. We could do better
|
||||
* if we had a way to resize regions but we don't really care
|
||||
* that much in practice as the stuff below really only happens
|
||||
* once early during boot
|
||||
*/
|
||||
|
||||
/* Handle unmaps */
|
||||
if (memory_region_is_mapped(&stack->mmbar0) &&
|
||||
!(bar_en & PEC_NEST_STK_BAR_EN_MMIO0)) {
|
||||
memory_region_del_subregion(sysmem, &stack->mmbar0);
|
||||
}
|
||||
if (memory_region_is_mapped(&stack->mmbar1) &&
|
||||
!(bar_en & PEC_NEST_STK_BAR_EN_MMIO1)) {
|
||||
memory_region_del_subregion(sysmem, &stack->mmbar1);
|
||||
}
|
||||
if (memory_region_is_mapped(&stack->phbbar) &&
|
||||
!(bar_en & PEC_NEST_STK_BAR_EN_PHB)) {
|
||||
memory_region_del_subregion(sysmem, &stack->phbbar);
|
||||
}
|
||||
if (memory_region_is_mapped(&stack->intbar) &&
|
||||
!(bar_en & PEC_NEST_STK_BAR_EN_INT)) {
|
||||
memory_region_del_subregion(sysmem, &stack->intbar);
|
||||
}
|
||||
|
||||
/* Update PHB */
|
||||
pnv_phb4_update_regions(stack);
|
||||
|
||||
/* Handle maps */
|
||||
if (!memory_region_is_mapped(&stack->mmbar0) &&
|
||||
(bar_en & PEC_NEST_STK_BAR_EN_MMIO0)) {
|
||||
bar = stack->nest_regs[PEC_NEST_STK_MMIO_BAR0] >> 8;
|
||||
mask = stack->nest_regs[PEC_NEST_STK_MMIO_BAR0_MASK];
|
||||
size = ((~mask) >> 8) + 1;
|
||||
snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-mmio0",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
memory_region_init(&stack->mmbar0, OBJECT(stack), name, size);
|
||||
memory_region_add_subregion(sysmem, bar, &stack->mmbar0);
|
||||
stack->mmio0_base = bar;
|
||||
stack->mmio0_size = size;
|
||||
}
|
||||
if (!memory_region_is_mapped(&stack->mmbar1) &&
|
||||
(bar_en & PEC_NEST_STK_BAR_EN_MMIO1)) {
|
||||
bar = stack->nest_regs[PEC_NEST_STK_MMIO_BAR1] >> 8;
|
||||
mask = stack->nest_regs[PEC_NEST_STK_MMIO_BAR1_MASK];
|
||||
size = ((~mask) >> 8) + 1;
|
||||
snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-mmio1",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
memory_region_init(&stack->mmbar1, OBJECT(stack), name, size);
|
||||
memory_region_add_subregion(sysmem, bar, &stack->mmbar1);
|
||||
stack->mmio1_base = bar;
|
||||
stack->mmio1_size = size;
|
||||
}
|
||||
if (!memory_region_is_mapped(&stack->phbbar) &&
|
||||
(bar_en & PEC_NEST_STK_BAR_EN_PHB)) {
|
||||
bar = stack->nest_regs[PEC_NEST_STK_PHB_REGS_BAR] >> 8;
|
||||
size = PNV_PHB4_NUM_REGS << 3;
|
||||
snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-phb",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
memory_region_init(&stack->phbbar, OBJECT(stack), name, size);
|
||||
memory_region_add_subregion(sysmem, bar, &stack->phbbar);
|
||||
}
|
||||
if (!memory_region_is_mapped(&stack->intbar) &&
|
||||
(bar_en & PEC_NEST_STK_BAR_EN_INT)) {
|
||||
bar = stack->nest_regs[PEC_NEST_STK_INT_BAR] >> 8;
|
||||
size = PNV_PHB4_MAX_INTs << 16;
|
||||
snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-int",
|
||||
stack->pec->chip_id, stack->pec->index, stack->stack_no);
|
||||
memory_region_init(&stack->intbar, OBJECT(stack), name, size);
|
||||
memory_region_add_subregion(sysmem, bar, &stack->intbar);
|
||||
}
|
||||
|
||||
/* Update PHB */
|
||||
pnv_phb4_update_regions(stack);
|
||||
}
|
||||
|
||||
static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
|
||||
PnvPhb4PecState *pec = stack->pec;
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
switch (reg) {
|
||||
case PEC_NEST_STK_PCI_NEST_FIR:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] = val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_CLR:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] &= val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_SET:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] |= val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_MSK:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] = val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_MSKC:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] &= val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_MSKS:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] |= val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_ACT0:
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_ACT1:
|
||||
stack->nest_regs[reg] = val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_WOF:
|
||||
stack->nest_regs[reg] = 0;
|
||||
break;
|
||||
case PEC_NEST_STK_ERR_REPORT_0:
|
||||
case PEC_NEST_STK_ERR_REPORT_1:
|
||||
case PEC_NEST_STK_PBCQ_GNRL_STATUS:
|
||||
/* Flag error ? */
|
||||
break;
|
||||
case PEC_NEST_STK_PBCQ_MODE:
|
||||
stack->nest_regs[reg] = val & 0xff00000000000000ull;
|
||||
break;
|
||||
case PEC_NEST_STK_MMIO_BAR0:
|
||||
case PEC_NEST_STK_MMIO_BAR0_MASK:
|
||||
case PEC_NEST_STK_MMIO_BAR1:
|
||||
case PEC_NEST_STK_MMIO_BAR1_MASK:
|
||||
if (stack->nest_regs[PEC_NEST_STK_BAR_EN] &
|
||||
(PEC_NEST_STK_BAR_EN_MMIO0 |
|
||||
PEC_NEST_STK_BAR_EN_MMIO1)) {
|
||||
phb_pec_error(pec, "Changing enabled BAR unsupported\n");
|
||||
}
|
||||
stack->nest_regs[reg] = val & 0xffffffffff000000ull;
|
||||
break;
|
||||
case PEC_NEST_STK_PHB_REGS_BAR:
|
||||
if (stack->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_PHB) {
|
||||
phb_pec_error(pec, "Changing enabled BAR unsupported\n");
|
||||
}
|
||||
stack->nest_regs[reg] = val & 0xffffffffffc00000ull;
|
||||
break;
|
||||
case PEC_NEST_STK_INT_BAR:
|
||||
if (stack->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_INT) {
|
||||
phb_pec_error(pec, "Changing enabled BAR unsupported\n");
|
||||
}
|
||||
stack->nest_regs[reg] = val & 0xfffffff000000000ull;
|
||||
break;
|
||||
case PEC_NEST_STK_BAR_EN:
|
||||
stack->nest_regs[reg] = val & 0xf000000000000000ull;
|
||||
pnv_pec_stk_update_map(stack);
|
||||
break;
|
||||
case PEC_NEST_STK_DATA_FRZ_TYPE:
|
||||
case PEC_NEST_STK_PBCQ_TUN_BAR:
|
||||
/* Not used for now */
|
||||
stack->nest_regs[reg] = val;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "phb4_pec: nest_xscom_write 0x%"HWADDR_PRIx
|
||||
"=%"PRIx64"\n", addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pnv_pec_stk_nest_xscom_ops = {
|
||||
.read = pnv_pec_stk_nest_xscom_read,
|
||||
.write = pnv_pec_stk_nest_xscom_write,
|
||||
.valid.min_access_size = 8,
|
||||
.valid.max_access_size = 8,
|
||||
.impl.min_access_size = 8,
|
||||
.impl.max_access_size = 8,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static uint64_t pnv_pec_stk_pci_xscom_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
/* TODO: add list of allowed registers and error out if not */
|
||||
return stack->pci_regs[reg];
|
||||
}
|
||||
|
||||
static void pnv_pec_stk_pci_xscom_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
switch (reg) {
|
||||
case PEC_PCI_STK_PCI_FIR:
|
||||
stack->nest_regs[reg] = val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_CLR:
|
||||
stack->nest_regs[PEC_PCI_STK_PCI_FIR] &= val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_SET:
|
||||
stack->nest_regs[PEC_PCI_STK_PCI_FIR] |= val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_MSK:
|
||||
stack->nest_regs[reg] = val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_MSKC:
|
||||
stack->nest_regs[PEC_PCI_STK_PCI_FIR_MSK] &= val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_MSKS:
|
||||
stack->nest_regs[PEC_PCI_STK_PCI_FIR_MSK] |= val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_ACT0:
|
||||
case PEC_PCI_STK_PCI_FIR_ACT1:
|
||||
stack->nest_regs[reg] = val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_WOF:
|
||||
stack->nest_regs[reg] = 0;
|
||||
break;
|
||||
case PEC_PCI_STK_ETU_RESET:
|
||||
stack->nest_regs[reg] = val & 0x8000000000000000ull;
|
||||
/* TODO: Implement reset */
|
||||
break;
|
||||
case PEC_PCI_STK_PBAIB_ERR_REPORT:
|
||||
break;
|
||||
case PEC_PCI_STK_PBAIB_TX_CMD_CRED:
|
||||
case PEC_PCI_STK_PBAIB_TX_DAT_CRED:
|
||||
stack->nest_regs[reg] = val;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "phb4_pec_stk: pci_xscom_write 0x%"HWADDR_PRIx
|
||||
"=%"PRIx64"\n", addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pnv_pec_stk_pci_xscom_ops = {
|
||||
.read = pnv_pec_stk_pci_xscom_read,
|
||||
.write = pnv_pec_stk_pci_xscom_write,
|
||||
.valid.min_access_size = 8,
|
||||
.valid.max_access_size = 8,
|
||||
.impl.min_access_size = 8,
|
||||
.impl.max_access_size = 8,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static void pnv_pec_instance_init(Object *obj)
|
||||
{
|
||||
PnvPhb4PecState *pec = PNV_PHB4_PEC(obj);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PHB4_PEC_MAX_STACKS; i++) {
|
||||
object_initialize_child(obj, "stack[*]", &pec->stacks[i],
|
||||
sizeof(pec->stacks[i]), TYPE_PNV_PHB4_PEC_STACK,
|
||||
&error_abort, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void pnv_pec_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvPhb4PecState *pec = PNV_PHB4_PEC(dev);
|
||||
Error *local_err = NULL;
|
||||
char name[64];
|
||||
int i;
|
||||
|
||||
assert(pec->system_memory);
|
||||
|
||||
/* Create stacks */
|
||||
for (i = 0; i < pec->num_stacks; i++) {
|
||||
PnvPhb4PecStack *stack = &pec->stacks[i];
|
||||
Object *stk_obj = OBJECT(stack);
|
||||
|
||||
object_property_set_int(stk_obj, i, "stack-no", &error_abort);
|
||||
object_property_set_link(stk_obj, OBJECT(pec), "pec", &error_abort);
|
||||
object_property_set_bool(stk_obj, true, "realized", errp);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize the XSCOM regions for the PEC registers */
|
||||
snprintf(name, sizeof(name), "xscom-pec-%d.%d-nest", pec->chip_id,
|
||||
pec->index);
|
||||
pnv_xscom_region_init(&pec->nest_regs_mr, OBJECT(dev),
|
||||
&pnv_pec_nest_xscom_ops, pec, name,
|
||||
PHB4_PEC_NEST_REGS_COUNT);
|
||||
|
||||
snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci", pec->chip_id,
|
||||
pec->index);
|
||||
pnv_xscom_region_init(&pec->pci_regs_mr, OBJECT(dev),
|
||||
&pnv_pec_pci_xscom_ops, pec, name,
|
||||
PHB4_PEC_PCI_REGS_COUNT);
|
||||
}
|
||||
|
||||
static int pnv_pec_dt_xscom(PnvXScomInterface *dev, void *fdt,
|
||||
int xscom_offset)
|
||||
{
|
||||
PnvPhb4PecState *pec = PNV_PHB4_PEC(dev);
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(dev);
|
||||
uint32_t nbase = pecc->xscom_nest_base(pec);
|
||||
uint32_t pbase = pecc->xscom_pci_base(pec);
|
||||
int offset, i;
|
||||
char *name;
|
||||
uint32_t reg[] = {
|
||||
cpu_to_be32(nbase),
|
||||
cpu_to_be32(pecc->xscom_nest_size),
|
||||
cpu_to_be32(pbase),
|
||||
cpu_to_be32(pecc->xscom_pci_size),
|
||||
};
|
||||
|
||||
name = g_strdup_printf("pbcq@%x", nbase);
|
||||
offset = fdt_add_subnode(fdt, xscom_offset, name);
|
||||
_FDT(offset);
|
||||
g_free(name);
|
||||
|
||||
_FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
|
||||
|
||||
_FDT((fdt_setprop_cell(fdt, offset, "ibm,pec-index", pec->index)));
|
||||
_FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 1)));
|
||||
_FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0)));
|
||||
_FDT((fdt_setprop(fdt, offset, "compatible", pecc->compat,
|
||||
pecc->compat_size)));
|
||||
|
||||
for (i = 0; i < pec->num_stacks; i++) {
|
||||
PnvPhb4PecStack *stack = &pec->stacks[i];
|
||||
PnvPHB4 *phb = &stack->phb;
|
||||
int stk_offset;
|
||||
|
||||
name = g_strdup_printf("stack@%x", i);
|
||||
stk_offset = fdt_add_subnode(fdt, offset, name);
|
||||
_FDT(stk_offset);
|
||||
g_free(name);
|
||||
_FDT((fdt_setprop(fdt, stk_offset, "compatible", pecc->stk_compat,
|
||||
pecc->stk_compat_size)));
|
||||
_FDT((fdt_setprop_cell(fdt, stk_offset, "reg", i)));
|
||||
_FDT((fdt_setprop_cell(fdt, stk_offset, "ibm,phb-index", phb->phb_id)));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static uint32_t pnv_pec_xscom_pci_base(PnvPhb4PecState *pec)
|
||||
{
|
||||
return PNV9_XSCOM_PEC_PCI_BASE + 0x1000000 * pec->index;
|
||||
}
|
||||
|
||||
static uint32_t pnv_pec_xscom_nest_base(PnvPhb4PecState *pec)
|
||||
{
|
||||
return PNV9_XSCOM_PEC_NEST_BASE + 0x400 * pec->index;
|
||||
}
|
||||
|
||||
static void pnv_pec_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_CLASS(klass);
|
||||
static const char compat[] = "ibm,power9-pbcq";
|
||||
static const char stk_compat[] = "ibm,power9-phb-stack";
|
||||
|
||||
xdc->dt_xscom = pnv_pec_dt_xscom;
|
||||
|
||||
dc->realize = pnv_pec_realize;
|
||||
device_class_set_props(dc, pnv_pec_properties);
|
||||
dc->user_creatable = false;
|
||||
|
||||
pecc->xscom_nest_base = pnv_pec_xscom_nest_base;
|
||||
pecc->xscom_pci_base = pnv_pec_xscom_pci_base;
|
||||
pecc->xscom_nest_size = PNV9_XSCOM_PEC_NEST_SIZE;
|
||||
pecc->xscom_pci_size = PNV9_XSCOM_PEC_PCI_SIZE;
|
||||
pecc->compat = compat;
|
||||
pecc->compat_size = sizeof(compat);
|
||||
pecc->stk_compat = stk_compat;
|
||||
pecc->stk_compat_size = sizeof(stk_compat);
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_pec_type_info = {
|
||||
.name = TYPE_PNV_PHB4_PEC,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(PnvPhb4PecState),
|
||||
.instance_init = pnv_pec_instance_init,
|
||||
.class_init = pnv_pec_class_init,
|
||||
.class_size = sizeof(PnvPhb4PecClass),
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_PNV_XSCOM_INTERFACE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void pnv_pec_stk_instance_init(Object *obj)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(obj);
|
||||
|
||||
object_initialize_child(obj, "phb", &stack->phb, sizeof(stack->phb),
|
||||
TYPE_PNV_PHB4, &error_abort, NULL);
|
||||
}
|
||||
|
||||
static void pnv_pec_stk_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(dev);
|
||||
PnvPhb4PecState *pec = stack->pec;
|
||||
char name[64];
|
||||
|
||||
assert(pec);
|
||||
|
||||
/* Initialize the XSCOM regions for the stack registers */
|
||||
snprintf(name, sizeof(name), "xscom-pec-%d.%d-nest-stack-%d",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
pnv_xscom_region_init(&stack->nest_regs_mr, OBJECT(stack),
|
||||
&pnv_pec_stk_nest_xscom_ops, stack, name,
|
||||
PHB4_PEC_NEST_STK_REGS_COUNT);
|
||||
|
||||
snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-stack-%d",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
pnv_xscom_region_init(&stack->pci_regs_mr, OBJECT(stack),
|
||||
&pnv_pec_stk_pci_xscom_ops, stack, name,
|
||||
PHB4_PEC_PCI_STK_REGS_COUNT);
|
||||
|
||||
/* PHB pass-through */
|
||||
snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-stack-%d-phb",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
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
|
||||
*/
|
||||
}
|
||||
|
||||
static Property pnv_pec_stk_properties[] = {
|
||||
DEFINE_PROP_UINT32("stack-no", PnvPhb4PecStack, stack_no, 0),
|
||||
DEFINE_PROP_LINK("pec", PnvPhb4PecStack, pec, TYPE_PNV_PHB4_PEC,
|
||||
PnvPhb4PecState *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void pnv_pec_stk_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
device_class_set_props(dc, pnv_pec_stk_properties);
|
||||
dc->realize = pnv_pec_stk_realize;
|
||||
dc->user_creatable = false;
|
||||
|
||||
/* TODO: reset regs ? */
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_pec_stk_type_info = {
|
||||
.name = TYPE_PNV_PHB4_PEC_STACK,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(PnvPhb4PecStack),
|
||||
.instance_init = pnv_pec_stk_instance_init,
|
||||
.class_init = pnv_pec_stk_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_PNV_XSCOM_INTERFACE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void pnv_pec_register_types(void)
|
||||
{
|
||||
type_register_static(&pnv_pec_type_info);
|
||||
type_register_static(&pnv_pec_stk_type_info);
|
||||
}
|
||||
|
||||
type_init(pnv_pec_register_types);
|
|
@ -135,6 +135,8 @@ config XIVE_SPAPR
|
|||
default y
|
||||
depends on PSERIES
|
||||
select XIVE
|
||||
select PCI
|
||||
select PCIE_PORT
|
||||
|
||||
config XIVE_KVM
|
||||
bool
|
||||
|
|
204
hw/ppc/pnv.c
204
hw/ppc/pnv.c
|
@ -40,6 +40,7 @@
|
|||
#include "hw/intc/intc.h"
|
||||
#include "hw/ipmi/ipmi.h"
|
||||
#include "target/ppc/mmu-hash64.h"
|
||||
#include "hw/pci/msi.h"
|
||||
|
||||
#include "hw/ppc/xics.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
|
@ -615,16 +616,29 @@ static ISABus *pnv_isa_create(PnvChip *chip, Error **errp)
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t pnv_chip_power8_xscom_core_base(PnvChip *chip,
|
||||
|
@ -716,7 +730,7 @@ static void pnv_init(MachineState *machine)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
fw_size = load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE);
|
||||
fw_size = load_image_targphys(fw_filename, pnv->fw_load_addr, FW_MAX_SIZE);
|
||||
if (fw_size < 0) {
|
||||
error_report("Could not load OPAL firmware '%s'", fw_filename);
|
||||
exit(1);
|
||||
|
@ -748,6 +762,9 @@ static void pnv_init(MachineState *machine)
|
|||
}
|
||||
}
|
||||
|
||||
/* MSIs are supported on this platform */
|
||||
msi_nonbroken = true;
|
||||
|
||||
/*
|
||||
* Check compatibility of the specified CPU with the machine
|
||||
* default.
|
||||
|
@ -1014,7 +1031,10 @@ static void pnv_chip_power10_intc_print_info(PnvChip *chip, PowerPCCPU *cpu,
|
|||
|
||||
static void pnv_chip_power8_instance_init(Object *obj)
|
||||
{
|
||||
PnvChip *chip = PNV_CHIP(obj);
|
||||
Pnv8Chip *chip8 = PNV8_CHIP(obj);
|
||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(obj);
|
||||
int i;
|
||||
|
||||
object_property_add_link(obj, "xics", TYPE_XICS_FABRIC,
|
||||
(Object **)&chip8->xics,
|
||||
|
@ -1033,6 +1053,17 @@ static void pnv_chip_power8_instance_init(Object *obj)
|
|||
|
||||
object_initialize_child(obj, "homer", &chip8->homer, sizeof(chip8->homer),
|
||||
TYPE_PNV8_HOMER, &error_abort, NULL);
|
||||
|
||||
for (i = 0; i < pcc->num_phbs; i++) {
|
||||
object_initialize_child(obj, "phb[*]", &chip8->phbs[i],
|
||||
sizeof(chip8->phbs[i]), TYPE_PNV_PHB3,
|
||||
&error_abort, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Number of PHBs is the chip default
|
||||
*/
|
||||
chip->num_phbs = pcc->num_phbs;
|
||||
}
|
||||
|
||||
static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp)
|
||||
|
@ -1071,6 +1102,7 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
|
|||
Pnv8Chip *chip8 = PNV8_CHIP(dev);
|
||||
Pnv8Psi *psi8 = &chip8->psi;
|
||||
Error *local_err = NULL;
|
||||
int i;
|
||||
|
||||
assert(chip8->xics);
|
||||
|
||||
|
@ -1151,6 +1183,33 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
|
|||
/* Homer mmio region */
|
||||
memory_region_add_subregion(get_system_memory(), PNV_HOMER_BASE(chip),
|
||||
&chip8->homer.regs);
|
||||
|
||||
/* 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), i, "index", &error_fatal);
|
||||
object_property_set_int(OBJECT(phb), chip->chip_id, "chip-id",
|
||||
&error_fatal);
|
||||
object_property_set_bool(OBJECT(phb), true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
qdev_set_parent_bus(DEVICE(phb), sysbus_get_default());
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t pnv_chip_power8_xscom_pcba(PnvChip *chip, uint64_t addr)
|
||||
|
@ -1166,6 +1225,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
k->chip_cfam_id = 0x221ef04980000000ull; /* P8 Murano DD2.1 */
|
||||
k->cores_mask = POWER8E_CORE_MASK;
|
||||
k->num_phbs = 3;
|
||||
k->core_pir = pnv_chip_core_pir_p8;
|
||||
k->intc_create = pnv_chip_power8_intc_create;
|
||||
k->intc_reset = pnv_chip_power8_intc_reset;
|
||||
|
@ -1189,6 +1249,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */
|
||||
k->cores_mask = POWER8_CORE_MASK;
|
||||
k->num_phbs = 3;
|
||||
k->core_pir = pnv_chip_core_pir_p8;
|
||||
k->intc_create = pnv_chip_power8_intc_create;
|
||||
k->intc_reset = pnv_chip_power8_intc_reset;
|
||||
|
@ -1212,6 +1273,7 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
k->chip_cfam_id = 0x120d304980000000ull; /* P8 Naples DD1.0 */
|
||||
k->cores_mask = POWER8_CORE_MASK;
|
||||
k->num_phbs = 3;
|
||||
k->core_pir = pnv_chip_core_pir_p8;
|
||||
k->intc_create = pnv_chip_power8_intc_create;
|
||||
k->intc_reset = pnv_chip_power8_intc_reset;
|
||||
|
@ -1230,7 +1292,10 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
static void pnv_chip_power9_instance_init(Object *obj)
|
||||
{
|
||||
PnvChip *chip = PNV_CHIP(obj);
|
||||
Pnv9Chip *chip9 = PNV9_CHIP(obj);
|
||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(obj);
|
||||
int i;
|
||||
|
||||
object_initialize_child(obj, "xive", &chip9->xive, sizeof(chip9->xive),
|
||||
TYPE_PNV_XIVE, &error_abort, NULL);
|
||||
|
@ -1248,6 +1313,17 @@ static void pnv_chip_power9_instance_init(Object *obj)
|
|||
|
||||
object_initialize_child(obj, "homer", &chip9->homer, sizeof(chip9->homer),
|
||||
TYPE_PNV9_HOMER, &error_abort, NULL);
|
||||
|
||||
for (i = 0; i < PNV9_CHIP_MAX_PEC; i++) {
|
||||
object_initialize_child(obj, "pec[*]", &chip9->pecs[i],
|
||||
sizeof(chip9->pecs[i]), TYPE_PNV_PHB4_PEC,
|
||||
&error_abort, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Number of PHBs is the chip default
|
||||
*/
|
||||
chip->num_phbs = pcc->num_phbs;
|
||||
}
|
||||
|
||||
static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp)
|
||||
|
@ -1276,6 +1352,78 @@ static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp)
|
|||
}
|
||||
}
|
||||
|
||||
static void pnv_chip_power9_phb_realize(PnvChip *chip, Error **errp)
|
||||
{
|
||||
Pnv9Chip *chip9 = PNV9_CHIP(chip);
|
||||
Error *local_err = NULL;
|
||||
int i, j;
|
||||
int phb_id = 0;
|
||||
|
||||
for (i = 0; i < PNV9_CHIP_MAX_PEC; 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), i, "index", &error_fatal);
|
||||
/*
|
||||
* PEC0 -> 1 stack
|
||||
* PEC1 -> 2 stacks
|
||||
* PEC2 -> 3 stacks
|
||||
*/
|
||||
object_property_set_int(OBJECT(pec), i + 1, "num-stacks",
|
||||
&error_fatal);
|
||||
object_property_set_int(OBJECT(pec), chip->chip_id, "chip-id",
|
||||
&error_fatal);
|
||||
object_property_set_link(OBJECT(pec), OBJECT(get_system_memory()),
|
||||
"system-memory", &error_abort);
|
||||
object_property_set_bool(OBJECT(pec), true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
pec_nest_base = pecc->xscom_nest_base(pec);
|
||||
pec_pci_base = pecc->xscom_pci_base(pec);
|
||||
|
||||
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, phb_id, "index", &error_fatal);
|
||||
object_property_set_int(obj, chip->chip_id, "chip-id",
|
||||
&error_fatal);
|
||||
object_property_set_int(obj, PNV_PHB4_VERSION, "version",
|
||||
&error_fatal);
|
||||
object_property_set_int(obj, PNV_PHB4_DEVICE_ID, "device-id",
|
||||
&error_fatal);
|
||||
object_property_set_link(obj, OBJECT(stack), "stack", &error_abort);
|
||||
object_property_set_bool(obj, true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
qdev_set_parent_bus(DEVICE(obj), sysbus_get_default());
|
||||
|
||||
/* 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 void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev);
|
||||
|
@ -1378,6 +1526,13 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
|
|||
/* Homer mmio region */
|
||||
memory_region_add_subregion(get_system_memory(), PNV9_HOMER_BASE(chip),
|
||||
&chip9->homer.regs);
|
||||
|
||||
/* PHBs */
|
||||
pnv_chip_power9_phb_realize(chip, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t pnv_chip_power9_xscom_pcba(PnvChip *chip, uint64_t addr)
|
||||
|
@ -1404,6 +1559,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;
|
||||
|
||||
device_class_set_parent_realize(dc, pnv_chip_power9_realize,
|
||||
&k->parent_realize);
|
||||
|
@ -1533,6 +1689,7 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp)
|
|||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
|
||||
const char *typename = pnv_chip_core_typename(chip);
|
||||
int i, core_hwid;
|
||||
PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
|
||||
|
||||
if (!object_class_by_name(typename)) {
|
||||
error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename);
|
||||
|
@ -1571,6 +1728,8 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp)
|
|||
object_property_set_int(OBJECT(pnv_core),
|
||||
pcc->core_pir(chip, core_hwid),
|
||||
"pir", &error_fatal);
|
||||
object_property_set_int(OBJECT(pnv_core), pnv->fw_load_addr,
|
||||
"hrmor", &error_fatal);
|
||||
object_property_set_link(OBJECT(pnv_core), OBJECT(chip), "chip",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(pnv_core), true, "realized",
|
||||
|
@ -1605,6 +1764,7 @@ 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(),
|
||||
};
|
||||
|
||||
|
@ -1638,14 +1798,23 @@ PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir)
|
|||
static ICSState *pnv_ics_get(XICSFabric *xi, int irq)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(xi);
|
||||
int i;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < pnv->num_chips; i++) {
|
||||
PnvChip *chip = pnv->chips[i];
|
||||
Pnv8Chip *chip8 = PNV8_CHIP(pnv->chips[i]);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1653,11 +1822,17 @@ static ICSState *pnv_ics_get(XICSFabric *xi, int irq)
|
|||
static void pnv_ics_resend(XICSFabric *xi)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(xi);
|
||||
int i;
|
||||
int i, j;
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1767,6 +1942,22 @@ static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
|
|||
pmc->dt_power_mgt = pnv_dt_power_mgt;
|
||||
}
|
||||
|
||||
static bool pnv_machine_get_hb(Object *obj, Error **errp)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(obj);
|
||||
|
||||
return !!pnv->fw_load_addr;
|
||||
}
|
||||
|
||||
static void pnv_machine_set_hb(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(obj);
|
||||
|
||||
if (value) {
|
||||
pnv->fw_load_addr = 0x8000000;
|
||||
}
|
||||
}
|
||||
|
||||
static void pnv_machine_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
|
@ -1786,6 +1977,13 @@ static void pnv_machine_class_init(ObjectClass *oc, void *data)
|
|||
*/
|
||||
mc->default_ram_size = INITRD_LOAD_ADDR + INITRD_MAX_SIZE;
|
||||
ispc->print_info = pnv_pic_print_info;
|
||||
|
||||
object_class_property_add_bool(oc, "hb-mode",
|
||||
pnv_machine_get_hb, pnv_machine_set_hb,
|
||||
&error_abort);
|
||||
object_class_property_set_description(oc, "hb-mode",
|
||||
"Use a hostboot like boot loader",
|
||||
NULL);
|
||||
}
|
||||
|
||||
#define DEFINE_PNV8_CHIP_TYPE(type, class_initfn) \
|
||||
|
|
|
@ -40,11 +40,11 @@ static const char *pnv_core_cpu_typename(PnvCore *pc)
|
|||
return cpu_type;
|
||||
}
|
||||
|
||||
static void pnv_core_cpu_reset(PowerPCCPU *cpu, PnvChip *chip)
|
||||
static void pnv_core_cpu_reset(PnvCore *pc, PowerPCCPU *cpu)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
|
||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pc->chip);
|
||||
|
||||
cpu_reset(cs);
|
||||
|
||||
|
@ -56,7 +56,9 @@ static void pnv_core_cpu_reset(PowerPCCPU *cpu, PnvChip *chip)
|
|||
env->nip = 0x10;
|
||||
env->msr |= MSR_HVB; /* Hypervisor mode */
|
||||
|
||||
pcc->intc_reset(chip, cpu);
|
||||
env->spr[SPR_HRMOR] = pc->hrmor;
|
||||
|
||||
pcc->intc_reset(pc->chip, cpu);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -162,14 +164,14 @@ static const MemoryRegionOps pnv_core_power9_xscom_ops = {
|
|||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static void pnv_core_cpu_realize(PowerPCCPU *cpu, PnvChip *chip, Error **errp)
|
||||
static void pnv_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
int core_pir;
|
||||
int thread_index = 0; /* TODO: TCG supports only one thread */
|
||||
ppc_spr_t *pir = &env->spr_cb[SPR_PIR];
|
||||
Error *local_err = NULL;
|
||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
|
||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pc->chip);
|
||||
|
||||
object_property_set_bool(OBJECT(cpu), true, "realized", &local_err);
|
||||
if (local_err) {
|
||||
|
@ -177,13 +179,13 @@ static void pnv_core_cpu_realize(PowerPCCPU *cpu, PnvChip *chip, Error **errp)
|
|||
return;
|
||||
}
|
||||
|
||||
pcc->intc_create(chip, cpu, &local_err);
|
||||
pcc->intc_create(pc->chip, cpu, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
core_pir = object_property_get_uint(OBJECT(cpu), "core-pir", &error_abort);
|
||||
core_pir = object_property_get_uint(OBJECT(pc), "pir", &error_abort);
|
||||
|
||||
/*
|
||||
* The PIR of a thread is the core PIR + the thread index. We will
|
||||
|
@ -203,7 +205,7 @@ static void pnv_core_reset(void *dev)
|
|||
int i;
|
||||
|
||||
for (i = 0; i < cc->nr_threads; i++) {
|
||||
pnv_core_cpu_reset(pc->threads[i], pc->chip);
|
||||
pnv_core_cpu_reset(pc, pc->threads[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -231,8 +233,6 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
|
|||
|
||||
snprintf(name, sizeof(name), "thread[%d]", i);
|
||||
object_property_add_child(OBJECT(pc), name, obj, &error_abort);
|
||||
object_property_add_alias(obj, "core-pir", OBJECT(pc),
|
||||
"pir", &error_abort);
|
||||
|
||||
cpu->machine_data = g_new0(PnvCPUState, 1);
|
||||
|
||||
|
@ -240,7 +240,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
|
|||
}
|
||||
|
||||
for (j = 0; j < cc->nr_threads; j++) {
|
||||
pnv_core_cpu_realize(pc->threads[j], pc->chip, &local_err);
|
||||
pnv_core_cpu_realize(pc, pc->threads[j], &local_err);
|
||||
if (local_err) {
|
||||
goto err;
|
||||
}
|
||||
|
@ -263,12 +263,12 @@ err:
|
|||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
static void pnv_core_cpu_unrealize(PowerPCCPU *cpu, PnvChip *chip)
|
||||
static void pnv_core_cpu_unrealize(PnvCore *pc, PowerPCCPU *cpu)
|
||||
{
|
||||
PnvCPUState *pnv_cpu = pnv_cpu_state(cpu);
|
||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
|
||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pc->chip);
|
||||
|
||||
pcc->intc_destroy(chip, cpu);
|
||||
pcc->intc_destroy(pc->chip, cpu);
|
||||
cpu_remove_sync(CPU(cpu));
|
||||
cpu->machine_data = NULL;
|
||||
g_free(pnv_cpu);
|
||||
|
@ -284,13 +284,14 @@ static void pnv_core_unrealize(DeviceState *dev, Error **errp)
|
|||
qemu_unregister_reset(pnv_core_reset, pc);
|
||||
|
||||
for (i = 0; i < cc->nr_threads; i++) {
|
||||
pnv_core_cpu_unrealize(pc->threads[i], pc->chip);
|
||||
pnv_core_cpu_unrealize(pc, pc->threads[i]);
|
||||
}
|
||||
g_free(pc->threads);
|
||||
}
|
||||
|
||||
static Property pnv_core_properties[] = {
|
||||
DEFINE_PROP_UINT32("pir", PnvCore, pir, 0),
|
||||
DEFINE_PROP_UINT64("hrmor", PnvCore, hrmor, 0),
|
||||
DEFINE_PROP_LINK("chip", PnvCore, chip, TYPE_PNV_CHIP, PnvChip *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
@ -324,6 +325,7 @@ static void pnv_core_class_init(ObjectClass *oc, void *data)
|
|||
dc->realize = pnv_core_realize;
|
||||
dc->unrealize = pnv_core_unrealize;
|
||||
device_class_set_props(dc, pnv_core_properties);
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
#define DEFINE_PNV_CORE_TYPE(family, cpu_model) \
|
||||
|
@ -422,6 +424,7 @@ static void pnv_quad_class_init(ObjectClass *oc, void *data)
|
|||
|
||||
dc->realize = pnv_quad_realize;
|
||||
device_class_set_props(dc, pnv_quad_properties);
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_quad_info = {
|
||||
|
|
|
@ -360,6 +360,7 @@ static void pnv_homer_class_init(ObjectClass *klass, void *data)
|
|||
dc->realize = pnv_homer_realize;
|
||||
dc->desc = "PowerNV HOMER Memory";
|
||||
device_class_set_props(dc, pnv_homer_properties);
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_homer_type_info = {
|
||||
|
|
|
@ -762,6 +762,7 @@ static void pnv_lpc_class_init(ObjectClass *klass, void *data)
|
|||
dc->realize = pnv_lpc_realize;
|
||||
dc->desc = "PowerNV LPC Controller";
|
||||
device_class_set_props(dc, pnv_lpc_properties);
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_lpc_info = {
|
||||
|
@ -825,6 +826,7 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp)
|
|||
qemu_irq *irqs;
|
||||
qemu_irq_handler handler;
|
||||
PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
|
||||
bool hostboot_mode = !!pnv->fw_load_addr;
|
||||
|
||||
/* let isa_bus_new() create its own bridge on SysBus otherwise
|
||||
* devices speficied on the command line won't find the bus and
|
||||
|
@ -859,7 +861,9 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp)
|
|||
* Start disabled. The HIOMAP protocol will activate the mapping
|
||||
* with HIOMAP_C_CREATE_WRITE_WINDOW
|
||||
*/
|
||||
memory_region_set_enabled(&pnv->pnor->mmio, false);
|
||||
if (!hostboot_mode) {
|
||||
memory_region_set_enabled(&pnv->pnor->mmio, false);
|
||||
}
|
||||
|
||||
return isa_bus;
|
||||
}
|
||||
|
|
|
@ -280,6 +280,7 @@ static void pnv_occ_class_init(ObjectClass *klass, void *data)
|
|||
dc->realize = pnv_occ_realize;
|
||||
dc->desc = "PowerNV OCC Controller";
|
||||
device_class_set_props(dc, pnv_occ_properties);
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_occ_type_info = {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/units.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "hw/loader.h"
|
||||
|
@ -46,7 +47,8 @@ static void pnv_pnor_update(PnvPnor *s, int offset, int size)
|
|||
ret = blk_pwrite(s->blk, offset, s->storage + offset,
|
||||
offset_end - offset, 0);
|
||||
if (ret < 0) {
|
||||
error_report("Could not update PNOR: %s", strerror(-ret));
|
||||
error_report("Could not update PNOR offset=0x%" PRIx32" : %s", offset,
|
||||
strerror(-ret));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,7 +113,7 @@ static void pnv_pnor_realize(DeviceState *dev, Error **errp)
|
|||
}
|
||||
|
||||
static Property pnv_pnor_properties[] = {
|
||||
DEFINE_PROP_INT64("size", PnvPnor, size, 128 << 20),
|
||||
DEFINE_PROP_INT64("size", PnvPnor, size, 128 * MiB),
|
||||
DEFINE_PROP_DRIVE("drive", PnvPnor, blk),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
|
18
hw/ppc/ppc.c
18
hw/ppc/ppc.c
|
@ -1490,24 +1490,6 @@ int ppc_dcr_init (CPUPPCState *env, int (*read_error)(int dcrn),
|
|||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Debug port */
|
||||
void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
addr &= 0xF;
|
||||
switch (addr) {
|
||||
case 0:
|
||||
printf("%c", val);
|
||||
break;
|
||||
case 1:
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
break;
|
||||
case 2:
|
||||
printf("Set loglevel to %04" PRIx32 "\n", val);
|
||||
qemu_set_log(val | 0x100);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int ppc_cpu_pir(PowerPCCPU *cpu)
|
||||
{
|
||||
|
|
384
hw/ppc/prep.c
384
hw/ppc/prep.c
|
@ -42,7 +42,7 @@
|
|||
#include "hw/loader.h"
|
||||
#include "hw/rtc/mc146818rtc.h"
|
||||
#include "hw/isa/pc87312.h"
|
||||
#include "hw/net/ne2000-isa.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/qtest.h"
|
||||
|
@ -60,178 +60,9 @@
|
|||
|
||||
#define CFG_ADDR 0xf0000510
|
||||
|
||||
#define BIOS_SIZE (1 * MiB)
|
||||
#define BIOS_FILENAME "ppc_rom.bin"
|
||||
#define KERNEL_LOAD_ADDR 0x01000000
|
||||
#define INITRD_LOAD_ADDR 0x01800000
|
||||
|
||||
/* Constants for devices init */
|
||||
static const int ide_iobase[2] = { 0x1f0, 0x170 };
|
||||
static const int ide_iobase2[2] = { 0x3f6, 0x376 };
|
||||
static const int ide_irq[2] = { 13, 13 };
|
||||
|
||||
#define NE2000_NB_MAX 6
|
||||
|
||||
static uint32_t ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280, 0x380 };
|
||||
static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
|
||||
|
||||
/* ISA IO ports bridge */
|
||||
#define PPC_IO_BASE 0x80000000
|
||||
|
||||
/* Fake super-io ports for PREP platform (Intel 82378ZB) */
|
||||
typedef struct sysctrl_t {
|
||||
qemu_irq reset_irq;
|
||||
Nvram *nvram;
|
||||
uint8_t state;
|
||||
uint8_t syscontrol;
|
||||
int contiguous_map;
|
||||
qemu_irq contiguous_map_irq;
|
||||
int endian;
|
||||
} sysctrl_t;
|
||||
|
||||
enum {
|
||||
STATE_HARDFILE = 0x01,
|
||||
};
|
||||
|
||||
static sysctrl_t *sysctrl;
|
||||
|
||||
static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
sysctrl_t *sysctrl = opaque;
|
||||
|
||||
trace_prep_io_800_writeb(addr - PPC_IO_BASE, val);
|
||||
switch (addr) {
|
||||
case 0x0092:
|
||||
/* Special port 92 */
|
||||
/* Check soft reset asked */
|
||||
if (val & 0x01) {
|
||||
qemu_irq_raise(sysctrl->reset_irq);
|
||||
} else {
|
||||
qemu_irq_lower(sysctrl->reset_irq);
|
||||
}
|
||||
/* Check LE mode */
|
||||
if (val & 0x02) {
|
||||
sysctrl->endian = 1;
|
||||
} else {
|
||||
sysctrl->endian = 0;
|
||||
}
|
||||
break;
|
||||
case 0x0800:
|
||||
/* Motorola CPU configuration register : read-only */
|
||||
break;
|
||||
case 0x0802:
|
||||
/* Motorola base module feature register : read-only */
|
||||
break;
|
||||
case 0x0803:
|
||||
/* Motorola base module status register : read-only */
|
||||
break;
|
||||
case 0x0808:
|
||||
/* Hardfile light register */
|
||||
if (val & 1)
|
||||
sysctrl->state |= STATE_HARDFILE;
|
||||
else
|
||||
sysctrl->state &= ~STATE_HARDFILE;
|
||||
break;
|
||||
case 0x0810:
|
||||
/* Password protect 1 register */
|
||||
if (sysctrl->nvram != NULL) {
|
||||
NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram);
|
||||
(k->toggle_lock)(sysctrl->nvram, 1);
|
||||
}
|
||||
break;
|
||||
case 0x0812:
|
||||
/* Password protect 2 register */
|
||||
if (sysctrl->nvram != NULL) {
|
||||
NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram);
|
||||
(k->toggle_lock)(sysctrl->nvram, 2);
|
||||
}
|
||||
break;
|
||||
case 0x0814:
|
||||
/* L2 invalidate register */
|
||||
// tlb_flush(first_cpu, 1);
|
||||
break;
|
||||
case 0x081C:
|
||||
/* system control register */
|
||||
sysctrl->syscontrol = val & 0x0F;
|
||||
break;
|
||||
case 0x0850:
|
||||
/* I/O map type register */
|
||||
sysctrl->contiguous_map = val & 0x01;
|
||||
qemu_set_irq(sysctrl->contiguous_map_irq, sysctrl->contiguous_map);
|
||||
break;
|
||||
default:
|
||||
printf("ERROR: unaffected IO port write: %04" PRIx32
|
||||
" => %02" PRIx32"\n", addr, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr)
|
||||
{
|
||||
sysctrl_t *sysctrl = opaque;
|
||||
uint32_t retval = 0xFF;
|
||||
|
||||
switch (addr) {
|
||||
case 0x0092:
|
||||
/* Special port 92 */
|
||||
retval = sysctrl->endian << 1;
|
||||
break;
|
||||
case 0x0800:
|
||||
/* Motorola CPU configuration register */
|
||||
retval = 0xEF; /* MPC750 */
|
||||
break;
|
||||
case 0x0802:
|
||||
/* Motorola Base module feature register */
|
||||
retval = 0xAD; /* No ESCC, PMC slot neither ethernet */
|
||||
break;
|
||||
case 0x0803:
|
||||
/* Motorola base module status register */
|
||||
retval = 0xE0; /* Standard MPC750 */
|
||||
break;
|
||||
case 0x080C:
|
||||
/* Equipment present register:
|
||||
* no L2 cache
|
||||
* no upgrade processor
|
||||
* no cards in PCI slots
|
||||
* SCSI fuse is bad
|
||||
*/
|
||||
retval = 0x3C;
|
||||
break;
|
||||
case 0x0810:
|
||||
/* Motorola base module extended feature register */
|
||||
retval = 0x39; /* No USB, CF and PCI bridge. NVRAM present */
|
||||
break;
|
||||
case 0x0814:
|
||||
/* L2 invalidate: don't care */
|
||||
break;
|
||||
case 0x0818:
|
||||
/* Keylock */
|
||||
retval = 0x00;
|
||||
break;
|
||||
case 0x081C:
|
||||
/* system control register
|
||||
* 7 - 6 / 1 - 0: L2 cache enable
|
||||
*/
|
||||
retval = sysctrl->syscontrol;
|
||||
break;
|
||||
case 0x0823:
|
||||
/* */
|
||||
retval = 0x03; /* no L2 cache */
|
||||
break;
|
||||
case 0x0850:
|
||||
/* I/O map type register */
|
||||
retval = sysctrl->contiguous_map;
|
||||
break;
|
||||
default:
|
||||
printf("ERROR: unaffected IO port: %04" PRIx32 " read\n", addr);
|
||||
break;
|
||||
}
|
||||
trace_prep_io_800_readb(addr - PPC_IO_BASE, retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
#define NVRAM_SIZE 0x2000
|
||||
|
||||
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
|
||||
|
@ -247,17 +78,6 @@ static void ppc_prep_reset(void *opaque)
|
|||
cpu_reset(CPU(cpu));
|
||||
}
|
||||
|
||||
static const MemoryRegionPortio prep_portio_list[] = {
|
||||
/* System control ports */
|
||||
{ 0x0092, 1, 1, .read = PREP_io_800_readb, .write = PREP_io_800_writeb, },
|
||||
{ 0x0800, 0x52, 1,
|
||||
.read = PREP_io_800_readb, .write = PREP_io_800_writeb, },
|
||||
/* Special port to get debug messages from Open-Firmware */
|
||||
{ 0x0F00, 4, 1, .write = PPC_debug_write, },
|
||||
PORTIO_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static PortioList prep_port_list;
|
||||
|
||||
/*****************************************************************************/
|
||||
/* NVRAM helpers */
|
||||
|
@ -397,207 +217,6 @@ static int PPC_NVRAM_set_params (Nvram *nvram, uint16_t NVRAM_size,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* PowerPC PREP hardware initialisation */
|
||||
static void ppc_prep_init(MachineState *machine)
|
||||
{
|
||||
ram_addr_t ram_size = machine->ram_size;
|
||||
const char *kernel_filename = machine->kernel_filename;
|
||||
const char *kernel_cmdline = machine->kernel_cmdline;
|
||||
const char *initrd_filename = machine->initrd_filename;
|
||||
const char *boot_device = machine->boot_order;
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
PowerPCCPU *cpu = NULL;
|
||||
CPUPPCState *env = NULL;
|
||||
Nvram *m48t59;
|
||||
#if 0
|
||||
MemoryRegion *xcsr = g_new(MemoryRegion, 1);
|
||||
#endif
|
||||
int linux_boot, i, nb_nics1;
|
||||
MemoryRegion *ram = g_new(MemoryRegion, 1);
|
||||
uint32_t kernel_base, initrd_base;
|
||||
long kernel_size, initrd_size;
|
||||
DeviceState *dev;
|
||||
PCIHostState *pcihost;
|
||||
PCIBus *pci_bus;
|
||||
PCIDevice *pci;
|
||||
ISABus *isa_bus;
|
||||
ISADevice *isa;
|
||||
int ppc_boot_device;
|
||||
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
|
||||
|
||||
sysctrl = g_malloc0(sizeof(sysctrl_t));
|
||||
|
||||
linux_boot = (kernel_filename != NULL);
|
||||
|
||||
/* init CPUs */
|
||||
for (i = 0; i < machine->smp.cpus; i++) {
|
||||
cpu = POWERPC_CPU(cpu_create(machine->cpu_type));
|
||||
env = &cpu->env;
|
||||
|
||||
if (env->flags & POWERPC_FLAG_RTC_CLK) {
|
||||
/* POWER / PowerPC 601 RTC clock frequency is 7.8125 MHz */
|
||||
cpu_ppc_tb_init(env, 7812500UL);
|
||||
} else {
|
||||
/* Set time-base frequency to 100 Mhz */
|
||||
cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL);
|
||||
}
|
||||
qemu_register_reset(ppc_prep_reset, cpu);
|
||||
}
|
||||
|
||||
/* allocate RAM */
|
||||
memory_region_allocate_system_memory(ram, NULL, "ppc_prep.ram", ram_size);
|
||||
memory_region_add_subregion(sysmem, 0, ram);
|
||||
|
||||
if (linux_boot) {
|
||||
kernel_base = KERNEL_LOAD_ADDR;
|
||||
/* now we can load the kernel */
|
||||
kernel_size = load_image_targphys(kernel_filename, kernel_base,
|
||||
ram_size - kernel_base);
|
||||
if (kernel_size < 0) {
|
||||
error_report("could not load kernel '%s'", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
/* load initrd */
|
||||
if (initrd_filename) {
|
||||
initrd_base = INITRD_LOAD_ADDR;
|
||||
initrd_size = load_image_targphys(initrd_filename, initrd_base,
|
||||
ram_size - initrd_base);
|
||||
if (initrd_size < 0) {
|
||||
error_report("could not load initial ram disk '%s'",
|
||||
initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
}
|
||||
ppc_boot_device = 'm';
|
||||
} else {
|
||||
kernel_base = 0;
|
||||
kernel_size = 0;
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
ppc_boot_device = '\0';
|
||||
/* For now, OHW cannot boot from the network. */
|
||||
for (i = 0; boot_device[i] != '\0'; i++) {
|
||||
if (boot_device[i] >= 'a' && boot_device[i] <= 'f') {
|
||||
ppc_boot_device = boot_device[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ppc_boot_device == '\0') {
|
||||
error_report("No valid boot device for Mac99 machine");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) {
|
||||
error_report("Only 6xx bus is supported on PREP machine");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
dev = qdev_create(NULL, "raven-pcihost");
|
||||
if (bios_name == NULL) {
|
||||
bios_name = BIOS_FILENAME;
|
||||
}
|
||||
qdev_prop_set_string(dev, "bios-name", bios_name);
|
||||
qdev_prop_set_uint32(dev, "elf-machine", PPC_ELF_MACHINE);
|
||||
qdev_prop_set_bit(dev, "is-legacy-prep", true);
|
||||
pcihost = PCI_HOST_BRIDGE(dev);
|
||||
object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev), NULL);
|
||||
qdev_init_nofail(dev);
|
||||
pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0");
|
||||
if (pci_bus == NULL) {
|
||||
error_report("Couldn't create PCI host controller");
|
||||
exit(1);
|
||||
}
|
||||
sysctrl->contiguous_map_irq = qdev_get_gpio_in(dev, 0);
|
||||
|
||||
/* PCI -> ISA bridge */
|
||||
pci = pci_create_simple(pci_bus, PCI_DEVFN(1, 0), "i82378");
|
||||
cpu = POWERPC_CPU(first_cpu);
|
||||
qdev_connect_gpio_out(&pci->qdev, 0,
|
||||
cpu->env.irq_inputs[PPC6xx_INPUT_INT]);
|
||||
sysbus_connect_irq(&pcihost->busdev, 0, qdev_get_gpio_in(&pci->qdev, 9));
|
||||
sysbus_connect_irq(&pcihost->busdev, 1, qdev_get_gpio_in(&pci->qdev, 11));
|
||||
sysbus_connect_irq(&pcihost->busdev, 2, qdev_get_gpio_in(&pci->qdev, 9));
|
||||
sysbus_connect_irq(&pcihost->busdev, 3, qdev_get_gpio_in(&pci->qdev, 11));
|
||||
isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci), "isa.0"));
|
||||
|
||||
/* Super I/O (parallel + serial ports) */
|
||||
isa = isa_create(isa_bus, TYPE_PC87312_SUPERIO);
|
||||
dev = DEVICE(isa);
|
||||
qdev_prop_set_uint8(dev, "config", 13); /* fdc, ser0, ser1, par0 */
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
/* init basic PC hardware */
|
||||
pci_vga_init(pci_bus);
|
||||
|
||||
nb_nics1 = nb_nics;
|
||||
if (nb_nics1 > NE2000_NB_MAX)
|
||||
nb_nics1 = NE2000_NB_MAX;
|
||||
for(i = 0; i < nb_nics1; i++) {
|
||||
if (nd_table[i].model == NULL) {
|
||||
nd_table[i].model = g_strdup("ne2k_isa");
|
||||
}
|
||||
if (strcmp(nd_table[i].model, "ne2k_isa") == 0) {
|
||||
isa_ne2000_init(isa_bus, ne2000_io[i], ne2000_irq[i],
|
||||
&nd_table[i]);
|
||||
} else {
|
||||
pci_nic_init_nofail(&nd_table[i], pci_bus, "ne2k_pci", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
ide_drive_get(hd, ARRAY_SIZE(hd));
|
||||
for(i = 0; i < MAX_IDE_BUS; i++) {
|
||||
isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], ide_irq[i],
|
||||
hd[2 * i],
|
||||
hd[2 * i + 1]);
|
||||
}
|
||||
|
||||
cpu = POWERPC_CPU(first_cpu);
|
||||
sysctrl->reset_irq = cpu->env.irq_inputs[PPC6xx_INPUT_HRESET];
|
||||
|
||||
portio_list_init(&prep_port_list, NULL, prep_portio_list, sysctrl, "prep");
|
||||
portio_list_add(&prep_port_list, isa_address_space_io(isa), 0x0);
|
||||
|
||||
/*
|
||||
* PowerPC control and status register group: unimplemented,
|
||||
* would be at address 0xFEFF0000.
|
||||
*/
|
||||
|
||||
if (machine_usb(machine)) {
|
||||
pci_create_simple(pci_bus, -1, "pci-ohci");
|
||||
}
|
||||
|
||||
m48t59 = m48t59_init_isa(isa_bus, 0x0074, NVRAM_SIZE, 2000, 59);
|
||||
if (m48t59 == NULL)
|
||||
return;
|
||||
sysctrl->nvram = m48t59;
|
||||
|
||||
/* Initialise NVRAM */
|
||||
PPC_NVRAM_set_params(m48t59, NVRAM_SIZE, "PREP", ram_size,
|
||||
ppc_boot_device,
|
||||
kernel_base, kernel_size,
|
||||
kernel_cmdline,
|
||||
initrd_base, initrd_size,
|
||||
/* XXX: need an option to load a NVRAM image */
|
||||
0,
|
||||
graphic_width, graphic_height, graphic_depth);
|
||||
}
|
||||
|
||||
static void prep_machine_init(MachineClass *mc)
|
||||
{
|
||||
mc->deprecation_reason = "use 40p machine type instead";
|
||||
mc->desc = "PowerPC PREP platform";
|
||||
mc->init = ppc_prep_init;
|
||||
mc->block_default_type = IF_IDE;
|
||||
mc->max_cpus = MAX_CPUS;
|
||||
mc->default_boot_order = "cad";
|
||||
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("602");
|
||||
mc->default_display = "std";
|
||||
}
|
||||
|
||||
static int prep_set_cmos_checksum(DeviceState *dev, void *opaque)
|
||||
{
|
||||
uint16_t checksum = *(uint16_t *)opaque;
|
||||
|
@ -821,4 +440,3 @@ static void ibm_40p_machine_init(MachineClass *mc)
|
|||
}
|
||||
|
||||
DEFINE_MACHINE("40p", ibm_40p_machine_init)
|
||||
DEFINE_MACHINE("prep", prep_machine_init)
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "migration/qemu-file-types.h"
|
||||
#include "migration/global_state.h"
|
||||
#include "migration/register.h"
|
||||
#include "migration/blocker.h"
|
||||
#include "mmu-hash64.h"
|
||||
#include "mmu-book3s-v3.h"
|
||||
#include "cpu-models.h"
|
||||
|
@ -1677,6 +1678,14 @@ static void spapr_machine_reset(MachineState *machine)
|
|||
first_ppc_cpu->env.gpr[5] = 0;
|
||||
|
||||
spapr->cas_reboot = false;
|
||||
|
||||
spapr->mc_status = -1;
|
||||
spapr->guest_machine_check_addr = -1;
|
||||
|
||||
/* Signal all vCPUs waiting on this condition */
|
||||
qemu_cond_broadcast(&spapr->mc_delivery_cond);
|
||||
|
||||
migrate_del_blocker(spapr->fwnmi_migration_blocker);
|
||||
}
|
||||
|
||||
static void spapr_create_nvram(SpaprMachineState *spapr)
|
||||
|
@ -1959,6 +1968,42 @@ static const VMStateDescription vmstate_spapr_dtb = {
|
|||
},
|
||||
};
|
||||
|
||||
static bool spapr_fwnmi_needed(void *opaque)
|
||||
{
|
||||
SpaprMachineState *spapr = (SpaprMachineState *)opaque;
|
||||
|
||||
return spapr->guest_machine_check_addr != -1;
|
||||
}
|
||||
|
||||
static int spapr_fwnmi_pre_save(void *opaque)
|
||||
{
|
||||
SpaprMachineState *spapr = (SpaprMachineState *)opaque;
|
||||
|
||||
/*
|
||||
* Check if machine check handling is in progress and print a
|
||||
* warning message.
|
||||
*/
|
||||
if (spapr->mc_status != -1) {
|
||||
warn_report("A machine check is being handled during migration. The"
|
||||
"handler may run and log hardware error on the destination");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_spapr_machine_check = {
|
||||
.name = "spapr_machine_check",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = spapr_fwnmi_needed,
|
||||
.pre_save = spapr_fwnmi_pre_save,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT64(guest_machine_check_addr, SpaprMachineState),
|
||||
VMSTATE_INT32(mc_status, SpaprMachineState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_spapr = {
|
||||
.name = "spapr",
|
||||
.version_id = 3,
|
||||
|
@ -1992,6 +2037,8 @@ static const VMStateDescription vmstate_spapr = {
|
|||
&vmstate_spapr_dtb,
|
||||
&vmstate_spapr_cap_large_decr,
|
||||
&vmstate_spapr_cap_ccf_assist,
|
||||
&vmstate_spapr_cap_fwnmi,
|
||||
&vmstate_spapr_machine_check,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
@ -2807,6 +2854,13 @@ static void spapr_machine_init(MachineState *machine)
|
|||
spapr_create_lmb_dr_connectors(spapr);
|
||||
}
|
||||
|
||||
if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI_MCE) == SPAPR_CAP_ON) {
|
||||
/* Create the error string for live migration blocker */
|
||||
error_setg(&spapr->fwnmi_migration_blocker,
|
||||
"A machine check is being handled during migration. The handler"
|
||||
"may run and log hardware error on the destination");
|
||||
}
|
||||
|
||||
/* Set up RTAS event infrastructure */
|
||||
spapr_events_init(spapr);
|
||||
|
||||
|
@ -2970,6 +3024,8 @@ static void spapr_machine_init(MachineState *machine)
|
|||
|
||||
kvmppc_spapr_enable_inkernel_multitce();
|
||||
}
|
||||
|
||||
qemu_cond_init(&spapr->mc_delivery_cond);
|
||||
}
|
||||
|
||||
static int spapr_kvm_type(MachineState *machine, const char *vm_type)
|
||||
|
@ -4397,7 +4453,8 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
|
|||
smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = 16; /* 64kiB */
|
||||
smc->default_caps.caps[SPAPR_CAP_NESTED_KVM_HV] = SPAPR_CAP_OFF;
|
||||
smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_ON;
|
||||
smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_OFF;
|
||||
smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_ON;
|
||||
smc->default_caps.caps[SPAPR_CAP_FWNMI_MCE] = SPAPR_CAP_ON;
|
||||
spapr_caps_add_properties(smc, &error_abort);
|
||||
smc->irq = &spapr_irq_dual;
|
||||
smc->dr_phb_enabled = true;
|
||||
|
@ -4465,8 +4522,12 @@ DEFINE_SPAPR_MACHINE(5_0, "5.0", true);
|
|||
*/
|
||||
static void spapr_machine_4_2_class_options(MachineClass *mc)
|
||||
{
|
||||
SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
|
||||
|
||||
spapr_machine_5_0_class_options(mc);
|
||||
compat_props_add(mc->compat_props, hw_compat_4_2, hw_compat_4_2_len);
|
||||
smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_OFF;
|
||||
smc->default_caps.caps[SPAPR_CAP_FWNMI_MCE] = SPAPR_CAP_OFF;
|
||||
}
|
||||
|
||||
DEFINE_SPAPR_MACHINE(4_2, "4.2", false);
|
||||
|
|
|
@ -485,17 +485,48 @@ static void cap_ccf_assist_apply(SpaprMachineState *spapr, uint8_t val,
|
|||
uint8_t kvm_val = kvmppc_get_cap_count_cache_flush_assist();
|
||||
|
||||
if (tcg_enabled() && val) {
|
||||
/* TODO - for now only allow broken for TCG */
|
||||
error_setg(errp,
|
||||
"Requested count cache flush assist capability level not supported by tcg,"
|
||||
" try appending -machine cap-ccf-assist=off");
|
||||
/* TCG doesn't implement anything here, but allow with a warning */
|
||||
warn_report("TCG doesn't support requested feature, cap-ccf-assist=on");
|
||||
} else if (kvm_enabled() && (val > kvm_val)) {
|
||||
uint8_t kvm_ibs = kvmppc_get_cap_safe_indirect_branch();
|
||||
|
||||
if (kvm_ibs == SPAPR_CAP_FIXED_CCD) {
|
||||
/*
|
||||
* If we don't have CCF assist on the host, the assist
|
||||
* instruction is a harmless no-op. It won't correctly
|
||||
* implement the cache count flush *but* if we have
|
||||
* count-cache-disabled in the host, that flush is
|
||||
* unnnecessary. So, specifically allow this case. This
|
||||
* allows us to have better performance on POWER9 DD2.3,
|
||||
* while still working on POWER9 DD2.2 and POWER8 host
|
||||
* cpus.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
error_setg(errp,
|
||||
"Requested count cache flush assist capability level not supported by kvm,"
|
||||
" try appending -machine cap-ccf-assist=off");
|
||||
}
|
||||
}
|
||||
|
||||
static void cap_fwnmi_mce_apply(SpaprMachineState *spapr, uint8_t val,
|
||||
Error **errp)
|
||||
{
|
||||
if (!val) {
|
||||
return; /* Disabled by default */
|
||||
}
|
||||
|
||||
if (tcg_enabled()) {
|
||||
warn_report("Firmware Assisted Non-Maskable Interrupts(FWNMI) not "
|
||||
"supported in TCG");
|
||||
} else if (kvm_enabled()) {
|
||||
if (kvmppc_set_fwnmi() < 0) {
|
||||
error_setg(errp, "Firmware Assisted Non-Maskable Interrupts(FWNMI) "
|
||||
"not supported by KVM");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = {
|
||||
[SPAPR_CAP_HTM] = {
|
||||
.name = "htm",
|
||||
|
@ -595,6 +626,15 @@ SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = {
|
|||
.type = "bool",
|
||||
.apply = cap_ccf_assist_apply,
|
||||
},
|
||||
[SPAPR_CAP_FWNMI_MCE] = {
|
||||
.name = "fwnmi-mce",
|
||||
.description = "Handle fwnmi machine check exceptions",
|
||||
.index = SPAPR_CAP_FWNMI_MCE,
|
||||
.get = spapr_cap_get_bool,
|
||||
.set = spapr_cap_set_bool,
|
||||
.type = "bool",
|
||||
.apply = cap_fwnmi_mce_apply,
|
||||
},
|
||||
};
|
||||
|
||||
static SpaprCapabilities default_caps_with_cpu(SpaprMachineState *spapr,
|
||||
|
@ -734,6 +774,7 @@ SPAPR_CAP_MIG_STATE(hpt_maxpagesize, SPAPR_CAP_HPT_MAXPAGESIZE);
|
|||
SPAPR_CAP_MIG_STATE(nested_kvm_hv, SPAPR_CAP_NESTED_KVM_HV);
|
||||
SPAPR_CAP_MIG_STATE(large_decr, SPAPR_CAP_LARGE_DECREMENTER);
|
||||
SPAPR_CAP_MIG_STATE(ccf_assist, SPAPR_CAP_CCF_ASSIST);
|
||||
SPAPR_CAP_MIG_STATE(fwnmi, SPAPR_CAP_FWNMI_MCE);
|
||||
|
||||
void spapr_caps_init(SpaprMachineState *spapr)
|
||||
{
|
||||
|
|
|
@ -40,8 +40,10 @@
|
|||
#include "hw/ppc/spapr_drc.h"
|
||||
#include "qemu/help_option.h"
|
||||
#include "qemu/bcd.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "hw/ppc/spapr_ovec.h"
|
||||
#include <libfdt.h>
|
||||
#include "migration/blocker.h"
|
||||
|
||||
#define RTAS_LOG_VERSION_MASK 0xff000000
|
||||
#define RTAS_LOG_VERSION_6 0x06000000
|
||||
|
@ -213,6 +215,104 @@ struct hp_extended_log {
|
|||
struct rtas_event_log_v6_hp hp;
|
||||
} QEMU_PACKED;
|
||||
|
||||
struct rtas_event_log_v6_mc {
|
||||
#define RTAS_LOG_V6_SECTION_ID_MC 0x4D43 /* MC */
|
||||
struct rtas_event_log_v6_section_header hdr;
|
||||
uint32_t fru_id;
|
||||
uint32_t proc_id;
|
||||
uint8_t error_type;
|
||||
#define RTAS_LOG_V6_MC_TYPE_UE 0
|
||||
#define RTAS_LOG_V6_MC_TYPE_SLB 1
|
||||
#define RTAS_LOG_V6_MC_TYPE_ERAT 2
|
||||
#define RTAS_LOG_V6_MC_TYPE_TLB 4
|
||||
#define RTAS_LOG_V6_MC_TYPE_D_CACHE 5
|
||||
#define RTAS_LOG_V6_MC_TYPE_I_CACHE 7
|
||||
uint8_t sub_err_type;
|
||||
#define RTAS_LOG_V6_MC_UE_INDETERMINATE 0
|
||||
#define RTAS_LOG_V6_MC_UE_IFETCH 1
|
||||
#define RTAS_LOG_V6_MC_UE_PAGE_TABLE_WALK_IFETCH 2
|
||||
#define RTAS_LOG_V6_MC_UE_LOAD_STORE 3
|
||||
#define RTAS_LOG_V6_MC_UE_PAGE_TABLE_WALK_LOAD_STORE 4
|
||||
#define RTAS_LOG_V6_MC_SLB_PARITY 0
|
||||
#define RTAS_LOG_V6_MC_SLB_MULTIHIT 1
|
||||
#define RTAS_LOG_V6_MC_SLB_INDETERMINATE 2
|
||||
#define RTAS_LOG_V6_MC_ERAT_PARITY 1
|
||||
#define RTAS_LOG_V6_MC_ERAT_MULTIHIT 2
|
||||
#define RTAS_LOG_V6_MC_ERAT_INDETERMINATE 3
|
||||
#define RTAS_LOG_V6_MC_TLB_PARITY 1
|
||||
#define RTAS_LOG_V6_MC_TLB_MULTIHIT 2
|
||||
#define RTAS_LOG_V6_MC_TLB_INDETERMINATE 3
|
||||
uint8_t reserved_1[6];
|
||||
uint64_t effective_address;
|
||||
uint64_t logical_address;
|
||||
} QEMU_PACKED;
|
||||
|
||||
struct mc_extended_log {
|
||||
struct rtas_event_log_v6 v6hdr;
|
||||
struct rtas_event_log_v6_mc mc;
|
||||
} QEMU_PACKED;
|
||||
|
||||
struct MC_ierror_table {
|
||||
unsigned long srr1_mask;
|
||||
unsigned long srr1_value;
|
||||
bool nip_valid; /* nip is a valid indicator of faulting address */
|
||||
uint8_t error_type;
|
||||
uint8_t error_subtype;
|
||||
unsigned int initiator;
|
||||
unsigned int severity;
|
||||
};
|
||||
|
||||
static const struct MC_ierror_table mc_ierror_table[] = {
|
||||
{ 0x00000000081c0000, 0x0000000000040000, true,
|
||||
RTAS_LOG_V6_MC_TYPE_UE, RTAS_LOG_V6_MC_UE_IFETCH,
|
||||
RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
|
||||
{ 0x00000000081c0000, 0x0000000000080000, true,
|
||||
RTAS_LOG_V6_MC_TYPE_SLB, RTAS_LOG_V6_MC_SLB_PARITY,
|
||||
RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
|
||||
{ 0x00000000081c0000, 0x00000000000c0000, true,
|
||||
RTAS_LOG_V6_MC_TYPE_SLB, RTAS_LOG_V6_MC_SLB_MULTIHIT,
|
||||
RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
|
||||
{ 0x00000000081c0000, 0x0000000000100000, true,
|
||||
RTAS_LOG_V6_MC_TYPE_ERAT, RTAS_LOG_V6_MC_ERAT_MULTIHIT,
|
||||
RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
|
||||
{ 0x00000000081c0000, 0x0000000000140000, true,
|
||||
RTAS_LOG_V6_MC_TYPE_TLB, RTAS_LOG_V6_MC_TLB_MULTIHIT,
|
||||
RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
|
||||
{ 0x00000000081c0000, 0x0000000000180000, true,
|
||||
RTAS_LOG_V6_MC_TYPE_UE, RTAS_LOG_V6_MC_UE_PAGE_TABLE_WALK_IFETCH,
|
||||
RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, } };
|
||||
|
||||
struct MC_derror_table {
|
||||
unsigned long dsisr_value;
|
||||
bool dar_valid; /* dar is a valid indicator of faulting address */
|
||||
uint8_t error_type;
|
||||
uint8_t error_subtype;
|
||||
unsigned int initiator;
|
||||
unsigned int severity;
|
||||
};
|
||||
|
||||
static const struct MC_derror_table mc_derror_table[] = {
|
||||
{ 0x00008000, false,
|
||||
RTAS_LOG_V6_MC_TYPE_UE, RTAS_LOG_V6_MC_UE_LOAD_STORE,
|
||||
RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
|
||||
{ 0x00004000, true,
|
||||
RTAS_LOG_V6_MC_TYPE_UE, RTAS_LOG_V6_MC_UE_PAGE_TABLE_WALK_LOAD_STORE,
|
||||
RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
|
||||
{ 0x00000800, true,
|
||||
RTAS_LOG_V6_MC_TYPE_ERAT, RTAS_LOG_V6_MC_ERAT_MULTIHIT,
|
||||
RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
|
||||
{ 0x00000400, true,
|
||||
RTAS_LOG_V6_MC_TYPE_TLB, RTAS_LOG_V6_MC_TLB_MULTIHIT,
|
||||
RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
|
||||
{ 0x00000080, true,
|
||||
RTAS_LOG_V6_MC_TYPE_SLB, RTAS_LOG_V6_MC_SLB_MULTIHIT, /* Before PARITY */
|
||||
RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, },
|
||||
{ 0x00000100, true,
|
||||
RTAS_LOG_V6_MC_TYPE_SLB, RTAS_LOG_V6_MC_SLB_PARITY,
|
||||
RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, } };
|
||||
|
||||
#define SRR1_MC_LOADSTORE(srr1) ((srr1) & PPC_BIT(42))
|
||||
|
||||
typedef enum EventClass {
|
||||
EVENT_CLASS_INTERNAL_ERRORS = 0,
|
||||
EVENT_CLASS_EPOW = 1,
|
||||
|
@ -622,6 +722,175 @@ void spapr_hotplug_req_remove_by_count_indexed(SpaprDrcType drc_type,
|
|||
RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id);
|
||||
}
|
||||
|
||||
static uint32_t spapr_mce_get_elog_type(PowerPCCPU *cpu, bool recovered,
|
||||
struct mc_extended_log *ext_elog)
|
||||
{
|
||||
int i;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
uint32_t summary;
|
||||
uint64_t dsisr = env->spr[SPR_DSISR];
|
||||
|
||||
summary = RTAS_LOG_VERSION_6 | RTAS_LOG_OPTIONAL_PART_PRESENT;
|
||||
if (recovered) {
|
||||
summary |= RTAS_LOG_DISPOSITION_FULLY_RECOVERED;
|
||||
} else {
|
||||
summary |= RTAS_LOG_DISPOSITION_NOT_RECOVERED;
|
||||
}
|
||||
|
||||
if (SRR1_MC_LOADSTORE(env->spr[SPR_SRR1])) {
|
||||
for (i = 0; i < ARRAY_SIZE(mc_derror_table); i++) {
|
||||
if (!(dsisr & mc_derror_table[i].dsisr_value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ext_elog->mc.error_type = mc_derror_table[i].error_type;
|
||||
ext_elog->mc.sub_err_type = mc_derror_table[i].error_subtype;
|
||||
if (mc_derror_table[i].dar_valid) {
|
||||
ext_elog->mc.effective_address = cpu_to_be64(env->spr[SPR_DAR]);
|
||||
}
|
||||
|
||||
summary |= mc_derror_table[i].initiator
|
||||
| mc_derror_table[i].severity;
|
||||
|
||||
return summary;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < ARRAY_SIZE(mc_ierror_table); i++) {
|
||||
if ((env->spr[SPR_SRR1] & mc_ierror_table[i].srr1_mask) !=
|
||||
mc_ierror_table[i].srr1_value) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ext_elog->mc.error_type = mc_ierror_table[i].error_type;
|
||||
ext_elog->mc.sub_err_type = mc_ierror_table[i].error_subtype;
|
||||
if (mc_ierror_table[i].nip_valid) {
|
||||
ext_elog->mc.effective_address = cpu_to_be64(env->nip);
|
||||
}
|
||||
|
||||
summary |= mc_ierror_table[i].initiator
|
||||
| mc_ierror_table[i].severity;
|
||||
|
||||
return summary;
|
||||
}
|
||||
}
|
||||
|
||||
summary |= RTAS_LOG_INITIATOR_CPU;
|
||||
return summary;
|
||||
}
|
||||
|
||||
static void spapr_mce_dispatch_elog(PowerPCCPU *cpu, bool recovered)
|
||||
{
|
||||
SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
|
||||
CPUState *cs = CPU(cpu);
|
||||
uint64_t rtas_addr;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
|
||||
target_ulong msr = 0;
|
||||
struct rtas_error_log log;
|
||||
struct mc_extended_log *ext_elog;
|
||||
uint32_t summary;
|
||||
|
||||
/*
|
||||
* Properly set bits in MSR before we invoke the handler.
|
||||
* SRR0/1, DAR and DSISR are properly set by KVM
|
||||
*/
|
||||
if (!(*pcc->interrupts_big_endian)(cpu)) {
|
||||
msr |= (1ULL << MSR_LE);
|
||||
}
|
||||
|
||||
if (env->msr & (1ULL << MSR_SF)) {
|
||||
msr |= (1ULL << MSR_SF);
|
||||
}
|
||||
|
||||
msr |= (1ULL << MSR_ME);
|
||||
|
||||
ext_elog = g_malloc0(sizeof(*ext_elog));
|
||||
summary = spapr_mce_get_elog_type(cpu, recovered, ext_elog);
|
||||
|
||||
log.summary = cpu_to_be32(summary);
|
||||
log.extended_length = cpu_to_be32(sizeof(*ext_elog));
|
||||
|
||||
spapr_init_v6hdr(&ext_elog->v6hdr);
|
||||
ext_elog->mc.hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MC);
|
||||
ext_elog->mc.hdr.section_length =
|
||||
cpu_to_be16(sizeof(struct rtas_event_log_v6_mc));
|
||||
ext_elog->mc.hdr.section_version = 1;
|
||||
|
||||
/* get rtas addr from fdt */
|
||||
rtas_addr = spapr_get_rtas_addr();
|
||||
if (!rtas_addr) {
|
||||
/* Unable to fetch rtas_addr. Hence reset the guest */
|
||||
ppc_cpu_do_system_reset(cs);
|
||||
g_free(ext_elog);
|
||||
return;
|
||||
}
|
||||
|
||||
stq_be_phys(&address_space_memory, rtas_addr + RTAS_ERROR_LOG_OFFSET,
|
||||
env->gpr[3]);
|
||||
cpu_physical_memory_write(rtas_addr + RTAS_ERROR_LOG_OFFSET +
|
||||
sizeof(env->gpr[3]), &log, sizeof(log));
|
||||
cpu_physical_memory_write(rtas_addr + RTAS_ERROR_LOG_OFFSET +
|
||||
sizeof(env->gpr[3]) + sizeof(log), ext_elog,
|
||||
sizeof(*ext_elog));
|
||||
|
||||
env->gpr[3] = rtas_addr + RTAS_ERROR_LOG_OFFSET;
|
||||
env->msr = msr;
|
||||
env->nip = spapr->guest_machine_check_addr;
|
||||
|
||||
g_free(ext_elog);
|
||||
}
|
||||
|
||||
void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered)
|
||||
{
|
||||
SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
|
||||
CPUState *cs = CPU(cpu);
|
||||
int ret;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (spapr->guest_machine_check_addr == -1) {
|
||||
/*
|
||||
* This implies that we have hit a machine check either when the
|
||||
* guest has not registered FWNMI (i.e., "ibm,nmi-register" not
|
||||
* called) or between system reset and "ibm,nmi-register".
|
||||
* Fall back to the old machine check behavior in such cases.
|
||||
*/
|
||||
cs->exception_index = POWERPC_EXCP_MCHECK;
|
||||
ppc_cpu_do_interrupt(cs);
|
||||
return;
|
||||
}
|
||||
|
||||
while (spapr->mc_status != -1) {
|
||||
/*
|
||||
* Check whether the same CPU got machine check error
|
||||
* while still handling the mc error (i.e., before
|
||||
* that CPU called "ibm,nmi-interlock")
|
||||
*/
|
||||
if (spapr->mc_status == cpu->vcpu_id) {
|
||||
qemu_system_guest_panicked(NULL);
|
||||
return;
|
||||
}
|
||||
qemu_cond_wait_iothread(&spapr->mc_delivery_cond);
|
||||
/* Meanwhile if the system is reset, then just return */
|
||||
if (spapr->guest_machine_check_addr == -1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ret = migrate_add_blocker(spapr->fwnmi_migration_blocker, &local_err);
|
||||
if (ret == -EBUSY) {
|
||||
/*
|
||||
* We don't want to abort so we let the migration to continue.
|
||||
* In a rare case, the machine check handler will run on the target.
|
||||
* Though this is not preferable, it is better than aborting
|
||||
* the migration or killing the VM.
|
||||
*/
|
||||
warn_report("Received a fwnmi while migration was in progress");
|
||||
}
|
||||
|
||||
spapr->mc_status = cpu->vcpu_id;
|
||||
spapr_mce_dispatch_elog(cpu, recovered);
|
||||
}
|
||||
|
||||
static void check_exception(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
uint32_t token, uint32_t nargs,
|
||||
target_ulong args,
|
||||
|
|
|
@ -1676,6 +1676,18 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
|
|||
Error *local_err = NULL;
|
||||
bool raw_mode_supported = false;
|
||||
bool guest_xive;
|
||||
CPUState *cs;
|
||||
|
||||
/* CAS is supposed to be called early when only the boot vCPU is active. */
|
||||
CPU_FOREACH(cs) {
|
||||
if (cs == CPU(cpu)) {
|
||||
continue;
|
||||
}
|
||||
if (!cs->halted) {
|
||||
warn_report("guest has multiple active vCPUs at CAS, which is not allowed");
|
||||
return H_MULTI_THREADS_ACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
cas_pvr = cas_check_pvr(spapr, cpu, &addr, &raw_mode_supported, &local_err);
|
||||
if (local_err) {
|
||||
|
@ -1703,7 +1715,15 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
|
|||
ov_table = addr;
|
||||
|
||||
ov1_guest = spapr_ovec_parse_vector(ov_table, 1);
|
||||
if (!ov1_guest) {
|
||||
warn_report("guest didn't provide option vector 1");
|
||||
return H_PARAMETER;
|
||||
}
|
||||
ov5_guest = spapr_ovec_parse_vector(ov_table, 5);
|
||||
if (!ov5_guest) {
|
||||
warn_report("guest didn't provide option vector 5");
|
||||
return H_PARAMETER;
|
||||
}
|
||||
if (spapr_ovec_test(ov5_guest, OV5_MMU_BOTH)) {
|
||||
error_report("guest requested hash and radix MMU, which is invalid.");
|
||||
exit(EXIT_FAILURE);
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include "hw/ppc/fdt.h"
|
||||
#include "target/ppc/mmu-hash64.h"
|
||||
#include "target/ppc/mmu-book3s-v3.h"
|
||||
#include "migration/blocker.h"
|
||||
|
||||
static void rtas_display_character(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
uint32_t token, uint32_t nargs,
|
||||
|
@ -399,6 +400,62 @@ static void rtas_get_power_level(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
|||
rtas_st(rets, 1, 100);
|
||||
}
|
||||
|
||||
static void rtas_ibm_nmi_register(PowerPCCPU *cpu,
|
||||
SpaprMachineState *spapr,
|
||||
uint32_t token, uint32_t nargs,
|
||||
target_ulong args,
|
||||
uint32_t nret, target_ulong rets)
|
||||
{
|
||||
hwaddr rtas_addr;
|
||||
|
||||
if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI_MCE) == SPAPR_CAP_OFF) {
|
||||
rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
|
||||
return;
|
||||
}
|
||||
|
||||
rtas_addr = spapr_get_rtas_addr();
|
||||
if (!rtas_addr) {
|
||||
rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
|
||||
return;
|
||||
}
|
||||
|
||||
spapr->guest_machine_check_addr = rtas_ld(args, 1);
|
||||
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
|
||||
}
|
||||
|
||||
static void rtas_ibm_nmi_interlock(PowerPCCPU *cpu,
|
||||
SpaprMachineState *spapr,
|
||||
uint32_t token, uint32_t nargs,
|
||||
target_ulong args,
|
||||
uint32_t nret, target_ulong rets)
|
||||
{
|
||||
if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI_MCE) == SPAPR_CAP_OFF) {
|
||||
rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (spapr->guest_machine_check_addr == -1) {
|
||||
/* NMI register not called */
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (spapr->mc_status != cpu->vcpu_id) {
|
||||
/* The vCPU that hit the NMI should invoke "ibm,nmi-interlock" */
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* vCPU issuing "ibm,nmi-interlock" is done with NMI handling,
|
||||
* hence unset mc_status.
|
||||
*/
|
||||
spapr->mc_status = -1;
|
||||
qemu_cond_signal(&spapr->mc_delivery_cond);
|
||||
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
|
||||
migrate_del_blocker(spapr->fwnmi_migration_blocker);
|
||||
}
|
||||
|
||||
static struct rtas_call {
|
||||
const char *name;
|
||||
spapr_rtas_fn fn;
|
||||
|
@ -476,6 +533,32 @@ void spapr_dt_rtas_tokens(void *fdt, int rtas)
|
|||
}
|
||||
}
|
||||
|
||||
hwaddr spapr_get_rtas_addr(void)
|
||||
{
|
||||
SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
|
||||
int rtas_node;
|
||||
const fdt32_t *rtas_data;
|
||||
void *fdt = spapr->fdt_blob;
|
||||
|
||||
/* fetch rtas addr from fdt */
|
||||
rtas_node = fdt_path_offset(fdt, "/rtas");
|
||||
if (rtas_node < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rtas_data = fdt_getprop(fdt, rtas_node, "linux,rtas-base", NULL);
|
||||
if (!rtas_data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We assume that the OS called RTAS instantiate-rtas, but some other
|
||||
* OS might call RTAS instantiate-rtas-64 instead. This fine as of now
|
||||
* as SLOF only supports 32-bit variant.
|
||||
*/
|
||||
return (hwaddr)fdt32_to_cpu(*rtas_data);
|
||||
}
|
||||
|
||||
static void core_rtas_register_types(void)
|
||||
{
|
||||
spapr_rtas_register(RTAS_DISPLAY_CHARACTER, "display-character",
|
||||
|
@ -501,6 +584,10 @@ static void core_rtas_register_types(void)
|
|||
rtas_set_power_level);
|
||||
spapr_rtas_register(RTAS_GET_POWER_LEVEL, "get-power-level",
|
||||
rtas_get_power_level);
|
||||
spapr_rtas_register(RTAS_IBM_NMI_REGISTER, "ibm,nmi-register",
|
||||
rtas_ibm_nmi_register);
|
||||
spapr_rtas_register(RTAS_IBM_NMI_INTERLOCK, "ibm,nmi-interlock",
|
||||
rtas_ibm_nmi_interlock);
|
||||
}
|
||||
|
||||
type_init(core_rtas_register_types)
|
||||
|
|
|
@ -87,6 +87,7 @@ static int vio_make_devnode(SpaprVioDevice *dev,
|
|||
SpaprVioDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
|
||||
int vdevice_off, node_off, ret;
|
||||
char *dt_name;
|
||||
const char *dt_compatible;
|
||||
|
||||
vdevice_off = fdt_path_offset(fdt, "/vdevice");
|
||||
if (vdevice_off < 0) {
|
||||
|
@ -113,9 +114,15 @@ static int vio_make_devnode(SpaprVioDevice *dev,
|
|||
}
|
||||
}
|
||||
|
||||
if (pc->dt_compatible) {
|
||||
if (pc->get_dt_compatible) {
|
||||
dt_compatible = pc->get_dt_compatible(dev);
|
||||
} else {
|
||||
dt_compatible = pc->dt_compatible;
|
||||
}
|
||||
|
||||
if (dt_compatible) {
|
||||
ret = fdt_setprop_string(fdt, node_off, "compatible",
|
||||
pc->dt_compatible);
|
||||
dt_compatible);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -89,10 +89,7 @@ static void mmubooke_create_initial_mapping(CPUPPCState *env,
|
|||
tlb->PID = 0;
|
||||
}
|
||||
|
||||
static PowerPCCPU *ppc440_init_xilinx(ram_addr_t *ram_size,
|
||||
int do_init,
|
||||
const char *cpu_type,
|
||||
uint32_t sysclk)
|
||||
static PowerPCCPU *ppc440_init_xilinx(const char *cpu_type, uint32_t sysclk)
|
||||
{
|
||||
PowerPCCPU *cpu;
|
||||
CPUPPCState *env;
|
||||
|
@ -213,7 +210,7 @@ static void virtex_init(MachineState *machine)
|
|||
int i;
|
||||
|
||||
/* init CPUs */
|
||||
cpu = ppc440_init_xilinx(&ram_size, 1, machine->cpu_type, 400000000);
|
||||
cpu = ppc440_init_xilinx(machine->cpu_type, 400000000);
|
||||
env = &cpu->env;
|
||||
|
||||
if (env->mmu_model != POWERPC_MMU_BOOKE) {
|
||||
|
|
|
@ -22,3 +22,9 @@ config TPM_EMULATOR
|
|||
bool
|
||||
default y
|
||||
depends on TPMDEV
|
||||
|
||||
config TPM_SPAPR
|
||||
bool
|
||||
default y
|
||||
depends on TPM && PSERIES
|
||||
select TPMDEV
|
||||
|
|
|
@ -4,3 +4,4 @@ common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
|
|||
common-obj-$(CONFIG_TPM_CRB) += tpm_crb.o
|
||||
common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
|
||||
common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o
|
||||
obj-$(CONFIG_TPM_SPAPR) += tpm_spapr.o
|
||||
|
|
429
hw/tpm/tpm_spapr.c
Normal file
429
hw/tpm/tpm_spapr.c
Normal file
|
@ -0,0 +1,429 @@
|
|||
/*
|
||||
* QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
|
||||
*
|
||||
* PAPR Virtual TPM
|
||||
*
|
||||
* Copyright (c) 2015, 2017, 2019 IBM Corporation.
|
||||
*
|
||||
* Authors:
|
||||
* Stefan Berger <stefanb@linux.vnet.ibm.com>
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See the
|
||||
* COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
#include "sysemu/tpm_backend.h"
|
||||
#include "tpm_int.h"
|
||||
#include "tpm_util.h"
|
||||
|
||||
#include "hw/ppc/spapr.h"
|
||||
#include "hw/ppc/spapr_vio.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define DEBUG_SPAPR 0
|
||||
|
||||
#define VIO_SPAPR_VTPM(obj) \
|
||||
OBJECT_CHECK(SpaprTpmState, (obj), TYPE_TPM_SPAPR)
|
||||
|
||||
typedef struct TpmCrq {
|
||||
uint8_t valid; /* 0x80: cmd; 0xc0: init crq */
|
||||
/* 0x81-0x83: CRQ message response */
|
||||
uint8_t msg; /* see below */
|
||||
uint16_t len; /* len of TPM request; len of TPM response */
|
||||
uint32_t data; /* rtce_dma_handle when sending TPM request */
|
||||
uint64_t reserved;
|
||||
} TpmCrq;
|
||||
|
||||
#define SPAPR_VTPM_VALID_INIT_CRQ_COMMAND 0xC0
|
||||
#define SPAPR_VTPM_VALID_COMMAND 0x80
|
||||
#define SPAPR_VTPM_MSG_RESULT 0x80
|
||||
|
||||
/* msg types for valid = SPAPR_VTPM_VALID_INIT_CRQ */
|
||||
#define SPAPR_VTPM_INIT_CRQ_RESULT 0x1
|
||||
#define SPAPR_VTPM_INIT_CRQ_COMPLETE_RESULT 0x2
|
||||
|
||||
/* msg types for valid = SPAPR_VTPM_VALID_CMD */
|
||||
#define SPAPR_VTPM_GET_VERSION 0x1
|
||||
#define SPAPR_VTPM_TPM_COMMAND 0x2
|
||||
#define SPAPR_VTPM_GET_RTCE_BUFFER_SIZE 0x3
|
||||
#define SPAPR_VTPM_PREPARE_TO_SUSPEND 0x4
|
||||
|
||||
/* response error messages */
|
||||
#define SPAPR_VTPM_VTPM_ERROR 0xff
|
||||
|
||||
/* error codes */
|
||||
#define SPAPR_VTPM_ERR_COPY_IN_FAILED 0x3
|
||||
#define SPAPR_VTPM_ERR_COPY_OUT_FAILED 0x4
|
||||
|
||||
#define TPM_SPAPR_BUFFER_MAX 4096
|
||||
|
||||
typedef struct {
|
||||
SpaprVioDevice vdev;
|
||||
|
||||
TpmCrq crq; /* track single TPM command */
|
||||
|
||||
uint8_t state;
|
||||
#define SPAPR_VTPM_STATE_NONE 0
|
||||
#define SPAPR_VTPM_STATE_EXECUTION 1
|
||||
#define SPAPR_VTPM_STATE_COMPLETION 2
|
||||
|
||||
unsigned char *buffer;
|
||||
|
||||
uint32_t numbytes; /* number of bytes to deliver on resume */
|
||||
|
||||
TPMBackendCmd cmd;
|
||||
|
||||
TPMBackend *be_driver;
|
||||
TPMVersion be_tpm_version;
|
||||
|
||||
size_t be_buffer_size;
|
||||
} SpaprTpmState;
|
||||
|
||||
/*
|
||||
* Send a request to the TPM.
|
||||
*/
|
||||
static void tpm_spapr_tpm_send(SpaprTpmState *s)
|
||||
{
|
||||
if (trace_event_get_state_backends(TRACE_TPM_SPAPR_SHOW_BUFFER)) {
|
||||
tpm_util_show_buffer(s->buffer, s->be_buffer_size, "To TPM");
|
||||
}
|
||||
|
||||
s->state = SPAPR_VTPM_STATE_EXECUTION;
|
||||
s->cmd = (TPMBackendCmd) {
|
||||
.locty = 0,
|
||||
.in = s->buffer,
|
||||
.in_len = MIN(tpm_cmd_get_size(s->buffer), s->be_buffer_size),
|
||||
.out = s->buffer,
|
||||
.out_len = s->be_buffer_size,
|
||||
};
|
||||
|
||||
tpm_backend_deliver_request(s->be_driver, &s->cmd);
|
||||
}
|
||||
|
||||
static int tpm_spapr_process_cmd(SpaprTpmState *s, uint64_t dataptr)
|
||||
{
|
||||
long rc;
|
||||
|
||||
/* a max. of be_buffer_size bytes can be transported */
|
||||
rc = spapr_vio_dma_read(&s->vdev, dataptr,
|
||||
s->buffer, s->be_buffer_size);
|
||||
if (rc) {
|
||||
error_report("tpm_spapr_got_payload: DMA read failure");
|
||||
}
|
||||
/* let vTPM handle any malformed request */
|
||||
tpm_spapr_tpm_send(s);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline int spapr_tpm_send_crq(struct SpaprVioDevice *dev, TpmCrq *crq)
|
||||
{
|
||||
return spapr_vio_send_crq(dev, (uint8_t *)crq);
|
||||
}
|
||||
|
||||
static int tpm_spapr_do_crq(struct SpaprVioDevice *dev, uint8_t *crq_data)
|
||||
{
|
||||
SpaprTpmState *s = VIO_SPAPR_VTPM(dev);
|
||||
TpmCrq local_crq;
|
||||
TpmCrq *crq = &s->crq; /* requests only */
|
||||
int rc;
|
||||
uint8_t valid = crq_data[0];
|
||||
uint8_t msg = crq_data[1];
|
||||
|
||||
trace_tpm_spapr_do_crq(valid, msg);
|
||||
|
||||
switch (valid) {
|
||||
case SPAPR_VTPM_VALID_INIT_CRQ_COMMAND: /* Init command/response */
|
||||
|
||||
/* Respond to initialization request */
|
||||
switch (msg) {
|
||||
case SPAPR_VTPM_INIT_CRQ_RESULT:
|
||||
trace_tpm_spapr_do_crq_crq_result();
|
||||
memset(&local_crq, 0, sizeof(local_crq));
|
||||
local_crq.valid = SPAPR_VTPM_VALID_INIT_CRQ_COMMAND;
|
||||
local_crq.msg = SPAPR_VTPM_INIT_CRQ_RESULT;
|
||||
spapr_tpm_send_crq(dev, &local_crq);
|
||||
break;
|
||||
|
||||
case SPAPR_VTPM_INIT_CRQ_COMPLETE_RESULT:
|
||||
trace_tpm_spapr_do_crq_crq_complete_result();
|
||||
memset(&local_crq, 0, sizeof(local_crq));
|
||||
local_crq.valid = SPAPR_VTPM_VALID_INIT_CRQ_COMMAND;
|
||||
local_crq.msg = SPAPR_VTPM_INIT_CRQ_COMPLETE_RESULT;
|
||||
spapr_tpm_send_crq(dev, &local_crq);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case SPAPR_VTPM_VALID_COMMAND: /* Payloads */
|
||||
switch (msg) {
|
||||
case SPAPR_VTPM_TPM_COMMAND:
|
||||
trace_tpm_spapr_do_crq_tpm_command();
|
||||
if (s->state == SPAPR_VTPM_STATE_EXECUTION) {
|
||||
return H_BUSY;
|
||||
}
|
||||
memcpy(crq, crq_data, sizeof(*crq));
|
||||
|
||||
rc = tpm_spapr_process_cmd(s, be32_to_cpu(crq->data));
|
||||
|
||||
if (rc == H_SUCCESS) {
|
||||
crq->valid = be16_to_cpu(0);
|
||||
} else {
|
||||
local_crq.valid = SPAPR_VTPM_MSG_RESULT;
|
||||
local_crq.msg = SPAPR_VTPM_VTPM_ERROR;
|
||||
local_crq.len = cpu_to_be16(0);
|
||||
local_crq.data = cpu_to_be32(SPAPR_VTPM_ERR_COPY_IN_FAILED);
|
||||
spapr_tpm_send_crq(dev, &local_crq);
|
||||
}
|
||||
break;
|
||||
|
||||
case SPAPR_VTPM_GET_RTCE_BUFFER_SIZE:
|
||||
trace_tpm_spapr_do_crq_tpm_get_rtce_buffer_size(s->be_buffer_size);
|
||||
local_crq.valid = SPAPR_VTPM_VALID_COMMAND;
|
||||
local_crq.msg = SPAPR_VTPM_GET_RTCE_BUFFER_SIZE |
|
||||
SPAPR_VTPM_MSG_RESULT;
|
||||
local_crq.len = cpu_to_be16(s->be_buffer_size);
|
||||
spapr_tpm_send_crq(dev, &local_crq);
|
||||
break;
|
||||
|
||||
case SPAPR_VTPM_GET_VERSION:
|
||||
local_crq.valid = SPAPR_VTPM_VALID_COMMAND;
|
||||
local_crq.msg = SPAPR_VTPM_GET_VERSION | SPAPR_VTPM_MSG_RESULT;
|
||||
local_crq.len = cpu_to_be16(0);
|
||||
switch (s->be_tpm_version) {
|
||||
case TPM_VERSION_1_2:
|
||||
local_crq.data = cpu_to_be32(1);
|
||||
break;
|
||||
case TPM_VERSION_2_0:
|
||||
local_crq.data = cpu_to_be32(2);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
trace_tpm_spapr_do_crq_get_version(be32_to_cpu(local_crq.data));
|
||||
spapr_tpm_send_crq(dev, &local_crq);
|
||||
break;
|
||||
|
||||
case SPAPR_VTPM_PREPARE_TO_SUSPEND:
|
||||
trace_tpm_spapr_do_crq_prepare_to_suspend();
|
||||
local_crq.valid = SPAPR_VTPM_VALID_COMMAND;
|
||||
local_crq.msg = SPAPR_VTPM_PREPARE_TO_SUSPEND |
|
||||
SPAPR_VTPM_MSG_RESULT;
|
||||
spapr_tpm_send_crq(dev, &local_crq);
|
||||
break;
|
||||
|
||||
default:
|
||||
trace_tpm_spapr_do_crq_unknown_msg_type(crq->msg);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
trace_tpm_spapr_do_crq_unknown_crq(valid, msg);
|
||||
};
|
||||
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static void tpm_spapr_request_completed(TPMIf *ti, int ret)
|
||||
{
|
||||
SpaprTpmState *s = VIO_SPAPR_VTPM(ti);
|
||||
TpmCrq *crq = &s->crq;
|
||||
uint32_t len;
|
||||
int rc;
|
||||
|
||||
s->state = SPAPR_VTPM_STATE_COMPLETION;
|
||||
|
||||
/* a max. of be_buffer_size bytes can be transported */
|
||||
len = MIN(tpm_cmd_get_size(s->buffer), s->be_buffer_size);
|
||||
|
||||
if (runstate_check(RUN_STATE_FINISH_MIGRATE)) {
|
||||
trace_tpm_spapr_caught_response(len);
|
||||
/* defer delivery of response until .post_load */
|
||||
s->numbytes = len;
|
||||
return;
|
||||
}
|
||||
|
||||
rc = spapr_vio_dma_write(&s->vdev, be32_to_cpu(crq->data),
|
||||
s->buffer, len);
|
||||
|
||||
if (trace_event_get_state_backends(TRACE_TPM_SPAPR_SHOW_BUFFER)) {
|
||||
tpm_util_show_buffer(s->buffer, len, "From TPM");
|
||||
}
|
||||
|
||||
crq->valid = SPAPR_VTPM_MSG_RESULT;
|
||||
if (rc == H_SUCCESS) {
|
||||
crq->msg = SPAPR_VTPM_TPM_COMMAND | SPAPR_VTPM_MSG_RESULT;
|
||||
crq->len = cpu_to_be16(len);
|
||||
} else {
|
||||
error_report("%s: DMA write failure", __func__);
|
||||
crq->msg = SPAPR_VTPM_VTPM_ERROR;
|
||||
crq->len = cpu_to_be16(0);
|
||||
crq->data = cpu_to_be32(SPAPR_VTPM_ERR_COPY_OUT_FAILED);
|
||||
}
|
||||
|
||||
rc = spapr_tpm_send_crq(&s->vdev, crq);
|
||||
if (rc) {
|
||||
error_report("%s: Error sending response", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
static int tpm_spapr_do_startup_tpm(SpaprTpmState *s, size_t buffersize)
|
||||
{
|
||||
return tpm_backend_startup_tpm(s->be_driver, buffersize);
|
||||
}
|
||||
|
||||
static const char *tpm_spapr_get_dt_compatible(SpaprVioDevice *dev)
|
||||
{
|
||||
SpaprTpmState *s = VIO_SPAPR_VTPM(dev);
|
||||
|
||||
switch (s->be_tpm_version) {
|
||||
case TPM_VERSION_1_2:
|
||||
return "IBM,vtpm";
|
||||
case TPM_VERSION_2_0:
|
||||
return "IBM,vtpm20";
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static void tpm_spapr_reset(SpaprVioDevice *dev)
|
||||
{
|
||||
SpaprTpmState *s = VIO_SPAPR_VTPM(dev);
|
||||
|
||||
s->state = SPAPR_VTPM_STATE_NONE;
|
||||
s->numbytes = 0;
|
||||
|
||||
s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver);
|
||||
|
||||
s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver),
|
||||
TPM_SPAPR_BUFFER_MAX);
|
||||
|
||||
tpm_backend_reset(s->be_driver);
|
||||
tpm_spapr_do_startup_tpm(s, s->be_buffer_size);
|
||||
}
|
||||
|
||||
static enum TPMVersion tpm_spapr_get_version(TPMIf *ti)
|
||||
{
|
||||
SpaprTpmState *s = VIO_SPAPR_VTPM(ti);
|
||||
|
||||
if (tpm_backend_had_startup_error(s->be_driver)) {
|
||||
return TPM_VERSION_UNSPEC;
|
||||
}
|
||||
|
||||
return tpm_backend_get_tpm_version(s->be_driver);
|
||||
}
|
||||
|
||||
/* persistent state handling */
|
||||
|
||||
static int tpm_spapr_pre_save(void *opaque)
|
||||
{
|
||||
SpaprTpmState *s = opaque;
|
||||
|
||||
tpm_backend_finish_sync(s->be_driver);
|
||||
/*
|
||||
* we cannot deliver the results to the VM since DMA would touch VM memory
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_spapr_post_load(void *opaque, int version_id)
|
||||
{
|
||||
SpaprTpmState *s = opaque;
|
||||
|
||||
if (s->numbytes) {
|
||||
trace_tpm_spapr_post_load();
|
||||
/* deliver the results to the VM via DMA */
|
||||
tpm_spapr_request_completed(TPM_IF(s), 0);
|
||||
s->numbytes = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_spapr_vtpm = {
|
||||
.name = "tpm-spapr",
|
||||
.pre_save = tpm_spapr_pre_save,
|
||||
.post_load = tpm_spapr_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_SPAPR_VIO(vdev, SpaprTpmState),
|
||||
|
||||
VMSTATE_UINT8(state, SpaprTpmState),
|
||||
VMSTATE_UINT32(numbytes, SpaprTpmState),
|
||||
VMSTATE_VBUFFER_UINT32(buffer, SpaprTpmState, 0, NULL, numbytes),
|
||||
/* remember DMA address */
|
||||
VMSTATE_UINT32(crq.data, SpaprTpmState),
|
||||
VMSTATE_END_OF_LIST(),
|
||||
}
|
||||
};
|
||||
|
||||
static Property tpm_spapr_properties[] = {
|
||||
DEFINE_SPAPR_PROPERTIES(SpaprTpmState, vdev),
|
||||
DEFINE_PROP_TPMBE("tpmdev", SpaprTpmState, be_driver),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void tpm_spapr_realizefn(SpaprVioDevice *dev, Error **errp)
|
||||
{
|
||||
SpaprTpmState *s = VIO_SPAPR_VTPM(dev);
|
||||
|
||||
if (!tpm_find()) {
|
||||
error_setg(errp, "at most one TPM device is permitted");
|
||||
return;
|
||||
}
|
||||
|
||||
dev->crq.SendFunc = tpm_spapr_do_crq;
|
||||
|
||||
if (!s->be_driver) {
|
||||
error_setg(errp, "'tpmdev' property is required");
|
||||
return;
|
||||
}
|
||||
s->buffer = g_malloc(TPM_SPAPR_BUFFER_MAX);
|
||||
}
|
||||
|
||||
static void tpm_spapr_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass);
|
||||
TPMIfClass *tc = TPM_IF_CLASS(klass);
|
||||
|
||||
k->realize = tpm_spapr_realizefn;
|
||||
k->reset = tpm_spapr_reset;
|
||||
k->dt_name = "vtpm";
|
||||
k->dt_type = "IBM,vtpm";
|
||||
k->get_dt_compatible = tpm_spapr_get_dt_compatible;
|
||||
k->signal_mask = 0x00000001;
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
device_class_set_props(dc, tpm_spapr_properties);
|
||||
k->rtce_window_size = 0x10000000;
|
||||
dc->vmsd = &vmstate_spapr_vtpm;
|
||||
|
||||
tc->model = TPM_MODEL_TPM_SPAPR;
|
||||
tc->get_version = tpm_spapr_get_version;
|
||||
tc->request_completed = tpm_spapr_request_completed;
|
||||
}
|
||||
|
||||
static const TypeInfo tpm_spapr_info = {
|
||||
.name = TYPE_TPM_SPAPR,
|
||||
.parent = TYPE_VIO_SPAPR_DEVICE,
|
||||
.instance_size = sizeof(SpaprTpmState),
|
||||
.class_init = tpm_spapr_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_TPM_IF },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void tpm_spapr_register_types(void)
|
||||
{
|
||||
type_register_static(&tpm_spapr_info);
|
||||
}
|
||||
|
||||
type_init(tpm_spapr_register_types)
|
|
@ -107,30 +107,6 @@ static uint8_t tpm_tis_locality_from_addr(hwaddr addr)
|
|||
return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7);
|
||||
}
|
||||
|
||||
static void tpm_tis_show_buffer(const unsigned char *buffer,
|
||||
size_t buffer_size, const char *string)
|
||||
{
|
||||
size_t len, i;
|
||||
char *line_buffer, *p;
|
||||
|
||||
len = MIN(tpm_cmd_get_size(buffer), buffer_size);
|
||||
|
||||
/*
|
||||
* allocate enough room for 3 chars per buffer entry plus a
|
||||
* newline after every 16 chars and a final null terminator.
|
||||
*/
|
||||
line_buffer = g_malloc(len * 3 + (len / 16) + 1);
|
||||
|
||||
for (i = 0, p = line_buffer; i < len; i++) {
|
||||
if (i && !(i % 16)) {
|
||||
p += sprintf(p, "\n");
|
||||
}
|
||||
p += sprintf(p, "%.2X ", buffer[i]);
|
||||
}
|
||||
trace_tpm_tis_show_buffer(string, len, line_buffer);
|
||||
|
||||
g_free(line_buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the given flags in the STS register by clearing the register but
|
||||
|
@ -156,8 +132,8 @@ static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags)
|
|||
*/
|
||||
static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
|
||||
{
|
||||
if (trace_event_get_state_backends(TRACE_TPM_TIS_SHOW_BUFFER)) {
|
||||
tpm_tis_show_buffer(s->buffer, s->be_buffer_size, "To TPM");
|
||||
if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) {
|
||||
tpm_util_show_buffer(s->buffer, s->be_buffer_size, "To TPM");
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -325,8 +301,8 @@ static void tpm_tis_request_completed(TPMIf *ti, int ret)
|
|||
s->loc[locty].state = TPM_TIS_STATE_COMPLETION;
|
||||
s->rw_offset = 0;
|
||||
|
||||
if (trace_event_get_state_backends(TRACE_TPM_TIS_SHOW_BUFFER)) {
|
||||
tpm_tis_show_buffer(s->buffer, s->be_buffer_size, "From TPM");
|
||||
if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) {
|
||||
tpm_util_show_buffer(s->buffer, s->be_buffer_size, "From TPM");
|
||||
}
|
||||
|
||||
if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) {
|
||||
|
|
|
@ -350,3 +350,28 @@ void tpm_sized_buffer_reset(TPMSizedBuffer *tsb)
|
|||
tsb->buffer = NULL;
|
||||
tsb->size = 0;
|
||||
}
|
||||
|
||||
void tpm_util_show_buffer(const unsigned char *buffer,
|
||||
size_t buffer_size, const char *string)
|
||||
{
|
||||
size_t len, i;
|
||||
char *line_buffer, *p;
|
||||
|
||||
len = MIN(tpm_cmd_get_size(buffer), buffer_size);
|
||||
|
||||
/*
|
||||
* allocate enough room for 3 chars per buffer entry plus a
|
||||
* newline after every 16 chars and a final null terminator.
|
||||
*/
|
||||
line_buffer = g_malloc(len * 3 + (len / 16) + 1);
|
||||
|
||||
for (i = 0, p = line_buffer; i < len; i++) {
|
||||
if (i && !(i % 16)) {
|
||||
p += sprintf(p, "\n");
|
||||
}
|
||||
p += sprintf(p, "%.2X ", buffer[i]);
|
||||
}
|
||||
trace_tpm_util_show_buffer(string, len, line_buffer);
|
||||
|
||||
g_free(line_buffer);
|
||||
}
|
||||
|
|
|
@ -79,4 +79,7 @@ typedef struct TPMSizedBuffer {
|
|||
|
||||
void tpm_sized_buffer_reset(TPMSizedBuffer *tsb);
|
||||
|
||||
void tpm_util_show_buffer(const unsigned char *buffer,
|
||||
size_t buffer_size, const char *string);
|
||||
|
||||
#endif /* TPM_TPM_UTIL_H */
|
||||
|
|
|
@ -14,6 +14,7 @@ tpm_util_get_buffer_size_len(uint32_t len, size_t expected) "tpm_resp->len = %u,
|
|||
tpm_util_get_buffer_size_hdr_len2(uint32_t len, size_t expected) "tpm2_resp->hdr.len = %u, expected = %zu"
|
||||
tpm_util_get_buffer_size_len2(uint32_t len, size_t expected) "tpm2_resp->len = %u, expected = %zu"
|
||||
tpm_util_get_buffer_size(size_t len) "buffersize of device: %zu"
|
||||
tpm_util_show_buffer(const char *direction, size_t len, const char *buf) "direction: %s len: %zu\n%s"
|
||||
|
||||
# tpm_emulator.c
|
||||
tpm_emulator_set_locality(uint8_t locty) "setting locality to %d"
|
||||
|
@ -36,7 +37,6 @@ tpm_emulator_pre_save(void) ""
|
|||
tpm_emulator_inst_init(void) ""
|
||||
|
||||
# tpm_tis.c
|
||||
tpm_tis_show_buffer(const char *direction, size_t len, const char *buf) "direction: %s len: %zu\nbuf: %s"
|
||||
tpm_tis_raise_irq(uint32_t irqmask) "Raising IRQ for flag 0x%08x"
|
||||
tpm_tis_new_active_locality(uint8_t locty) "Active locality is now %d"
|
||||
tpm_tis_abort(uint8_t locty) "New active locality is %d"
|
||||
|
@ -55,3 +55,17 @@ tpm_tis_pre_save(uint8_t locty, uint32_t rw_offset) "locty: %d, rw_offset = %u"
|
|||
|
||||
# tpm_ppi.c
|
||||
tpm_ppi_memset(uint8_t *ptr, size_t size) "memset: %p %zu"
|
||||
|
||||
# hw/tpm/tpm_spapr.c
|
||||
tpm_spapr_show_buffer(const char *direction, size_t len, const char *buf) "direction: %s len: %zu\n%s"
|
||||
tpm_spapr_do_crq(uint8_t raw1, uint8_t raw2) "1st 2 bytes in CRQ: 0x%02x 0x%02x"
|
||||
tpm_spapr_do_crq_crq_result(void) "SPAPR_VTPM_INIT_CRQ_RESULT"
|
||||
tpm_spapr_do_crq_crq_complete_result(void) "SPAPR_VTPM_INIT_CRQ_COMP_RESULT"
|
||||
tpm_spapr_do_crq_tpm_command(void) "got TPM command payload"
|
||||
tpm_spapr_do_crq_tpm_get_rtce_buffer_size(size_t buffersize) "response: buffer size is %zu"
|
||||
tpm_spapr_do_crq_get_version(uint32_t version) "response: version %u"
|
||||
tpm_spapr_do_crq_prepare_to_suspend(void) "response: preparing to suspend"
|
||||
tpm_spapr_do_crq_unknown_msg_type(uint8_t type) "Unknown message type 0x%02x"
|
||||
tpm_spapr_do_crq_unknown_crq(uint8_t raw1, uint8_t raw2) "unknown CRQ 0x%02x 0x%02x ..."
|
||||
tpm_spapr_post_load(void) "Delivering TPM response after resume"
|
||||
tpm_spapr_caught_response(uint32_t v) "Caught response to deliver after resume: %u bytes"
|
||||
|
|
164
include/hw/pci-host/pnv_phb3.h
Normal file
164
include/hw/pci-host/pnv_phb3.h
Normal file
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* QEMU PowerPC PowerNV (POWER8) PHB3 model
|
||||
*
|
||||
* Copyright (c) 2014-2020, IBM Corporation.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See the
|
||||
* COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef PCI_HOST_PNV_PHB3_H
|
||||
#define PCI_HOST_PNV_PHB3_H
|
||||
|
||||
#include "hw/pci/pcie_host.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
#include "hw/ppc/xics.h"
|
||||
|
||||
typedef struct PnvPHB3 PnvPHB3;
|
||||
|
||||
/*
|
||||
* PHB3 XICS Source for MSIs
|
||||
*/
|
||||
#define TYPE_PHB3_MSI "phb3-msi"
|
||||
#define PHB3_MSI(obj) OBJECT_CHECK(Phb3MsiState, (obj), TYPE_PHB3_MSI)
|
||||
|
||||
#define PHB3_MAX_MSI 2048
|
||||
|
||||
typedef struct Phb3MsiState {
|
||||
ICSState ics;
|
||||
qemu_irq *qirqs;
|
||||
|
||||
PnvPHB3 *phb;
|
||||
uint64_t rba[PHB3_MAX_MSI / 64];
|
||||
uint32_t rba_sum;
|
||||
} Phb3MsiState;
|
||||
|
||||
void pnv_phb3_msi_update_config(Phb3MsiState *msis, uint32_t base,
|
||||
uint32_t count);
|
||||
void pnv_phb3_msi_send(Phb3MsiState *msis, uint64_t addr, uint16_t data,
|
||||
int32_t dev_pe);
|
||||
void pnv_phb3_msi_ffi(Phb3MsiState *msis, uint64_t val);
|
||||
void pnv_phb3_msi_pic_print_info(Phb3MsiState *msis, Monitor *mon);
|
||||
|
||||
|
||||
/*
|
||||
* We have one such address space wrapper per possible device under
|
||||
* the PHB since they need to be assigned statically at qemu device
|
||||
* creation time. The relationship to a PE is done later dynamically.
|
||||
* This means we can potentially create a lot of these guys. Q35
|
||||
* stores them as some kind of radix tree but we never really need to
|
||||
* do fast lookups so instead we simply keep a QLIST of them for now,
|
||||
* we can add the radix if needed later on.
|
||||
*
|
||||
* We do cache the PE number to speed things up a bit though.
|
||||
*/
|
||||
typedef struct PnvPhb3DMASpace {
|
||||
PCIBus *bus;
|
||||
uint8_t devfn;
|
||||
int pe_num; /* Cached PE number */
|
||||
#define PHB_INVALID_PE (-1)
|
||||
PnvPHB3 *phb;
|
||||
AddressSpace dma_as;
|
||||
IOMMUMemoryRegion dma_mr;
|
||||
MemoryRegion msi32_mr;
|
||||
MemoryRegion msi64_mr;
|
||||
QLIST_ENTRY(PnvPhb3DMASpace) list;
|
||||
} PnvPhb3DMASpace;
|
||||
|
||||
/*
|
||||
* PHB3 Power Bus Common Queue
|
||||
*/
|
||||
#define TYPE_PNV_PBCQ "pnv-pbcq"
|
||||
#define PNV_PBCQ(obj) OBJECT_CHECK(PnvPBCQState, (obj), TYPE_PNV_PBCQ)
|
||||
|
||||
typedef struct PnvPBCQState {
|
||||
DeviceState parent;
|
||||
|
||||
uint32_t nest_xbase;
|
||||
uint32_t spci_xbase;
|
||||
uint32_t pci_xbase;
|
||||
#define PBCQ_NEST_REGS_COUNT 0x46
|
||||
#define PBCQ_PCI_REGS_COUNT 0x15
|
||||
#define PBCQ_SPCI_REGS_COUNT 0x5
|
||||
|
||||
uint64_t nest_regs[PBCQ_NEST_REGS_COUNT];
|
||||
uint64_t spci_regs[PBCQ_SPCI_REGS_COUNT];
|
||||
uint64_t pci_regs[PBCQ_PCI_REGS_COUNT];
|
||||
MemoryRegion mmbar0;
|
||||
MemoryRegion mmbar1;
|
||||
MemoryRegion phbbar;
|
||||
uint64_t mmio0_base;
|
||||
uint64_t mmio0_size;
|
||||
uint64_t mmio1_base;
|
||||
uint64_t mmio1_size;
|
||||
PnvPHB3 *phb;
|
||||
|
||||
MemoryRegion xscom_nest_regs;
|
||||
MemoryRegion xscom_pci_regs;
|
||||
MemoryRegion xscom_spci_regs;
|
||||
} PnvPBCQState;
|
||||
|
||||
/*
|
||||
* PHB3 PCIe Root port
|
||||
*/
|
||||
#define TYPE_PNV_PHB3_ROOT_BUS "pnv-phb3-root-bus"
|
||||
|
||||
#define TYPE_PNV_PHB3_ROOT_PORT "pnv-phb3-root-port"
|
||||
|
||||
typedef struct PnvPHB3RootPort {
|
||||
PCIESlot parent_obj;
|
||||
} PnvPHB3RootPort;
|
||||
|
||||
/*
|
||||
* PHB3 PCIe Host Bridge for PowerNV machines (POWER8)
|
||||
*/
|
||||
#define TYPE_PNV_PHB3 "pnv-phb3"
|
||||
#define PNV_PHB3(obj) OBJECT_CHECK(PnvPHB3, (obj), TYPE_PNV_PHB3)
|
||||
|
||||
#define PNV_PHB3_NUM_M64 16
|
||||
#define PNV_PHB3_NUM_REGS (0x1000 >> 3)
|
||||
#define PNV_PHB3_NUM_LSI 8
|
||||
#define PNV_PHB3_NUM_PE 256
|
||||
|
||||
#define PCI_MMIO_TOTAL_SIZE (0x1ull << 60)
|
||||
|
||||
struct PnvPHB3 {
|
||||
PCIExpressHost parent_obj;
|
||||
|
||||
uint32_t chip_id;
|
||||
uint32_t phb_id;
|
||||
char bus_path[8];
|
||||
|
||||
uint64_t regs[PNV_PHB3_NUM_REGS];
|
||||
MemoryRegion mr_regs;
|
||||
|
||||
MemoryRegion mr_m32;
|
||||
MemoryRegion mr_m64[PNV_PHB3_NUM_M64];
|
||||
MemoryRegion pci_mmio;
|
||||
MemoryRegion pci_io;
|
||||
|
||||
uint64_t ioda_LIST[8];
|
||||
uint64_t ioda_LXIVT[8];
|
||||
uint64_t ioda_TVT[512];
|
||||
uint64_t ioda_M64BT[16];
|
||||
uint64_t ioda_MDT[256];
|
||||
uint64_t ioda_PEEV[4];
|
||||
|
||||
uint32_t total_irq;
|
||||
ICSState lsis;
|
||||
qemu_irq *qirqs;
|
||||
Phb3MsiState msis;
|
||||
|
||||
PnvPBCQState pbcq;
|
||||
|
||||
PnvPHB3RootPort root;
|
||||
|
||||
QLIST_HEAD(, PnvPhb3DMASpace) dma_spaces;
|
||||
};
|
||||
|
||||
uint64_t pnv_phb3_reg_read(void *opaque, hwaddr off, unsigned size);
|
||||
void pnv_phb3_reg_write(void *opaque, hwaddr off, uint64_t val, unsigned size);
|
||||
void pnv_phb3_update_regions(PnvPHB3 *phb);
|
||||
void pnv_phb3_remap_irqs(PnvPHB3 *phb);
|
||||
|
||||
#endif /* PCI_HOST_PNV_PHB3_H */
|
450
include/hw/pci-host/pnv_phb3_regs.h
Normal file
450
include/hw/pci-host/pnv_phb3_regs.h
Normal file
|
@ -0,0 +1,450 @@
|
|||
/*
|
||||
* QEMU PowerPC PowerNV (POWER8) PHB3 model
|
||||
*
|
||||
* Copyright (c) 2013-2020, IBM Corporation.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See the
|
||||
* COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef PCI_HOST_PNV_PHB3_REGS_H
|
||||
#define PCI_HOST_PNV_PHB3_REGS_H
|
||||
|
||||
#include "qemu/host-utils.h"
|
||||
|
||||
/*
|
||||
* QEMU version of the GETFIELD/SETFIELD macros
|
||||
*
|
||||
* These are common with the PnvXive model.
|
||||
*/
|
||||
static inline uint64_t GETFIELD(uint64_t mask, uint64_t word)
|
||||
{
|
||||
return (word & mask) >> ctz64(mask);
|
||||
}
|
||||
|
||||
static inline uint64_t SETFIELD(uint64_t mask, uint64_t word,
|
||||
uint64_t value)
|
||||
{
|
||||
return (word & ~mask) | ((value << ctz64(mask)) & mask);
|
||||
}
|
||||
|
||||
/*
|
||||
* PBCQ XSCOM registers
|
||||
*/
|
||||
|
||||
#define PBCQ_NEST_IRSN_COMPARE 0x1a
|
||||
#define PBCQ_NEST_IRSN_COMP PPC_BITMASK(0, 18)
|
||||
#define PBCQ_NEST_IRSN_MASK 0x1b
|
||||
#define PBCQ_NEST_LSI_SRC_ID 0x1f
|
||||
#define PBCQ_NEST_LSI_SRC PPC_BITMASK(0, 7)
|
||||
#define PBCQ_NEST_REGS_COUNT 0x46
|
||||
#define PBCQ_NEST_MMIO_BAR0 0x40
|
||||
#define PBCQ_NEST_MMIO_BAR1 0x41
|
||||
#define PBCQ_NEST_PHB_BAR 0x42
|
||||
#define PBCQ_NEST_MMIO_MASK0 0x43
|
||||
#define PBCQ_NEST_MMIO_MASK1 0x44
|
||||
#define PBCQ_NEST_BAR_EN 0x45
|
||||
#define PBCQ_NEST_BAR_EN_MMIO0 PPC_BIT(0)
|
||||
#define PBCQ_NEST_BAR_EN_MMIO1 PPC_BIT(1)
|
||||
#define PBCQ_NEST_BAR_EN_PHB PPC_BIT(2)
|
||||
#define PBCQ_NEST_BAR_EN_IRSN_RX PPC_BIT(3)
|
||||
#define PBCQ_NEST_BAR_EN_IRSN_TX PPC_BIT(4)
|
||||
|
||||
#define PBCQ_PCI_REGS_COUNT 0x15
|
||||
#define PBCQ_PCI_BAR2 0x0b
|
||||
|
||||
#define PBCQ_SPCI_REGS_COUNT 0x5
|
||||
#define PBCQ_SPCI_ASB_ADDR 0x0
|
||||
#define PBCQ_SPCI_ASB_STATUS 0x1
|
||||
#define PBCQ_SPCI_ASB_DATA 0x2
|
||||
#define PBCQ_SPCI_AIB_CAPP_EN 0x3
|
||||
#define PBCQ_SPCI_CAPP_SEC_TMR 0x4
|
||||
|
||||
/*
|
||||
* PHB MMIO registers
|
||||
*/
|
||||
|
||||
/* PHB Fundamental register set A */
|
||||
#define PHB_LSI_SOURCE_ID 0x100
|
||||
#define PHB_LSI_SRC_ID PPC_BITMASK(5, 12)
|
||||
#define PHB_DMA_CHAN_STATUS 0x110
|
||||
#define PHB_DMA_CHAN_ANY_ERR PPC_BIT(27)
|
||||
#define PHB_DMA_CHAN_ANY_ERR1 PPC_BIT(28)
|
||||
#define PHB_DMA_CHAN_ANY_FREEZE PPC_BIT(29)
|
||||
#define PHB_CPU_LOADSTORE_STATUS 0x120
|
||||
#define PHB_CPU_LS_ANY_ERR PPC_BIT(27)
|
||||
#define PHB_CPU_LS_ANY_ERR1 PPC_BIT(28)
|
||||
#define PHB_CPU_LS_ANY_FREEZE PPC_BIT(29)
|
||||
#define PHB_DMA_MSI_NODE_ID 0x128
|
||||
#define PHB_DMAMSI_NID_FIXED PPC_BIT(0)
|
||||
#define PHB_DMAMSI_NID PPC_BITMASK(24, 31)
|
||||
#define PHB_CONFIG_DATA 0x130
|
||||
#define PHB_LOCK0 0x138
|
||||
#define PHB_CONFIG_ADDRESS 0x140
|
||||
#define PHB_CA_ENABLE PPC_BIT(0)
|
||||
#define PHB_CA_BUS PPC_BITMASK(4, 11)
|
||||
#define PHB_CA_DEV PPC_BITMASK(12, 16)
|
||||
#define PHB_CA_FUNC PPC_BITMASK(17, 19)
|
||||
#define PHB_CA_REG PPC_BITMASK(20, 31)
|
||||
#define PHB_CA_PE PPC_BITMASK(40, 47)
|
||||
#define PHB_LOCK1 0x148
|
||||
#define PHB_IVT_BAR 0x150
|
||||
#define PHB_IVT_BAR_ENABLE PPC_BIT(0)
|
||||
#define PHB_IVT_BASE_ADDRESS_MASK PPC_BITMASK(14, 48)
|
||||
#define PHB_IVT_LENGTH_MASK PPC_BITMASK(52, 63)
|
||||
#define PHB_RBA_BAR 0x158
|
||||
#define PHB_RBA_BAR_ENABLE PPC_BIT(0)
|
||||
#define PHB_RBA_BASE_ADDRESS PPC_BITMASK(14, 55)
|
||||
#define PHB_PHB3_CONFIG 0x160
|
||||
#define PHB_PHB3C_64B_TCE_EN PPC_BIT(2)
|
||||
#define PHB_PHB3C_32BIT_MSI_EN PPC_BIT(8)
|
||||
#define PHB_PHB3C_64BIT_MSI_EN PPC_BIT(14)
|
||||
#define PHB_PHB3C_M32_EN PPC_BIT(16)
|
||||
#define PHB_RTT_BAR 0x168
|
||||
#define PHB_RTT_BAR_ENABLE PPC_BIT(0)
|
||||
#define PHB_RTT_BASE_ADDRESS_MASK PPC_BITMASK(14, 46)
|
||||
#define PHB_PELTV_BAR 0x188
|
||||
#define PHB_PELTV_BAR_ENABLE PPC_BIT(0)
|
||||
#define PHB_PELTV_BASE_ADDRESS PPC_BITMASK(14, 50)
|
||||
#define PHB_M32_BASE_ADDR 0x190
|
||||
#define PHB_M32_BASE_MASK 0x198
|
||||
#define PHB_M32_START_ADDR 0x1a0
|
||||
#define PHB_PEST_BAR 0x1a8
|
||||
#define PHB_PEST_BAR_ENABLE PPC_BIT(0)
|
||||
#define PHB_PEST_BASE_ADDRESS PPC_BITMASK(14, 51)
|
||||
#define PHB_M64_UPPER_BITS 0x1f0
|
||||
#define PHB_INTREP_TIMER 0x1f8
|
||||
#define PHB_DMARD_SYNC 0x200
|
||||
#define PHB_DMARD_SYNC_START PPC_BIT(0)
|
||||
#define PHB_DMARD_SYNC_COMPLETE PPC_BIT(1)
|
||||
#define PHB_RTC_INVALIDATE 0x208
|
||||
#define PHB_RTC_INVALIDATE_ALL PPC_BIT(0)
|
||||
#define PHB_RTC_INVALIDATE_RID PPC_BITMASK(16, 31)
|
||||
#define PHB_TCE_KILL 0x210
|
||||
#define PHB_TCE_KILL_ALL PPC_BIT(0)
|
||||
#define PHB_TCE_SPEC_CTL 0x218
|
||||
#define PHB_IODA_ADDR 0x220
|
||||
#define PHB_IODA_AD_AUTOINC PPC_BIT(0)
|
||||
#define PHB_IODA_AD_TSEL PPC_BITMASK(11, 15)
|
||||
#define PHB_IODA_AD_TADR PPC_BITMASK(55, 63)
|
||||
#define PHB_IODA_DATA0 0x228
|
||||
#define PHB_FFI_REQUEST 0x238
|
||||
#define PHB_FFI_LOCK_CLEAR PPC_BIT(3)
|
||||
#define PHB_FFI_REQUEST_ISN PPC_BITMASK(49, 59)
|
||||
#define PHB_FFI_LOCK 0x240
|
||||
#define PHB_FFI_LOCK_STATE PPC_BIT(0)
|
||||
#define PHB_XIVE_UPDATE 0x248 /* Broken in DD1 */
|
||||
#define PHB_PHB3_GEN_CAP 0x250
|
||||
#define PHB_PHB3_TCE_CAP 0x258
|
||||
#define PHB_PHB3_IRQ_CAP 0x260
|
||||
#define PHB_PHB3_EEH_CAP 0x268
|
||||
#define PHB_IVC_INVALIDATE 0x2a0
|
||||
#define PHB_IVC_INVALIDATE_ALL PPC_BIT(0)
|
||||
#define PHB_IVC_INVALIDATE_SID PPC_BITMASK(16, 31)
|
||||
#define PHB_IVC_UPDATE 0x2a8
|
||||
#define PHB_IVC_UPDATE_ENABLE_P PPC_BIT(0)
|
||||
#define PHB_IVC_UPDATE_ENABLE_Q PPC_BIT(1)
|
||||
#define PHB_IVC_UPDATE_ENABLE_SERVER PPC_BIT(2)
|
||||
#define PHB_IVC_UPDATE_ENABLE_PRI PPC_BIT(3)
|
||||
#define PHB_IVC_UPDATE_ENABLE_GEN PPC_BIT(4)
|
||||
#define PHB_IVC_UPDATE_ENABLE_CON PPC_BIT(5)
|
||||
#define PHB_IVC_UPDATE_GEN_MATCH PPC_BITMASK(6, 7)
|
||||
#define PHB_IVC_UPDATE_SERVER PPC_BITMASK(8, 23)
|
||||
#define PHB_IVC_UPDATE_PRI PPC_BITMASK(24, 31)
|
||||
#define PHB_IVC_UPDATE_GEN PPC_BITMASK(32, 33)
|
||||
#define PHB_IVC_UPDATE_P PPC_BITMASK(34, 34)
|
||||
#define PHB_IVC_UPDATE_Q PPC_BITMASK(35, 35)
|
||||
#define PHB_IVC_UPDATE_SID PPC_BITMASK(48, 63)
|
||||
#define PHB_PAPR_ERR_INJ_CTL 0x2b0
|
||||
#define PHB_PAPR_ERR_INJ_CTL_INB PPC_BIT(0)
|
||||
#define PHB_PAPR_ERR_INJ_CTL_OUTB PPC_BIT(1)
|
||||
#define PHB_PAPR_ERR_INJ_CTL_STICKY PPC_BIT(2)
|
||||
#define PHB_PAPR_ERR_INJ_CTL_CFG PPC_BIT(3)
|
||||
#define PHB_PAPR_ERR_INJ_CTL_RD PPC_BIT(4)
|
||||
#define PHB_PAPR_ERR_INJ_CTL_WR PPC_BIT(5)
|
||||
#define PHB_PAPR_ERR_INJ_CTL_FREEZE PPC_BIT(6)
|
||||
#define PHB_PAPR_ERR_INJ_ADDR 0x2b8
|
||||
#define PHB_PAPR_ERR_INJ_ADDR_MMIO PPC_BITMASK(16, 63)
|
||||
#define PHB_PAPR_ERR_INJ_MASK 0x2c0
|
||||
#define PHB_PAPR_ERR_INJ_MASK_CFG PPC_BITMASK(4, 11)
|
||||
#define PHB_PAPR_ERR_INJ_MASK_MMIO PPC_BITMASK(16, 63)
|
||||
#define PHB_ETU_ERR_SUMMARY 0x2c8
|
||||
|
||||
/* UTL registers */
|
||||
#define UTL_SYS_BUS_CONTROL 0x400
|
||||
#define UTL_STATUS 0x408
|
||||
#define UTL_SYS_BUS_AGENT_STATUS 0x410
|
||||
#define UTL_SYS_BUS_AGENT_ERR_SEVERITY 0x418
|
||||
#define UTL_SYS_BUS_AGENT_IRQ_EN 0x420
|
||||
#define UTL_SYS_BUS_BURST_SZ_CONF 0x440
|
||||
#define UTL_REVISION_ID 0x448
|
||||
#define UTL_BCLK_DOMAIN_DBG1 0x460
|
||||
#define UTL_BCLK_DOMAIN_DBG2 0x468
|
||||
#define UTL_BCLK_DOMAIN_DBG3 0x470
|
||||
#define UTL_BCLK_DOMAIN_DBG4 0x478
|
||||
#define UTL_BCLK_DOMAIN_DBG5 0x480
|
||||
#define UTL_BCLK_DOMAIN_DBG6 0x488
|
||||
#define UTL_OUT_POST_HDR_BUF_ALLOC 0x4c0
|
||||
#define UTL_OUT_POST_DAT_BUF_ALLOC 0x4d0
|
||||
#define UTL_IN_POST_HDR_BUF_ALLOC 0x4e0
|
||||
#define UTL_IN_POST_DAT_BUF_ALLOC 0x4f0
|
||||
#define UTL_OUT_NP_BUF_ALLOC 0x500
|
||||
#define UTL_IN_NP_BUF_ALLOC 0x510
|
||||
#define UTL_PCIE_TAGS_ALLOC 0x520
|
||||
#define UTL_GBIF_READ_TAGS_ALLOC 0x530
|
||||
#define UTL_PCIE_PORT_CONTROL 0x540
|
||||
#define UTL_PCIE_PORT_STATUS 0x548
|
||||
#define UTL_PCIE_PORT_ERROR_SEV 0x550
|
||||
#define UTL_PCIE_PORT_IRQ_EN 0x558
|
||||
#define UTL_RC_STATUS 0x560
|
||||
#define UTL_RC_ERR_SEVERITY 0x568
|
||||
#define UTL_RC_IRQ_EN 0x570
|
||||
#define UTL_EP_STATUS 0x578
|
||||
#define UTL_EP_ERR_SEVERITY 0x580
|
||||
#define UTL_EP_ERR_IRQ_EN 0x588
|
||||
#define UTL_PCI_PM_CTRL1 0x590
|
||||
#define UTL_PCI_PM_CTRL2 0x598
|
||||
#define UTL_GP_CTL1 0x5a0
|
||||
#define UTL_GP_CTL2 0x5a8
|
||||
#define UTL_PCLK_DOMAIN_DBG1 0x5b0
|
||||
#define UTL_PCLK_DOMAIN_DBG2 0x5b8
|
||||
#define UTL_PCLK_DOMAIN_DBG3 0x5c0
|
||||
#define UTL_PCLK_DOMAIN_DBG4 0x5c8
|
||||
|
||||
/* PCI-E Stack registers */
|
||||
#define PHB_PCIE_SYSTEM_CONFIG 0x600
|
||||
#define PHB_PCIE_BUS_NUMBER 0x608
|
||||
#define PHB_PCIE_SYSTEM_TEST 0x618
|
||||
#define PHB_PCIE_LINK_MANAGEMENT 0x630
|
||||
#define PHB_PCIE_LM_LINK_ACTIVE PPC_BIT(8)
|
||||
#define PHB_PCIE_DLP_TRAIN_CTL 0x640
|
||||
#define PHB_PCIE_DLP_TCTX_DISABLE PPC_BIT(1)
|
||||
#define PHB_PCIE_DLP_TCRX_DISABLED PPC_BIT(16)
|
||||
#define PHB_PCIE_DLP_INBAND_PRESENCE PPC_BIT(19)
|
||||
#define PHB_PCIE_DLP_TC_DL_LINKUP PPC_BIT(21)
|
||||
#define PHB_PCIE_DLP_TC_DL_PGRESET PPC_BIT(22)
|
||||
#define PHB_PCIE_DLP_TC_DL_LINKACT PPC_BIT(23)
|
||||
#define PHB_PCIE_SLOP_LOOPBACK_STATUS 0x648
|
||||
#define PHB_PCIE_SYS_LINK_INIT 0x668
|
||||
#define PHB_PCIE_UTL_CONFIG 0x670
|
||||
#define PHB_PCIE_DLP_CONTROL 0x678
|
||||
#define PHB_PCIE_UTL_ERRLOG1 0x680
|
||||
#define PHB_PCIE_UTL_ERRLOG2 0x688
|
||||
#define PHB_PCIE_UTL_ERRLOG3 0x690
|
||||
#define PHB_PCIE_UTL_ERRLOG4 0x698
|
||||
#define PHB_PCIE_DLP_ERRLOG1 0x6a0
|
||||
#define PHB_PCIE_DLP_ERRLOG2 0x6a8
|
||||
#define PHB_PCIE_DLP_ERR_STATUS 0x6b0
|
||||
#define PHB_PCIE_DLP_ERR_COUNTERS 0x6b8
|
||||
#define PHB_PCIE_UTL_ERR_INJECT 0x6c0
|
||||
#define PHB_PCIE_TLDLP_ERR_INJECT 0x6c8
|
||||
#define PHB_PCIE_LANE_EQ_CNTL0 0x6d0
|
||||
#define PHB_PCIE_LANE_EQ_CNTL1 0x6d8
|
||||
#define PHB_PCIE_LANE_EQ_CNTL2 0x6e0
|
||||
#define PHB_PCIE_LANE_EQ_CNTL3 0x6e8
|
||||
#define PHB_PCIE_STRAPPING 0x700
|
||||
|
||||
/* Fundamental register set B */
|
||||
#define PHB_VERSION 0x800
|
||||
#define PHB_RESET 0x808
|
||||
#define PHB_CONTROL 0x810
|
||||
#define PHB_CTRL_IVE_128_BYTES PPC_BIT(24)
|
||||
#define PHB_AIB_RX_CRED_INIT_TIMER 0x818
|
||||
#define PHB_AIB_RX_CMD_CRED 0x820
|
||||
#define PHB_AIB_RX_DATA_CRED 0x828
|
||||
#define PHB_AIB_TX_CMD_CRED 0x830
|
||||
#define PHB_AIB_TX_DATA_CRED 0x838
|
||||
#define PHB_AIB_TX_CHAN_MAPPING 0x840
|
||||
#define PHB_AIB_TAG_ENABLE 0x858
|
||||
#define PHB_AIB_FENCE_CTRL 0x860
|
||||
#define PHB_TCE_TAG_ENABLE 0x868
|
||||
#define PHB_TCE_WATERMARK 0x870
|
||||
#define PHB_TIMEOUT_CTRL1 0x878
|
||||
#define PHB_TIMEOUT_CTRL2 0x880
|
||||
#define PHB_Q_DMA_R 0x888
|
||||
#define PHB_Q_DMA_R_QUIESCE_DMA PPC_BIT(0)
|
||||
#define PHB_Q_DMA_R_AUTORESET PPC_BIT(1)
|
||||
#define PHB_Q_DMA_R_DMA_RESP_STATUS PPC_BIT(4)
|
||||
#define PHB_Q_DMA_R_MMIO_RESP_STATUS PPC_BIT(5)
|
||||
#define PHB_Q_DMA_R_TCE_RESP_STATUS PPC_BIT(6)
|
||||
#define PHB_AIB_TAG_STATUS 0x900
|
||||
#define PHB_TCE_TAG_STATUS 0x908
|
||||
|
||||
/* FIR & Error registers */
|
||||
#define PHB_LEM_FIR_ACCUM 0xc00
|
||||
#define PHB_LEM_FIR_AND_MASK 0xc08
|
||||
#define PHB_LEM_FIR_OR_MASK 0xc10
|
||||
#define PHB_LEM_ERROR_MASK 0xc18
|
||||
#define PHB_LEM_ERROR_AND_MASK 0xc20
|
||||
#define PHB_LEM_ERROR_OR_MASK 0xc28
|
||||
#define PHB_LEM_ACTION0 0xc30
|
||||
#define PHB_LEM_ACTION1 0xc38
|
||||
#define PHB_LEM_WOF 0xc40
|
||||
#define PHB_ERR_STATUS 0xc80
|
||||
#define PHB_ERR1_STATUS 0xc88
|
||||
#define PHB_ERR_INJECT 0xc90
|
||||
#define PHB_ERR_LEM_ENABLE 0xc98
|
||||
#define PHB_ERR_IRQ_ENABLE 0xca0
|
||||
#define PHB_ERR_FREEZE_ENABLE 0xca8
|
||||
#define PHB_ERR_AIB_FENCE_ENABLE 0xcb0
|
||||
#define PHB_ERR_LOG_0 0xcc0
|
||||
#define PHB_ERR_LOG_1 0xcc8
|
||||
#define PHB_ERR_STATUS_MASK 0xcd0
|
||||
#define PHB_ERR1_STATUS_MASK 0xcd8
|
||||
|
||||
#define PHB_OUT_ERR_STATUS 0xd00
|
||||
#define PHB_OUT_ERR1_STATUS 0xd08
|
||||
#define PHB_OUT_ERR_INJECT 0xd10
|
||||
#define PHB_OUT_ERR_LEM_ENABLE 0xd18
|
||||
#define PHB_OUT_ERR_IRQ_ENABLE 0xd20
|
||||
#define PHB_OUT_ERR_FREEZE_ENABLE 0xd28
|
||||
#define PHB_OUT_ERR_AIB_FENCE_ENABLE 0xd30
|
||||
#define PHB_OUT_ERR_LOG_0 0xd40
|
||||
#define PHB_OUT_ERR_LOG_1 0xd48
|
||||
#define PHB_OUT_ERR_STATUS_MASK 0xd50
|
||||
#define PHB_OUT_ERR1_STATUS_MASK 0xd58
|
||||
|
||||
#define PHB_INA_ERR_STATUS 0xd80
|
||||
#define PHB_INA_ERR1_STATUS 0xd88
|
||||
#define PHB_INA_ERR_INJECT 0xd90
|
||||
#define PHB_INA_ERR_LEM_ENABLE 0xd98
|
||||
#define PHB_INA_ERR_IRQ_ENABLE 0xda0
|
||||
#define PHB_INA_ERR_FREEZE_ENABLE 0xda8
|
||||
#define PHB_INA_ERR_AIB_FENCE_ENABLE 0xdb0
|
||||
#define PHB_INA_ERR_LOG_0 0xdc0
|
||||
#define PHB_INA_ERR_LOG_1 0xdc8
|
||||
#define PHB_INA_ERR_STATUS_MASK 0xdd0
|
||||
#define PHB_INA_ERR1_STATUS_MASK 0xdd8
|
||||
|
||||
#define PHB_INB_ERR_STATUS 0xe00
|
||||
#define PHB_INB_ERR1_STATUS 0xe08
|
||||
#define PHB_INB_ERR_INJECT 0xe10
|
||||
#define PHB_INB_ERR_LEM_ENABLE 0xe18
|
||||
#define PHB_INB_ERR_IRQ_ENABLE 0xe20
|
||||
#define PHB_INB_ERR_FREEZE_ENABLE 0xe28
|
||||
#define PHB_INB_ERR_AIB_FENCE_ENABLE 0xe30
|
||||
#define PHB_INB_ERR_LOG_0 0xe40
|
||||
#define PHB_INB_ERR_LOG_1 0xe48
|
||||
#define PHB_INB_ERR_STATUS_MASK 0xe50
|
||||
#define PHB_INB_ERR1_STATUS_MASK 0xe58
|
||||
|
||||
/* Performance monitor & Debug registers */
|
||||
#define PHB_TRACE_CONTROL 0xf80
|
||||
#define PHB_PERFMON_CONFIG 0xf88
|
||||
#define PHB_PERFMON_CTR0 0xf90
|
||||
#define PHB_PERFMON_CTR1 0xf98
|
||||
#define PHB_PERFMON_CTR2 0xfa0
|
||||
#define PHB_PERFMON_CTR3 0xfa8
|
||||
#define PHB_HOTPLUG_OVERRIDE 0xfb0
|
||||
#define PHB_HPOVR_FORCE_RESAMPLE PPC_BIT(9)
|
||||
#define PHB_HPOVR_PRESENCE_A PPC_BIT(10)
|
||||
#define PHB_HPOVR_PRESENCE_B PPC_BIT(11)
|
||||
#define PHB_HPOVR_LINK_ACTIVE PPC_BIT(12)
|
||||
#define PHB_HPOVR_LINK_BIFURCATED PPC_BIT(13)
|
||||
#define PHB_HPOVR_LINK_LANE_SWAPPED PPC_BIT(14)
|
||||
|
||||
/*
|
||||
* IODA2 on-chip tables
|
||||
*/
|
||||
|
||||
#define IODA2_TBL_LIST 1
|
||||
#define IODA2_TBL_LXIVT 2
|
||||
#define IODA2_TBL_IVC_CAM 3
|
||||
#define IODA2_TBL_RBA 4
|
||||
#define IODA2_TBL_RCAM 5
|
||||
#define IODA2_TBL_MRT 6
|
||||
#define IODA2_TBL_PESTA 7
|
||||
#define IODA2_TBL_PESTB 8
|
||||
#define IODA2_TBL_TVT 9
|
||||
#define IODA2_TBL_TCAM 10
|
||||
#define IODA2_TBL_TDR 11
|
||||
#define IODA2_TBL_M64BT 16
|
||||
#define IODA2_TBL_M32DT 17
|
||||
#define IODA2_TBL_PEEV 20
|
||||
|
||||
/* LXIVT */
|
||||
#define IODA2_LXIVT_SERVER PPC_BITMASK(8, 23)
|
||||
#define IODA2_LXIVT_PRIORITY PPC_BITMASK(24, 31)
|
||||
#define IODA2_LXIVT_NODE_ID PPC_BITMASK(56, 63)
|
||||
|
||||
/* IVT */
|
||||
#define IODA2_IVT_SERVER PPC_BITMASK(0, 23)
|
||||
#define IODA2_IVT_PRIORITY PPC_BITMASK(24, 31)
|
||||
#define IODA2_IVT_GEN PPC_BITMASK(37, 38)
|
||||
#define IODA2_IVT_P PPC_BITMASK(39, 39)
|
||||
#define IODA2_IVT_Q PPC_BITMASK(47, 47)
|
||||
#define IODA2_IVT_PE PPC_BITMASK(48, 63)
|
||||
|
||||
/* TVT */
|
||||
#define IODA2_TVT_TABLE_ADDR PPC_BITMASK(0, 47)
|
||||
#define IODA2_TVT_NUM_LEVELS PPC_BITMASK(48, 50)
|
||||
#define IODA2_TVE_1_LEVEL 0
|
||||
#define IODA2_TVE_2_LEVELS 1
|
||||
#define IODA2_TVE_3_LEVELS 2
|
||||
#define IODA2_TVE_4_LEVELS 3
|
||||
#define IODA2_TVE_5_LEVELS 4
|
||||
#define IODA2_TVT_TCE_TABLE_SIZE PPC_BITMASK(51, 55)
|
||||
#define IODA2_TVT_IO_PSIZE PPC_BITMASK(59, 63)
|
||||
|
||||
/* PESTA */
|
||||
#define IODA2_PESTA_MMIO_FROZEN PPC_BIT(0)
|
||||
|
||||
/* PESTB */
|
||||
#define IODA2_PESTB_DMA_STOPPED PPC_BIT(0)
|
||||
|
||||
/* M32DT */
|
||||
#define IODA2_M32DT_PE PPC_BITMASK(8, 15)
|
||||
|
||||
/* M64BT */
|
||||
#define IODA2_M64BT_ENABLE PPC_BIT(0)
|
||||
#define IODA2_M64BT_SINGLE_PE PPC_BIT(1)
|
||||
#define IODA2_M64BT_BASE PPC_BITMASK(2, 31)
|
||||
#define IODA2_M64BT_MASK PPC_BITMASK(34, 63)
|
||||
#define IODA2_M64BT_SINGLE_BASE PPC_BITMASK(2, 26)
|
||||
#define IODA2_M64BT_PE_HI PPC_BITMASK(27, 31)
|
||||
#define IODA2_M64BT_SINGLE_MASK PPC_BITMASK(34, 58)
|
||||
#define IODA2_M64BT_PE_LOW PPC_BITMASK(59, 63)
|
||||
|
||||
/*
|
||||
* IODA2 in-memory tables
|
||||
*/
|
||||
|
||||
/*
|
||||
* PEST
|
||||
*
|
||||
* 2x8 bytes entries, PEST0 and PEST1
|
||||
*/
|
||||
|
||||
#define IODA2_PEST0_MMIO_CAUSE PPC_BIT(2)
|
||||
#define IODA2_PEST0_CFG_READ PPC_BIT(3)
|
||||
#define IODA2_PEST0_CFG_WRITE PPC_BIT(4)
|
||||
#define IODA2_PEST0_TTYPE PPC_BITMASK(5, 7)
|
||||
#define PEST_TTYPE_DMA_WRITE 0
|
||||
#define PEST_TTYPE_MSI 1
|
||||
#define PEST_TTYPE_DMA_READ 2
|
||||
#define PEST_TTYPE_DMA_READ_RESP 3
|
||||
#define PEST_TTYPE_MMIO_LOAD 4
|
||||
#define PEST_TTYPE_MMIO_STORE 5
|
||||
#define PEST_TTYPE_OTHER 7
|
||||
#define IODA2_PEST0_CA_RETURN PPC_BIT(8)
|
||||
#define IODA2_PEST0_UTL_RTOS_TIMEOUT PPC_BIT(8) /* Same bit as CA return */
|
||||
#define IODA2_PEST0_UR_RETURN PPC_BIT(9)
|
||||
#define IODA2_PEST0_UTL_NONFATAL PPC_BIT(10)
|
||||
#define IODA2_PEST0_UTL_FATAL PPC_BIT(11)
|
||||
#define IODA2_PEST0_PARITY_UE PPC_BIT(13)
|
||||
#define IODA2_PEST0_UTL_CORRECTABLE PPC_BIT(14)
|
||||
#define IODA2_PEST0_UTL_INTERRUPT PPC_BIT(15)
|
||||
#define IODA2_PEST0_MMIO_XLATE PPC_BIT(16)
|
||||
#define IODA2_PEST0_IODA2_ERROR PPC_BIT(16) /* Same bit as MMIO xlate */
|
||||
#define IODA2_PEST0_TCE_PAGE_FAULT PPC_BIT(18)
|
||||
#define IODA2_PEST0_TCE_ACCESS_FAULT PPC_BIT(19)
|
||||
#define IODA2_PEST0_DMA_RESP_TIMEOUT PPC_BIT(20)
|
||||
#define IODA2_PEST0_AIB_SIZE_INVALID PPC_BIT(21)
|
||||
#define IODA2_PEST0_LEM_BIT PPC_BITMASK(26, 31)
|
||||
#define IODA2_PEST0_RID PPC_BITMASK(32, 47)
|
||||
#define IODA2_PEST0_MSI_DATA PPC_BITMASK(48, 63)
|
||||
|
||||
#define IODA2_PEST1_FAIL_ADDR PPC_BITMASK(3, 63)
|
||||
|
||||
|
||||
#endif /* PCI_HOST_PNV_PHB3_REGS_H */
|
230
include/hw/pci-host/pnv_phb4.h
Normal file
230
include/hw/pci-host/pnv_phb4.h
Normal file
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* QEMU PowerPC PowerNV (POWER9) PHB4 model
|
||||
*
|
||||
* Copyright (c) 2018-2020, IBM Corporation.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See the
|
||||
* COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef PCI_HOST_PNV_PHB4_H
|
||||
#define PCI_HOST_PNV_PHB4_H
|
||||
|
||||
#include "hw/pci/pcie_host.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
#include "hw/ppc/xive.h"
|
||||
|
||||
typedef struct PnvPhb4PecState PnvPhb4PecState;
|
||||
typedef struct PnvPhb4PecStack PnvPhb4PecStack;
|
||||
typedef struct PnvPHB4 PnvPHB4;
|
||||
typedef struct PnvChip PnvChip;
|
||||
|
||||
/*
|
||||
* We have one such address space wrapper per possible device under
|
||||
* the PHB since they need to be assigned statically at qemu device
|
||||
* creation time. The relationship to a PE is done later
|
||||
* dynamically. This means we can potentially create a lot of these
|
||||
* guys. Q35 stores them as some kind of radix tree but we never
|
||||
* really need to do fast lookups so instead we simply keep a QLIST of
|
||||
* them for now, we can add the radix if needed later on.
|
||||
*
|
||||
* We do cache the PE number to speed things up a bit though.
|
||||
*/
|
||||
typedef struct PnvPhb4DMASpace {
|
||||
PCIBus *bus;
|
||||
uint8_t devfn;
|
||||
int pe_num; /* Cached PE number */
|
||||
#define PHB_INVALID_PE (-1)
|
||||
PnvPHB4 *phb;
|
||||
AddressSpace dma_as;
|
||||
IOMMUMemoryRegion dma_mr;
|
||||
MemoryRegion msi32_mr;
|
||||
MemoryRegion msi64_mr;
|
||||
QLIST_ENTRY(PnvPhb4DMASpace) list;
|
||||
} PnvPhb4DMASpace;
|
||||
|
||||
/*
|
||||
* PHB4 PCIe Root port
|
||||
*/
|
||||
#define TYPE_PNV_PHB4_ROOT_BUS "pnv-phb4-root-bus"
|
||||
#define TYPE_PNV_PHB4_ROOT_PORT "pnv-phb4-root-port"
|
||||
|
||||
typedef struct PnvPHB4RootPort {
|
||||
PCIESlot parent_obj;
|
||||
} PnvPHB4RootPort;
|
||||
|
||||
/*
|
||||
* PHB4 PCIe Host Bridge for PowerNV machines (POWER9)
|
||||
*/
|
||||
#define TYPE_PNV_PHB4 "pnv-phb4"
|
||||
#define PNV_PHB4(obj) OBJECT_CHECK(PnvPHB4, (obj), TYPE_PNV_PHB4)
|
||||
|
||||
#define PNV_PHB4_MAX_LSIs 8
|
||||
#define PNV_PHB4_MAX_INTs 4096
|
||||
#define PNV_PHB4_MAX_MIST (PNV_PHB4_MAX_INTs >> 2)
|
||||
#define PNV_PHB4_MAX_MMIO_WINDOWS 32
|
||||
#define PNV_PHB4_MIN_MMIO_WINDOWS 16
|
||||
#define PNV_PHB4_NUM_REGS (0x3000 >> 3)
|
||||
#define PNV_PHB4_MAX_PEs 512
|
||||
#define PNV_PHB4_MAX_TVEs (PNV_PHB4_MAX_PEs * 2)
|
||||
#define PNV_PHB4_MAX_PEEVs (PNV_PHB4_MAX_PEs / 64)
|
||||
#define PNV_PHB4_MAX_MBEs (PNV_PHB4_MAX_MMIO_WINDOWS * 2)
|
||||
|
||||
#define PNV_PHB4_VERSION 0x000000a400000002ull
|
||||
#define PNV_PHB4_DEVICE_ID 0x04c1
|
||||
|
||||
#define PCI_MMIO_TOTAL_SIZE (0x1ull << 60)
|
||||
|
||||
struct PnvPHB4 {
|
||||
PCIExpressHost parent_obj;
|
||||
|
||||
PnvPHB4RootPort root;
|
||||
|
||||
uint32_t chip_id;
|
||||
uint32_t phb_id;
|
||||
|
||||
uint64_t version;
|
||||
uint16_t device_id;
|
||||
|
||||
char bus_path[8];
|
||||
|
||||
/* Main register images */
|
||||
uint64_t regs[PNV_PHB4_NUM_REGS];
|
||||
MemoryRegion mr_regs;
|
||||
|
||||
/* Extra SCOM-only register */
|
||||
uint64_t scom_hv_ind_addr_reg;
|
||||
|
||||
/*
|
||||
* Geometry of the PHB. There are two types, small and big PHBs, a
|
||||
* number of resources (number of PEs, windows etc...) are doubled
|
||||
* for a big PHB
|
||||
*/
|
||||
bool big_phb;
|
||||
|
||||
/* Memory regions for MMIO space */
|
||||
MemoryRegion mr_mmio[PNV_PHB4_MAX_MMIO_WINDOWS];
|
||||
|
||||
/* PCI side space */
|
||||
MemoryRegion pci_mmio;
|
||||
MemoryRegion pci_io;
|
||||
|
||||
/* On-chip IODA tables */
|
||||
uint64_t ioda_LIST[PNV_PHB4_MAX_LSIs];
|
||||
uint64_t ioda_MIST[PNV_PHB4_MAX_MIST];
|
||||
uint64_t ioda_TVT[PNV_PHB4_MAX_TVEs];
|
||||
uint64_t ioda_MBT[PNV_PHB4_MAX_MBEs];
|
||||
uint64_t ioda_MDT[PNV_PHB4_MAX_PEs];
|
||||
uint64_t ioda_PEEV[PNV_PHB4_MAX_PEEVs];
|
||||
|
||||
/*
|
||||
* The internal PESTA/B is 2 bits per PE split into two tables, we
|
||||
* store them in a single array here to avoid wasting space.
|
||||
*/
|
||||
uint8_t ioda_PEST_AB[PNV_PHB4_MAX_PEs];
|
||||
|
||||
/* P9 Interrupt generation */
|
||||
XiveSource xsrc;
|
||||
qemu_irq *qirqs;
|
||||
|
||||
PnvPhb4PecStack *stack;
|
||||
|
||||
QLIST_HEAD(, PnvPhb4DMASpace) dma_spaces;
|
||||
};
|
||||
|
||||
void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon);
|
||||
void pnv_phb4_update_regions(PnvPhb4PecStack *stack);
|
||||
extern const MemoryRegionOps pnv_phb4_xscom_ops;
|
||||
|
||||
/*
|
||||
* PHB4 PEC (PCI Express Controller)
|
||||
*/
|
||||
#define TYPE_PNV_PHB4_PEC "pnv-phb4-pec"
|
||||
#define PNV_PHB4_PEC(obj) \
|
||||
OBJECT_CHECK(PnvPhb4PecState, (obj), TYPE_PNV_PHB4_PEC)
|
||||
|
||||
#define TYPE_PNV_PHB4_PEC_STACK "pnv-phb4-pec-stack"
|
||||
#define PNV_PHB4_PEC_STACK(obj) \
|
||||
OBJECT_CHECK(PnvPhb4PecStack, (obj), TYPE_PNV_PHB4_PEC_STACK)
|
||||
|
||||
/* Per-stack data */
|
||||
struct PnvPhb4PecStack {
|
||||
DeviceState parent;
|
||||
|
||||
/* My own stack number */
|
||||
uint32_t stack_no;
|
||||
|
||||
/* Nest registers */
|
||||
#define PHB4_PEC_NEST_STK_REGS_COUNT 0x17
|
||||
uint64_t nest_regs[PHB4_PEC_NEST_STK_REGS_COUNT];
|
||||
MemoryRegion nest_regs_mr;
|
||||
|
||||
/* PCI registers (excluding pass-through) */
|
||||
#define PHB4_PEC_PCI_STK_REGS_COUNT 0xf
|
||||
uint64_t pci_regs[PHB4_PEC_PCI_STK_REGS_COUNT];
|
||||
MemoryRegion pci_regs_mr;
|
||||
|
||||
/* PHB pass-through XSCOM */
|
||||
MemoryRegion phb_regs_mr;
|
||||
|
||||
/* Memory windows from PowerBus to PHB */
|
||||
MemoryRegion mmbar0;
|
||||
MemoryRegion mmbar1;
|
||||
MemoryRegion phbbar;
|
||||
MemoryRegion intbar;
|
||||
uint64_t mmio0_base;
|
||||
uint64_t mmio0_size;
|
||||
uint64_t mmio1_base;
|
||||
uint64_t mmio1_size;
|
||||
|
||||
/* The owner PEC */
|
||||
PnvPhb4PecState *pec;
|
||||
|
||||
/* The actual PHB */
|
||||
PnvPHB4 phb;
|
||||
};
|
||||
|
||||
struct PnvPhb4PecState {
|
||||
DeviceState parent;
|
||||
|
||||
/* PEC number in chip */
|
||||
uint32_t index;
|
||||
uint32_t chip_id;
|
||||
|
||||
MemoryRegion *system_memory;
|
||||
|
||||
/* Nest registers, excuding per-stack */
|
||||
#define PHB4_PEC_NEST_REGS_COUNT 0xf
|
||||
uint64_t nest_regs[PHB4_PEC_NEST_REGS_COUNT];
|
||||
MemoryRegion nest_regs_mr;
|
||||
|
||||
/* PCI registers, excluding per-stack */
|
||||
#define PHB4_PEC_PCI_REGS_COUNT 0x2
|
||||
uint64_t pci_regs[PHB4_PEC_PCI_REGS_COUNT];
|
||||
MemoryRegion pci_regs_mr;
|
||||
|
||||
/* Stacks */
|
||||
#define PHB4_PEC_MAX_STACKS 3
|
||||
uint32_t num_stacks;
|
||||
PnvPhb4PecStack stacks[PHB4_PEC_MAX_STACKS];
|
||||
};
|
||||
|
||||
#define PNV_PHB4_PEC_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(PnvPhb4PecClass, (klass), TYPE_PNV_PHB4_PEC)
|
||||
#define PNV_PHB4_PEC_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(PnvPhb4PecClass, (obj), TYPE_PNV_PHB4_PEC)
|
||||
|
||||
typedef struct PnvPhb4PecClass {
|
||||
DeviceClass parent_class;
|
||||
|
||||
uint32_t (*xscom_nest_base)(PnvPhb4PecState *pec);
|
||||
uint32_t xscom_nest_size;
|
||||
uint32_t (*xscom_pci_base)(PnvPhb4PecState *pec);
|
||||
uint32_t xscom_pci_size;
|
||||
const char *compat;
|
||||
int compat_size;
|
||||
const char *stk_compat;
|
||||
int stk_compat_size;
|
||||
} PnvPhb4PecClass;
|
||||
|
||||
#endif /* PCI_HOST_PNV_PHB4_H */
|
553
include/hw/pci-host/pnv_phb4_regs.h
Normal file
553
include/hw/pci-host/pnv_phb4_regs.h
Normal file
|
@ -0,0 +1,553 @@
|
|||
/*
|
||||
* QEMU PowerPC PowerNV (POWER9) PHB4 model
|
||||
*
|
||||
* Copyright (c) 2013-2020, IBM Corporation.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See the
|
||||
* COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef PCI_HOST_PNV_PHB4_REGS_H
|
||||
#define PCI_HOST_PNV_PHB4_REGS_H
|
||||
|
||||
/*
|
||||
* PEC XSCOM registers
|
||||
*
|
||||
* There a 3 PECs in P9. Each PEC can have several PHBs. Each PEC has some
|
||||
* "global" registers and some "per-stack" (per-PHB) registers. Those are
|
||||
* organized in two XSCOM ranges, the "Nest" range and the "PCI" range, each
|
||||
* range contains both some "PEC" registers and some "per-stack" registers.
|
||||
*
|
||||
* Finally the PCI range also contains an additional range per stack that
|
||||
* passes through to some of the PHB own registers.
|
||||
*
|
||||
* PEC0 can contain 1 PHB (PHB0)
|
||||
* PEC1 can contain 2 PHBs (PHB1 and PHB2)
|
||||
* PEC2 can contain 3 PHBs (PHB3, PHB4 and PHB5)
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is the "stack" offset, it's the offset from a given range base
|
||||
* to the first "per-stack" registers and also the stride between
|
||||
* stacks, thus for PEC2, the global registers are at offset 0, the
|
||||
* PHB3 registers at offset 0x40, the PHB4 at offset 0x80 etc....
|
||||
*
|
||||
* It is *also* the offset to the pass-through SCOM region but in this case
|
||||
* it is 0 based, ie PHB3 is at 0x100 PHB4 is a 0x140 etc..
|
||||
*/
|
||||
#define PEC_STACK_OFFSET 0x40
|
||||
|
||||
/* XSCOM Nest global registers */
|
||||
#define PEC_NEST_PBCQ_HW_CONFIG 0x00
|
||||
#define PEC_NEST_DROP_PRIO_CTRL 0x01
|
||||
#define PEC_NEST_PBCQ_ERR_INJECT 0x02
|
||||
#define PEC_NEST_PCI_NEST_CLK_TRACE_CTL 0x03
|
||||
#define PEC_NEST_PBCQ_PMON_CTRL 0x04
|
||||
#define PEC_NEST_PBCQ_PBUS_ADDR_EXT 0x05
|
||||
#define PEC_NEST_PBCQ_PRED_VEC_TIMEOUT 0x06
|
||||
#define PEC_NEST_CAPP_CTRL 0x07
|
||||
#define PEC_NEST_PBCQ_READ_STK_OVR 0x08
|
||||
#define PEC_NEST_PBCQ_WRITE_STK_OVR 0x09
|
||||
#define PEC_NEST_PBCQ_STORE_STK_OVR 0x0a
|
||||
#define PEC_NEST_PBCQ_RETRY_BKOFF_CTRL 0x0b
|
||||
|
||||
/* XSCOM Nest per-stack registers */
|
||||
#define PEC_NEST_STK_PCI_NEST_FIR 0x00
|
||||
#define PEC_NEST_STK_PCI_NEST_FIR_CLR 0x01
|
||||
#define PEC_NEST_STK_PCI_NEST_FIR_SET 0x02
|
||||
#define PEC_NEST_STK_PCI_NEST_FIR_MSK 0x03
|
||||
#define PEC_NEST_STK_PCI_NEST_FIR_MSKC 0x04
|
||||
#define PEC_NEST_STK_PCI_NEST_FIR_MSKS 0x05
|
||||
#define PEC_NEST_STK_PCI_NEST_FIR_ACT0 0x06
|
||||
#define PEC_NEST_STK_PCI_NEST_FIR_ACT1 0x07
|
||||
#define PEC_NEST_STK_PCI_NEST_FIR_WOF 0x08
|
||||
#define PEC_NEST_STK_ERR_REPORT_0 0x0a
|
||||
#define PEC_NEST_STK_ERR_REPORT_1 0x0b
|
||||
#define PEC_NEST_STK_PBCQ_GNRL_STATUS 0x0c
|
||||
#define PEC_NEST_STK_PBCQ_MODE 0x0d
|
||||
#define PEC_NEST_STK_MMIO_BAR0 0x0e
|
||||
#define PEC_NEST_STK_MMIO_BAR0_MASK 0x0f
|
||||
#define PEC_NEST_STK_MMIO_BAR1 0x10
|
||||
#define PEC_NEST_STK_MMIO_BAR1_MASK 0x11
|
||||
#define PEC_NEST_STK_PHB_REGS_BAR 0x12
|
||||
#define PEC_NEST_STK_INT_BAR 0x13
|
||||
#define PEC_NEST_STK_BAR_EN 0x14
|
||||
#define PEC_NEST_STK_BAR_EN_MMIO0 PPC_BIT(0)
|
||||
#define PEC_NEST_STK_BAR_EN_MMIO1 PPC_BIT(1)
|
||||
#define PEC_NEST_STK_BAR_EN_PHB PPC_BIT(2)
|
||||
#define PEC_NEST_STK_BAR_EN_INT PPC_BIT(3)
|
||||
#define PEC_NEST_STK_DATA_FRZ_TYPE 0x15
|
||||
#define PEC_NEST_STK_PBCQ_TUN_BAR 0x16
|
||||
|
||||
/* XSCOM PCI global registers */
|
||||
#define PEC_PCI_PBAIB_HW_CONFIG 0x00
|
||||
#define PEC_PCI_PBAIB_READ_STK_OVR 0x02
|
||||
|
||||
/* XSCOM PCI per-stack registers */
|
||||
#define PEC_PCI_STK_PCI_FIR 0x00
|
||||
#define PEC_PCI_STK_PCI_FIR_CLR 0x01
|
||||
#define PEC_PCI_STK_PCI_FIR_SET 0x02
|
||||
#define PEC_PCI_STK_PCI_FIR_MSK 0x03
|
||||
#define PEC_PCI_STK_PCI_FIR_MSKC 0x04
|
||||
#define PEC_PCI_STK_PCI_FIR_MSKS 0x05
|
||||
#define PEC_PCI_STK_PCI_FIR_ACT0 0x06
|
||||
#define PEC_PCI_STK_PCI_FIR_ACT1 0x07
|
||||
#define PEC_PCI_STK_PCI_FIR_WOF 0x08
|
||||
#define PEC_PCI_STK_ETU_RESET 0x0a
|
||||
#define PEC_PCI_STK_PBAIB_ERR_REPORT 0x0b
|
||||
#define PEC_PCI_STK_PBAIB_TX_CMD_CRED 0x0d
|
||||
#define PEC_PCI_STK_PBAIB_TX_DAT_CRED 0x0e
|
||||
|
||||
/*
|
||||
* PHB "SCOM" registers. This is accessed via the above window
|
||||
* and provides a backdoor to the PHB when the AIB bus is not
|
||||
* functional. Some of these directly map some of the PHB MMIO
|
||||
* registers, some are specific and allow indirect access to a
|
||||
* wider range of PHB registers
|
||||
*/
|
||||
#define PHB_SCOM_HV_IND_ADDR 0x00
|
||||
#define PHB_SCOM_HV_IND_ADDR_VALID PPC_BIT(0)
|
||||
#define PHB_SCOM_HV_IND_ADDR_4B PPC_BIT(1)
|
||||
#define PHB_SCOM_HV_IND_ADDR_AUTOINC PPC_BIT(2)
|
||||
#define PHB_SCOM_HV_IND_ADDR_ADDR PPC_BITMASK(51, 63)
|
||||
#define PHB_SCOM_HV_IND_DATA 0x01
|
||||
#define PHB_SCOM_ETU_LEM_FIR 0x08
|
||||
#define PHB_SCOM_ETU_LEM_FIR_AND 0x09
|
||||
#define PHB_SCOM_ETU_LEM_FIR_OR 0x0a
|
||||
#define PHB_SCOM_ETU_LEM_FIR_MSK 0x0b
|
||||
#define PHB_SCOM_ETU_LEM_ERR_MSK_AND 0x0c
|
||||
#define PHB_SCOM_ETU_LEM_ERR_MSK_OR 0x0d
|
||||
#define PHB_SCOM_ETU_LEM_ACT0 0x0e
|
||||
#define PHB_SCOM_ETU_LEM_ACT1 0x0f
|
||||
#define PHB_SCOM_ETU_LEM_WOF 0x10
|
||||
#define PHB_SCOM_ETU_PMON_CONFIG 0x17
|
||||
#define PHB_SCOM_ETU_PMON_CTR0 0x18
|
||||
#define PHB_SCOM_ETU_PMON_CTR1 0x19
|
||||
#define PHB_SCOM_ETU_PMON_CTR2 0x1a
|
||||
#define PHB_SCOM_ETU_PMON_CTR3 0x1b
|
||||
|
||||
|
||||
/*
|
||||
* PHB MMIO registers
|
||||
*/
|
||||
|
||||
/* PHB Fundamental register set A */
|
||||
#define PHB_LSI_SOURCE_ID 0x100
|
||||
#define PHB_LSI_SRC_ID PPC_BITMASK(4, 12)
|
||||
#define PHB_DMA_CHAN_STATUS 0x110
|
||||
#define PHB_DMA_CHAN_ANY_ERR PPC_BIT(27)
|
||||
#define PHB_DMA_CHAN_ANY_ERR1 PPC_BIT(28)
|
||||
#define PHB_DMA_CHAN_ANY_FREEZE PPC_BIT(29)
|
||||
#define PHB_CPU_LOADSTORE_STATUS 0x120
|
||||
#define PHB_CPU_LS_ANY_ERR PPC_BIT(27)
|
||||
#define PHB_CPU_LS_ANY_ERR1 PPC_BIT(28)
|
||||
#define PHB_CPU_LS_ANY_FREEZE PPC_BIT(29)
|
||||
#define PHB_CONFIG_DATA 0x130
|
||||
#define PHB_LOCK0 0x138
|
||||
#define PHB_CONFIG_ADDRESS 0x140
|
||||
#define PHB_CA_ENABLE PPC_BIT(0)
|
||||
#define PHB_CA_STATUS PPC_BITMASK(1, 3)
|
||||
#define PHB_CA_STATUS_GOOD 0
|
||||
#define PHB_CA_STATUS_UR 1
|
||||
#define PHB_CA_STATUS_CRS 2
|
||||
#define PHB_CA_STATUS_CA 4
|
||||
#define PHB_CA_BUS PPC_BITMASK(4, 11)
|
||||
#define PHB_CA_DEV PPC_BITMASK(12, 16)
|
||||
#define PHB_CA_FUNC PPC_BITMASK(17, 19)
|
||||
#define PHB_CA_BDFN PPC_BITMASK(4, 19) /* bus,dev,func */
|
||||
#define PHB_CA_REG PPC_BITMASK(20, 31)
|
||||
#define PHB_CA_PE PPC_BITMASK(39, 47)
|
||||
#define PHB_LOCK1 0x148
|
||||
#define PHB_PHB4_CONFIG 0x160
|
||||
#define PHB_PHB4C_32BIT_MSI_EN PPC_BIT(8)
|
||||
#define PHB_PHB4C_64BIT_MSI_EN PPC_BIT(14)
|
||||
#define PHB_RTT_BAR 0x168
|
||||
#define PHB_RTT_BAR_ENABLE PPC_BIT(0)
|
||||
#define PHB_RTT_BASE_ADDRESS_MASK PPC_BITMASK(8, 46)
|
||||
#define PHB_PELTV_BAR 0x188
|
||||
#define PHB_PELTV_BAR_ENABLE PPC_BIT(0)
|
||||
#define PHB_PELTV_BASE_ADDRESS PPC_BITMASK(8, 50)
|
||||
#define PHB_M32_START_ADDR 0x1a0
|
||||
#define PHB_PEST_BAR 0x1a8
|
||||
#define PHB_PEST_BAR_ENABLE PPC_BIT(0)
|
||||
#define PHB_PEST_BASE_ADDRESS PPC_BITMASK(8, 51)
|
||||
#define PHB_ASN_CMPM 0x1C0
|
||||
#define PHB_ASN_CMPM_ENABLE PPC_BIT(63)
|
||||
#define PHB_CAPI_CMPM 0x1C8
|
||||
#define PHB_CAPI_CMPM_ENABLE PPC_BIT(63)
|
||||
#define PHB_M64_AOMASK 0x1d0
|
||||
#define PHB_M64_UPPER_BITS 0x1f0
|
||||
#define PHB_NXLATE_PREFIX 0x1f8
|
||||
#define PHB_DMARD_SYNC 0x200
|
||||
#define PHB_DMARD_SYNC_START PPC_BIT(0)
|
||||
#define PHB_DMARD_SYNC_COMPLETE PPC_BIT(1)
|
||||
#define PHB_RTC_INVALIDATE 0x208
|
||||
#define PHB_RTC_INVALIDATE_ALL PPC_BIT(0)
|
||||
#define PHB_RTC_INVALIDATE_RID PPC_BITMASK(16, 31)
|
||||
#define PHB_TCE_KILL 0x210
|
||||
#define PHB_TCE_KILL_ALL PPC_BIT(0)
|
||||
#define PHB_TCE_KILL_PE PPC_BIT(1)
|
||||
#define PHB_TCE_KILL_ONE PPC_BIT(2)
|
||||
#define PHB_TCE_KILL_PSEL PPC_BIT(3)
|
||||
#define PHB_TCE_KILL_64K 0x1000 /* Address override */
|
||||
#define PHB_TCE_KILL_2M 0x2000 /* Address override */
|
||||
#define PHB_TCE_KILL_1G 0x3000 /* Address override */
|
||||
#define PHB_TCE_KILL_PENUM PPC_BITMASK(55, 63)
|
||||
#define PHB_TCE_SPEC_CTL 0x218
|
||||
#define PHB_IODA_ADDR 0x220
|
||||
#define PHB_IODA_AD_AUTOINC PPC_BIT(0)
|
||||
#define PHB_IODA_AD_TSEL PPC_BITMASK(11, 15)
|
||||
#define PHB_IODA_AD_MIST_PWV PPC_BITMASK(28, 31)
|
||||
#define PHB_IODA_AD_TADR PPC_BITMASK(54, 63)
|
||||
#define PHB_IODA_DATA0 0x228
|
||||
#define PHB_PHB4_GEN_CAP 0x250
|
||||
#define PHB_PHB4_TCE_CAP 0x258
|
||||
#define PHB_PHB4_IRQ_CAP 0x260
|
||||
#define PHB_PHB4_EEH_CAP 0x268
|
||||
#define PHB_PAPR_ERR_INJ_CTL 0x2b0
|
||||
#define PHB_PAPR_ERR_INJ_CTL_INB PPC_BIT(0)
|
||||
#define PHB_PAPR_ERR_INJ_CTL_OUTB PPC_BIT(1)
|
||||
#define PHB_PAPR_ERR_INJ_CTL_STICKY PPC_BIT(2)
|
||||
#define PHB_PAPR_ERR_INJ_CTL_CFG PPC_BIT(3)
|
||||
#define PHB_PAPR_ERR_INJ_CTL_RD PPC_BIT(4)
|
||||
#define PHB_PAPR_ERR_INJ_CTL_WR PPC_BIT(5)
|
||||
#define PHB_PAPR_ERR_INJ_CTL_FREEZE PPC_BIT(6)
|
||||
#define PHB_PAPR_ERR_INJ_ADDR 0x2b8
|
||||
#define PHB_PAPR_ERR_INJ_ADDR_MMIO PPC_BITMASK(16, 63)
|
||||
#define PHB_PAPR_ERR_INJ_MASK 0x2c0
|
||||
#define PHB_PAPR_ERR_INJ_MASK_CFG PPC_BITMASK(4, 11)
|
||||
#define PHB_PAPR_ERR_INJ_MASK_CFG_ALL PPC_BITMASK(4, 19)
|
||||
#define PHB_PAPR_ERR_INJ_MASK_MMIO PPC_BITMASK(16, 63)
|
||||
#define PHB_ETU_ERR_SUMMARY 0x2c8
|
||||
#define PHB_INT_NOTIFY_ADDR 0x300
|
||||
#define PHB_INT_NOTIFY_INDEX 0x308
|
||||
|
||||
/* Fundamental register set B */
|
||||
#define PHB_VERSION 0x800
|
||||
#define PHB_CTRLR 0x810
|
||||
#define PHB_CTRLR_IRQ_PGSZ_64K PPC_BIT(11)
|
||||
#define PHB_CTRLR_IRQ_STORE_EOI PPC_BIT(12)
|
||||
#define PHB_CTRLR_MMIO_RD_STRICT PPC_BIT(13)
|
||||
#define PHB_CTRLR_MMIO_EEH_DISABLE PPC_BIT(14)
|
||||
#define PHB_CTRLR_CFG_EEH_BLOCK PPC_BIT(15)
|
||||
#define PHB_CTRLR_FENCE_LNKILL_DIS PPC_BIT(16)
|
||||
#define PHB_CTRLR_TVT_ADDR_SEL PPC_BITMASK(17, 19)
|
||||
#define TVT_DD1_1_PER_PE 0
|
||||
#define TVT_DD1_2_PER_PE 1
|
||||
#define TVT_DD1_4_PER_PE 2
|
||||
#define TVT_DD1_8_PER_PE 3
|
||||
#define TVT_DD1_16_PER_PE 4
|
||||
#define TVT_2_PER_PE 0
|
||||
#define TVT_4_PER_PE 1
|
||||
#define TVT_8_PER_PE 2
|
||||
#define TVT_16_PER_PE 3
|
||||
#define PHB_CTRLR_DMA_RD_SPACING PPC_BITMASK(28, 31)
|
||||
#define PHB_AIB_FENCE_CTRL 0x860
|
||||
#define PHB_TCE_TAG_ENABLE 0x868
|
||||
#define PHB_TCE_WATERMARK 0x870
|
||||
#define PHB_TIMEOUT_CTRL1 0x878
|
||||
#define PHB_TIMEOUT_CTRL2 0x880
|
||||
#define PHB_Q_DMA_R 0x888
|
||||
#define PHB_Q_DMA_R_QUIESCE_DMA PPC_BIT(0)
|
||||
#define PHB_Q_DMA_R_AUTORESET PPC_BIT(1)
|
||||
#define PHB_Q_DMA_R_DMA_RESP_STATUS PPC_BIT(4)
|
||||
#define PHB_Q_DMA_R_MMIO_RESP_STATUS PPC_BIT(5)
|
||||
#define PHB_Q_DMA_R_TCE_RESP_STATUS PPC_BIT(6)
|
||||
#define PHB_Q_DMA_R_TCE_KILL_STATUS PPC_BIT(7)
|
||||
#define PHB_TCE_TAG_STATUS 0x908
|
||||
|
||||
/* FIR & Error registers */
|
||||
#define PHB_LEM_FIR_ACCUM 0xc00
|
||||
#define PHB_LEM_FIR_AND_MASK 0xc08
|
||||
#define PHB_LEM_FIR_OR_MASK 0xc10
|
||||
#define PHB_LEM_ERROR_MASK 0xc18
|
||||
#define PHB_LEM_ERROR_AND_MASK 0xc20
|
||||
#define PHB_LEM_ERROR_OR_MASK 0xc28
|
||||
#define PHB_LEM_ACTION0 0xc30
|
||||
#define PHB_LEM_ACTION1 0xc38
|
||||
#define PHB_LEM_WOF 0xc40
|
||||
#define PHB_ERR_STATUS 0xc80
|
||||
#define PHB_ERR1_STATUS 0xc88
|
||||
#define PHB_ERR_INJECT 0xc90
|
||||
#define PHB_ERR_LEM_ENABLE 0xc98
|
||||
#define PHB_ERR_IRQ_ENABLE 0xca0
|
||||
#define PHB_ERR_FREEZE_ENABLE 0xca8
|
||||
#define PHB_ERR_AIB_FENCE_ENABLE 0xcb0
|
||||
#define PHB_ERR_LOG_0 0xcc0
|
||||
#define PHB_ERR_LOG_1 0xcc8
|
||||
#define PHB_ERR_STATUS_MASK 0xcd0
|
||||
#define PHB_ERR1_STATUS_MASK 0xcd8
|
||||
|
||||
#define PHB_TXE_ERR_STATUS 0xd00
|
||||
#define PHB_TXE_ERR1_STATUS 0xd08
|
||||
#define PHB_TXE_ERR_INJECT 0xd10
|
||||
#define PHB_TXE_ERR_LEM_ENABLE 0xd18
|
||||
#define PHB_TXE_ERR_IRQ_ENABLE 0xd20
|
||||
#define PHB_TXE_ERR_FREEZE_ENABLE 0xd28
|
||||
#define PHB_TXE_ERR_AIB_FENCE_ENABLE 0xd30
|
||||
#define PHB_TXE_ERR_LOG_0 0xd40
|
||||
#define PHB_TXE_ERR_LOG_1 0xd48
|
||||
#define PHB_TXE_ERR_STATUS_MASK 0xd50
|
||||
#define PHB_TXE_ERR1_STATUS_MASK 0xd58
|
||||
|
||||
#define PHB_RXE_ARB_ERR_STATUS 0xd80
|
||||
#define PHB_RXE_ARB_ERR1_STATUS 0xd88
|
||||
#define PHB_RXE_ARB_ERR_INJECT 0xd90
|
||||
#define PHB_RXE_ARB_ERR_LEM_ENABLE 0xd98
|
||||
#define PHB_RXE_ARB_ERR_IRQ_ENABLE 0xda0
|
||||
#define PHB_RXE_ARB_ERR_FREEZE_ENABLE 0xda8
|
||||
#define PHB_RXE_ARB_ERR_AIB_FENCE_ENABLE 0xdb0
|
||||
#define PHB_RXE_ARB_ERR_LOG_0 0xdc0
|
||||
#define PHB_RXE_ARB_ERR_LOG_1 0xdc8
|
||||
#define PHB_RXE_ARB_ERR_STATUS_MASK 0xdd0
|
||||
#define PHB_RXE_ARB_ERR1_STATUS_MASK 0xdd8
|
||||
|
||||
#define PHB_RXE_MRG_ERR_STATUS 0xe00
|
||||
#define PHB_RXE_MRG_ERR1_STATUS 0xe08
|
||||
#define PHB_RXE_MRG_ERR_INJECT 0xe10
|
||||
#define PHB_RXE_MRG_ERR_LEM_ENABLE 0xe18
|
||||
#define PHB_RXE_MRG_ERR_IRQ_ENABLE 0xe20
|
||||
#define PHB_RXE_MRG_ERR_FREEZE_ENABLE 0xe28
|
||||
#define PHB_RXE_MRG_ERR_AIB_FENCE_ENABLE 0xe30
|
||||
#define PHB_RXE_MRG_ERR_LOG_0 0xe40
|
||||
#define PHB_RXE_MRG_ERR_LOG_1 0xe48
|
||||
#define PHB_RXE_MRG_ERR_STATUS_MASK 0xe50
|
||||
#define PHB_RXE_MRG_ERR1_STATUS_MASK 0xe58
|
||||
|
||||
#define PHB_RXE_TCE_ERR_STATUS 0xe80
|
||||
#define PHB_RXE_TCE_ERR1_STATUS 0xe88
|
||||
#define PHB_RXE_TCE_ERR_INJECT 0xe90
|
||||
#define PHB_RXE_TCE_ERR_LEM_ENABLE 0xe98
|
||||
#define PHB_RXE_TCE_ERR_IRQ_ENABLE 0xea0
|
||||
#define PHB_RXE_TCE_ERR_FREEZE_ENABLE 0xea8
|
||||
#define PHB_RXE_TCE_ERR_AIB_FENCE_ENABLE 0xeb0
|
||||
#define PHB_RXE_TCE_ERR_LOG_0 0xec0
|
||||
#define PHB_RXE_TCE_ERR_LOG_1 0xec8
|
||||
#define PHB_RXE_TCE_ERR_STATUS_MASK 0xed0
|
||||
#define PHB_RXE_TCE_ERR1_STATUS_MASK 0xed8
|
||||
|
||||
/* Performance monitor & Debug registers */
|
||||
#define PHB_TRACE_CONTROL 0xf80
|
||||
#define PHB_PERFMON_CONFIG 0xf88
|
||||
#define PHB_PERFMON_CTR0 0xf90
|
||||
#define PHB_PERFMON_CTR1 0xf98
|
||||
#define PHB_PERFMON_CTR2 0xfa0
|
||||
#define PHB_PERFMON_CTR3 0xfa8
|
||||
|
||||
/* Root complex config space memory mapped */
|
||||
#define PHB_RC_CONFIG_BASE 0x1000
|
||||
#define PHB_RC_CONFIG_SIZE 0x800
|
||||
|
||||
/* PHB4 REGB registers */
|
||||
|
||||
/* PBL core */
|
||||
#define PHB_PBL_CONTROL 0x1800
|
||||
#define PHB_PBL_TIMEOUT_CTRL 0x1810
|
||||
#define PHB_PBL_NPTAG_ENABLE 0x1820
|
||||
#define PHB_PBL_NBW_CMP_MASK 0x1830
|
||||
#define PHB_PBL_NBW_MASK_ENABLE PPC_BIT(63)
|
||||
#define PHB_PBL_SYS_LINK_INIT 0x1838
|
||||
#define PHB_PBL_BUF_STATUS 0x1840
|
||||
#define PHB_PBL_ERR_STATUS 0x1900
|
||||
#define PHB_PBL_ERR1_STATUS 0x1908
|
||||
#define PHB_PBL_ERR_INJECT 0x1910
|
||||
#define PHB_PBL_ERR_INF_ENABLE 0x1920
|
||||
#define PHB_PBL_ERR_ERC_ENABLE 0x1928
|
||||
#define PHB_PBL_ERR_FAT_ENABLE 0x1930
|
||||
#define PHB_PBL_ERR_LOG_0 0x1940
|
||||
#define PHB_PBL_ERR_LOG_1 0x1948
|
||||
#define PHB_PBL_ERR_STATUS_MASK 0x1950
|
||||
#define PHB_PBL_ERR1_STATUS_MASK 0x1958
|
||||
|
||||
/* PCI-E stack */
|
||||
#define PHB_PCIE_SCR 0x1A00
|
||||
#define PHB_PCIE_SCR_SLOT_CAP PPC_BIT(15)
|
||||
#define PHB_PCIE_SCR_MAXLINKSPEED PPC_BITMASK(32, 35)
|
||||
|
||||
|
||||
#define PHB_PCIE_CRESET 0x1A10
|
||||
#define PHB_PCIE_CRESET_CFG_CORE PPC_BIT(0)
|
||||
#define PHB_PCIE_CRESET_TLDLP PPC_BIT(1)
|
||||
#define PHB_PCIE_CRESET_PBL PPC_BIT(2)
|
||||
#define PHB_PCIE_CRESET_PERST_N PPC_BIT(3)
|
||||
#define PHB_PCIE_CRESET_PIPE_N PPC_BIT(4)
|
||||
|
||||
|
||||
#define PHB_PCIE_HOTPLUG_STATUS 0x1A20
|
||||
#define PHB_PCIE_HPSTAT_PRESENCE PPC_BIT(10)
|
||||
|
||||
#define PHB_PCIE_DLP_TRAIN_CTL 0x1A40
|
||||
#define PHB_PCIE_DLP_LINK_WIDTH PPC_BITMASK(30, 35)
|
||||
#define PHB_PCIE_DLP_LINK_SPEED PPC_BITMASK(36, 39)
|
||||
#define PHB_PCIE_DLP_LTSSM_TRC PPC_BITMASK(24, 27)
|
||||
#define PHB_PCIE_DLP_LTSSM_RESET 0
|
||||
#define PHB_PCIE_DLP_LTSSM_DETECT 1
|
||||
#define PHB_PCIE_DLP_LTSSM_POLLING 2
|
||||
#define PHB_PCIE_DLP_LTSSM_CONFIG 3
|
||||
#define PHB_PCIE_DLP_LTSSM_L0 4
|
||||
#define PHB_PCIE_DLP_LTSSM_REC 5
|
||||
#define PHB_PCIE_DLP_LTSSM_L1 6
|
||||
#define PHB_PCIE_DLP_LTSSM_L2 7
|
||||
#define PHB_PCIE_DLP_LTSSM_HOTRESET 8
|
||||
#define PHB_PCIE_DLP_LTSSM_DISABLED 9
|
||||
#define PHB_PCIE_DLP_LTSSM_LOOPBACK 10
|
||||
#define PHB_PCIE_DLP_TL_LINKACT PPC_BIT(23)
|
||||
#define PHB_PCIE_DLP_DL_PGRESET PPC_BIT(22)
|
||||
#define PHB_PCIE_DLP_TRAINING PPC_BIT(20)
|
||||
#define PHB_PCIE_DLP_INBAND_PRESENCE PPC_BIT(19)
|
||||
|
||||
#define PHB_PCIE_DLP_CTL 0x1A78
|
||||
#define PHB_PCIE_DLP_CTL_BYPASS_PH2 PPC_BIT(4)
|
||||
#define PHB_PCIE_DLP_CTL_BYPASS_PH3 PPC_BIT(5)
|
||||
|
||||
#define PHB_PCIE_DLP_TRWCTL 0x1A80
|
||||
#define PHB_PCIE_DLP_TRWCTL_EN PPC_BIT(0)
|
||||
|
||||
#define PHB_PCIE_DLP_ERRLOG1 0x1AA0
|
||||
#define PHB_PCIE_DLP_ERRLOG2 0x1AA8
|
||||
#define PHB_PCIE_DLP_ERR_STATUS 0x1AB0
|
||||
#define PHB_PCIE_DLP_ERR_COUNTERS 0x1AB8
|
||||
|
||||
#define PHB_PCIE_LANE_EQ_CNTL0 0x1AD0
|
||||
#define PHB_PCIE_LANE_EQ_CNTL1 0x1AD8
|
||||
#define PHB_PCIE_LANE_EQ_CNTL2 0x1AE0
|
||||
#define PHB_PCIE_LANE_EQ_CNTL3 0x1AE8
|
||||
#define PHB_PCIE_LANE_EQ_CNTL20 0x1AF0
|
||||
#define PHB_PCIE_LANE_EQ_CNTL21 0x1AF8
|
||||
#define PHB_PCIE_LANE_EQ_CNTL22 0x1B00 /* DD1 only */
|
||||
#define PHB_PCIE_LANE_EQ_CNTL23 0x1B08 /* DD1 only */
|
||||
#define PHB_PCIE_TRACE_CTRL 0x1B20
|
||||
#define PHB_PCIE_MISC_STRAP 0x1B30
|
||||
|
||||
/* Error */
|
||||
#define PHB_REGB_ERR_STATUS 0x1C00
|
||||
#define PHB_REGB_ERR1_STATUS 0x1C08
|
||||
#define PHB_REGB_ERR_INJECT 0x1C10
|
||||
#define PHB_REGB_ERR_INF_ENABLE 0x1C20
|
||||
#define PHB_REGB_ERR_ERC_ENABLE 0x1C28
|
||||
#define PHB_REGB_ERR_FAT_ENABLE 0x1C30
|
||||
#define PHB_REGB_ERR_LOG_0 0x1C40
|
||||
#define PHB_REGB_ERR_LOG_1 0x1C48
|
||||
#define PHB_REGB_ERR_STATUS_MASK 0x1C50
|
||||
#define PHB_REGB_ERR1_STATUS_MASK 0x1C58
|
||||
|
||||
/*
|
||||
* IODA3 on-chip tables
|
||||
*/
|
||||
|
||||
#define IODA3_TBL_LIST 1
|
||||
#define IODA3_TBL_MIST 2
|
||||
#define IODA3_TBL_RCAM 5
|
||||
#define IODA3_TBL_MRT 6
|
||||
#define IODA3_TBL_PESTA 7
|
||||
#define IODA3_TBL_PESTB 8
|
||||
#define IODA3_TBL_TVT 9
|
||||
#define IODA3_TBL_TCR 10
|
||||
#define IODA3_TBL_TDR 11
|
||||
#define IODA3_TBL_MBT 16
|
||||
#define IODA3_TBL_MDT 17
|
||||
#define IODA3_TBL_PEEV 20
|
||||
|
||||
/* LIST */
|
||||
#define IODA3_LIST_P PPC_BIT(6)
|
||||
#define IODA3_LIST_Q PPC_BIT(7)
|
||||
#define IODA3_LIST_STATE PPC_BIT(14)
|
||||
|
||||
/* MIST */
|
||||
#define IODA3_MIST_P3 PPC_BIT(48 + 0)
|
||||
#define IODA3_MIST_Q3 PPC_BIT(48 + 1)
|
||||
#define IODA3_MIST_PE3 PPC_BITMASK(48 + 4, 48 + 15)
|
||||
|
||||
/* TVT */
|
||||
#define IODA3_TVT_TABLE_ADDR PPC_BITMASK(0, 47)
|
||||
#define IODA3_TVT_NUM_LEVELS PPC_BITMASK(48, 50)
|
||||
#define IODA3_TVE_1_LEVEL 0
|
||||
#define IODA3_TVE_2_LEVELS 1
|
||||
#define IODA3_TVE_3_LEVELS 2
|
||||
#define IODA3_TVE_4_LEVELS 3
|
||||
#define IODA3_TVE_5_LEVELS 4
|
||||
#define IODA3_TVT_TCE_TABLE_SIZE PPC_BITMASK(51, 55)
|
||||
#define IODA3_TVT_NON_TRANSLATE_50 PPC_BIT(56)
|
||||
#define IODA3_TVT_IO_PSIZE PPC_BITMASK(59, 63)
|
||||
|
||||
/* PESTA */
|
||||
#define IODA3_PESTA_MMIO_FROZEN PPC_BIT(0)
|
||||
#define IODA3_PESTA_TRANS_TYPE PPC_BITMASK(5, 7)
|
||||
#define IODA3_PESTA_TRANS_TYPE_MMIOLOAD 0x4
|
||||
#define IODA3_PESTA_CA_CMPLT_TMT PPC_BIT(8)
|
||||
#define IODA3_PESTA_UR PPC_BIT(9)
|
||||
|
||||
/* PESTB */
|
||||
#define IODA3_PESTB_DMA_STOPPED PPC_BIT(0)
|
||||
|
||||
/* MDT */
|
||||
/* FIXME: check this field with Eric and add a B, C and D */
|
||||
#define IODA3_MDT_PE_A PPC_BITMASK(0, 15)
|
||||
#define IODA3_MDT_PE_B PPC_BITMASK(16, 31)
|
||||
#define IODA3_MDT_PE_C PPC_BITMASK(32, 47)
|
||||
#define IODA3_MDT_PE_D PPC_BITMASK(48, 63)
|
||||
|
||||
/* MBT */
|
||||
#define IODA3_MBT0_ENABLE PPC_BIT(0)
|
||||
#define IODA3_MBT0_TYPE PPC_BIT(1)
|
||||
#define IODA3_MBT0_TYPE_M32 IODA3_MBT0_TYPE
|
||||
#define IODA3_MBT0_TYPE_M64 0
|
||||
#define IODA3_MBT0_MODE PPC_BITMASK(2, 3)
|
||||
#define IODA3_MBT0_MODE_PE_SEG 0
|
||||
#define IODA3_MBT0_MODE_MDT 1
|
||||
#define IODA3_MBT0_MODE_SINGLE_PE 2
|
||||
#define IODA3_MBT0_SEG_DIV PPC_BITMASK(4, 5)
|
||||
#define IODA3_MBT0_SEG_DIV_MAX 0
|
||||
#define IODA3_MBT0_SEG_DIV_128 1
|
||||
#define IODA3_MBT0_SEG_DIV_64 2
|
||||
#define IODA3_MBT0_SEG_DIV_8 3
|
||||
#define IODA3_MBT0_MDT_COLUMN PPC_BITMASK(4, 5)
|
||||
#define IODA3_MBT0_BASE_ADDR PPC_BITMASK(8, 51)
|
||||
|
||||
#define IODA3_MBT1_ENABLE PPC_BIT(0)
|
||||
#define IODA3_MBT1_MASK PPC_BITMASK(8, 51)
|
||||
#define IODA3_MBT1_SEG_BASE PPC_BITMASK(55, 63)
|
||||
#define IODA3_MBT1_SINGLE_PE_NUM PPC_BITMASK(55, 63)
|
||||
|
||||
/*
|
||||
* IODA3 in-memory tables
|
||||
*/
|
||||
|
||||
/*
|
||||
* PEST
|
||||
*
|
||||
* 2x8 bytes entries, PEST0 and PEST1
|
||||
*/
|
||||
|
||||
#define IODA3_PEST0_MMIO_CAUSE PPC_BIT(2)
|
||||
#define IODA3_PEST0_CFG_READ PPC_BIT(3)
|
||||
#define IODA3_PEST0_CFG_WRITE PPC_BIT(4)
|
||||
#define IODA3_PEST0_TTYPE PPC_BITMASK(5, 7)
|
||||
#define PEST_TTYPE_DMA_WRITE 0
|
||||
#define PEST_TTYPE_MSI 1
|
||||
#define PEST_TTYPE_DMA_READ 2
|
||||
#define PEST_TTYPE_DMA_READ_RESP 3
|
||||
#define PEST_TTYPE_MMIO_LOAD 4
|
||||
#define PEST_TTYPE_MMIO_STORE 5
|
||||
#define PEST_TTYPE_OTHER 7
|
||||
#define IODA3_PEST0_CA_RETURN PPC_BIT(8)
|
||||
#define IODA3_PEST0_UR_RETURN PPC_BIT(9)
|
||||
#define IODA3_PEST0_PCIE_NONFATAL PPC_BIT(10)
|
||||
#define IODA3_PEST0_PCIE_FATAL PPC_BIT(11)
|
||||
#define IODA3_PEST0_PARITY_UE PPC_BIT(13)
|
||||
#define IODA3_PEST0_PCIE_CORRECTABLE PPC_BIT(14)
|
||||
#define IODA3_PEST0_PCIE_INTERRUPT PPC_BIT(15)
|
||||
#define IODA3_PEST0_MMIO_XLATE PPC_BIT(16)
|
||||
#define IODA3_PEST0_IODA3_ERROR PPC_BIT(16) /* Same bit as MMIO xlate */
|
||||
#define IODA3_PEST0_TCE_PAGE_FAULT PPC_BIT(18)
|
||||
#define IODA3_PEST0_TCE_ACCESS_FAULT PPC_BIT(19)
|
||||
#define IODA3_PEST0_DMA_RESP_TIMEOUT PPC_BIT(20)
|
||||
#define IODA3_PEST0_AIB_SIZE_INVALID PPC_BIT(21)
|
||||
#define IODA3_PEST0_LEM_BIT PPC_BITMASK(26, 31)
|
||||
#define IODA3_PEST0_RID PPC_BITMASK(32, 47)
|
||||
#define IODA3_PEST0_MSI_DATA PPC_BITMASK(48, 63)
|
||||
|
||||
#define IODA3_PEST1_FAIL_ADDR PPC_BITMASK(3, 63)
|
||||
|
||||
|
||||
#endif /* PCI_HOST_PNV_PHB4_REGS_H */
|
|
@ -72,6 +72,7 @@ void pcie_chassis_del_slot(PCIESlot *s);
|
|||
typedef struct PCIERootPortClass {
|
||||
PCIDeviceClass parent_class;
|
||||
DeviceRealize parent_realize;
|
||||
DeviceReset parent_reset;
|
||||
|
||||
uint8_t (*aer_vector)(const PCIDevice *dev);
|
||||
int (*interrupts_init)(PCIDevice *dev, Error **errp);
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include "hw/ppc/pnv_homer.h"
|
||||
#include "hw/ppc/pnv_xive.h"
|
||||
#include "hw/ppc/pnv_core.h"
|
||||
#include "hw/pci-host/pnv_phb3.h"
|
||||
#include "hw/pci-host/pnv_phb4.h"
|
||||
|
||||
#define TYPE_PNV_CHIP "pnv-chip"
|
||||
#define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
|
||||
|
@ -52,6 +54,8 @@ typedef struct PnvChip {
|
|||
uint64_t cores_mask;
|
||||
PnvCore **cores;
|
||||
|
||||
uint32_t num_phbs;
|
||||
|
||||
MemoryRegion xscom_mmio;
|
||||
MemoryRegion xscom;
|
||||
AddressSpace xscom_as;
|
||||
|
@ -74,6 +78,9 @@ typedef struct Pnv8Chip {
|
|||
PnvOCC occ;
|
||||
PnvHomer homer;
|
||||
|
||||
#define PNV8_CHIP_PHB3_MAX 4
|
||||
PnvPHB3 phbs[PNV8_CHIP_PHB3_MAX];
|
||||
|
||||
XICSFabric *xics;
|
||||
} Pnv8Chip;
|
||||
|
||||
|
@ -93,6 +100,9 @@ typedef struct Pnv9Chip {
|
|||
|
||||
uint32_t nr_quads;
|
||||
PnvQuad *quads;
|
||||
|
||||
#define PNV9_CHIP_MAX_PEC 3
|
||||
PnvPhb4PecState pecs[PNV9_CHIP_MAX_PEC];
|
||||
} Pnv9Chip;
|
||||
|
||||
/*
|
||||
|
@ -120,6 +130,7 @@ typedef struct PnvChipClass {
|
|||
/*< public >*/
|
||||
uint64_t chip_cfam_id;
|
||||
uint64_t cores_mask;
|
||||
uint32_t num_phbs;
|
||||
|
||||
DeviceRealize parent_realize;
|
||||
|
||||
|
@ -217,6 +228,8 @@ struct PnvMachineState {
|
|||
Notifier powerdown_notifier;
|
||||
|
||||
PnvPnor *pnor;
|
||||
|
||||
hwaddr fw_load_addr;
|
||||
};
|
||||
|
||||
#define PNV_FDT_ADDR 0x01000000
|
||||
|
|
|
@ -40,6 +40,7 @@ typedef struct PnvCore {
|
|||
/*< public >*/
|
||||
PowerPCCPU **threads;
|
||||
uint32_t pir;
|
||||
uint64_t hrmor;
|
||||
PnvChip *chip;
|
||||
|
||||
MemoryRegion xscom_regs;
|
||||
|
|
|
@ -71,6 +71,15 @@ typedef struct PnvXScomInterfaceClass {
|
|||
#define PNV_XSCOM_PBA_BASE 0x2013f00
|
||||
#define PNV_XSCOM_PBA_SIZE 0x40
|
||||
|
||||
#define PNV_XSCOM_PBCQ_NEST_BASE 0x2012000
|
||||
#define PNV_XSCOM_PBCQ_NEST_SIZE 0x46
|
||||
|
||||
#define PNV_XSCOM_PBCQ_PCI_BASE 0x9012000
|
||||
#define PNV_XSCOM_PBCQ_PCI_SIZE 0x15
|
||||
|
||||
#define PNV_XSCOM_PBCQ_SPCI_BASE 0x9013c00
|
||||
#define PNV_XSCOM_PBCQ_SPCI_SIZE 0x5
|
||||
|
||||
/*
|
||||
* Layout of the XSCOM PCB addresses (POWER 9)
|
||||
*/
|
||||
|
@ -94,6 +103,17 @@ typedef struct PnvXScomInterfaceClass {
|
|||
#define PNV9_XSCOM_XIVE_BASE 0x5013000
|
||||
#define PNV9_XSCOM_XIVE_SIZE 0x300
|
||||
|
||||
#define PNV9_XSCOM_PEC_NEST_BASE 0x4010c00
|
||||
#define PNV9_XSCOM_PEC_NEST_SIZE 0x100
|
||||
|
||||
#define PNV9_XSCOM_PEC_PCI_BASE 0xd010800
|
||||
#define PNV9_XSCOM_PEC_PCI_SIZE 0x200
|
||||
|
||||
/* XSCOM PCI "pass-through" window to PHB SCOM */
|
||||
#define PNV9_XSCOM_PEC_PCI_STK0 0x100
|
||||
#define PNV9_XSCOM_PEC_PCI_STK1 0x140
|
||||
#define PNV9_XSCOM_PEC_PCI_STK2 0x180
|
||||
|
||||
/*
|
||||
* Layout of the XSCOM PCB addresses (POWER 10)
|
||||
*/
|
||||
|
|
|
@ -68,7 +68,6 @@ clk_setup_cb ppc_40x_timers_init (CPUPPCState *env, uint32_t freq,
|
|||
void ppc40x_core_reset(PowerPCCPU *cpu);
|
||||
void ppc40x_chip_reset(PowerPCCPU *cpu);
|
||||
void ppc40x_system_reset(PowerPCCPU *cpu);
|
||||
void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val);
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
static inline void ppc40x_irq_init(PowerPCCPU *cpu) {}
|
||||
|
|
|
@ -79,8 +79,10 @@ typedef enum {
|
|||
#define SPAPR_CAP_LARGE_DECREMENTER 0x08
|
||||
/* Count Cache Flush Assist HW Instruction */
|
||||
#define SPAPR_CAP_CCF_ASSIST 0x09
|
||||
/* FWNMI machine check handling */
|
||||
#define SPAPR_CAP_FWNMI_MCE 0x0A
|
||||
/* Num Caps */
|
||||
#define SPAPR_CAP_NUM (SPAPR_CAP_CCF_ASSIST + 1)
|
||||
#define SPAPR_CAP_NUM (SPAPR_CAP_FWNMI_MCE + 1)
|
||||
|
||||
/*
|
||||
* Capability Values
|
||||
|
@ -189,6 +191,15 @@ struct SpaprMachineState {
|
|||
* occurs during the unplug process. */
|
||||
QTAILQ_HEAD(, SpaprDimmState) pending_dimm_unplugs;
|
||||
|
||||
/* State related to "ibm,nmi-register" and "ibm,nmi-interlock" calls */
|
||||
target_ulong guest_machine_check_addr;
|
||||
/*
|
||||
* mc_status is set to -1 if mc is not in progress, else is set to the CPU
|
||||
* handling the mc.
|
||||
*/
|
||||
int mc_status;
|
||||
QemuCond mc_delivery_cond;
|
||||
|
||||
/*< public >*/
|
||||
char *kvm_type;
|
||||
char *host_model;
|
||||
|
@ -207,6 +218,8 @@ struct SpaprMachineState {
|
|||
|
||||
unsigned gpu_numa_id;
|
||||
SpaprTpmProxy *tpm_proxy;
|
||||
|
||||
Error *fwnmi_migration_blocker;
|
||||
};
|
||||
|
||||
#define H_SUCCESS 0
|
||||
|
@ -645,8 +658,10 @@ target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode,
|
|||
#define RTAS_IBM_REMOVE_PE_DMA_WINDOW (RTAS_TOKEN_BASE + 0x28)
|
||||
#define RTAS_IBM_RESET_PE_DMA_WINDOW (RTAS_TOKEN_BASE + 0x29)
|
||||
#define RTAS_IBM_SUSPEND_ME (RTAS_TOKEN_BASE + 0x2A)
|
||||
#define RTAS_IBM_NMI_REGISTER (RTAS_TOKEN_BASE + 0x2B)
|
||||
#define RTAS_IBM_NMI_INTERLOCK (RTAS_TOKEN_BASE + 0x2C)
|
||||
|
||||
#define RTAS_TOKEN_MAX (RTAS_TOKEN_BASE + 0x2B)
|
||||
#define RTAS_TOKEN_MAX (RTAS_TOKEN_BASE + 0x2D)
|
||||
|
||||
/* RTAS ibm,get-system-parameter token values */
|
||||
#define RTAS_SYSPARM_SPLPAR_CHARACTERISTICS 20
|
||||
|
@ -716,6 +731,9 @@ void spapr_load_rtas(SpaprMachineState *spapr, void *fdt, hwaddr addr);
|
|||
|
||||
#define RTAS_ERROR_LOG_MAX 2048
|
||||
|
||||
/* Offset from rtas-base where error log is placed */
|
||||
#define RTAS_ERROR_LOG_OFFSET 0x30
|
||||
|
||||
#define RTAS_EVENT_SCAN_RATE 1
|
||||
|
||||
/* This helper should be used to encode interrupt specifiers when the related
|
||||
|
@ -802,6 +820,7 @@ void spapr_clear_pending_events(SpaprMachineState *spapr);
|
|||
int spapr_max_server_number(SpaprMachineState *spapr);
|
||||
void spapr_store_hpte(PowerPCCPU *cpu, hwaddr ptex,
|
||||
uint64_t pte0, uint64_t pte1);
|
||||
void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered);
|
||||
|
||||
/* DRC callbacks. */
|
||||
void spapr_core_release(DeviceState *dev);
|
||||
|
@ -869,6 +888,7 @@ extern const VMStateDescription vmstate_spapr_cap_hpt_maxpagesize;
|
|||
extern const VMStateDescription vmstate_spapr_cap_nested_kvm_hv;
|
||||
extern const VMStateDescription vmstate_spapr_cap_large_decr;
|
||||
extern const VMStateDescription vmstate_spapr_cap_ccf_assist;
|
||||
extern const VMStateDescription vmstate_spapr_cap_fwnmi;
|
||||
|
||||
static inline uint8_t spapr_get_cap(SpaprMachineState *spapr, int cap)
|
||||
{
|
||||
|
@ -891,4 +911,5 @@ void spapr_check_pagesize(SpaprMachineState *spapr, hwaddr pagesize,
|
|||
#define SPAPR_OV5_XIVE_BOTH 0x80 /* Only to advertise on the platform */
|
||||
|
||||
void spapr_set_all_lpcrs(target_ulong value, target_ulong mask);
|
||||
hwaddr spapr_get_rtas_addr(void);
|
||||
#endif /* HW_SPAPR_H */
|
||||
|
|
|
@ -58,6 +58,7 @@ typedef struct SpaprVioDeviceClass {
|
|||
void (*realize)(SpaprVioDevice *dev, Error **errp);
|
||||
void (*reset)(SpaprVioDevice *dev);
|
||||
int (*devnode)(SpaprVioDevice *dev, void *fdt, int node_off);
|
||||
const char *(*get_dt_compatible)(SpaprVioDevice *dev);
|
||||
} SpaprVioDeviceClass;
|
||||
|
||||
struct SpaprVioDevice {
|
||||
|
|
|
@ -101,6 +101,10 @@ struct ICSStateClass {
|
|||
DeviceClass parent_class;
|
||||
|
||||
DeviceRealize parent_realize;
|
||||
DeviceReset parent_reset;
|
||||
|
||||
void (*reject)(ICSState *s, uint32_t irq);
|
||||
void (*resend)(ICSState *s);
|
||||
};
|
||||
|
||||
struct ICSState {
|
||||
|
@ -161,6 +165,7 @@ void icp_set_mfrr(ICPState *icp, uint8_t mfrr);
|
|||
uint32_t icp_accept(ICPState *ss);
|
||||
uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr);
|
||||
void icp_eoi(ICPState *icp, uint32_t xirr);
|
||||
void icp_irq(ICSState *ics, int server, int nr, uint8_t priority);
|
||||
void icp_reset(ICPState *icp);
|
||||
|
||||
void ics_write_xive(ICSState *ics, int nr, int server,
|
||||
|
|
|
@ -295,6 +295,14 @@ void qemu_mutex_lock_iothread_impl(const char *file, int line);
|
|||
*/
|
||||
void qemu_mutex_unlock_iothread(void);
|
||||
|
||||
/*
|
||||
* qemu_cond_wait_iothread: Wait on condition for the main loop mutex
|
||||
*
|
||||
* This function atomically releases the main loop mutex and causes
|
||||
* the calling thread to block on the condition.
|
||||
*/
|
||||
void qemu_cond_wait_iothread(QemuCond *cond);
|
||||
|
||||
/* internal interfaces */
|
||||
|
||||
void qemu_fd_register(int fd);
|
||||
|
|
|
@ -45,11 +45,14 @@ typedef struct TPMIfClass {
|
|||
|
||||
#define TYPE_TPM_TIS "tpm-tis"
|
||||
#define TYPE_TPM_CRB "tpm-crb"
|
||||
#define TYPE_TPM_SPAPR "tpm-spapr"
|
||||
|
||||
#define TPM_IS_TIS(chr) \
|
||||
object_dynamic_cast(OBJECT(chr), TYPE_TPM_TIS)
|
||||
#define TPM_IS_CRB(chr) \
|
||||
object_dynamic_cast(OBJECT(chr), TYPE_TPM_CRB)
|
||||
#define TPM_IS_SPAPR(chr) \
|
||||
object_dynamic_cast(OBJECT(chr), TYPE_TPM_SPAPR)
|
||||
|
||||
/* returns NULL unless there is exactly one TPM device */
|
||||
static inline TPMIf *tpm_find(void)
|
||||
|
|
|
@ -4,9 +4,6 @@
|
|||
- The VGA BIOS and the Cirrus VGA BIOS come from the LGPL VGA bios
|
||||
project (http://www.nongnu.org/vgabios/).
|
||||
|
||||
- The PowerPC Open Hack'Ware Open Firmware Compatible BIOS is
|
||||
available at https://repo.or.cz/openhackware.git.
|
||||
|
||||
- OpenBIOS (http://www.openbios.org/) is a free (GPL v2) portable
|
||||
firmware implementation. The goal is to implement a 100% IEEE
|
||||
1275-1994 (referred to as Open Firmware) compliant firmware.
|
||||
|
|
Binary file not shown.
|
@ -12,11 +12,11 @@
|
|||
#
|
||||
# @tpm-tis: TPM TIS model
|
||||
# @tpm-crb: TPM CRB model (since 2.12)
|
||||
# @tpm-spapr: TPM SPAPR model (since 5.0)
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'enum': 'TpmModel', 'data': [ 'tpm-tis', 'tpm-crb' ] }
|
||||
|
||||
{ 'enum': 'TpmModel', 'data': [ 'tpm-tis', 'tpm-crb', 'tpm-spapr' ] }
|
||||
##
|
||||
# @query-tpm-models:
|
||||
#
|
||||
|
@ -29,7 +29,7 @@
|
|||
# Example:
|
||||
#
|
||||
# -> { "execute": "query-tpm-models" }
|
||||
# <- { "return": [ "tpm-tis", "tpm-crb" ] }
|
||||
# <- { "return": [ "tpm-tis", "tpm-crb", "tpm-spapr" ] }
|
||||
#
|
||||
##
|
||||
{ 'command': 'query-tpm-models', 'returns': ['TpmModel'] }
|
||||
|
|
|
@ -270,12 +270,6 @@ machine type instead.
|
|||
These machine types are very old and likely can not be used for live migration
|
||||
from old QEMU versions anymore. A newer machine type should be used instead.
|
||||
|
||||
@subsection prep (PowerPC) (since 3.1)
|
||||
|
||||
This machine type uses an unmaintained firmware, broken in lots of ways,
|
||||
and unable to start post-2004 operating systems. 40p machine type should be
|
||||
used instead.
|
||||
|
||||
@subsection spike_v1.9.1 and spike_v1.10 (since 4.1)
|
||||
|
||||
The version specific Spike machines have been deprecated in favour of the
|
||||
|
|
|
@ -1729,7 +1729,7 @@ differences are mentioned in the following sections.
|
|||
@section PowerPC System emulator
|
||||
@cindex system emulation (PowerPC)
|
||||
|
||||
Use the executable @file{qemu-system-ppc} to simulate a complete PREP
|
||||
Use the executable @file{qemu-system-ppc} to simulate a complete 40P (PREP)
|
||||
or PowerMac PowerPC system.
|
||||
|
||||
QEMU emulates the following PowerMac peripherals:
|
||||
|
@ -1749,7 +1749,7 @@ Non Volatile RAM
|
|||
VIA-CUDA with ADB keyboard and mouse.
|
||||
@end itemize
|
||||
|
||||
QEMU emulates the following PREP peripherals:
|
||||
QEMU emulates the following 40P (PREP) peripherals:
|
||||
|
||||
@itemize @minus
|
||||
@item
|
||||
|
@ -1761,7 +1761,7 @@ PCI VGA compatible card with VESA Bochs Extensions
|
|||
@item
|
||||
Floppy disk
|
||||
@item
|
||||
NE2000 network adapters
|
||||
PCnet network adapters
|
||||
@item
|
||||
Serial port
|
||||
@item
|
||||
|
@ -1770,12 +1770,9 @@ PREP Non Volatile RAM
|
|||
PC compatible keyboard and mouse.
|
||||
@end itemize
|
||||
|
||||
QEMU uses the Open Hack'Ware Open Firmware Compatible BIOS available at
|
||||
@url{http://perso.magic.fr/l_indien/OpenHackWare/index.htm}.
|
||||
|
||||
Since version 0.9.1, QEMU uses OpenBIOS @url{https://www.openbios.org/}
|
||||
for the g3beige and mac99 PowerMac machines. OpenBIOS is a free (GPL
|
||||
v2) portable firmware implementation. The goal is to implement a 100%
|
||||
for the g3beige and mac99 PowerMac and the 40p machines. OpenBIOS is a free
|
||||
(GPL v2) portable firmware implementation. The goal is to implement a 100%
|
||||
IEEE 1275-1994 (referred to as Open Firmware) compliant firmware.
|
||||
|
||||
@c man begin OPTIONS
|
||||
|
@ -1798,8 +1795,6 @@ qemu-system-ppc -prom-env 'auto-boot?=false' \
|
|||
-prom-env 'boot-args=conf=hd:2,\yaboot.conf'
|
||||
@end example
|
||||
|
||||
These variables are not used by Open Hack'Ware.
|
||||
|
||||
@end table
|
||||
|
||||
@c man end
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Subproject commit c559da7c8eec5e45ef1f67978827af6f0b9546f5
|
|
@ -180,7 +180,7 @@ enum {
|
|||
POWERPC_EXCP_TRAP = 0x40,
|
||||
};
|
||||
|
||||
#define PPC_INPUT(env) (env->bus_model)
|
||||
#define PPC_INPUT(env) ((env)->bus_model)
|
||||
|
||||
/*****************************************************************************/
|
||||
typedef struct opc_handler_t opc_handler_t;
|
||||
|
@ -397,6 +397,10 @@ typedef struct ppc_v3_pate_t {
|
|||
#define PSSCR_ESL PPC_BIT(42) /* Enable State Loss */
|
||||
#define PSSCR_EC PPC_BIT(43) /* Exit Criterion */
|
||||
|
||||
/* HFSCR bits */
|
||||
#define HFSCR_MSGP PPC_BIT(53) /* Privileged Message Send Facilities */
|
||||
#define HFSCR_IC_MSGP 0xA
|
||||
|
||||
#define msr_sf ((env->msr >> MSR_SF) & 1)
|
||||
#define msr_isf ((env->msr >> MSR_ISF) & 1)
|
||||
#define msr_shv ((env->msr >> MSR_SHV) & 1)
|
||||
|
@ -1329,6 +1333,8 @@ void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp);
|
|||
#endif
|
||||
|
||||
void store_fpscr(CPUPPCState *env, uint64_t arg, uint32_t mask);
|
||||
void helper_hfscr_facility_check(CPUPPCState *env, uint32_t bit,
|
||||
const char *caller, uint32_t cause);
|
||||
|
||||
static inline uint64_t ppc_dump_gpr(CPUPPCState *env, int gprn)
|
||||
{
|
||||
|
|
|
@ -471,6 +471,15 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
|||
case POWERPC_EXCP_FU: /* Facility unavailable exception */
|
||||
#ifdef TARGET_PPC64
|
||||
env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56);
|
||||
#endif
|
||||
break;
|
||||
case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */
|
||||
#ifdef TARGET_PPC64
|
||||
env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS);
|
||||
srr0 = SPR_HSRR0;
|
||||
srr1 = SPR_HSRR1;
|
||||
new_msr |= (target_ulong)MSR_HVB;
|
||||
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
|
||||
#endif
|
||||
break;
|
||||
case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */
|
||||
|
@ -900,7 +909,11 @@ static void ppc_hw_interrupt(CPUPPCState *env)
|
|||
}
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) {
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL);
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORI);
|
||||
if (is_book3s_arch2x(env)) {
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_SDOOR);
|
||||
} else {
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORI);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDOORBELL)) {
|
||||
|
@ -1221,39 +1234,30 @@ void helper_msgsnd(target_ulong rb)
|
|||
}
|
||||
|
||||
/* Server Processor Control */
|
||||
static int book3s_dbell2irq(target_ulong rb)
|
||||
{
|
||||
int msg = rb & DBELL_TYPE_MASK;
|
||||
|
||||
static bool dbell_type_server(target_ulong rb)
|
||||
{
|
||||
/*
|
||||
* A Directed Hypervisor Doorbell message is sent only if the
|
||||
* message type is 5. All other types are reserved and the
|
||||
* instruction is a no-op
|
||||
*/
|
||||
return msg == DBELL_TYPE_DBELL_SERVER ? PPC_INTERRUPT_HDOORBELL : -1;
|
||||
return (rb & DBELL_TYPE_MASK) == DBELL_TYPE_DBELL_SERVER;
|
||||
}
|
||||
|
||||
void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb)
|
||||
{
|
||||
int irq = book3s_dbell2irq(rb);
|
||||
|
||||
if (irq < 0) {
|
||||
if (!dbell_type_server(rb)) {
|
||||
return;
|
||||
}
|
||||
|
||||
env->pending_interrupts &= ~(1 << irq);
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDOORBELL);
|
||||
}
|
||||
|
||||
void helper_book3s_msgsnd(target_ulong rb)
|
||||
static void book3s_msgsnd_common(int pir, int irq)
|
||||
{
|
||||
int irq = book3s_dbell2irq(rb);
|
||||
int pir = rb & DBELL_PROCIDTAG_MASK;
|
||||
CPUState *cs;
|
||||
|
||||
if (irq < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
CPU_FOREACH(cs) {
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
|
@ -1267,6 +1271,49 @@ void helper_book3s_msgsnd(target_ulong rb)
|
|||
}
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
|
||||
void helper_book3s_msgsnd(target_ulong rb)
|
||||
{
|
||||
int pir = rb & DBELL_PROCIDTAG_MASK;
|
||||
|
||||
if (!dbell_type_server(rb)) {
|
||||
return;
|
||||
}
|
||||
|
||||
book3s_msgsnd_common(pir, PPC_INTERRUPT_HDOORBELL);
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb)
|
||||
{
|
||||
helper_hfscr_facility_check(env, HFSCR_MSGP, "msgclrp", HFSCR_IC_MSGP);
|
||||
|
||||
if (!dbell_type_server(rb)) {
|
||||
return;
|
||||
}
|
||||
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL);
|
||||
}
|
||||
|
||||
/*
|
||||
* sends a message to other threads that are on the same
|
||||
* multi-threaded processor
|
||||
*/
|
||||
void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb)
|
||||
{
|
||||
int pir = env->spr_cb[SPR_PIR].default_value;
|
||||
|
||||
helper_hfscr_facility_check(env, HFSCR_MSGP, "msgsndp", HFSCR_IC_MSGP);
|
||||
|
||||
if (!dbell_type_server(rb)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: TCG supports only one thread */
|
||||
|
||||
book3s_msgsnd_common(pir, PPC_INTERRUPT_DOORBELL);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr,
|
||||
|
|
|
@ -657,6 +657,10 @@ DEF_HELPER_FLAGS_1(load_601_rtcu, TCG_CALL_NO_RWG, tl, env)
|
|||
DEF_HELPER_FLAGS_1(load_purr, TCG_CALL_NO_RWG, tl, env)
|
||||
DEF_HELPER_FLAGS_2(store_purr, TCG_CALL_NO_RWG, void, env, tl)
|
||||
DEF_HELPER_2(store_ptcr, void, env, tl)
|
||||
DEF_HELPER_FLAGS_1(load_dpdes, TCG_CALL_NO_RWG, tl, env)
|
||||
DEF_HELPER_FLAGS_2(store_dpdes, TCG_CALL_NO_RWG, void, env, tl)
|
||||
DEF_HELPER_2(book3s_msgsndp, void, env, tl)
|
||||
DEF_HELPER_2(book3s_msgclrp, void, env, tl)
|
||||
#endif
|
||||
DEF_HELPER_2(store_sdr1, void, env, tl)
|
||||
DEF_HELPER_2(store_pidr, void, env, tl)
|
||||
|
|
|
@ -53,6 +53,9 @@
|
|||
|
||||
#define PROC_DEVTREE_CPU "/proc/device-tree/cpus/"
|
||||
|
||||
#define DEBUG_RETURN_GUEST 0
|
||||
#define DEBUG_RETURN_GDB 1
|
||||
|
||||
const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
|
||||
KVM_CAP_LAST_INFO
|
||||
};
|
||||
|
@ -1564,7 +1567,7 @@ void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg)
|
|||
static int kvm_handle_hw_breakpoint(CPUState *cs,
|
||||
struct kvm_debug_exit_arch *arch_info)
|
||||
{
|
||||
int handle = 0;
|
||||
int handle = DEBUG_RETURN_GUEST;
|
||||
int n;
|
||||
int flag = 0;
|
||||
|
||||
|
@ -1572,13 +1575,13 @@ static int kvm_handle_hw_breakpoint(CPUState *cs,
|
|||
if (arch_info->status & KVMPPC_DEBUG_BREAKPOINT) {
|
||||
n = find_hw_breakpoint(arch_info->address, GDB_BREAKPOINT_HW);
|
||||
if (n >= 0) {
|
||||
handle = 1;
|
||||
handle = DEBUG_RETURN_GDB;
|
||||
}
|
||||
} else if (arch_info->status & (KVMPPC_DEBUG_WATCH_READ |
|
||||
KVMPPC_DEBUG_WATCH_WRITE)) {
|
||||
n = find_hw_watchpoint(arch_info->address, &flag);
|
||||
if (n >= 0) {
|
||||
handle = 1;
|
||||
handle = DEBUG_RETURN_GDB;
|
||||
cs->watchpoint_hit = &hw_watchpoint;
|
||||
hw_watchpoint.vaddr = hw_debug_points[n].addr;
|
||||
hw_watchpoint.flags = flag;
|
||||
|
@ -1590,12 +1593,12 @@ static int kvm_handle_hw_breakpoint(CPUState *cs,
|
|||
|
||||
static int kvm_handle_singlestep(void)
|
||||
{
|
||||
return 1;
|
||||
return DEBUG_RETURN_GDB;
|
||||
}
|
||||
|
||||
static int kvm_handle_sw_breakpoint(void)
|
||||
{
|
||||
return 1;
|
||||
return DEBUG_RETURN_GDB;
|
||||
}
|
||||
|
||||
static int kvm_handle_debug(PowerPCCPU *cpu, struct kvm_run *run)
|
||||
|
@ -1647,7 +1650,7 @@ static int kvm_handle_debug(PowerPCCPU *cpu, struct kvm_run *run)
|
|||
env->error_code = POWERPC_EXCP_INVAL;
|
||||
ppc_cpu_do_interrupt(cs);
|
||||
|
||||
return 0;
|
||||
return DEBUG_RETURN_GUEST;
|
||||
}
|
||||
|
||||
int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
|
||||
|
@ -1702,6 +1705,13 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
|
|||
ret = 0;
|
||||
break;
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
case KVM_EXIT_NMI:
|
||||
trace_kvm_handle_nmi_exception();
|
||||
ret = kvm_handle_nmi(cpu, run);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
|
||||
ret = -1;
|
||||
|
@ -2054,6 +2064,14 @@ void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy)
|
|||
}
|
||||
}
|
||||
|
||||
int kvmppc_set_fwnmi(void)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(first_cpu);
|
||||
CPUState *cs = CPU(cpu);
|
||||
|
||||
return kvm_vcpu_enable_cap(cs, KVM_CAP_PPC_FWNMI, 0);
|
||||
}
|
||||
|
||||
int kvmppc_smt_threads(void)
|
||||
{
|
||||
return cap_ppc_smt ? cap_ppc_smt : 1;
|
||||
|
@ -2789,6 +2807,19 @@ int kvm_arch_msi_data_to_gsi(uint32_t data)
|
|||
return data & 0xffff;
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
int kvm_handle_nmi(PowerPCCPU *cpu, struct kvm_run *run)
|
||||
{
|
||||
bool recovered = run->flags & KVM_RUN_PPC_NMI_DISP_FULLY_RECOV;
|
||||
|
||||
cpu_synchronize_state(CPU(cpu));
|
||||
|
||||
spapr_mce_req_event(cpu, recovered);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int kvmppc_enable_hwrng(void)
|
||||
{
|
||||
if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_PPC_HWRNG)) {
|
||||
|
|
|
@ -27,6 +27,7 @@ void kvmppc_enable_h_page_init(void);
|
|||
void kvmppc_set_papr(PowerPCCPU *cpu);
|
||||
int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr);
|
||||
void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy);
|
||||
int kvmppc_set_fwnmi(void);
|
||||
int kvmppc_smt_threads(void);
|
||||
void kvmppc_error_append_smt_possible_hint(Error *const *errp);
|
||||
int kvmppc_set_smt_threads(int smt);
|
||||
|
@ -83,6 +84,8 @@ void kvm_check_mmu(PowerPCCPU *cpu, Error **errp);
|
|||
void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, unsigned int online);
|
||||
void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset);
|
||||
|
||||
int kvm_handle_nmi(PowerPCCPU *cpu, struct kvm_run *run);
|
||||
|
||||
#else
|
||||
|
||||
static inline uint32_t kvmppc_get_tbfreq(void)
|
||||
|
@ -160,6 +163,11 @@ static inline void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy)
|
|||
{
|
||||
}
|
||||
|
||||
static inline int kvmppc_set_fwnmi(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int kvmppc_smt_threads(void)
|
||||
{
|
||||
return 1;
|
||||
|
|
|
@ -56,51 +56,138 @@ static inline target_ulong addr_add(CPUPPCState *env, target_ulong addr,
|
|||
}
|
||||
}
|
||||
|
||||
static void *probe_contiguous(CPUPPCState *env, target_ulong addr, uint32_t nb,
|
||||
MMUAccessType access_type, int mmu_idx,
|
||||
uintptr_t raddr)
|
||||
{
|
||||
void *host1, *host2;
|
||||
uint32_t nb_pg1, nb_pg2;
|
||||
|
||||
nb_pg1 = -(addr | TARGET_PAGE_MASK);
|
||||
if (likely(nb <= nb_pg1)) {
|
||||
/* The entire operation is on a single page. */
|
||||
return probe_access(env, addr, nb, access_type, mmu_idx, raddr);
|
||||
}
|
||||
|
||||
/* The operation spans two pages. */
|
||||
nb_pg2 = nb - nb_pg1;
|
||||
host1 = probe_access(env, addr, nb_pg1, access_type, mmu_idx, raddr);
|
||||
addr = addr_add(env, addr, nb_pg1);
|
||||
host2 = probe_access(env, addr, nb_pg2, access_type, mmu_idx, raddr);
|
||||
|
||||
/* If the two host pages are contiguous, optimize. */
|
||||
if (host2 == host1 + nb_pg1) {
|
||||
return host1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void helper_lmw(CPUPPCState *env, target_ulong addr, uint32_t reg)
|
||||
{
|
||||
for (; reg < 32; reg++) {
|
||||
if (needs_byteswap(env)) {
|
||||
env->gpr[reg] = bswap32(cpu_ldl_data_ra(env, addr, GETPC()));
|
||||
} else {
|
||||
env->gpr[reg] = cpu_ldl_data_ra(env, addr, GETPC());
|
||||
uintptr_t raddr = GETPC();
|
||||
int mmu_idx = cpu_mmu_index(env, false);
|
||||
void *host = probe_contiguous(env, addr, (32 - reg) * 4,
|
||||
MMU_DATA_LOAD, mmu_idx, raddr);
|
||||
|
||||
if (likely(host)) {
|
||||
/* Fast path -- the entire operation is in RAM at host. */
|
||||
for (; reg < 32; reg++) {
|
||||
env->gpr[reg] = (uint32_t)ldl_be_p(host);
|
||||
host += 4;
|
||||
}
|
||||
} else {
|
||||
/* Slow path -- at least some of the operation requires i/o. */
|
||||
for (; reg < 32; reg++) {
|
||||
env->gpr[reg] = cpu_ldl_mmuidx_ra(env, addr, mmu_idx, raddr);
|
||||
addr = addr_add(env, addr, 4);
|
||||
}
|
||||
addr = addr_add(env, addr, 4);
|
||||
}
|
||||
}
|
||||
|
||||
void helper_stmw(CPUPPCState *env, target_ulong addr, uint32_t reg)
|
||||
{
|
||||
for (; reg < 32; reg++) {
|
||||
if (needs_byteswap(env)) {
|
||||
cpu_stl_data_ra(env, addr, bswap32((uint32_t)env->gpr[reg]),
|
||||
GETPC());
|
||||
} else {
|
||||
cpu_stl_data_ra(env, addr, (uint32_t)env->gpr[reg], GETPC());
|
||||
uintptr_t raddr = GETPC();
|
||||
int mmu_idx = cpu_mmu_index(env, false);
|
||||
void *host = probe_contiguous(env, addr, (32 - reg) * 4,
|
||||
MMU_DATA_STORE, mmu_idx, raddr);
|
||||
|
||||
if (likely(host)) {
|
||||
/* Fast path -- the entire operation is in RAM at host. */
|
||||
for (; reg < 32; reg++) {
|
||||
stl_be_p(host, env->gpr[reg]);
|
||||
host += 4;
|
||||
}
|
||||
} else {
|
||||
/* Slow path -- at least some of the operation requires i/o. */
|
||||
for (; reg < 32; reg++) {
|
||||
cpu_stl_mmuidx_ra(env, addr, env->gpr[reg], mmu_idx, raddr);
|
||||
addr = addr_add(env, addr, 4);
|
||||
}
|
||||
addr = addr_add(env, addr, 4);
|
||||
}
|
||||
}
|
||||
|
||||
static void do_lsw(CPUPPCState *env, target_ulong addr, uint32_t nb,
|
||||
uint32_t reg, uintptr_t raddr)
|
||||
{
|
||||
int sh;
|
||||
int mmu_idx;
|
||||
void *host;
|
||||
uint32_t val;
|
||||
|
||||
for (; nb > 3; nb -= 4) {
|
||||
env->gpr[reg] = cpu_ldl_data_ra(env, addr, raddr);
|
||||
reg = (reg + 1) % 32;
|
||||
addr = addr_add(env, addr, 4);
|
||||
if (unlikely(nb == 0)) {
|
||||
return;
|
||||
}
|
||||
if (unlikely(nb > 0)) {
|
||||
env->gpr[reg] = 0;
|
||||
for (sh = 24; nb > 0; nb--, sh -= 8) {
|
||||
env->gpr[reg] |= cpu_ldub_data_ra(env, addr, raddr) << sh;
|
||||
addr = addr_add(env, addr, 1);
|
||||
|
||||
mmu_idx = cpu_mmu_index(env, false);
|
||||
host = probe_contiguous(env, addr, nb, MMU_DATA_LOAD, mmu_idx, raddr);
|
||||
|
||||
if (likely(host)) {
|
||||
/* Fast path -- the entire operation is in RAM at host. */
|
||||
for (; nb > 3; nb -= 4) {
|
||||
env->gpr[reg] = (uint32_t)ldl_be_p(host);
|
||||
reg = (reg + 1) % 32;
|
||||
host += 4;
|
||||
}
|
||||
switch (nb) {
|
||||
default:
|
||||
return;
|
||||
case 1:
|
||||
val = ldub_p(host) << 24;
|
||||
break;
|
||||
case 2:
|
||||
val = lduw_be_p(host) << 16;
|
||||
break;
|
||||
case 3:
|
||||
val = (lduw_be_p(host) << 16) | (ldub_p(host + 2) << 8);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Slow path -- at least some of the operation requires i/o. */
|
||||
for (; nb > 3; nb -= 4) {
|
||||
env->gpr[reg] = cpu_ldl_mmuidx_ra(env, addr, mmu_idx, raddr);
|
||||
reg = (reg + 1) % 32;
|
||||
addr = addr_add(env, addr, 4);
|
||||
}
|
||||
switch (nb) {
|
||||
default:
|
||||
return;
|
||||
case 1:
|
||||
val = cpu_ldub_mmuidx_ra(env, addr, mmu_idx, raddr) << 24;
|
||||
break;
|
||||
case 2:
|
||||
val = cpu_lduw_mmuidx_ra(env, addr, mmu_idx, raddr) << 16;
|
||||
break;
|
||||
case 3:
|
||||
val = cpu_lduw_mmuidx_ra(env, addr, mmu_idx, raddr) << 16;
|
||||
addr = addr_add(env, addr, 2);
|
||||
val |= cpu_ldub_mmuidx_ra(env, addr, mmu_idx, raddr) << 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
env->gpr[reg] = val;
|
||||
}
|
||||
|
||||
void helper_lsw(CPUPPCState *env, target_ulong addr, uint32_t nb, uint32_t reg)
|
||||
void helper_lsw(CPUPPCState *env, target_ulong addr,
|
||||
uint32_t nb, uint32_t reg)
|
||||
{
|
||||
do_lsw(env, addr, nb, reg, GETPC());
|
||||
}
|
||||
|
@ -130,17 +217,57 @@ void helper_lswx(CPUPPCState *env, target_ulong addr, uint32_t reg,
|
|||
void helper_stsw(CPUPPCState *env, target_ulong addr, uint32_t nb,
|
||||
uint32_t reg)
|
||||
{
|
||||
int sh;
|
||||
uintptr_t raddr = GETPC();
|
||||
int mmu_idx;
|
||||
void *host;
|
||||
uint32_t val;
|
||||
|
||||
for (; nb > 3; nb -= 4) {
|
||||
cpu_stl_data_ra(env, addr, env->gpr[reg], GETPC());
|
||||
reg = (reg + 1) % 32;
|
||||
addr = addr_add(env, addr, 4);
|
||||
if (unlikely(nb == 0)) {
|
||||
return;
|
||||
}
|
||||
if (unlikely(nb > 0)) {
|
||||
for (sh = 24; nb > 0; nb--, sh -= 8) {
|
||||
cpu_stb_data_ra(env, addr, (env->gpr[reg] >> sh) & 0xFF, GETPC());
|
||||
addr = addr_add(env, addr, 1);
|
||||
|
||||
mmu_idx = cpu_mmu_index(env, false);
|
||||
host = probe_contiguous(env, addr, nb, MMU_DATA_STORE, mmu_idx, raddr);
|
||||
|
||||
if (likely(host)) {
|
||||
/* Fast path -- the entire operation is in RAM at host. */
|
||||
for (; nb > 3; nb -= 4) {
|
||||
stl_be_p(host, env->gpr[reg]);
|
||||
reg = (reg + 1) % 32;
|
||||
host += 4;
|
||||
}
|
||||
val = env->gpr[reg];
|
||||
switch (nb) {
|
||||
case 1:
|
||||
stb_p(host, val >> 24);
|
||||
break;
|
||||
case 2:
|
||||
stw_be_p(host, val >> 16);
|
||||
break;
|
||||
case 3:
|
||||
stw_be_p(host, val >> 16);
|
||||
stb_p(host + 2, val >> 8);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (; nb > 3; nb -= 4) {
|
||||
cpu_stl_mmuidx_ra(env, addr, env->gpr[reg], mmu_idx, raddr);
|
||||
reg = (reg + 1) % 32;
|
||||
addr = addr_add(env, addr, 4);
|
||||
}
|
||||
val = env->gpr[reg];
|
||||
switch (nb) {
|
||||
case 1:
|
||||
cpu_stb_mmuidx_ra(env, addr, val >> 24, mmu_idx, raddr);
|
||||
break;
|
||||
case 2:
|
||||
cpu_stw_mmuidx_ra(env, addr, val >> 16, mmu_idx, raddr);
|
||||
break;
|
||||
case 3:
|
||||
cpu_stw_mmuidx_ra(env, addr, val >> 16, mmu_idx, raddr);
|
||||
addr = addr_add(env, addr, 2);
|
||||
cpu_stb_mmuidx_ra(env, addr, val >> 8, mmu_idx, raddr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -166,12 +293,12 @@ static void dcbz_common(CPUPPCState *env, target_ulong addr,
|
|||
addr &= mask;
|
||||
|
||||
/* Check reservation */
|
||||
if ((env->reserve_addr & mask) == (addr & mask)) {
|
||||
if ((env->reserve_addr & mask) == addr) {
|
||||
env->reserve_addr = (target_ulong)-1ULL;
|
||||
}
|
||||
|
||||
/* Try fast path translate */
|
||||
haddr = tlb_vaddr_to_host(env, addr, MMU_DATA_STORE, mmu_idx);
|
||||
haddr = probe_write(env, addr, dcbz_size, mmu_idx, retaddr);
|
||||
if (haddr) {
|
||||
memset(haddr, 0, dcbz_size);
|
||||
} else {
|
||||
|
|
|
@ -41,6 +41,18 @@ void helper_store_dump_spr(CPUPPCState *env, uint32_t sprn)
|
|||
}
|
||||
|
||||
#ifdef TARGET_PPC64
|
||||
static void raise_hv_fu_exception(CPUPPCState *env, uint32_t bit,
|
||||
const char *caller, uint32_t cause,
|
||||
uintptr_t raddr)
|
||||
{
|
||||
qemu_log_mask(CPU_LOG_INT, "HV Facility %d is unavailable (%s)\n",
|
||||
bit, caller);
|
||||
|
||||
env->spr[SPR_HFSCR] &= ~((target_ulong)FSCR_IC_MASK << FSCR_IC_POS);
|
||||
|
||||
raise_exception_err_ra(env, POWERPC_EXCP_HV_FU, cause, raddr);
|
||||
}
|
||||
|
||||
static void raise_fu_exception(CPUPPCState *env, uint32_t bit,
|
||||
uint32_t sprn, uint32_t cause,
|
||||
uintptr_t raddr)
|
||||
|
@ -55,6 +67,17 @@ static void raise_fu_exception(CPUPPCState *env, uint32_t bit,
|
|||
}
|
||||
#endif
|
||||
|
||||
void helper_hfscr_facility_check(CPUPPCState *env, uint32_t bit,
|
||||
const char *caller, uint32_t cause)
|
||||
{
|
||||
#ifdef TARGET_PPC64
|
||||
if ((env->msr_mask & MSR_HVB) && !msr_hv &&
|
||||
!(env->spr[SPR_HFSCR] & (1UL << bit))) {
|
||||
raise_hv_fu_exception(env, bit, caller, cause, GETPC());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void helper_fscr_facility_check(CPUPPCState *env, uint32_t bit,
|
||||
uint32_t sprn, uint32_t cause)
|
||||
{
|
||||
|
@ -105,6 +128,46 @@ void helper_store_pcr(CPUPPCState *env, target_ulong value)
|
|||
|
||||
env->spr[SPR_PCR] = value & pcc->pcr_mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* DPDES register is shared. Each bit reflects the state of the
|
||||
* doorbell interrupt of a thread of the same core.
|
||||
*/
|
||||
target_ulong helper_load_dpdes(CPUPPCState *env)
|
||||
{
|
||||
target_ulong dpdes = 0;
|
||||
|
||||
helper_hfscr_facility_check(env, HFSCR_MSGP, "load DPDES", HFSCR_IC_MSGP);
|
||||
|
||||
/* TODO: TCG supports only one thread */
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) {
|
||||
dpdes = 1;
|
||||
}
|
||||
|
||||
return dpdes;
|
||||
}
|
||||
|
||||
void helper_store_dpdes(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
PowerPCCPU *cpu = env_archcpu(env);
|
||||
CPUState *cs = CPU(cpu);
|
||||
|
||||
helper_hfscr_facility_check(env, HFSCR_MSGP, "store DPDES", HFSCR_IC_MSGP);
|
||||
|
||||
/* TODO: TCG supports only one thread */
|
||||
if (val & ~0x1) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "Invalid DPDES register value "
|
||||
TARGET_FMT_lx"\n", val);
|
||||
return;
|
||||
}
|
||||
|
||||
if (val & 0x1) {
|
||||
env->pending_interrupts |= 1 << PPC_INTERRUPT_DOORBELL;
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
} else {
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL);
|
||||
}
|
||||
}
|
||||
#endif /* defined(TARGET_PPC64) */
|
||||
|
||||
void helper_store_pidr(CPUPPCState *env, target_ulong val)
|
||||
|
|
|
@ -235,6 +235,12 @@ int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
|
|||
/* In real mode top 4 effective addr bits (mostly) ignored */
|
||||
raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL;
|
||||
|
||||
/* In HV mode, add HRMOR if top EA bit is clear */
|
||||
if (msr_hv || !env->has_hv_mode) {
|
||||
if (!(eaddr >> 63)) {
|
||||
raddr |= env->spr[SPR_HRMOR];
|
||||
}
|
||||
}
|
||||
tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
|
||||
PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx,
|
||||
TARGET_PAGE_SIZE);
|
||||
|
|
|
@ -28,3 +28,4 @@ kvm_handle_papr_hcall(void) "handle PAPR hypercall"
|
|||
kvm_handle_epr(void) "handle epr"
|
||||
kvm_handle_watchdog_expiry(void) "handle watchdog expiry"
|
||||
kvm_handle_debug_exception(void) "handle debug exception"
|
||||
kvm_handle_nmi_exception(void) "handle NMI exception"
|
||||
|
|
|
@ -6645,6 +6645,28 @@ static void gen_msgsnd(DisasContext *ctx)
|
|||
#endif /* defined(CONFIG_USER_ONLY) */
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
static void gen_msgclrp(DisasContext *ctx)
|
||||
{
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
GEN_PRIV;
|
||||
#else
|
||||
CHK_SV;
|
||||
gen_helper_book3s_msgclrp(cpu_env, cpu_gpr[rB(ctx->opcode)]);
|
||||
#endif /* defined(CONFIG_USER_ONLY) */
|
||||
}
|
||||
|
||||
static void gen_msgsndp(DisasContext *ctx)
|
||||
{
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
GEN_PRIV;
|
||||
#else
|
||||
CHK_SV;
|
||||
gen_helper_book3s_msgsndp(cpu_env, cpu_gpr[rB(ctx->opcode)]);
|
||||
#endif /* defined(CONFIG_USER_ONLY) */
|
||||
}
|
||||
#endif
|
||||
|
||||
static void gen_msgsync(DisasContext *ctx)
|
||||
{
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
|
@ -7187,6 +7209,10 @@ GEN_HANDLER(vmladduhm, 0x04, 0x11, 0xFF, 0x00000000, PPC_ALTIVEC),
|
|||
GEN_HANDLER_E(maddhd_maddhdu, 0x04, 0x18, 0xFF, 0x00000000, PPC_NONE,
|
||||
PPC2_ISA300),
|
||||
GEN_HANDLER_E(maddld, 0x04, 0x19, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA300),
|
||||
GEN_HANDLER2_E(msgsndp, "msgsndp", 0x1F, 0x0E, 0x04, 0x03ff0001,
|
||||
PPC_NONE, PPC2_ISA207S),
|
||||
GEN_HANDLER2_E(msgclrp, "msgclrp", 0x1F, 0x0E, 0x05, 0x03ff0001,
|
||||
PPC_NONE, PPC2_ISA207S),
|
||||
#endif
|
||||
|
||||
#undef GEN_INT_ARITH_ADD
|
||||
|
|
|
@ -464,6 +464,17 @@ static void spr_write_pcr(DisasContext *ctx, int sprn, int gprn)
|
|||
{
|
||||
gen_helper_store_pcr(cpu_env, cpu_gpr[gprn]);
|
||||
}
|
||||
|
||||
/* DPDES */
|
||||
static void spr_read_dpdes(DisasContext *ctx, int gprn, int sprn)
|
||||
{
|
||||
gen_helper_load_dpdes(cpu_gpr[gprn], cpu_env);
|
||||
}
|
||||
|
||||
static void spr_write_dpdes(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
gen_helper_store_dpdes(cpu_env, cpu_gpr[gprn]);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
@ -8238,10 +8249,11 @@ static void gen_spr_power8_dpdes(CPUPPCState *env)
|
|||
{
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
/* Directed Privileged Door-bell Exception State, used for IPI */
|
||||
spr_register(env, SPR_DPDES, "DPDES",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, SPR_NOACCESS,
|
||||
0x00000000);
|
||||
spr_register_kvm_hv(env, SPR_DPDES, "DPDES",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_dpdes, SPR_NOACCESS,
|
||||
&spr_read_dpdes, &spr_write_dpdes,
|
||||
KVM_REG_PPC_DPDES, 0x00000000);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -108,30 +108,6 @@ static void test_pc_boot_order(void)
|
|||
test_boot_orders(NULL, read_boot_order_pc, test_cases_pc);
|
||||
}
|
||||
|
||||
static uint8_t read_m48t59(QTestState *qts, uint64_t addr, uint16_t reg)
|
||||
{
|
||||
qtest_writeb(qts, addr, reg & 0xff);
|
||||
qtest_writeb(qts, addr + 1, reg >> 8);
|
||||
return qtest_readb(qts, addr + 3);
|
||||
}
|
||||
|
||||
static uint64_t read_boot_order_prep(QTestState *qts)
|
||||
{
|
||||
return read_m48t59(qts, 0x80000000 + 0x74, 0x34);
|
||||
}
|
||||
|
||||
static const boot_order_test test_cases_prep[] = {
|
||||
{ "", 'c', 'c' },
|
||||
{ "-boot c", 'c', 'c' },
|
||||
{ "-boot d", 'd', 'd' },
|
||||
{}
|
||||
};
|
||||
|
||||
static void test_prep_boot_order(void)
|
||||
{
|
||||
test_boot_orders("prep", read_boot_order_prep, test_cases_prep);
|
||||
}
|
||||
|
||||
static uint64_t read_boot_order_pmac(QTestState *qts)
|
||||
{
|
||||
QFWCFG *fw_cfg = mm_fw_cfg_init(qts, 0xf0000510);
|
||||
|
@ -190,7 +166,6 @@ int main(int argc, char *argv[])
|
|||
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
|
||||
qtest_add_func("boot-order/pc", test_pc_boot_order);
|
||||
} else if (strcmp(arch, "ppc") == 0 || strcmp(arch, "ppc64") == 0) {
|
||||
qtest_add_func("boot-order/prep", test_prep_boot_order);
|
||||
qtest_add_func("boot-order/pmac_oldworld",
|
||||
test_pmac_oldworld_boot_order);
|
||||
qtest_add_func("boot-order/pmac_newworld",
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/libqos-spapr.h"
|
||||
|
||||
static const uint8_t kernel_mcf5208[] = {
|
||||
0x41, 0xf9, 0xfc, 0x06, 0x00, 0x00, /* lea 0xfc060000,%a0 */
|
||||
|
@ -112,7 +113,7 @@ static testdef_t tests[] = {
|
|||
{ "ppc64", "40p", "-m 192", "Memory: 192M" },
|
||||
{ "ppc64", "mac99", "", "PowerPC,970FX" },
|
||||
{ "ppc64", "pseries",
|
||||
"-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken",
|
||||
"-machine " PSERIES_DEFAULT_CAPABILITIES,
|
||||
"Open Firmware" },
|
||||
{ "ppc64", "powernv8", "", "OPAL" },
|
||||
{ "ppc64", "powernv9", "", "OPAL" },
|
||||
|
|
|
@ -189,7 +189,7 @@ int main(int argc, char **argv)
|
|||
add_s390x_tests();
|
||||
} else if (g_str_equal(arch, "ppc64")) {
|
||||
const char *ppcmachines[] = {
|
||||
"pseries", "mac99", "g3beige", "40p", "prep", NULL
|
||||
"pseries", "mac99", "g3beige", "40p", NULL
|
||||
};
|
||||
add_cdrom_param_tests(ppcmachines);
|
||||
} else if (g_str_equal(arch, "sparc")) {
|
||||
|
|
|
@ -35,7 +35,7 @@ static const TestCase test_cases[] = {
|
|||
{ "mips64", "malta", 0x10000000, .bswap = true },
|
||||
{ "mips64el", "fulong2e", 0x1fd00000 },
|
||||
{ "ppc", "g3beige", 0xfe000000, .bswap = true, .superio = "i82378" },
|
||||
{ "ppc", "prep", 0x80000000, .bswap = true },
|
||||
{ "ppc", "40p", 0x80000000, .bswap = true },
|
||||
{ "ppc", "bamboo", 0xe8000000, .bswap = true, .superio = "i82378" },
|
||||
{ "ppc64", "mac99", 0xf2000000, .bswap = true, .superio = "i82378" },
|
||||
{ "ppc64", "pseries", (1ULL << 45), .bswap = true, .superio = "i82378" },
|
||||
|
|
|
@ -7,4 +7,12 @@ QOSState *qtest_spapr_vboot(const char *cmdline_fmt, va_list ap);
|
|||
QOSState *qtest_spapr_boot(const char *cmdline_fmt, ...);
|
||||
void qtest_spapr_shutdown(QOSState *qs);
|
||||
|
||||
/* List of capabilities needed to silence warnings with TCG */
|
||||
#define PSERIES_DEFAULT_CAPABILITIES \
|
||||
"cap-cfpc=broken," \
|
||||
"cap-sbbc=broken," \
|
||||
"cap-ibs=broken," \
|
||||
"cap-ccf-assist=off," \
|
||||
"cap-fwnmi-mce=off"
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/libqos-spapr.h"
|
||||
|
||||
#define MAGIC 0xcafec0de
|
||||
#define ADDRESS 0x4000
|
||||
|
@ -54,7 +55,7 @@ static void test_machine(const void *machine)
|
|||
*/
|
||||
if (strcmp(machine, "pseries") == 0) {
|
||||
extra_args = "-nodefaults"
|
||||
" -machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken";
|
||||
" -machine " PSERIES_DEFAULT_CAPABILITIES;
|
||||
}
|
||||
|
||||
qts = qtest_initf("-M %s -accel tcg %s -prom-env 'use-nvramrc?=true' "
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "qemu-common.h"
|
||||
#include "libqtest.h"
|
||||
#include "boot-sector.h"
|
||||
#include "libqos/libqos-spapr.h"
|
||||
|
||||
#define NETNAME "net0"
|
||||
|
||||
|
@ -46,15 +47,15 @@ static testdef_t x86_tests_slow[] = {
|
|||
|
||||
static testdef_t ppc64_tests[] = {
|
||||
{ "pseries", "spapr-vlan",
|
||||
"-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,vsmt=8" },
|
||||
"-machine vsmt=8," PSERIES_DEFAULT_CAPABILITIES },
|
||||
{ "pseries", "virtio-net-pci",
|
||||
"-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,vsmt=8" },
|
||||
"-machine vsmt=8," PSERIES_DEFAULT_CAPABILITIES },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
static testdef_t ppc64_tests_slow[] = {
|
||||
{ "pseries", "e1000",
|
||||
"-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,vsmt=8" },
|
||||
"-machine vsmt=8," PSERIES_DEFAULT_CAPABILITIES },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue