ppc 7.0 queue:
* New SLOF for PPC970 and POWER5+ (Alexey) * Fixes for POWER5+ pseries (Cedric) * Updates of documentation (Leonardo and Thomas) * First step of exception model cleanup (Fabiano) * User created PHB3/PHB4 devices (Daniel and Cedric) -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmHesMkACgkQUaNDx8/7 7KG7LQ//bhaWJXgzhQcGc49QhNh2SCQjeqfnuwzlxthcIQ1YZpocOefGW3OZLksy HEbbJ1dfWBm4bJa23YFf2OFpsLtBfSzg0W0o3m/9+Ufe5dXwae/MnMQoG/pNU+v2 gDk9179iORyDCk8dR28qQYYSz1AS0c+n0M7FCkcOF1vVklUWWV+2DMNEzagWkFDf Ggu6X3MaIZAigvIOFKmny5AYqcRLF/OYHazDPe0I1XLM4GvrlO4Jy5Gz+RPtXtGN sEFE+w0EifdwfXxsf2N3ecgWqOlfQt6N2wWGLp2YGwB0ro38L5/BuZTuaWnF1C4N bu0dxGIhufYN5fvEc3SM3Jyx0agLRyQR3FQmNim92B9TWtJQ8cG+JZ7rbrMMhxt6 +KifMCqsDVV4eK2NSPTN++Fu5htoHotJqwkr1ajQriStX2Ihkr4hj3Yp4S724Ogn U6LuYpB/bcrYeaEKBjPJXt4WgGy+nAp5Ije88BY9KXcw/5ZGIlAtTQ/HQ1HRWChN CwlfBK0DZX83YJYsjZ3/k59HnpOsG2sOI3gSbG4cUiws7sFRuToEA9cThNY+d/Vr Phx8bRijRTqa2nRYdxEM0z3vqjldkyMU4n7LzChqUJucwkfmLscdDZVTfRjV+ete uLLqU6Y/ELMVKCRh8GRtmL/nHMulCzmoDLuKwJqmwBHeUS9BkBM= =H+2M -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/legoater/tags/pull-ppc-20220112' into staging ppc 7.0 queue: * New SLOF for PPC970 and POWER5+ (Alexey) * Fixes for POWER5+ pseries (Cedric) * Updates of documentation (Leonardo and Thomas) * First step of exception model cleanup (Fabiano) * User created PHB3/PHB4 devices (Daniel and Cedric) # gpg: Signature made Wed 12 Jan 2022 10:43:21 GMT # gpg: using RSA key A0F66548F04895EBFE6B0B6051A343C7CFFBECA1 # gpg: Good signature from "Cédric Le Goater <clg@kaod.org>" [undefined] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: A0F6 6548 F048 95EB FE6B 0B60 51A3 43C7 CFFB ECA1 * remotes/legoater/tags/pull-ppc-20220112: (34 commits) ppc/pnv: use stack->pci_regs[] in pnv_pec_stk_pci_xscom_write() ppc/pnv: turn pnv_phb4_update_regions() into static ppc/pnv: Introduce user creatable pnv-phb4 devices ppc/pnv: turn 'phb' into a pointer in struct PnvPhb4PecStack ppc/pnv: move PHB4 XSCOM init to phb4_realize() ppc/pnv: set phb4 properties in stk_realize() pnv_phb4_pec: use pnv_phb4_pec_get_phb_id() in pnv_pec_dt_xscom() pnv_phb4_pec.c: move pnv_pec_phb_offset() to pnv_phb4.c pnv_phb4.c: change TYPE_PNV_PHB4_ROOT_BUS name pnv_phb3.h: change TYPE_PNV_PHB3_ROOT_BUS name ppc/pnv: Move num_phbs under Pnv8Chip ppc/pnv: Complete user created PHB3 devices ppc/pnv: Reparent user created PHB3 devices to the PnvChip ppc/pnv: Introduce support for user created PHB3 devices pnv_phb4.c: check if root port exists in rc_config functions pnv_phb4.c: make pnv-phb4-root-port user creatable ppc/pnv: Attach PHB3 root port device when defaults are enabled pnv_phb4.c: add unique chassis and slot for pnv_phb4_root_port pnv_phb3.c: add unique chassis and slot for pnv_phb3_root_port target/ppc: Set the correct endianness for powernv memory dumps ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
f8d75e10d3
12
MAINTAINERS
12
MAINTAINERS
@ -1245,7 +1245,7 @@ F: hw/openrisc/openrisc_sim.c
|
||||
|
||||
PowerPC Machines
|
||||
----------------
|
||||
405
|
||||
405 (ref405ep and taihu)
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Orphan
|
||||
F: hw/ppc/ppc405_boards.c
|
||||
@ -1281,6 +1281,7 @@ New World (mac99)
|
||||
M: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: docs/system/ppc/powermac.rst
|
||||
F: hw/ppc/mac_newworld.c
|
||||
F: hw/pci-host/uninorth.c
|
||||
F: hw/pci-bridge/dec.[hc]
|
||||
@ -1299,6 +1300,7 @@ Old World (g3beige)
|
||||
M: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: docs/system/ppc/powermac.rst
|
||||
F: hw/ppc/mac_oldworld.c
|
||||
F: hw/pci-host/grackle.c
|
||||
F: hw/misc/macio/
|
||||
@ -1312,6 +1314,7 @@ PReP
|
||||
M: Hervé Poussineau <hpoussin@reactos.org>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Maintained
|
||||
F: docs/system/ppc/prep.rst
|
||||
F: hw/ppc/prep.c
|
||||
F: hw/ppc/prep_systemio.c
|
||||
F: hw/ppc/rs6000_mc.c
|
||||
@ -1324,7 +1327,7 @@ F: include/hw/isa/pc87312.h
|
||||
F: include/hw/rtc/m48t59.h
|
||||
F: tests/avocado/ppc_prep_40p.py
|
||||
|
||||
sPAPR
|
||||
sPAPR (pseries)
|
||||
M: Cédric Le Goater <clg@kaod.org>
|
||||
M: Daniel Henrique Barboza <danielhb413@gmail.com>
|
||||
R: David Gibson <david@gibson.dropbear.id.au>
|
||||
@ -1336,8 +1339,8 @@ F: include/hw/*/spapr*
|
||||
F: hw/*/xics*
|
||||
F: include/hw/*/xics*
|
||||
F: pc-bios/slof.bin
|
||||
F: docs/specs/ppc-spapr-hcalls.txt
|
||||
F: docs/specs/ppc-spapr-hotplug.txt
|
||||
F: docs/system/ppc/pseries.rst
|
||||
F: docs/specs/ppc-spapr-*
|
||||
F: tests/qtest/spapr*
|
||||
F: tests/qtest/libqos/*spapr*
|
||||
F: tests/qtest/rtas*
|
||||
@ -1348,6 +1351,7 @@ PowerNV (Non-Virtualized)
|
||||
M: Cédric Le Goater <clg@kaod.org>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Maintained
|
||||
F: docs/system/ppc/powernv.rst
|
||||
F: hw/ppc/pnv*
|
||||
F: hw/intc/pnv*
|
||||
F: hw/intc/xics_pnv.c
|
||||
|
@ -1,30 +0,0 @@
|
||||
POWER (PAPR) Protected Execution Facility (PEF)
|
||||
===============================================
|
||||
|
||||
Protected Execution Facility (PEF), also known as Secure Guest support
|
||||
is a feature found on IBM POWER9 and POWER10 processors.
|
||||
|
||||
If a suitable firmware including an Ultravisor is installed, it adds
|
||||
an extra memory protection mode to the CPU. The ultravisor manages a
|
||||
pool of secure memory which cannot be accessed by the hypervisor.
|
||||
|
||||
When this feature is enabled in QEMU, a guest can use ultracalls to
|
||||
enter "secure mode". This transfers most of its memory to secure
|
||||
memory, where it cannot be eavesdropped by a compromised hypervisor.
|
||||
|
||||
Launching
|
||||
---------
|
||||
|
||||
To launch a guest which will be permitted to enter PEF secure mode:
|
||||
|
||||
# ${QEMU} \
|
||||
-object pef-guest,id=pef0 \
|
||||
-machine confidential-guest-support=pef0 \
|
||||
...
|
||||
|
||||
Live Migration
|
||||
----------------
|
||||
|
||||
Live migration is not yet implemented for PEF guests. For
|
||||
consistency, we currently prevent migration if the PEF feature is
|
||||
enabled, whether or not the guest has actually entered secure mode.
|
@ -1,13 +1,12 @@
|
||||
======================
|
||||
sPAPR hypervisor calls
|
||||
----------------------
|
||||
======================
|
||||
|
||||
When used with the ``pseries`` machine type, ``qemu-system-ppc64`` implements
|
||||
a set of hypervisor calls (a.k.a. hcalls) defined in the `Linux on Power
|
||||
Architecture Reference document (LoPAR)
|
||||
<https://cdn.openpowerfoundation.org/wp-content/uploads/2020/07/LoPAR-20200812.pdf>`_.
|
||||
This document is a subset of the Power Architecture Platform Reference (PAPR+)
|
||||
specification (IBM internal only), which is what PowerVM, the IBM proprietary
|
||||
hypervisor, adheres to.
|
||||
a set of hypervisor calls (a.k.a. hcalls) defined in the Linux on Power
|
||||
Architecture Reference ([LoPAR]_) document. This document is a subset of the
|
||||
Power Architecture Platform Reference (PAPR+) specification (IBM internal only),
|
||||
which is what PowerVM, the IBM proprietary hypervisor, adheres to.
|
||||
|
||||
The subset in LoPAR is selected based on the requirements of Linux as a guest.
|
||||
|
||||
@ -18,8 +17,8 @@ running in the guest and QEMU.
|
||||
All those hypercalls start at hcall number 0xf000 which correspond
|
||||
to an implementation specific range in PAPR.
|
||||
|
||||
H_RTAS (0xf000)
|
||||
^^^^^^^^^^^^^^^
|
||||
``H_RTAS (0xf000)``
|
||||
===================
|
||||
|
||||
RTAS stands for Run-Time Abstraction Sercies and is a set of runtime services
|
||||
generally provided by the firmware inside the guest to the operating system. It
|
||||
@ -44,8 +43,8 @@ Returns:
|
||||
|
||||
``H_PARAMETER``: Unknown token.
|
||||
|
||||
H_LOGICAL_MEMOP (0xf001)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
``H_LOGICAL_MEMOP (0xf001)``
|
||||
============================
|
||||
|
||||
When the guest runs in "real mode" (in powerpc terminology this means with MMU
|
||||
disabled, i.e. guest effective address equals to guest physical address), it
|
||||
|
@ -1,19 +1,18 @@
|
||||
===================================
|
||||
pSeries family boards (``pseries``)
|
||||
===================================
|
||||
|
||||
The Power machine para-virtualized environment described by the `Linux on Power
|
||||
Architecture Reference document (LoPAR)
|
||||
<https://openpowerfoundation.org/wp-content/uploads/2020/07/LoPAR-20200812.pdf>`_
|
||||
is called pSeries. This environment is also known as sPAPR, System p guests, or
|
||||
simply Power Linux guests (although it is capable of running other operating
|
||||
systems, such as AIX).
|
||||
The Power machine para-virtualized environment described by the Linux on Power
|
||||
Architecture Reference ([LoPAR]_) document is called pSeries. This environment
|
||||
is also known as sPAPR, System p guests, or simply Power Linux guests (although
|
||||
it is capable of running other operating systems, such as AIX).
|
||||
|
||||
Even though pSeries is designed to behave as a guest environment, it is also
|
||||
capable of acting as a hypervisor OS, providing, on that role, nested
|
||||
virtualization capabilities.
|
||||
|
||||
Supported devices
|
||||
-----------------
|
||||
=================
|
||||
|
||||
* Multi processor support for many Power processors generations: POWER7,
|
||||
POWER7+, POWER8, POWER8NVL, POWER9, and Power10. Support for POWER5+ exists,
|
||||
@ -26,12 +25,12 @@ Supported devices
|
||||
* PCIe device pass through.
|
||||
|
||||
Missing devices
|
||||
---------------
|
||||
===============
|
||||
|
||||
* SPICE support.
|
||||
|
||||
Firmware
|
||||
--------
|
||||
========
|
||||
|
||||
`SLOF <https://github.com/aik/SLOF>`_ (Slimline Open Firmware) is an
|
||||
implementation of the `IEEE 1275-1994, Standard for Boot (Initialization
|
||||
@ -42,14 +41,14 @@ QEMU includes a prebuilt image of SLOF which is updated when a more recent
|
||||
version is required.
|
||||
|
||||
Build directions
|
||||
----------------
|
||||
================
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./configure --target-list=ppc64-softmmu && make
|
||||
|
||||
Running instructions
|
||||
--------------------
|
||||
====================
|
||||
|
||||
Someone can select the pSeries machine type by running QEMU with the following
|
||||
options:
|
||||
@ -59,7 +58,7 @@ options:
|
||||
qemu-system-ppc64 -M pseries <other QEMU arguments>
|
||||
|
||||
sPAPR devices
|
||||
-------------
|
||||
=============
|
||||
|
||||
The sPAPR specification defines a set of para-virtualized devices, which are
|
||||
also supported by the pSeries machine in QEMU and can be instantiated with the
|
||||
@ -102,11 +101,9 @@ device, or specify one with an ID
|
||||
NVRAM device with ``-global spapr-nvram.drive=pfid``.
|
||||
|
||||
sPAPR specification
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
-------------------
|
||||
|
||||
The main source of documentation on the sPAPR standard is the `Linux on Power
|
||||
Architecture Reference document (LoPAR)
|
||||
<https://openpowerfoundation.org/wp-content/uploads/2020/07/LoPAR-20200812.pdf>`_.
|
||||
The main source of documentation on the sPAPR standard is the [LoPAR]_ document.
|
||||
However, documentation specific to QEMU's implementation of the specification
|
||||
can also be found in QEMU documentation:
|
||||
|
||||
@ -124,7 +121,7 @@ Other documentation available in QEMU docs directory:
|
||||
(``/docs/specs/ppc-spapr-uv-hcalls.txt``).
|
||||
|
||||
Switching between the KVM-PR and KVM-HV kernel module
|
||||
-----------------------------------------------------
|
||||
=====================================================
|
||||
|
||||
Currently, there are two implementations of KVM on Power, ``kvm_hv.ko`` and
|
||||
``kvm_pr.ko``.
|
||||
@ -139,7 +136,7 @@ possible to switch between the two modes with the ``kvm-type`` parameter:
|
||||
instead.
|
||||
|
||||
KVM-PR
|
||||
^^^^^^
|
||||
------
|
||||
|
||||
KVM-PR uses the so-called **PR**\ oblem state of the PPC CPUs to run the guests,
|
||||
i.e. the virtual machine is run in user mode and all privileged instructions
|
||||
@ -166,7 +163,7 @@ In order to run KVM-PR guests with POWER9 processors, someone will need to start
|
||||
QEMU with ``kernel_irqchip=off`` command line option.
|
||||
|
||||
KVM-HV
|
||||
^^^^^^
|
||||
------
|
||||
|
||||
KVM-HV uses the hypervisor mode of more recent Power processors, that allow
|
||||
access to the bare metal hardware directly. Although POWER7 had this capability,
|
||||
@ -188,7 +185,7 @@ CPUs generations, e.g. you can run a POWER7 guest on a POWER8 host by using
|
||||
``-cpu POWER8,compat=power7`` as parameter to QEMU.
|
||||
|
||||
Modules support
|
||||
---------------
|
||||
===============
|
||||
|
||||
As noticed in the sections above, each module can run in a different
|
||||
environment. The following table shows with which environment each module can
|
||||
@ -230,9 +227,45 @@ nested. Combinations not shown in the table are not available.
|
||||
|
||||
.. [3] Introduced on Power10 machines.
|
||||
|
||||
|
||||
POWER (PAPR) Protected Execution Facility (PEF)
|
||||
-----------------------------------------------
|
||||
|
||||
Protected Execution Facility (PEF), also known as Secure Guest support
|
||||
is a feature found on IBM POWER9 and POWER10 processors.
|
||||
|
||||
If a suitable firmware including an Ultravisor is installed, it adds
|
||||
an extra memory protection mode to the CPU. The ultravisor manages a
|
||||
pool of secure memory which cannot be accessed by the hypervisor.
|
||||
|
||||
When this feature is enabled in QEMU, a guest can use ultracalls to
|
||||
enter "secure mode". This transfers most of its memory to secure
|
||||
memory, where it cannot be eavesdropped by a compromised hypervisor.
|
||||
|
||||
Launching
|
||||
^^^^^^^^^
|
||||
|
||||
To launch a guest which will be permitted to enter PEF secure mode::
|
||||
|
||||
$ qemu-system-ppc64 \
|
||||
-object pef-guest,id=pef0 \
|
||||
-machine confidential-guest-support=pef0 \
|
||||
...
|
||||
|
||||
Live Migration
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
Live migration is not yet implemented for PEF guests. For
|
||||
consistency, QEMU currently prevents migration if the PEF feature is
|
||||
enabled, whether or not the guest has actually entered secure mode.
|
||||
|
||||
|
||||
Maintainer contact information
|
||||
------------------------------
|
||||
==============================
|
||||
|
||||
Cédric Le Goater <clg@kaod.org>
|
||||
|
||||
Daniel Henrique Barboza <danielhb413@gmail.com>
|
||||
|
||||
.. [LoPAR] `Linux on Power Architecture Reference document (LoPAR) revision
|
||||
2.9 <https://openpowerfoundation.org/wp-content/uploads/2020/07/LoPAR-20200812.pdf>`_.
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qom/object.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
#define phb3_error(phb, fmt, ...) \
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "phb3[%d:%d]: " fmt "\n", \
|
||||
@ -981,10 +982,6 @@ static void pnv_phb3_instance_init(Object *obj)
|
||||
/* Power Bus Common Queue */
|
||||
object_initialize_child(obj, "pbcq", &phb->pbcq, TYPE_PNV_PBCQ);
|
||||
|
||||
/* Root Port */
|
||||
object_initialize_child(obj, "root", &phb->root, TYPE_PNV_PHB3_ROOT_PORT);
|
||||
qdev_prop_set_int32(DEVICE(&phb->root), "addr", PCI_DEVFN(0, 0));
|
||||
qdev_prop_set_bit(DEVICE(&phb->root), "multifunction", false);
|
||||
}
|
||||
|
||||
static void pnv_phb3_realize(DeviceState *dev, Error **errp)
|
||||
@ -994,6 +991,30 @@ static void pnv_phb3_realize(DeviceState *dev, Error **errp)
|
||||
PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
|
||||
int i;
|
||||
|
||||
/* User created devices */
|
||||
if (!phb->chip) {
|
||||
Error *local_err = NULL;
|
||||
BusState *s;
|
||||
|
||||
phb->chip = pnv_get_chip(pnv, phb->chip_id);
|
||||
if (!phb->chip) {
|
||||
error_setg(errp, "invalid chip id: %d", phb->chip_id);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reparent user created devices to the chip to build
|
||||
* correctly the device tree.
|
||||
*/
|
||||
pnv_chip_parent_fixup(phb->chip, OBJECT(phb), phb->phb_id);
|
||||
|
||||
s = qdev_get_parent_bus(DEVICE(phb->chip));
|
||||
if (!qdev_set_parent_bus(DEVICE(phb), s, &local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (phb->phb_id >= PNV_CHIP_GET_CLASS(phb->chip)->num_phbs) {
|
||||
error_setg(errp, "invalid PHB index: %d", phb->phb_id);
|
||||
return;
|
||||
@ -1053,10 +1074,10 @@ static void pnv_phb3_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
pci_setup_iommu(pci->bus, pnv_phb3_dma_iommu, phb);
|
||||
|
||||
/* Add a single Root port */
|
||||
qdev_prop_set_uint8(DEVICE(&phb->root), "chassis", phb->chip_id);
|
||||
qdev_prop_set_uint16(DEVICE(&phb->root), "slot", phb->phb_id);
|
||||
qdev_realize(DEVICE(&phb->root), BUS(pci->bus), &error_fatal);
|
||||
if (defaults_enabled()) {
|
||||
pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb),
|
||||
TYPE_PNV_PHB3_ROOT_PORT);
|
||||
}
|
||||
}
|
||||
|
||||
void pnv_phb3_update_regions(PnvPHB3 *phb)
|
||||
@ -1107,7 +1128,7 @@ static void pnv_phb3_class_init(ObjectClass *klass, void *data)
|
||||
dc->realize = pnv_phb3_realize;
|
||||
device_class_set_props(dc, pnv_phb3_properties);
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
dc->user_creatable = false;
|
||||
dc->user_creatable = true;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_phb3_type_info = {
|
||||
@ -1142,8 +1163,24 @@ static const TypeInfo pnv_phb3_root_bus_info = {
|
||||
static void pnv_phb3_root_port_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev);
|
||||
PCIDevice *pci = PCI_DEVICE(dev);
|
||||
PCIBus *bus = pci_get_bus(pci);
|
||||
PnvPHB3 *phb = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
phb = (PnvPHB3 *) object_dynamic_cast(OBJECT(bus->qbus.parent),
|
||||
TYPE_PNV_PHB3);
|
||||
|
||||
if (!phb) {
|
||||
error_setg(errp,
|
||||
"pnv_phb3_root_port devices must be connected to pnv-phb3 buses");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set unique chassis/slot values for the root port */
|
||||
qdev_prop_set_uint8(&pci->qdev, "chassis", phb->chip_id);
|
||||
qdev_prop_set_uint16(&pci->qdev, "slot", phb->phb_id);
|
||||
|
||||
rpc->parent_realize(dev, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@ -1161,7 +1198,7 @@ static void pnv_phb3_root_port_class_init(ObjectClass *klass, void *data)
|
||||
|
||||
device_class_set_parent_realize(dc, pnv_phb3_root_port_realize,
|
||||
&rpc->parent_realize);
|
||||
dc->user_creatable = false;
|
||||
dc->user_creatable = true;
|
||||
|
||||
k->vendor_id = PCI_VENDOR_ID_IBM;
|
||||
k->device_id = 0x03dc;
|
||||
|
@ -22,12 +22,17 @@
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qom/object.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define phb_error(phb, fmt, ...) \
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "phb4[%d:%d]: " fmt "\n", \
|
||||
(phb)->chip_id, (phb)->phb_id, ## __VA_ARGS__)
|
||||
|
||||
#define phb_pec_error(pec, fmt, ...) \
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "phb4_pec[%d:%d]: " fmt "\n", \
|
||||
(pec)->chip_id, (pec)->index, ## __VA_ARGS__)
|
||||
|
||||
/*
|
||||
* QEMU version of the GETFIELD/SETFIELD macros
|
||||
*
|
||||
@ -151,7 +156,10 @@ static void pnv_phb4_rc_config_write(PnvPHB4 *phb, unsigned off,
|
||||
}
|
||||
|
||||
pdev = pci_find_device(pci->bus, 0, 0);
|
||||
assert(pdev);
|
||||
if (!pdev) {
|
||||
phb_error(phb, "rc_config_write device not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pci_host_config_write_common(pdev, off, PHB_RC_CONFIG_SIZE,
|
||||
bswap32(val), 4);
|
||||
@ -170,7 +178,10 @@ static uint64_t pnv_phb4_rc_config_read(PnvPHB4 *phb, unsigned off,
|
||||
}
|
||||
|
||||
pdev = pci_find_device(pci->bus, 0, 0);
|
||||
assert(pdev);
|
||||
if (!pdev) {
|
||||
phb_error(phb, "rc_config_read device not found\n");
|
||||
return ~0ull;
|
||||
}
|
||||
|
||||
val = pci_host_config_read_common(pdev, off, PHB_RC_CONFIG_SIZE, 4);
|
||||
return bswap32(val);
|
||||
@ -847,6 +858,284 @@ const MemoryRegionOps pnv_phb4_xscom_ops = {
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static uint64_t pnv_pec_stk_nest_xscom_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
/* TODO: add list of allowed registers and error out if not */
|
||||
return stack->nest_regs[reg];
|
||||
}
|
||||
|
||||
static void pnv_phb4_update_regions(PnvPhb4PecStack *stack)
|
||||
{
|
||||
PnvPHB4 *phb = stack->phb;
|
||||
|
||||
/* Unmap first always */
|
||||
if (memory_region_is_mapped(&phb->mr_regs)) {
|
||||
memory_region_del_subregion(&stack->phbbar, &phb->mr_regs);
|
||||
}
|
||||
if (memory_region_is_mapped(&phb->xsrc.esb_mmio)) {
|
||||
memory_region_del_subregion(&stack->intbar, &phb->xsrc.esb_mmio);
|
||||
}
|
||||
|
||||
/* Map registers if enabled */
|
||||
if (memory_region_is_mapped(&stack->phbbar)) {
|
||||
memory_region_add_subregion(&stack->phbbar, 0, &phb->mr_regs);
|
||||
}
|
||||
|
||||
/* Map ESB if enabled */
|
||||
if (memory_region_is_mapped(&stack->intbar)) {
|
||||
memory_region_add_subregion(&stack->intbar, 0, &phb->xsrc.esb_mmio);
|
||||
}
|
||||
|
||||
/* Check/update m32 */
|
||||
pnv_phb4_check_all_mbt(phb);
|
||||
}
|
||||
|
||||
static void pnv_pec_stk_update_map(PnvPhb4PecStack *stack)
|
||||
{
|
||||
PnvPhb4PecState *pec = stack->pec;
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
uint64_t bar_en = stack->nest_regs[PEC_NEST_STK_BAR_EN];
|
||||
uint64_t bar, mask, size;
|
||||
char name[64];
|
||||
|
||||
/*
|
||||
* NOTE: This will really not work well if those are remapped
|
||||
* after the PHB has created its sub regions. We could do better
|
||||
* if we had a way to resize regions but we don't really care
|
||||
* that much in practice as the stuff below really only happens
|
||||
* once early during boot
|
||||
*/
|
||||
|
||||
/* Handle unmaps */
|
||||
if (memory_region_is_mapped(&stack->mmbar0) &&
|
||||
!(bar_en & PEC_NEST_STK_BAR_EN_MMIO0)) {
|
||||
memory_region_del_subregion(sysmem, &stack->mmbar0);
|
||||
}
|
||||
if (memory_region_is_mapped(&stack->mmbar1) &&
|
||||
!(bar_en & PEC_NEST_STK_BAR_EN_MMIO1)) {
|
||||
memory_region_del_subregion(sysmem, &stack->mmbar1);
|
||||
}
|
||||
if (memory_region_is_mapped(&stack->phbbar) &&
|
||||
!(bar_en & PEC_NEST_STK_BAR_EN_PHB)) {
|
||||
memory_region_del_subregion(sysmem, &stack->phbbar);
|
||||
}
|
||||
if (memory_region_is_mapped(&stack->intbar) &&
|
||||
!(bar_en & PEC_NEST_STK_BAR_EN_INT)) {
|
||||
memory_region_del_subregion(sysmem, &stack->intbar);
|
||||
}
|
||||
|
||||
/* Update PHB */
|
||||
pnv_phb4_update_regions(stack);
|
||||
|
||||
/* Handle maps */
|
||||
if (!memory_region_is_mapped(&stack->mmbar0) &&
|
||||
(bar_en & PEC_NEST_STK_BAR_EN_MMIO0)) {
|
||||
bar = stack->nest_regs[PEC_NEST_STK_MMIO_BAR0] >> 8;
|
||||
mask = stack->nest_regs[PEC_NEST_STK_MMIO_BAR0_MASK];
|
||||
size = ((~mask) >> 8) + 1;
|
||||
snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-mmio0",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
memory_region_init(&stack->mmbar0, OBJECT(stack), name, size);
|
||||
memory_region_add_subregion(sysmem, bar, &stack->mmbar0);
|
||||
stack->mmio0_base = bar;
|
||||
stack->mmio0_size = size;
|
||||
}
|
||||
if (!memory_region_is_mapped(&stack->mmbar1) &&
|
||||
(bar_en & PEC_NEST_STK_BAR_EN_MMIO1)) {
|
||||
bar = stack->nest_regs[PEC_NEST_STK_MMIO_BAR1] >> 8;
|
||||
mask = stack->nest_regs[PEC_NEST_STK_MMIO_BAR1_MASK];
|
||||
size = ((~mask) >> 8) + 1;
|
||||
snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-mmio1",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
memory_region_init(&stack->mmbar1, OBJECT(stack), name, size);
|
||||
memory_region_add_subregion(sysmem, bar, &stack->mmbar1);
|
||||
stack->mmio1_base = bar;
|
||||
stack->mmio1_size = size;
|
||||
}
|
||||
if (!memory_region_is_mapped(&stack->phbbar) &&
|
||||
(bar_en & PEC_NEST_STK_BAR_EN_PHB)) {
|
||||
bar = stack->nest_regs[PEC_NEST_STK_PHB_REGS_BAR] >> 8;
|
||||
size = PNV_PHB4_NUM_REGS << 3;
|
||||
snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-phb",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
memory_region_init(&stack->phbbar, OBJECT(stack), name, size);
|
||||
memory_region_add_subregion(sysmem, bar, &stack->phbbar);
|
||||
}
|
||||
if (!memory_region_is_mapped(&stack->intbar) &&
|
||||
(bar_en & PEC_NEST_STK_BAR_EN_INT)) {
|
||||
bar = stack->nest_regs[PEC_NEST_STK_INT_BAR] >> 8;
|
||||
size = PNV_PHB4_MAX_INTs << 16;
|
||||
snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-int",
|
||||
stack->pec->chip_id, stack->pec->index, stack->stack_no);
|
||||
memory_region_init(&stack->intbar, OBJECT(stack), name, size);
|
||||
memory_region_add_subregion(sysmem, bar, &stack->intbar);
|
||||
}
|
||||
|
||||
/* Update PHB */
|
||||
pnv_phb4_update_regions(stack);
|
||||
}
|
||||
|
||||
static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
|
||||
PnvPhb4PecState *pec = stack->pec;
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
switch (reg) {
|
||||
case PEC_NEST_STK_PCI_NEST_FIR:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] = val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_CLR:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] &= val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_SET:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] |= val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_MSK:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] = val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_MSKC:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] &= val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_MSKS:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] |= val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_ACT0:
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_ACT1:
|
||||
stack->nest_regs[reg] = val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_WOF:
|
||||
stack->nest_regs[reg] = 0;
|
||||
break;
|
||||
case PEC_NEST_STK_ERR_REPORT_0:
|
||||
case PEC_NEST_STK_ERR_REPORT_1:
|
||||
case PEC_NEST_STK_PBCQ_GNRL_STATUS:
|
||||
/* Flag error ? */
|
||||
break;
|
||||
case PEC_NEST_STK_PBCQ_MODE:
|
||||
stack->nest_regs[reg] = val & 0xff00000000000000ull;
|
||||
break;
|
||||
case PEC_NEST_STK_MMIO_BAR0:
|
||||
case PEC_NEST_STK_MMIO_BAR0_MASK:
|
||||
case PEC_NEST_STK_MMIO_BAR1:
|
||||
case PEC_NEST_STK_MMIO_BAR1_MASK:
|
||||
if (stack->nest_regs[PEC_NEST_STK_BAR_EN] &
|
||||
(PEC_NEST_STK_BAR_EN_MMIO0 |
|
||||
PEC_NEST_STK_BAR_EN_MMIO1)) {
|
||||
phb_pec_error(pec, "Changing enabled BAR unsupported\n");
|
||||
}
|
||||
stack->nest_regs[reg] = val & 0xffffffffff000000ull;
|
||||
break;
|
||||
case PEC_NEST_STK_PHB_REGS_BAR:
|
||||
if (stack->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_PHB) {
|
||||
phb_pec_error(pec, "Changing enabled BAR unsupported\n");
|
||||
}
|
||||
stack->nest_regs[reg] = val & 0xffffffffffc00000ull;
|
||||
break;
|
||||
case PEC_NEST_STK_INT_BAR:
|
||||
if (stack->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_INT) {
|
||||
phb_pec_error(pec, "Changing enabled BAR unsupported\n");
|
||||
}
|
||||
stack->nest_regs[reg] = val & 0xfffffff000000000ull;
|
||||
break;
|
||||
case PEC_NEST_STK_BAR_EN:
|
||||
stack->nest_regs[reg] = val & 0xf000000000000000ull;
|
||||
pnv_pec_stk_update_map(stack);
|
||||
break;
|
||||
case PEC_NEST_STK_DATA_FRZ_TYPE:
|
||||
case PEC_NEST_STK_PBCQ_TUN_BAR:
|
||||
/* Not used for now */
|
||||
stack->nest_regs[reg] = val;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "phb4_pec: nest_xscom_write 0x%"HWADDR_PRIx
|
||||
"=%"PRIx64"\n", addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pnv_pec_stk_nest_xscom_ops = {
|
||||
.read = pnv_pec_stk_nest_xscom_read,
|
||||
.write = pnv_pec_stk_nest_xscom_write,
|
||||
.valid.min_access_size = 8,
|
||||
.valid.max_access_size = 8,
|
||||
.impl.min_access_size = 8,
|
||||
.impl.max_access_size = 8,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static uint64_t pnv_pec_stk_pci_xscom_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
/* TODO: add list of allowed registers and error out if not */
|
||||
return stack->pci_regs[reg];
|
||||
}
|
||||
|
||||
static void pnv_pec_stk_pci_xscom_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
switch (reg) {
|
||||
case PEC_PCI_STK_PCI_FIR:
|
||||
stack->pci_regs[reg] = val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_CLR:
|
||||
stack->pci_regs[PEC_PCI_STK_PCI_FIR] &= val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_SET:
|
||||
stack->pci_regs[PEC_PCI_STK_PCI_FIR] |= val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_MSK:
|
||||
stack->pci_regs[reg] = val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_MSKC:
|
||||
stack->pci_regs[PEC_PCI_STK_PCI_FIR_MSK] &= val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_MSKS:
|
||||
stack->pci_regs[PEC_PCI_STK_PCI_FIR_MSK] |= val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_ACT0:
|
||||
case PEC_PCI_STK_PCI_FIR_ACT1:
|
||||
stack->pci_regs[reg] = val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_WOF:
|
||||
stack->pci_regs[reg] = 0;
|
||||
break;
|
||||
case PEC_PCI_STK_ETU_RESET:
|
||||
stack->pci_regs[reg] = val & 0x8000000000000000ull;
|
||||
/* TODO: Implement reset */
|
||||
break;
|
||||
case PEC_PCI_STK_PBAIB_ERR_REPORT:
|
||||
break;
|
||||
case PEC_PCI_STK_PBAIB_TX_CMD_CRED:
|
||||
case PEC_PCI_STK_PBAIB_TX_DAT_CRED:
|
||||
stack->pci_regs[reg] = val;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "phb4_pec_stk: pci_xscom_write 0x%"HWADDR_PRIx
|
||||
"=%"PRIx64"\n", addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pnv_pec_stk_pci_xscom_ops = {
|
||||
.read = pnv_pec_stk_pci_xscom_read,
|
||||
.write = pnv_pec_stk_pci_xscom_write,
|
||||
.valid.min_access_size = 8,
|
||||
.valid.max_access_size = 8,
|
||||
.impl.min_access_size = 8,
|
||||
.impl.max_access_size = 8,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static int pnv_phb4_map_irq(PCIDevice *pci_dev, int irq_num)
|
||||
{
|
||||
/* Check that out properly ... */
|
||||
@ -1062,6 +1351,23 @@ static const TypeInfo pnv_phb4_iommu_memory_region_info = {
|
||||
.class_init = pnv_phb4_iommu_memory_region_class_init,
|
||||
};
|
||||
|
||||
/*
|
||||
* Return the index/phb-id of a PHB4 that belongs to a
|
||||
* pec->stacks[stack_index] stack.
|
||||
*/
|
||||
int pnv_phb4_pec_get_phb_id(PnvPhb4PecState *pec, int stack_index)
|
||||
{
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
|
||||
int index = pec->index;
|
||||
int offset = 0;
|
||||
|
||||
while (index--) {
|
||||
offset += pecc->num_stacks[index];
|
||||
}
|
||||
|
||||
return offset + stack_index;
|
||||
}
|
||||
|
||||
/*
|
||||
* MSI/MSIX memory region implementation.
|
||||
* The handler handles both MSI and MSIX.
|
||||
@ -1151,6 +1457,52 @@ static AddressSpace *pnv_phb4_dma_iommu(PCIBus *bus, void *opaque, int devfn)
|
||||
return &ds->dma_as;
|
||||
}
|
||||
|
||||
static void pnv_phb4_xscom_realize(PnvPHB4 *phb)
|
||||
{
|
||||
PnvPhb4PecStack *stack = phb->stack;
|
||||
PnvPhb4PecState *pec = stack->pec;
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
|
||||
uint32_t pec_nest_base;
|
||||
uint32_t pec_pci_base;
|
||||
char name[64];
|
||||
|
||||
assert(pec);
|
||||
|
||||
/* Initialize the XSCOM regions for the stack registers */
|
||||
snprintf(name, sizeof(name), "xscom-pec-%d.%d-nest-stack-%d",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
pnv_xscom_region_init(&stack->nest_regs_mr, OBJECT(stack),
|
||||
&pnv_pec_stk_nest_xscom_ops, stack, name,
|
||||
PHB4_PEC_NEST_STK_REGS_COUNT);
|
||||
|
||||
snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-stack-%d",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
pnv_xscom_region_init(&stack->pci_regs_mr, OBJECT(stack),
|
||||
&pnv_pec_stk_pci_xscom_ops, stack, name,
|
||||
PHB4_PEC_PCI_STK_REGS_COUNT);
|
||||
|
||||
/* PHB pass-through */
|
||||
snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-stack-%d-phb",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
pnv_xscom_region_init(&stack->phb_regs_mr, OBJECT(phb),
|
||||
&pnv_phb4_xscom_ops, phb, name, 0x40);
|
||||
|
||||
pec_nest_base = pecc->xscom_nest_base(pec);
|
||||
pec_pci_base = pecc->xscom_pci_base(pec);
|
||||
|
||||
/* Populate the XSCOM address space. */
|
||||
pnv_xscom_add_subregion(pec->chip,
|
||||
pec_nest_base + 0x40 * (stack->stack_no + 1),
|
||||
&stack->nest_regs_mr);
|
||||
pnv_xscom_add_subregion(pec->chip,
|
||||
pec_pci_base + 0x40 * (stack->stack_no + 1),
|
||||
&stack->pci_regs_mr);
|
||||
pnv_xscom_add_subregion(pec->chip,
|
||||
pec_pci_base + PNV9_XSCOM_PEC_PCI_STK0 +
|
||||
0x40 * stack->stack_no,
|
||||
&stack->phb_regs_mr);
|
||||
}
|
||||
|
||||
static void pnv_phb4_instance_init(Object *obj)
|
||||
{
|
||||
PnvPHB4 *phb = PNV_PHB4(obj);
|
||||
@ -1159,12 +1511,35 @@ static void pnv_phb4_instance_init(Object *obj)
|
||||
|
||||
/* XIVE interrupt source object */
|
||||
object_initialize_child(obj, "source", &phb->xsrc, TYPE_XIVE_SOURCE);
|
||||
}
|
||||
|
||||
/* Root Port */
|
||||
object_initialize_child(obj, "root", &phb->root, TYPE_PNV_PHB4_ROOT_PORT);
|
||||
static PnvPhb4PecStack *pnv_phb4_get_stack(PnvChip *chip, PnvPHB4 *phb,
|
||||
Error **errp)
|
||||
{
|
||||
Pnv9Chip *chip9 = PNV9_CHIP(chip);
|
||||
int chip_id = phb->chip_id;
|
||||
int index = phb->phb_id;
|
||||
int i, j;
|
||||
|
||||
qdev_prop_set_int32(DEVICE(&phb->root), "addr", PCI_DEVFN(0, 0));
|
||||
qdev_prop_set_bit(DEVICE(&phb->root), "multifunction", false);
|
||||
for (i = 0; i < chip->num_pecs; i++) {
|
||||
/*
|
||||
* For each PEC, check the amount of stacks it supports
|
||||
* and see if the given phb4 index matches a stack.
|
||||
*/
|
||||
PnvPhb4PecState *pec = &chip9->pecs[i];
|
||||
|
||||
for (j = 0; j < pec->num_stacks; j++) {
|
||||
if (index == pnv_phb4_pec_get_phb_id(pec, j)) {
|
||||
return &pec->stacks[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error_setg(errp,
|
||||
"pnv-phb4 chip-id %d index %d didn't match any existing PEC",
|
||||
chip_id, index);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void pnv_phb4_realize(DeviceState *dev, Error **errp)
|
||||
@ -1172,10 +1547,51 @@ static void pnv_phb4_realize(DeviceState *dev, Error **errp)
|
||||
PnvPHB4 *phb = PNV_PHB4(dev);
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
|
||||
XiveSource *xsrc = &phb->xsrc;
|
||||
Error *local_err = NULL;
|
||||
int nr_irqs;
|
||||
char name[32];
|
||||
|
||||
assert(phb->stack);
|
||||
/* User created PHB */
|
||||
if (!phb->stack) {
|
||||
PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
|
||||
PnvChip *chip = pnv_get_chip(pnv, phb->chip_id);
|
||||
PnvPhb4PecClass *pecc;
|
||||
BusState *s;
|
||||
|
||||
if (!chip) {
|
||||
error_setg(errp, "invalid chip id: %d", phb->chip_id);
|
||||
return;
|
||||
}
|
||||
|
||||
phb->stack = pnv_phb4_get_stack(chip, phb, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
/* All other phb properties but 'version' are already set */
|
||||
pecc = PNV_PHB4_PEC_GET_CLASS(phb->stack->pec);
|
||||
object_property_set_int(OBJECT(phb), "version", pecc->version,
|
||||
&error_fatal);
|
||||
|
||||
/*
|
||||
* Assign stack->phb since pnv_phb4_update_regions() uses it
|
||||
* to access the phb.
|
||||
*/
|
||||
phb->stack->phb = phb;
|
||||
|
||||
/*
|
||||
* Reparent user created devices to the chip to build
|
||||
* correctly the device tree.
|
||||
*/
|
||||
pnv_chip_parent_fixup(chip, OBJECT(phb), phb->phb_id);
|
||||
|
||||
s = qdev_get_parent_bus(DEVICE(chip));
|
||||
if (!qdev_set_parent_bus(DEVICE(phb), s, &local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the "big_phb" flag */
|
||||
phb->big_phb = phb->phb_id == 0 || phb->phb_id == 3;
|
||||
@ -1208,10 +1624,11 @@ static void pnv_phb4_realize(DeviceState *dev, Error **errp)
|
||||
pci_setup_iommu(pci->bus, pnv_phb4_dma_iommu, phb);
|
||||
pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE;
|
||||
|
||||
/* Add a single Root port */
|
||||
qdev_prop_set_uint8(DEVICE(&phb->root), "chassis", phb->chip_id);
|
||||
qdev_prop_set_uint16(DEVICE(&phb->root), "slot", phb->phb_id);
|
||||
qdev_realize(DEVICE(&phb->root), BUS(pci->bus), &error_fatal);
|
||||
/* Add a single Root port if running with defaults */
|
||||
if (defaults_enabled()) {
|
||||
pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb),
|
||||
TYPE_PNV_PHB4_ROOT_PORT);
|
||||
}
|
||||
|
||||
/* Setup XIVE Source */
|
||||
if (phb->big_phb) {
|
||||
@ -1228,6 +1645,8 @@ static void pnv_phb4_realize(DeviceState *dev, Error **errp)
|
||||
pnv_phb4_update_xsrc(phb);
|
||||
|
||||
phb->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc, xsrc->nr_irqs);
|
||||
|
||||
pnv_phb4_xscom_realize(phb);
|
||||
}
|
||||
|
||||
static const char *pnv_phb4_root_bus_path(PCIHostState *host_bridge,
|
||||
@ -1277,7 +1696,7 @@ static void pnv_phb4_class_init(ObjectClass *klass, void *data)
|
||||
dc->realize = pnv_phb4_realize;
|
||||
device_class_set_props(dc, pnv_phb4_properties);
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
dc->user_creatable = false;
|
||||
dc->user_creatable = true;
|
||||
|
||||
xfc->notify = pnv_phb4_xive_notify;
|
||||
}
|
||||
@ -1338,8 +1757,23 @@ static void pnv_phb4_root_port_reset(DeviceState *dev)
|
||||
static void pnv_phb4_root_port_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev);
|
||||
PCIDevice *pci = PCI_DEVICE(dev);
|
||||
PCIBus *bus = pci_get_bus(pci);
|
||||
PnvPHB4 *phb = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
phb = (PnvPHB4 *) object_dynamic_cast(OBJECT(bus->qbus.parent),
|
||||
TYPE_PNV_PHB4);
|
||||
|
||||
if (!phb) {
|
||||
error_setg(errp, "%s must be connected to pnv-phb4 buses", dev->id);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set unique chassis/slot values for the root port */
|
||||
qdev_prop_set_uint8(&pci->qdev, "chassis", phb->chip_id);
|
||||
qdev_prop_set_uint16(&pci->qdev, "slot", phb->phb_id);
|
||||
|
||||
rpc->parent_realize(dev, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@ -1354,7 +1788,7 @@ static void pnv_phb4_root_port_class_init(ObjectClass *klass, void *data)
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass);
|
||||
|
||||
dc->desc = "IBM PHB4 PCIE Root Port";
|
||||
dc->user_creatable = false;
|
||||
dc->user_creatable = true;
|
||||
|
||||
device_class_set_parent_realize(dc, pnv_phb4_root_port_realize,
|
||||
&rpc->parent_realize);
|
||||
@ -1388,32 +1822,6 @@ static void pnv_phb4_register_types(void)
|
||||
|
||||
type_init(pnv_phb4_register_types);
|
||||
|
||||
void pnv_phb4_update_regions(PnvPhb4PecStack *stack)
|
||||
{
|
||||
PnvPHB4 *phb = &stack->phb;
|
||||
|
||||
/* Unmap first always */
|
||||
if (memory_region_is_mapped(&phb->mr_regs)) {
|
||||
memory_region_del_subregion(&stack->phbbar, &phb->mr_regs);
|
||||
}
|
||||
if (memory_region_is_mapped(&phb->xsrc.esb_mmio)) {
|
||||
memory_region_del_subregion(&stack->intbar, &phb->xsrc.esb_mmio);
|
||||
}
|
||||
|
||||
/* Map registers if enabled */
|
||||
if (memory_region_is_mapped(&stack->phbbar)) {
|
||||
memory_region_add_subregion(&stack->phbbar, 0, &phb->mr_regs);
|
||||
}
|
||||
|
||||
/* Map ESB if enabled */
|
||||
if (memory_region_is_mapped(&stack->intbar)) {
|
||||
memory_region_add_subregion(&stack->intbar, 0, &phb->xsrc.esb_mmio);
|
||||
}
|
||||
|
||||
/* Check/update m32 */
|
||||
pnv_phb4_check_all_mbt(phb);
|
||||
}
|
||||
|
||||
void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon)
|
||||
{
|
||||
uint32_t offset = phb->regs[PHB_INT_NOTIFY_INDEX >> 3];
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "hw/pci/pci_bus.h"
|
||||
#include "hw/ppc/pnv.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
@ -111,258 +112,6 @@ static const MemoryRegionOps pnv_pec_pci_xscom_ops = {
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static uint64_t pnv_pec_stk_nest_xscom_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
/* TODO: add list of allowed registers and error out if not */
|
||||
return stack->nest_regs[reg];
|
||||
}
|
||||
|
||||
static void pnv_pec_stk_update_map(PnvPhb4PecStack *stack)
|
||||
{
|
||||
PnvPhb4PecState *pec = stack->pec;
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
uint64_t bar_en = stack->nest_regs[PEC_NEST_STK_BAR_EN];
|
||||
uint64_t bar, mask, size;
|
||||
char name[64];
|
||||
|
||||
/*
|
||||
* NOTE: This will really not work well if those are remapped
|
||||
* after the PHB has created its sub regions. We could do better
|
||||
* if we had a way to resize regions but we don't really care
|
||||
* that much in practice as the stuff below really only happens
|
||||
* once early during boot
|
||||
*/
|
||||
|
||||
/* Handle unmaps */
|
||||
if (memory_region_is_mapped(&stack->mmbar0) &&
|
||||
!(bar_en & PEC_NEST_STK_BAR_EN_MMIO0)) {
|
||||
memory_region_del_subregion(sysmem, &stack->mmbar0);
|
||||
}
|
||||
if (memory_region_is_mapped(&stack->mmbar1) &&
|
||||
!(bar_en & PEC_NEST_STK_BAR_EN_MMIO1)) {
|
||||
memory_region_del_subregion(sysmem, &stack->mmbar1);
|
||||
}
|
||||
if (memory_region_is_mapped(&stack->phbbar) &&
|
||||
!(bar_en & PEC_NEST_STK_BAR_EN_PHB)) {
|
||||
memory_region_del_subregion(sysmem, &stack->phbbar);
|
||||
}
|
||||
if (memory_region_is_mapped(&stack->intbar) &&
|
||||
!(bar_en & PEC_NEST_STK_BAR_EN_INT)) {
|
||||
memory_region_del_subregion(sysmem, &stack->intbar);
|
||||
}
|
||||
|
||||
/* Update PHB */
|
||||
pnv_phb4_update_regions(stack);
|
||||
|
||||
/* Handle maps */
|
||||
if (!memory_region_is_mapped(&stack->mmbar0) &&
|
||||
(bar_en & PEC_NEST_STK_BAR_EN_MMIO0)) {
|
||||
bar = stack->nest_regs[PEC_NEST_STK_MMIO_BAR0] >> 8;
|
||||
mask = stack->nest_regs[PEC_NEST_STK_MMIO_BAR0_MASK];
|
||||
size = ((~mask) >> 8) + 1;
|
||||
snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-mmio0",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
memory_region_init(&stack->mmbar0, OBJECT(stack), name, size);
|
||||
memory_region_add_subregion(sysmem, bar, &stack->mmbar0);
|
||||
stack->mmio0_base = bar;
|
||||
stack->mmio0_size = size;
|
||||
}
|
||||
if (!memory_region_is_mapped(&stack->mmbar1) &&
|
||||
(bar_en & PEC_NEST_STK_BAR_EN_MMIO1)) {
|
||||
bar = stack->nest_regs[PEC_NEST_STK_MMIO_BAR1] >> 8;
|
||||
mask = stack->nest_regs[PEC_NEST_STK_MMIO_BAR1_MASK];
|
||||
size = ((~mask) >> 8) + 1;
|
||||
snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-mmio1",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
memory_region_init(&stack->mmbar1, OBJECT(stack), name, size);
|
||||
memory_region_add_subregion(sysmem, bar, &stack->mmbar1);
|
||||
stack->mmio1_base = bar;
|
||||
stack->mmio1_size = size;
|
||||
}
|
||||
if (!memory_region_is_mapped(&stack->phbbar) &&
|
||||
(bar_en & PEC_NEST_STK_BAR_EN_PHB)) {
|
||||
bar = stack->nest_regs[PEC_NEST_STK_PHB_REGS_BAR] >> 8;
|
||||
size = PNV_PHB4_NUM_REGS << 3;
|
||||
snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-phb",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
memory_region_init(&stack->phbbar, OBJECT(stack), name, size);
|
||||
memory_region_add_subregion(sysmem, bar, &stack->phbbar);
|
||||
}
|
||||
if (!memory_region_is_mapped(&stack->intbar) &&
|
||||
(bar_en & PEC_NEST_STK_BAR_EN_INT)) {
|
||||
bar = stack->nest_regs[PEC_NEST_STK_INT_BAR] >> 8;
|
||||
size = PNV_PHB4_MAX_INTs << 16;
|
||||
snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-int",
|
||||
stack->pec->chip_id, stack->pec->index, stack->stack_no);
|
||||
memory_region_init(&stack->intbar, OBJECT(stack), name, size);
|
||||
memory_region_add_subregion(sysmem, bar, &stack->intbar);
|
||||
}
|
||||
|
||||
/* Update PHB */
|
||||
pnv_phb4_update_regions(stack);
|
||||
}
|
||||
|
||||
static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
|
||||
PnvPhb4PecState *pec = stack->pec;
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
switch (reg) {
|
||||
case PEC_NEST_STK_PCI_NEST_FIR:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] = val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_CLR:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] &= val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_SET:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] |= val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_MSK:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] = val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_MSKC:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] &= val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_MSKS:
|
||||
stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] |= val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_ACT0:
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_ACT1:
|
||||
stack->nest_regs[reg] = val;
|
||||
break;
|
||||
case PEC_NEST_STK_PCI_NEST_FIR_WOF:
|
||||
stack->nest_regs[reg] = 0;
|
||||
break;
|
||||
case PEC_NEST_STK_ERR_REPORT_0:
|
||||
case PEC_NEST_STK_ERR_REPORT_1:
|
||||
case PEC_NEST_STK_PBCQ_GNRL_STATUS:
|
||||
/* Flag error ? */
|
||||
break;
|
||||
case PEC_NEST_STK_PBCQ_MODE:
|
||||
stack->nest_regs[reg] = val & 0xff00000000000000ull;
|
||||
break;
|
||||
case PEC_NEST_STK_MMIO_BAR0:
|
||||
case PEC_NEST_STK_MMIO_BAR0_MASK:
|
||||
case PEC_NEST_STK_MMIO_BAR1:
|
||||
case PEC_NEST_STK_MMIO_BAR1_MASK:
|
||||
if (stack->nest_regs[PEC_NEST_STK_BAR_EN] &
|
||||
(PEC_NEST_STK_BAR_EN_MMIO0 |
|
||||
PEC_NEST_STK_BAR_EN_MMIO1)) {
|
||||
phb_pec_error(pec, "Changing enabled BAR unsupported\n");
|
||||
}
|
||||
stack->nest_regs[reg] = val & 0xffffffffff000000ull;
|
||||
break;
|
||||
case PEC_NEST_STK_PHB_REGS_BAR:
|
||||
if (stack->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_PHB) {
|
||||
phb_pec_error(pec, "Changing enabled BAR unsupported\n");
|
||||
}
|
||||
stack->nest_regs[reg] = val & 0xffffffffffc00000ull;
|
||||
break;
|
||||
case PEC_NEST_STK_INT_BAR:
|
||||
if (stack->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_INT) {
|
||||
phb_pec_error(pec, "Changing enabled BAR unsupported\n");
|
||||
}
|
||||
stack->nest_regs[reg] = val & 0xfffffff000000000ull;
|
||||
break;
|
||||
case PEC_NEST_STK_BAR_EN:
|
||||
stack->nest_regs[reg] = val & 0xf000000000000000ull;
|
||||
pnv_pec_stk_update_map(stack);
|
||||
break;
|
||||
case PEC_NEST_STK_DATA_FRZ_TYPE:
|
||||
case PEC_NEST_STK_PBCQ_TUN_BAR:
|
||||
/* Not used for now */
|
||||
stack->nest_regs[reg] = val;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "phb4_pec: nest_xscom_write 0x%"HWADDR_PRIx
|
||||
"=%"PRIx64"\n", addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pnv_pec_stk_nest_xscom_ops = {
|
||||
.read = pnv_pec_stk_nest_xscom_read,
|
||||
.write = pnv_pec_stk_nest_xscom_write,
|
||||
.valid.min_access_size = 8,
|
||||
.valid.max_access_size = 8,
|
||||
.impl.min_access_size = 8,
|
||||
.impl.max_access_size = 8,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static uint64_t pnv_pec_stk_pci_xscom_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
/* TODO: add list of allowed registers and error out if not */
|
||||
return stack->pci_regs[reg];
|
||||
}
|
||||
|
||||
static void pnv_pec_stk_pci_xscom_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
|
||||
uint32_t reg = addr >> 3;
|
||||
|
||||
switch (reg) {
|
||||
case PEC_PCI_STK_PCI_FIR:
|
||||
stack->nest_regs[reg] = val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_CLR:
|
||||
stack->nest_regs[PEC_PCI_STK_PCI_FIR] &= val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_SET:
|
||||
stack->nest_regs[PEC_PCI_STK_PCI_FIR] |= val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_MSK:
|
||||
stack->nest_regs[reg] = val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_MSKC:
|
||||
stack->nest_regs[PEC_PCI_STK_PCI_FIR_MSK] &= val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_MSKS:
|
||||
stack->nest_regs[PEC_PCI_STK_PCI_FIR_MSK] |= val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_ACT0:
|
||||
case PEC_PCI_STK_PCI_FIR_ACT1:
|
||||
stack->nest_regs[reg] = val;
|
||||
break;
|
||||
case PEC_PCI_STK_PCI_FIR_WOF:
|
||||
stack->nest_regs[reg] = 0;
|
||||
break;
|
||||
case PEC_PCI_STK_ETU_RESET:
|
||||
stack->nest_regs[reg] = val & 0x8000000000000000ull;
|
||||
/* TODO: Implement reset */
|
||||
break;
|
||||
case PEC_PCI_STK_PBAIB_ERR_REPORT:
|
||||
break;
|
||||
case PEC_PCI_STK_PBAIB_TX_CMD_CRED:
|
||||
case PEC_PCI_STK_PBAIB_TX_DAT_CRED:
|
||||
stack->nest_regs[reg] = val;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "phb4_pec_stk: pci_xscom_write 0x%"HWADDR_PRIx
|
||||
"=%"PRIx64"\n", addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pnv_pec_stk_pci_xscom_ops = {
|
||||
.read = pnv_pec_stk_pci_xscom_read,
|
||||
.write = pnv_pec_stk_pci_xscom_write,
|
||||
.valid.min_access_size = 8,
|
||||
.valid.max_access_size = 8,
|
||||
.impl.min_access_size = 8,
|
||||
.impl.max_access_size = 8,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static void pnv_pec_instance_init(Object *obj)
|
||||
{
|
||||
PnvPhb4PecState *pec = PNV_PHB4_PEC(obj);
|
||||
@ -374,19 +123,6 @@ static void pnv_pec_instance_init(Object *obj)
|
||||
}
|
||||
}
|
||||
|
||||
static int pnv_pec_phb_offset(PnvPhb4PecState *pec)
|
||||
{
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
|
||||
int index = pec->index;
|
||||
int offset = 0;
|
||||
|
||||
while (index--) {
|
||||
offset += pecc->num_stacks[index];
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static void pnv_pec_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvPhb4PecState *pec = PNV_PHB4_PEC(dev);
|
||||
@ -405,10 +141,8 @@ static void pnv_pec_realize(DeviceState *dev, Error **errp)
|
||||
for (i = 0; i < pec->num_stacks; i++) {
|
||||
PnvPhb4PecStack *stack = &pec->stacks[i];
|
||||
Object *stk_obj = OBJECT(stack);
|
||||
int phb_id = pnv_pec_phb_offset(pec) + i;
|
||||
|
||||
object_property_set_int(stk_obj, "stack-no", i, &error_abort);
|
||||
object_property_set_int(stk_obj, "phb-id", phb_id, &error_abort);
|
||||
object_property_set_link(stk_obj, "pec", OBJECT(pec), &error_abort);
|
||||
if (!qdev_realize(DEVICE(stk_obj), NULL, errp)) {
|
||||
return;
|
||||
@ -462,8 +196,7 @@ static int pnv_pec_dt_xscom(PnvXScomInterface *dev, void *fdt,
|
||||
pecc->compat_size)));
|
||||
|
||||
for (i = 0; i < pec->num_stacks; i++) {
|
||||
PnvPhb4PecStack *stack = &pec->stacks[i];
|
||||
PnvPHB4 *phb = &stack->phb;
|
||||
int phb_id = pnv_phb4_pec_get_phb_id(pec, i);
|
||||
int stk_offset;
|
||||
|
||||
name = g_strdup_printf("stack@%x", i);
|
||||
@ -473,7 +206,7 @@ static int pnv_pec_dt_xscom(PnvXScomInterface *dev, void *fdt,
|
||||
_FDT((fdt_setprop(fdt, stk_offset, "compatible", pecc->stk_compat,
|
||||
pecc->stk_compat_size)));
|
||||
_FDT((fdt_setprop_cell(fdt, stk_offset, "reg", i)));
|
||||
_FDT((fdt_setprop_cell(fdt, stk_offset, "ibm,phb-index", phb->phb_id)));
|
||||
_FDT((fdt_setprop_cell(fdt, stk_offset, "ibm,phb-index", phb_id)));
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -543,69 +276,38 @@ static const TypeInfo pnv_pec_type_info = {
|
||||
}
|
||||
};
|
||||
|
||||
static void pnv_pec_stk_instance_init(Object *obj)
|
||||
static void pnv_pec_stk_default_phb_realize(PnvPhb4PecStack *stack,
|
||||
Error **errp)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(obj);
|
||||
PnvPhb4PecState *pec = stack->pec;
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
|
||||
int phb_id = pnv_phb4_pec_get_phb_id(pec, stack->stack_no);
|
||||
|
||||
object_initialize_child(obj, "phb", &stack->phb, TYPE_PNV_PHB4);
|
||||
object_property_add_alias(obj, "phb-id", OBJECT(&stack->phb), "index");
|
||||
stack->phb = PNV_PHB4(qdev_new(TYPE_PNV_PHB4));
|
||||
|
||||
object_property_set_int(OBJECT(stack->phb), "chip-id", pec->chip_id,
|
||||
&error_fatal);
|
||||
object_property_set_int(OBJECT(stack->phb), "index", phb_id,
|
||||
&error_fatal);
|
||||
object_property_set_int(OBJECT(stack->phb), "version", pecc->version,
|
||||
&error_fatal);
|
||||
object_property_set_link(OBJECT(stack->phb), "stack", OBJECT(stack),
|
||||
&error_abort);
|
||||
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(stack->phb), errp)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void pnv_pec_stk_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(dev);
|
||||
PnvPhb4PecState *pec = stack->pec;
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
|
||||
PnvChip *chip = pec->chip;
|
||||
uint32_t pec_nest_base;
|
||||
uint32_t pec_pci_base;
|
||||
char name[64];
|
||||
|
||||
assert(pec);
|
||||
|
||||
/* Initialize the XSCOM regions for the stack registers */
|
||||
snprintf(name, sizeof(name), "xscom-pec-%d.%d-nest-stack-%d",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
pnv_xscom_region_init(&stack->nest_regs_mr, OBJECT(stack),
|
||||
&pnv_pec_stk_nest_xscom_ops, stack, name,
|
||||
PHB4_PEC_NEST_STK_REGS_COUNT);
|
||||
|
||||
snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-stack-%d",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
pnv_xscom_region_init(&stack->pci_regs_mr, OBJECT(stack),
|
||||
&pnv_pec_stk_pci_xscom_ops, stack, name,
|
||||
PHB4_PEC_PCI_STK_REGS_COUNT);
|
||||
|
||||
/* PHB pass-through */
|
||||
snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-stack-%d-phb",
|
||||
pec->chip_id, pec->index, stack->stack_no);
|
||||
pnv_xscom_region_init(&stack->phb_regs_mr, OBJECT(&stack->phb),
|
||||
&pnv_phb4_xscom_ops, &stack->phb, name, 0x40);
|
||||
|
||||
object_property_set_int(OBJECT(&stack->phb), "chip-id", pec->chip_id,
|
||||
&error_fatal);
|
||||
object_property_set_int(OBJECT(&stack->phb), "version", pecc->version,
|
||||
&error_fatal);
|
||||
object_property_set_link(OBJECT(&stack->phb), "stack", OBJECT(stack),
|
||||
&error_abort);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&stack->phb), errp)) {
|
||||
if (!defaults_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pec_nest_base = pecc->xscom_nest_base(pec);
|
||||
pec_pci_base = pecc->xscom_pci_base(pec);
|
||||
|
||||
/* Populate the XSCOM address space. */
|
||||
pnv_xscom_add_subregion(chip,
|
||||
pec_nest_base + 0x40 * (stack->stack_no + 1),
|
||||
&stack->nest_regs_mr);
|
||||
pnv_xscom_add_subregion(chip,
|
||||
pec_pci_base + 0x40 * (stack->stack_no + 1),
|
||||
&stack->pci_regs_mr);
|
||||
pnv_xscom_add_subregion(chip,
|
||||
pec_pci_base + PNV9_XSCOM_PEC_PCI_STK0 +
|
||||
0x40 * stack->stack_no,
|
||||
&stack->phb_regs_mr);
|
||||
pnv_pec_stk_default_phb_realize(stack, errp);
|
||||
}
|
||||
|
||||
static Property pnv_pec_stk_properties[] = {
|
||||
@ -630,7 +332,6 @@ static const TypeInfo pnv_pec_stk_type_info = {
|
||||
.name = TYPE_PNV_PHB4_PEC_STACK,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(PnvPhb4PecStack),
|
||||
.instance_init = pnv_pec_stk_instance_init,
|
||||
.class_init = pnv_pec_stk_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_PNV_XSCOM_INTERFACE },
|
||||
|
55
hw/ppc/pnv.c
55
hw/ppc/pnv.c
@ -1099,7 +1099,6 @@ 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;
|
||||
@ -1117,14 +1116,14 @@ static void pnv_chip_power8_instance_init(Object *obj)
|
||||
|
||||
object_initialize_child(obj, "homer", &chip8->homer, TYPE_PNV8_HOMER);
|
||||
|
||||
for (i = 0; i < pcc->num_phbs; i++) {
|
||||
if (defaults_enabled()) {
|
||||
chip8->num_phbs = pcc->num_phbs;
|
||||
}
|
||||
|
||||
for (i = 0; i < chip8->num_phbs; i++) {
|
||||
object_initialize_child(obj, "phb[*]", &chip8->phbs[i], TYPE_PNV_PHB3);
|
||||
}
|
||||
|
||||
/*
|
||||
* Number of PHBs is the chip default
|
||||
*/
|
||||
chip->num_phbs = pcc->num_phbs;
|
||||
}
|
||||
|
||||
static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp)
|
||||
@ -1156,6 +1155,14 @@ static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
/* Attach a root port device */
|
||||
void pnv_phb_attach_root_port(PCIHostState *pci, const char *name)
|
||||
{
|
||||
PCIDevice *root = pci_new(PCI_DEVFN(0, 0), name);
|
||||
|
||||
pci_realize_and_unref(root, pci->bus, &error_fatal);
|
||||
}
|
||||
|
||||
static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev);
|
||||
@ -1239,7 +1246,7 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
|
||||
&chip8->homer.regs);
|
||||
|
||||
/* PHB3 controllers */
|
||||
for (i = 0; i < chip->num_phbs; i++) {
|
||||
for (i = 0; i < chip8->num_phbs; i++) {
|
||||
PnvPHB3 *phb = &chip8->phbs[i];
|
||||
|
||||
object_property_set_int(OBJECT(phb), "index", i, &error_fatal);
|
||||
@ -1806,6 +1813,36 @@ static ICSState *pnv_ics_get(XICSFabric *xi, int irq)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void pnv_chip_parent_fixup(PnvChip *chip, Object *obj, int index)
|
||||
{
|
||||
Object *parent = OBJECT(chip);
|
||||
g_autofree char *default_id =
|
||||
g_strdup_printf("%s[%d]", object_get_typename(obj), index);
|
||||
|
||||
if (obj->parent == parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
object_ref(obj);
|
||||
object_unparent(obj);
|
||||
object_property_add_child(
|
||||
parent, DEVICE(obj)->id ? DEVICE(obj)->id : default_id, obj);
|
||||
object_unref(obj);
|
||||
}
|
||||
|
||||
PnvChip *pnv_get_chip(PnvMachineState *pnv, uint32_t chip_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pnv->num_chips; i++) {
|
||||
PnvChip *chip = pnv->chips[i];
|
||||
if (chip->chip_id == chip_id) {
|
||||
return chip;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int pnv_ics_resend_child(Object *child, void *opaque)
|
||||
{
|
||||
PnvPHB3 *phb3 = (PnvPHB3 *) object_dynamic_cast(child, TYPE_PNV_PHB3);
|
||||
@ -1903,6 +1940,8 @@ static void pnv_machine_power8_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
pmc->compat = compat;
|
||||
pmc->compat_size = sizeof(compat);
|
||||
|
||||
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB3);
|
||||
}
|
||||
|
||||
static void pnv_machine_power9_class_init(ObjectClass *oc, void *data)
|
||||
@ -1921,6 +1960,8 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data)
|
||||
pmc->compat = compat;
|
||||
pmc->compat_size = sizeof(compat);
|
||||
pmc->dt_power_mgt = pnv_dt_power_mgt;
|
||||
|
||||
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB4);
|
||||
}
|
||||
|
||||
static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
|
||||
|
@ -723,10 +723,12 @@ static void spapr_dt_cpu(CPUState *cs, void *fdt, int offset,
|
||||
*
|
||||
* Only CPUs for which we create core types in spapr_cpu_core.c
|
||||
* are possible, and all of those have VMX */
|
||||
if (spapr_get_cap(spapr, SPAPR_CAP_VSX) != 0) {
|
||||
_FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 2)));
|
||||
} else {
|
||||
_FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 1)));
|
||||
if (env->insns_flags & PPC_ALTIVEC) {
|
||||
if (spapr_get_cap(spapr, SPAPR_CAP_VSX) != 0) {
|
||||
_FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 2)));
|
||||
} else {
|
||||
_FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 1)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Advertise DFP (Decimal Floating Point) if available
|
||||
|
@ -105,7 +105,7 @@ struct PnvPBCQState {
|
||||
/*
|
||||
* PHB3 PCIe Root port
|
||||
*/
|
||||
#define TYPE_PNV_PHB3_ROOT_BUS "pnv-phb3-root-bus"
|
||||
#define TYPE_PNV_PHB3_ROOT_BUS "pnv-phb3-root"
|
||||
|
||||
#define TYPE_PNV_PHB3_ROOT_PORT "pnv-phb3-root-port"
|
||||
|
||||
@ -155,8 +155,6 @@ struct PnvPHB3 {
|
||||
|
||||
PnvPBCQState pbcq;
|
||||
|
||||
PnvPHB3RootPort root;
|
||||
|
||||
QLIST_HEAD(, PnvPhb3DMASpace) dma_spaces;
|
||||
|
||||
PnvChip *chip;
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "hw/ppc/xive.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
typedef struct PnvPhb4PecState PnvPhb4PecState;
|
||||
typedef struct PnvPhb4PecStack PnvPhb4PecStack;
|
||||
typedef struct PnvPHB4 PnvPHB4;
|
||||
typedef struct PnvChip PnvChip;
|
||||
@ -46,7 +47,7 @@ typedef struct PnvPhb4DMASpace {
|
||||
/*
|
||||
* PHB4 PCIe Root port
|
||||
*/
|
||||
#define TYPE_PNV_PHB4_ROOT_BUS "pnv-phb4-root-bus"
|
||||
#define TYPE_PNV_PHB4_ROOT_BUS "pnv-phb4-root"
|
||||
#define TYPE_PNV_PHB4_ROOT_PORT "pnv-phb4-root-port"
|
||||
|
||||
typedef struct PnvPHB4RootPort {
|
||||
@ -78,8 +79,6 @@ OBJECT_DECLARE_SIMPLE_TYPE(PnvPHB4, PNV_PHB4)
|
||||
struct PnvPHB4 {
|
||||
PCIExpressHost parent_obj;
|
||||
|
||||
PnvPHB4RootPort root;
|
||||
|
||||
uint32_t chip_id;
|
||||
uint32_t phb_id;
|
||||
|
||||
@ -132,7 +131,7 @@ struct PnvPHB4 {
|
||||
};
|
||||
|
||||
void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon);
|
||||
void pnv_phb4_update_regions(PnvPhb4PecStack *stack);
|
||||
int pnv_phb4_pec_get_phb_id(PnvPhb4PecState *pec, int stack_index);
|
||||
extern const MemoryRegionOps pnv_phb4_xscom_ops;
|
||||
|
||||
/*
|
||||
@ -177,8 +176,11 @@ struct PnvPhb4PecStack {
|
||||
/* The owner PEC */
|
||||
PnvPhb4PecState *pec;
|
||||
|
||||
/* The actual PHB */
|
||||
PnvPHB4 phb;
|
||||
/*
|
||||
* PHB4 pointer. pnv_phb4_update_regions() needs to access
|
||||
* the PHB4 via a PnvPhb4PecStack pointer.
|
||||
*/
|
||||
PnvPHB4 *phb;
|
||||
};
|
||||
|
||||
struct PnvPhb4PecState {
|
||||
|
@ -52,7 +52,6 @@ struct PnvChip {
|
||||
uint64_t cores_mask;
|
||||
PnvCore **cores;
|
||||
|
||||
uint32_t num_phbs;
|
||||
uint32_t num_pecs;
|
||||
|
||||
MemoryRegion xscom_mmio;
|
||||
@ -82,6 +81,7 @@ struct Pnv8Chip {
|
||||
|
||||
#define PNV8_CHIP_PHB3_MAX 4
|
||||
PnvPHB3 phbs[PNV8_CHIP_PHB3_MAX];
|
||||
uint32_t num_phbs;
|
||||
|
||||
XICSFabric *xics;
|
||||
};
|
||||
@ -136,8 +136,8 @@ struct PnvChipClass {
|
||||
/*< public >*/
|
||||
uint64_t chip_cfam_id;
|
||||
uint64_t cores_mask;
|
||||
uint32_t num_phbs;
|
||||
uint32_t num_pecs;
|
||||
uint32_t num_phbs;
|
||||
|
||||
DeviceRealize parent_realize;
|
||||
|
||||
@ -177,6 +177,8 @@ DECLARE_INSTANCE_CHECKER(PnvChip, PNV_CHIP_POWER10,
|
||||
TYPE_PNV_CHIP_POWER10)
|
||||
|
||||
PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir);
|
||||
void pnv_phb_attach_root_port(PCIHostState *pci, const char *name);
|
||||
void pnv_chip_parent_fixup(PnvChip *chip, Object *obj, int index);
|
||||
|
||||
#define TYPE_PNV_MACHINE MACHINE_TYPE_NAME("powernv")
|
||||
typedef struct PnvMachineClass PnvMachineClass;
|
||||
@ -217,6 +219,8 @@ struct PnvMachineState {
|
||||
hwaddr fw_load_addr;
|
||||
};
|
||||
|
||||
PnvChip *pnv_get_chip(PnvMachineState *pnv, uint32_t chip_id);
|
||||
|
||||
#define PNV_FDT_ADDR 0x01000000
|
||||
#define PNV_TIMEBASE_FREQ 512000000ULL
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
|
||||
implementation for certain IBM POWER hardware. The sources are at
|
||||
https://github.com/aik/SLOF, and the image currently in qemu is
|
||||
built from git tag qemu-slof-20211112.
|
||||
built from git tag qemu-slof-20220110.
|
||||
|
||||
- VOF (Virtual Open Firmware) is a minimalistic firmware to work with
|
||||
-machine pseries,x-vof=on. When enabled, the firmware acts as a slim shim and
|
||||
|
BIN
pc-bios/slof.bin
BIN
pc-bios/slof.bin
Binary file not shown.
@ -1 +1 @@
|
||||
Subproject commit a6906b024c6cca5a86496f51eb4bfee3a0c36148
|
||||
Subproject commit 5b4c5acdcd552a4e1796aeca6bb700f6cbb0282d
|
@ -237,7 +237,7 @@ int cpu_get_dump_info(ArchDumpInfo *info,
|
||||
info->d_machine = PPC_ELF_MACHINE;
|
||||
info->d_class = ELFCLASS;
|
||||
|
||||
if (ppc_interrupts_little_endian(cpu)) {
|
||||
if (ppc_interrupts_little_endian(cpu, cpu->env.has_hv_mode)) {
|
||||
info->d_endian = ELFDATA2LSB;
|
||||
} else {
|
||||
info->d_endian = ELFDATA2MSB;
|
||||
|
@ -2728,20 +2728,29 @@ static inline bool ppc_has_spr(PowerPCCPU *cpu, int spr)
|
||||
return cpu->env.spr_cb[spr].name != NULL;
|
||||
}
|
||||
|
||||
static inline bool ppc_interrupts_little_endian(PowerPCCPU *cpu)
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
static inline bool ppc_interrupts_little_endian(PowerPCCPU *cpu, bool hv)
|
||||
{
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
bool ile;
|
||||
|
||||
/*
|
||||
* Only models that have an LPCR and know about LPCR_ILE can do little
|
||||
* endian.
|
||||
*/
|
||||
if (pcc->lpcr_mask & LPCR_ILE) {
|
||||
return !!(cpu->env.spr[SPR_LPCR] & LPCR_ILE);
|
||||
if (hv && env->has_hv_mode) {
|
||||
if (is_isa300(pcc)) {
|
||||
ile = !!(env->spr[SPR_HID0] & HID0_POWER9_HILE);
|
||||
} else {
|
||||
ile = !!(env->spr[SPR_HID0] & HID0_HILE);
|
||||
}
|
||||
|
||||
} else if (pcc->lpcr_mask & LPCR_ILE) {
|
||||
ile = !!(env->spr[SPR_LPCR] & LPCR_ILE);
|
||||
} else {
|
||||
ile = !!(msr_ile);
|
||||
}
|
||||
|
||||
return false;
|
||||
return ile;
|
||||
}
|
||||
#endif
|
||||
|
||||
void dump_mmu(CPUPPCState *env);
|
||||
|
||||
|
@ -6953,10 +6953,12 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data)
|
||||
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
|
||||
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
|
||||
PPC_FLOAT_STFIWX |
|
||||
PPC_FLOAT_EXT |
|
||||
PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
|
||||
PPC_64B |
|
||||
PPC_POPCNTB |
|
||||
PPC_SEGMENT_64B | PPC_SLBI;
|
||||
pcc->insns_flags2 = PPC2_FP_CVT_S64;
|
||||
pcc->msr_mask = (1ull << MSR_SF) |
|
||||
|
@ -30,8 +30,6 @@
|
||||
#include "exec/cpu_ldst.h"
|
||||
#endif
|
||||
|
||||
/* #define DEBUG_SOFTWARE_TLB */
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Exception processing */
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
@ -135,6 +133,39 @@ static void dump_hcall(CPUPPCState *env)
|
||||
env->nip);
|
||||
}
|
||||
|
||||
static void ppc_excp_debug_sw_tlb(CPUPPCState *env, int excp)
|
||||
{
|
||||
const char *es;
|
||||
target_ulong *miss, *cmp;
|
||||
int en;
|
||||
|
||||
if (!qemu_loglevel_mask(CPU_LOG_MMU)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (excp == POWERPC_EXCP_IFTLB) {
|
||||
es = "I";
|
||||
en = 'I';
|
||||
miss = &env->spr[SPR_IMISS];
|
||||
cmp = &env->spr[SPR_ICMP];
|
||||
} else {
|
||||
if (excp == POWERPC_EXCP_DLTLB) {
|
||||
es = "DL";
|
||||
} else {
|
||||
es = "DS";
|
||||
}
|
||||
en = 'D';
|
||||
miss = &env->spr[SPR_DMISS];
|
||||
cmp = &env->spr[SPR_DCMP];
|
||||
}
|
||||
qemu_log("6xx %sTLB miss: %cM " TARGET_FMT_lx " %cC "
|
||||
TARGET_FMT_lx " H1 " TARGET_FMT_lx " H2 "
|
||||
TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp,
|
||||
env->spr[SPR_HASH1], env->spr[SPR_HASH2],
|
||||
env->error_code);
|
||||
}
|
||||
|
||||
|
||||
static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp,
|
||||
target_ulong *msr)
|
||||
{
|
||||
@ -365,7 +396,7 @@ static void powerpc_set_excp_state(PowerPCCPU *cpu,
|
||||
* Note that this function should be greatly optimized when called
|
||||
* with a constant excp, from ppc_hw_interrupt
|
||||
*/
|
||||
static void powerpc_excp(PowerPCCPU *cpu, int excp)
|
||||
static inline void powerpc_excp_legacy(PowerPCCPU *cpu, int excp)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
@ -669,23 +700,6 @@ static void powerpc_excp(PowerPCCPU *cpu, int excp)
|
||||
case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */
|
||||
env->spr[SPR_BOOKE_ESR] = ESR_SPV;
|
||||
break;
|
||||
case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */
|
||||
/* XXX: TODO */
|
||||
cpu_abort(cs, "Embedded floating point data exception "
|
||||
"is not implemented yet !\n");
|
||||
env->spr[SPR_BOOKE_ESR] = ESR_SPV;
|
||||
break;
|
||||
case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */
|
||||
/* XXX: TODO */
|
||||
cpu_abort(cs, "Embedded floating point round exception "
|
||||
"is not implemented yet !\n");
|
||||
env->spr[SPR_BOOKE_ESR] = ESR_SPV;
|
||||
break;
|
||||
case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */
|
||||
/* XXX: TODO */
|
||||
cpu_abort(cs,
|
||||
"Performance counter exception is not implemented yet !\n");
|
||||
break;
|
||||
case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */
|
||||
break;
|
||||
case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */
|
||||
@ -750,19 +764,6 @@ static void powerpc_excp(PowerPCCPU *cpu, int excp)
|
||||
case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */
|
||||
trace_ppc_excp_print("PIT");
|
||||
break;
|
||||
case POWERPC_EXCP_IO: /* IO error exception */
|
||||
/* XXX: TODO */
|
||||
cpu_abort(cs, "601 IO error exception is not implemented yet !\n");
|
||||
break;
|
||||
case POWERPC_EXCP_RUNM: /* Run mode exception */
|
||||
/* XXX: TODO */
|
||||
cpu_abort(cs, "601 run mode exception is not implemented yet !\n");
|
||||
break;
|
||||
case POWERPC_EXCP_EMUL: /* Emulation trap exception */
|
||||
/* XXX: TODO */
|
||||
cpu_abort(cs, "602 emulation trap exception "
|
||||
"is not implemented yet !\n");
|
||||
break;
|
||||
case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */
|
||||
case POWERPC_EXCP_DLTLB: /* Data load TLB miss */
|
||||
case POWERPC_EXCP_DSTLB: /* Data store TLB miss */
|
||||
@ -777,34 +778,8 @@ static void powerpc_excp(PowerPCCPU *cpu, int excp)
|
||||
}
|
||||
/* fall through */
|
||||
case POWERPC_EXCP_7x5:
|
||||
#if defined(DEBUG_SOFTWARE_TLB)
|
||||
if (qemu_log_enabled()) {
|
||||
const char *es;
|
||||
target_ulong *miss, *cmp;
|
||||
int en;
|
||||
ppc_excp_debug_sw_tlb(env, excp);
|
||||
|
||||
if (excp == POWERPC_EXCP_IFTLB) {
|
||||
es = "I";
|
||||
en = 'I';
|
||||
miss = &env->spr[SPR_IMISS];
|
||||
cmp = &env->spr[SPR_ICMP];
|
||||
} else {
|
||||
if (excp == POWERPC_EXCP_DLTLB) {
|
||||
es = "DL";
|
||||
} else {
|
||||
es = "DS";
|
||||
}
|
||||
en = 'D';
|
||||
miss = &env->spr[SPR_DMISS];
|
||||
cmp = &env->spr[SPR_DCMP];
|
||||
}
|
||||
qemu_log("6xx %sTLB miss: %cM " TARGET_FMT_lx " %cC "
|
||||
TARGET_FMT_lx " H1 " TARGET_FMT_lx " H2 "
|
||||
TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp,
|
||||
env->spr[SPR_HASH1], env->spr[SPR_HASH2],
|
||||
env->error_code);
|
||||
}
|
||||
#endif
|
||||
msr |= env->crf[0] << 28;
|
||||
msr |= env->error_code; /* key, D/I, S/L bits */
|
||||
/* Set way using a LRU mechanism */
|
||||
@ -815,56 +790,25 @@ static void powerpc_excp(PowerPCCPU *cpu, int excp)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */
|
||||
case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */
|
||||
case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */
|
||||
case POWERPC_EXCP_IO: /* IO error exception */
|
||||
case POWERPC_EXCP_RUNM: /* Run mode exception */
|
||||
case POWERPC_EXCP_EMUL: /* Emulation trap exception */
|
||||
case POWERPC_EXCP_FPA: /* Floating-point assist exception */
|
||||
/* XXX: TODO */
|
||||
cpu_abort(cs, "Floating point assist exception "
|
||||
"is not implemented yet !\n");
|
||||
break;
|
||||
case POWERPC_EXCP_DABR: /* Data address breakpoint */
|
||||
/* XXX: TODO */
|
||||
cpu_abort(cs, "DABR exception is not implemented yet !\n");
|
||||
break;
|
||||
case POWERPC_EXCP_IABR: /* Instruction address breakpoint */
|
||||
/* XXX: TODO */
|
||||
cpu_abort(cs, "IABR exception is not implemented yet !\n");
|
||||
break;
|
||||
case POWERPC_EXCP_SMI: /* System management interrupt */
|
||||
/* XXX: TODO */
|
||||
cpu_abort(cs, "SMI exception is not implemented yet !\n");
|
||||
break;
|
||||
case POWERPC_EXCP_THERM: /* Thermal interrupt */
|
||||
/* XXX: TODO */
|
||||
cpu_abort(cs, "Thermal management exception "
|
||||
"is not implemented yet !\n");
|
||||
break;
|
||||
case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */
|
||||
/* XXX: TODO */
|
||||
cpu_abort(cs,
|
||||
"Performance counter exception is not implemented yet !\n");
|
||||
break;
|
||||
case POWERPC_EXCP_VPUA: /* Vector assist exception */
|
||||
/* XXX: TODO */
|
||||
cpu_abort(cs, "VPU assist exception is not implemented yet !\n");
|
||||
break;
|
||||
case POWERPC_EXCP_SOFTP: /* Soft patch exception */
|
||||
/* XXX: TODO */
|
||||
cpu_abort(cs,
|
||||
"970 soft-patch exception is not implemented yet !\n");
|
||||
break;
|
||||
case POWERPC_EXCP_MAINT: /* Maintenance exception */
|
||||
/* XXX: TODO */
|
||||
cpu_abort(cs,
|
||||
"970 maintenance exception is not implemented yet !\n");
|
||||
break;
|
||||
case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */
|
||||
/* XXX: TODO */
|
||||
cpu_abort(cs, "Maskable external exception "
|
||||
"is not implemented yet !\n");
|
||||
break;
|
||||
case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */
|
||||
/* XXX: TODO */
|
||||
cpu_abort(cs, "Non maskable external exception "
|
||||
"is not implemented yet !\n");
|
||||
cpu_abort(cs, "%s exception not implemented\n",
|
||||
powerpc_excp_name(excp));
|
||||
break;
|
||||
default:
|
||||
excp_invalid:
|
||||
@ -888,36 +832,9 @@ static void powerpc_excp(PowerPCCPU *cpu, int excp)
|
||||
* Sort out endianness of interrupt, this differs depending on the
|
||||
* CPU, the HV mode, etc...
|
||||
*/
|
||||
#ifdef TARGET_PPC64
|
||||
if (excp_model == POWERPC_EXCP_POWER7) {
|
||||
if (!(new_msr & MSR_HVB) && (env->spr[SPR_LPCR] & LPCR_ILE)) {
|
||||
new_msr |= (target_ulong)1 << MSR_LE;
|
||||
}
|
||||
} else if (excp_model == POWERPC_EXCP_POWER8) {
|
||||
if (new_msr & MSR_HVB) {
|
||||
if (env->spr[SPR_HID0] & HID0_HILE) {
|
||||
new_msr |= (target_ulong)1 << MSR_LE;
|
||||
}
|
||||
} else if (env->spr[SPR_LPCR] & LPCR_ILE) {
|
||||
new_msr |= (target_ulong)1 << MSR_LE;
|
||||
}
|
||||
} else if (excp_model == POWERPC_EXCP_POWER9 ||
|
||||
excp_model == POWERPC_EXCP_POWER10) {
|
||||
if (new_msr & MSR_HVB) {
|
||||
if (env->spr[SPR_HID0] & HID0_POWER9_HILE) {
|
||||
new_msr |= (target_ulong)1 << MSR_LE;
|
||||
}
|
||||
} else if (env->spr[SPR_LPCR] & LPCR_ILE) {
|
||||
new_msr |= (target_ulong)1 << MSR_LE;
|
||||
}
|
||||
} else if (msr_ile) {
|
||||
if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) {
|
||||
new_msr |= (target_ulong)1 << MSR_LE;
|
||||
}
|
||||
#else
|
||||
if (msr_ile) {
|
||||
new_msr |= (target_ulong)1 << MSR_LE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
if (excp_model == POWERPC_EXCP_BOOKE) {
|
||||
@ -950,6 +867,16 @@ static void powerpc_excp(PowerPCCPU *cpu, int excp)
|
||||
powerpc_set_excp_state(cpu, vector, new_msr);
|
||||
}
|
||||
|
||||
static void powerpc_excp(PowerPCCPU *cpu, int excp)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
switch (env->excp_model) {
|
||||
default:
|
||||
powerpc_excp_legacy(cpu, excp);
|
||||
}
|
||||
}
|
||||
|
||||
void ppc_cpu_do_interrupt(CPUState *cs)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
@ -1126,7 +1053,7 @@ void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector)
|
||||
*/
|
||||
msr = (1ULL << MSR_ME);
|
||||
msr |= env->msr & (1ULL << MSR_SF);
|
||||
if (ppc_interrupts_little_endian(cpu)) {
|
||||
if (ppc_interrupts_little_endian(cpu, false)) {
|
||||
msr |= (1ULL << MSR_LE);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user