mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 = {
|
||||
|
||||
+5
-1
@@ -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
Reference in New Issue
Block a user