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:
Peter Maydell
2020-02-03 09:52:43 +00:00
76 changed files with 7565 additions and 1018 deletions
-3
View File
@@ -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
-1
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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;
+1 -2
View File
@@ -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.
#
+1
View File
@@ -13,3 +13,4 @@ Contents:
ppc-xive
ppc-spapr-xive
acpi_hw_reduced_hotplug
tpm
+503
View File
File diff suppressed because it is too large Load Diff
-427
View File
@@ -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 ... )
+13 -1
View File
@@ -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) {
+2
View File
@@ -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
File diff suppressed because it is too large Load Diff
+349
View 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
View 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)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2
View File
@@ -135,6 +135,8 @@ config XIVE_SPAPR
default y
depends on PSERIES
select XIVE
select PCI
select PCIE_PORT
config XIVE_KVM
bool
+201 -3
View File
@@ -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) \
+18 -15
View File
@@ -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 = {
+1
View File
@@ -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 = {
+5 -1
View File
@@ -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;
}

Some files were not shown because too many files have changed in this diff Show More