aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2022-07-07 06:21:05 +0530
committerRichard Henderson <richard.henderson@linaro.org>2022-07-07 06:21:05 +0530
commit8e9398e3b1a860b8c29c670c1b6c36afe8d87849 (patch)
tree7f7e081e7cebdc04955398b711eb8fb5d2640574 /hw
parent0e3723005bfea2f70157bf951f3e6be03bbf6e74 (diff)
parent0b83377f46042529adbbf3a77f7ffb6f1e8a0aaa (diff)
downloadqemu-8e9398e3b1a860b8c29c670c1b6c36afe8d87849.zip
qemu-8e9398e3b1a860b8c29c670c1b6c36afe8d87849.tar.gz
qemu-8e9398e3b1a860b8c29c670c1b6c36afe8d87849.tar.bz2
Merge tag 'pull-ppc-20220706' of https://gitlab.com/danielhb/qemu into staging
ppc patch queue for 2022-07-06: This queue consists of improvements and bug fixes in TCG, powernv and pSeries, with some fixes in other areas as well. - tcg and target/ppc: BCDA and mffscdrn implementations, Remove CONFIG_INT128 conditional code - fix '-cpu max' alias - remove '-cpu default' alias - spapr: fixes in DDW handling, H_WATCHDOG support - powernv: cleanups in the pnv-phb3/4 models - fix core type of MPC8555 and MPC8560 models # -----BEGIN PGP SIGNATURE----- # # iHUEABYKAB0WIQQX6/+ZI9AYAK8oOBk82cqW3gMxZAUCYsXrpgAKCRA82cqW3gMx # ZBe9AP4oqMTFw7r9EQPJU4QFMUeAVABl4o0xNb2wLyYov9CtKAD+LoVERSmtLTJ1 # kFpgBrRTWKVylaLEdZQoTdFlJeBwzQg= # =GPG1 # -----END PGP SIGNATURE----- # gpg: Signature made Thu 07 Jul 2022 01:38:06 AM +0530 # gpg: using EDDSA key 17EBFF9923D01800AF2838193CD9CA96DE033164 # gpg: Good signature from "Daniel Henrique Barboza <danielhb413@gmail.com>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 17EB FF99 23D0 1800 AF28 3819 3CD9 CA96 DE03 3164 * tag 'pull-ppc-20220706' of https://gitlab.com/danielhb/qemu: (34 commits) target/ppc: Fix MPC8555 and MPC8560 core type to e500v1 target/ppc/cpu-models: Remove the "default" CPU alias target/ppc: Return default CPU for max CPU target/ppc: implement cdtbcd target/ppc: implement cbcdtd target/ppc: implement addg6s target/ppc: Add flag for ISA v2.06 BCDA instructions tests/tcg/ppc64: Add mffsce test target/ppc: Implement mffscdrn[i] instructions target/ppc: Move mffs[.] to decodetree target/ppc: Move mffsl to decodetree target/ppc: Move mffsce to decodetree target/ppc: Move mffscrn[i] to decodetree target/ppc: Fix insn32.decode style issues ppc/spapr: Implement H_WATCHDOG ppc: Define SETFIELD for the ppc target target/ppc: use int128.h methods in vsubcuq target/ppc: use int128.h methods in vsubecuq and vsubeuqm target/ppc: use int128.h methods in vsubuqm target/ppc: use int128.h methods in vaddcuq ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r--hw/intc/pnv_xive.c20
-rw-r--r--hw/intc/pnv_xive2.c20
-rw-r--r--hw/pci-host/pnv_phb3.c22
-rw-r--r--hw/pci-host/pnv_phb4.c40
-rw-r--r--hw/pci-host/pnv_phb4_pec.c3
-rw-r--r--hw/ppc/pnv.c102
-rw-r--r--hw/ppc/spapr.c4
-rw-r--r--hw/ppc/spapr_iommu.c3
-rw-r--r--hw/ppc/spapr_pci.c6
-rw-r--r--hw/ppc/spapr_rtas_ddw.c34
-rw-r--r--hw/watchdog/meson.build1
-rw-r--r--hw/watchdog/spapr_watchdog.c274
-rw-r--r--hw/watchdog/trace-events7
13 files changed, 374 insertions, 162 deletions
diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c
index 1ce1d7b..c7b75ed 100644
--- a/hw/intc/pnv_xive.c
+++ b/hw/intc/pnv_xive.c
@@ -67,26 +67,6 @@ static const XiveVstInfo vst_infos[] = {
(xive)->chip->chip_id, ## __VA_ARGS__);
/*
- * QEMU version of the GETFIELD/SETFIELD macros
- *
- * TODO: It might be better to use the existing extract64() and
- * deposit64() but this means that all the register definitions will
- * change and become incompatible with the ones found in skiboot.
- *
- * Keep it as it is for now until we find a common ground.
- */
-static inline uint64_t GETFIELD(uint64_t mask, uint64_t word)
-{
- return (word & mask) >> ctz64(mask);
-}
-
-static inline uint64_t SETFIELD(uint64_t mask, uint64_t word,
- uint64_t value)
-{
- return (word & ~mask) | ((value << ctz64(mask)) & mask);
-}
-
-/*
* When PC_TCTXT_CHIPID_OVERRIDE is configured, the PC_TCTXT_CHIPID
* field overrides the hardwired chip ID in the Powerbus operations
* and for CAM compares
diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c
index f31c53c..f22ce5c 100644
--- a/hw/intc/pnv_xive2.c
+++ b/hw/intc/pnv_xive2.c
@@ -76,26 +76,6 @@ static const XiveVstInfo vst_infos[] = {
(xive)->chip->chip_id, ## __VA_ARGS__);
/*
- * QEMU version of the GETFIELD/SETFIELD macros
- *
- * TODO: It might be better to use the existing extract64() and
- * deposit64() but this means that all the register definitions will
- * change and become incompatible with the ones found in skiboot.
- *
- * Keep it as it is for now until we find a common ground.
- */
-static inline uint64_t GETFIELD(uint64_t mask, uint64_t word)
-{
- return (word & mask) >> ctz64(mask);
-}
-
-static inline uint64_t SETFIELD(uint64_t mask, uint64_t word,
- uint64_t value)
-{
- return (word & ~mask) | ((value << ctz64(mask)) & mask);
-}
-
-/*
* TODO: Document block id override
*/
static uint32_t pnv_xive2_block_id(PnvXive2 *xive)
diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c
index 26ac9b7..d58d3c1 100644
--- a/hw/pci-host/pnv_phb3.c
+++ b/hw/pci-host/pnv_phb3.c
@@ -1052,7 +1052,8 @@ static void pnv_phb3_realize(DeviceState *dev, Error **errp)
pci_setup_iommu(pci->bus, pnv_phb3_dma_iommu, phb);
- pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb), TYPE_PNV_PHB3_ROOT_PORT);
+ pnv_phb_attach_root_port(pci, TYPE_PNV_PHB3_ROOT_PORT,
+ phb->phb_id, phb->chip_id);
}
void pnv_phb3_update_regions(PnvPHB3 *phb)
@@ -1129,33 +1130,14 @@ static const TypeInfo pnv_phb3_root_bus_info = {
.name = TYPE_PNV_PHB3_ROOT_BUS,
.parent = TYPE_PCIE_BUS,
.class_init = pnv_phb3_root_bus_class_init,
- .interfaces = (InterfaceInfo[]) {
- { INTERFACE_PCIE_DEVICE },
- { }
- },
};
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);
diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c
index 6594016..67ddde4 100644
--- a/hw/pci-host/pnv_phb4.c
+++ b/hw/pci-host/pnv_phb4.c
@@ -31,22 +31,6 @@
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
- *
- * These are common with the PnvXive model.
- */
-static inline uint64_t GETFIELD(uint64_t mask, uint64_t word)
-{
- return (word & mask) >> ctz64(mask);
-}
-
-static inline uint64_t SETFIELD(uint64_t mask, uint64_t word,
- uint64_t value)
-{
- return (word & ~mask) | ((value << ctz64(mask)) & mask);
-}
-
static PCIDevice *pnv_phb4_find_cfg_dev(PnvPHB4 *phb)
{
PCIHostState *pci = PCI_HOST_BRIDGE(phb);
@@ -1547,6 +1531,7 @@ static void pnv_phb4_instance_init(Object *obj)
static void pnv_phb4_realize(DeviceState *dev, Error **errp)
{
PnvPHB4 *phb = PNV_PHB4(dev);
+ PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(phb->pec);
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
XiveSource *xsrc = &phb->xsrc;
int nr_irqs;
@@ -1583,6 +1568,10 @@ 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 if running with defaults */
+ pnv_phb_attach_root_port(pci, pecc->rp_model,
+ phb->phb_id, phb->chip_id);
+
/* Setup XIVE Source */
if (phb->big_phb) {
nr_irqs = PNV_PHB4_MAX_INTs;
@@ -1747,10 +1736,6 @@ static const TypeInfo pnv_phb4_root_bus_info = {
.name = TYPE_PNV_PHB4_ROOT_BUS,
.parent = TYPE_PCIE_BUS,
.class_init = pnv_phb4_root_bus_class_init,
- .interfaces = (InterfaceInfo[]) {
- { INTERFACE_PCIE_DEVICE },
- { }
- },
};
static void pnv_phb4_root_port_reset(DeviceState *dev)
@@ -1777,23 +1762,8 @@ 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);
diff --git a/hw/pci-host/pnv_phb4_pec.c b/hw/pci-host/pnv_phb4_pec.c
index 8b7e823..c9aaf1c 100644
--- a/hw/pci-host/pnv_phb4_pec.c
+++ b/hw/pci-host/pnv_phb4_pec.c
@@ -130,9 +130,6 @@ static void pnv_pec_default_phb_realize(PnvPhb4PecState *pec,
if (!sysbus_realize(SYS_BUS_DEVICE(phb), errp)) {
return;
}
-
- /* Add a single Root port if running with defaults */
- pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb), pecc->rp_model);
}
static void pnv_pec_realize(DeviceState *dev, Error **errp)
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 7c08a78..d3f77c8 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -652,25 +652,19 @@ static ISABus *pnv_isa_create(PnvChip *chip, Error **errp)
return PNV_CHIP_GET_CLASS(chip)->isa_create(chip, errp);
}
-static int pnv_chip_power8_pic_print_info_child(Object *child, void *opaque)
-{
- Monitor *mon = opaque;
- PnvPHB3 *phb3 = (PnvPHB3 *) object_dynamic_cast(child, TYPE_PNV_PHB3);
-
- if (phb3) {
- pnv_phb3_msi_pic_print_info(&phb3->msis, mon);
- ics_pic_print_info(&phb3->lsis, mon);
- }
- return 0;
-}
-
static void pnv_chip_power8_pic_print_info(PnvChip *chip, Monitor *mon)
{
Pnv8Chip *chip8 = PNV8_CHIP(chip);
+ int i;
ics_pic_print_info(&chip8->psi.ics, mon);
- object_child_foreach(OBJECT(chip),
- pnv_chip_power8_pic_print_info_child, mon);
+
+ for (i = 0; i < chip8->num_phbs; i++) {
+ PnvPHB3 *phb3 = &chip8->phbs[i];
+
+ pnv_phb3_msi_pic_print_info(&phb3->msis, mon);
+ ics_pic_print_info(&phb3->lsis, mon);
+ }
}
static int pnv_chip_power9_pic_print_info_child(Object *child, void *opaque)
@@ -1189,10 +1183,26 @@ 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)
+/*
+ * Attach a root port device.
+ *
+ * 'index' will be used both as a PCIE slot value and to calculate
+ * QOM id. 'chip_id' is going to be used as PCIE chassis for the
+ * root port.
+ */
+void pnv_phb_attach_root_port(PCIHostState *pci, const char *name,
+ int index, int chip_id)
{
PCIDevice *root = pci_new(PCI_DEVFN(0, 0), name);
+ g_autofree char *default_id = g_strdup_printf("%s[%d]", name, index);
+ const char *dev_id = DEVICE(root)->id;
+
+ object_property_add_child(OBJECT(pci->bus), dev_id ? dev_id : default_id,
+ OBJECT(root));
+
+ /* Set unique chassis/slot values for the root port */
+ qdev_prop_set_uint8(DEVICE(root), "chassis", chip_id);
+ qdev_prop_set_uint16(DEVICE(root), "slot", index);
pci_realize_and_unref(root, pci->bus, &error_fatal);
}
@@ -1934,44 +1944,28 @@ PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir)
return NULL;
}
-typedef struct ForeachPhb3Args {
- int irq;
- ICSState *ics;
-} ForeachPhb3Args;
-
-static int pnv_ics_get_child(Object *child, void *opaque)
-{
- ForeachPhb3Args *args = opaque;
- PnvPHB3 *phb3 = (PnvPHB3 *) object_dynamic_cast(child, TYPE_PNV_PHB3);
-
- if (phb3) {
- if (ics_valid_irq(&phb3->lsis, args->irq)) {
- args->ics = &phb3->lsis;
- }
- if (ics_valid_irq(ICS(&phb3->msis), args->irq)) {
- args->ics = ICS(&phb3->msis);
- }
- }
- return args->ics ? 1 : 0;
-}
-
static ICSState *pnv_ics_get(XICSFabric *xi, int irq)
{
PnvMachineState *pnv = PNV_MACHINE(xi);
- ForeachPhb3Args args = { irq, NULL };
- 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;
}
- object_child_foreach(OBJECT(chip), pnv_ics_get_child, &args);
- if (args.ics) {
- return args.ics;
+ for (j = 0; j < chip8->num_phbs; j++) {
+ PnvPHB3 *phb3 = &chip8->phbs[j];
+
+ if (ics_valid_irq(&phb3->lsis, irq)) {
+ return &phb3->lsis;
+ }
+
+ if (ics_valid_irq(ICS(&phb3->msis), irq)) {
+ return ICS(&phb3->msis);
+ }
}
}
return NULL;
@@ -1990,28 +1984,22 @@ PnvChip *pnv_get_chip(PnvMachineState *pnv, uint32_t chip_id)
return NULL;
}
-static int pnv_ics_resend_child(Object *child, void *opaque)
-{
- PnvPHB3 *phb3 = (PnvPHB3 *) object_dynamic_cast(child, TYPE_PNV_PHB3);
-
- if (phb3) {
- ics_resend(&phb3->lsis);
- ics_resend(ICS(&phb3->msis));
- }
- return 0;
-}
-
static void pnv_ics_resend(XICSFabric *xi)
{
PnvMachineState *pnv = PNV_MACHINE(xi);
- int i;
+ 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);
- object_child_foreach(OBJECT(chip), pnv_ics_resend_child, NULL);
+
+ for (j = 0; j < chip8->num_phbs; j++) {
+ PnvPHB3 *phb3 = &chip8->phbs[j];
+
+ ics_resend(&phb3->lsis);
+ ics_resend(ICS(&phb3->msis));
+ }
}
}
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index fd4942e..9a5382d 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -898,6 +898,8 @@ static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt)
add_str(hypertas, "hcall-hpt-resize");
}
+ add_str(hypertas, "hcall-watchdog");
+
_FDT(fdt_setprop(fdt, rtas, "ibm,hypertas-functions",
hypertas->str, hypertas->len));
g_string_free(hypertas, TRUE);
@@ -3051,6 +3053,8 @@ static void spapr_machine_init(MachineState *machine)
spapr->vof->fw_size = fw_size; /* for claim() on itself */
spapr_register_hypercall(KVMPPC_H_VOF_CLIENT, spapr_h_vof_client);
}
+
+ spapr_watchdog_init(spapr);
}
#define DEFAULT_KVM_TYPE "auto"
diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c
index 81e5a1a..63e34d4 100644
--- a/hw/ppc/spapr_iommu.c
+++ b/hw/ppc/spapr_iommu.c
@@ -279,7 +279,7 @@ static const VMStateDescription vmstate_spapr_tce_table_ex = {
static const VMStateDescription vmstate_spapr_tce_table = {
.name = "spapr_iommu",
- .version_id = 2,
+ .version_id = 3,
.minimum_version_id = 2,
.pre_save = spapr_tce_table_pre_save,
.post_load = spapr_tce_table_post_load,
@@ -292,6 +292,7 @@ static const VMStateDescription vmstate_spapr_tce_table = {
VMSTATE_BOOL(bypass, SpaprTceTable),
VMSTATE_VARRAY_UINT32_ALLOC(mig_table, SpaprTceTable, mig_nb_table, 0,
vmstate_info_uint64, uint64_t),
+ VMSTATE_BOOL_V(def_win, SpaprTceTable, 3),
VMSTATE_END_OF_LIST()
},
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index b2f5fbe..67e9d46 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -2067,6 +2067,7 @@ void spapr_phb_dma_reset(SpaprPhbState *sphb)
tcet = spapr_tce_find_by_liobn(sphb->dma_liobn[0]);
spapr_tce_table_enable(tcet, SPAPR_TCE_PAGE_SHIFT, sphb->dma_win_addr,
sphb->dma_win_size >> SPAPR_TCE_PAGE_SHIFT);
+ tcet->def_win = true;
}
static void spapr_phb_reset(DeviceState *qdev)
@@ -2359,8 +2360,9 @@ int spapr_dt_phb(SpaprMachineState *spapr, SpaprPhbState *phb,
cpu_to_be32(RTAS_IBM_REMOVE_PE_DMA_WINDOW)
};
uint32_t ddw_extensions[] = {
- cpu_to_be32(1),
- cpu_to_be32(RTAS_IBM_RESET_PE_DMA_WINDOW)
+ cpu_to_be32(2),
+ cpu_to_be32(RTAS_IBM_RESET_PE_DMA_WINDOW),
+ cpu_to_be32(1), /* 1: ibm,query-pe-dma-window 6 outputs, PAPR 2.8 */
};
SpaprTceTable *tcet;
SpaprDrc *drc;
diff --git a/hw/ppc/spapr_rtas_ddw.c b/hw/ppc/spapr_rtas_ddw.c
index 13d339c..7ba1138 100644
--- a/hw/ppc/spapr_rtas_ddw.c
+++ b/hw/ppc/spapr_rtas_ddw.c
@@ -100,7 +100,7 @@ static void rtas_ibm_query_pe_dma_window(PowerPCCPU *cpu,
uint64_t buid;
uint32_t avail, addr, pgmask = 0;
- if ((nargs != 3) || (nret != 5)) {
+ if ((nargs != 3) || ((nret != 5) && (nret != 6))) {
goto param_error_exit;
}
@@ -118,9 +118,20 @@ static void rtas_ibm_query_pe_dma_window(PowerPCCPU *cpu,
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
rtas_st(rets, 1, avail);
- rtas_st(rets, 2, 0x80000000); /* The largest window we can possibly have */
- rtas_st(rets, 3, pgmask);
- rtas_st(rets, 4, 0); /* DMA migration mask, not supported */
+ if (nret == 6) {
+ /*
+ * Set the Max TCE number as 1<<(58-21) = 0x20.0000.0000
+ * 1<<59 is the huge window start and 21 is 2M page shift.
+ */
+ rtas_st(rets, 2, 0x00000020);
+ rtas_st(rets, 3, 0x00000000);
+ rtas_st(rets, 4, pgmask);
+ rtas_st(rets, 5, 0); /* DMA migration mask, not supported */
+ } else {
+ rtas_st(rets, 2, 0x80000000);
+ rtas_st(rets, 3, pgmask);
+ rtas_st(rets, 4, 0); /* DMA migration mask, not supported */
+ }
trace_spapr_iommu_ddw_query(buid, addr, avail, 0x80000000, pgmask);
return;
@@ -215,6 +226,7 @@ static void rtas_ibm_remove_pe_dma_window(PowerPCCPU *cpu,
SpaprPhbState *sphb;
SpaprTceTable *tcet;
uint32_t liobn;
+ bool def_win_removed;
if ((nargs != 1) || (nret != 1)) {
goto param_error_exit;
@@ -231,9 +243,23 @@ static void rtas_ibm_remove_pe_dma_window(PowerPCCPU *cpu,
goto param_error_exit;
}
+ def_win_removed = tcet->def_win;
spapr_tce_table_disable(tcet);
trace_spapr_iommu_ddw_remove(liobn);
+ /*
+ * PAPR+/LoPAPR says:
+ * The platform must restore the default DMA window for the PE on a call
+ * to the ibm,remove-pe-dma-window RTAS call when all of the following
+ * are true:
+ * a. The call removes the last DMA window remaining for the PE.
+ * b. The DMA window being removed is not the default window
+ */
+ if (spapr_phb_get_active_win_num(sphb) == 0 && !def_win_removed) {
+ spapr_phb_dma_reset(sphb);
+ trace_spapr_iommu_ddw_reset(sphb->buid, 0);
+ }
+
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
return;
diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build
index 054c403..8974b5c 100644
--- a/hw/watchdog/meson.build
+++ b/hw/watchdog/meson.build
@@ -6,3 +6,4 @@ softmmu_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: files('wdt_diag288.c'))
softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('wdt_aspeed.c'))
softmmu_ss.add(when: 'CONFIG_WDT_IMX2', if_true: files('wdt_imx2.c'))
softmmu_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('sbsa_gwdt.c'))
+specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_watchdog.c'))
diff --git a/hw/watchdog/spapr_watchdog.c b/hw/watchdog/spapr_watchdog.c
new file mode 100644
index 0000000..55ff1f0
--- /dev/null
+++ b/hw/watchdog/spapr_watchdog.c
@@ -0,0 +1,274 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "target/ppc/cpu.h"
+#include "migration/vmstate.h"
+#include "trace.h"
+
+#include "hw/ppc/spapr.h"
+
+#define FIELD_BE(reg, field, start, len) \
+ FIELD(reg, field, 64 - (start + len), len)
+
+/*
+ * Bits 47: "leaveOtherWatchdogsRunningOnTimeout", specified on
+ * the "Start watchdog" operation,
+ * 0 - stop out-standing watchdogs on timeout,
+ * 1 - leave outstanding watchdogs running on timeout
+ */
+FIELD_BE(PSERIES_WDTF, LEAVE_OTHER, 47, 1)
+
+/* Bits 48-55: "operation" */
+FIELD_BE(PSERIES_WDTF, OP, 48, 8)
+#define PSERIES_WDTF_OP_START 0x1
+#define PSERIES_WDTF_OP_STOP 0x2
+#define PSERIES_WDTF_OP_QUERY 0x3
+#define PSERIES_WDTF_OP_QUERY_LPM 0x4
+
+/* Bits 56-63: "timeoutAction" */
+FIELD_BE(PSERIES_WDTF, ACTION, 56, 8)
+#define PSERIES_WDTF_ACTION_HARD_POWER_OFF 0x1
+#define PSERIES_WDTF_ACTION_HARD_RESTART 0x2
+#define PSERIES_WDTF_ACTION_DUMP_RESTART 0x3
+
+FIELD_BE(PSERIES_WDTF, RESERVED, 0, 47)
+
+/* Special watchdogNumber for the "stop all watchdogs" operation */
+#define PSERIES_WDT_STOP_ALL ((uint64_t)~0)
+
+/*
+ * For the "Query watchdog capabilities" operation, a uint64 structure
+ * defined as:
+ * Bits 0-15: The minimum supported timeout in milliseconds
+ * Bits 16-31: The number of watchdogs supported
+ * Bits 32-63: Reserved
+ */
+FIELD_BE(PSERIES_WDTQ, MIN_TIMEOUT, 0, 16)
+FIELD_BE(PSERIES_WDTQ, NUM, 16, 16)
+
+/*
+ * For the "Query watchdog LPM requirement" operation:
+ * 1 = The given "watchdogNumber" must be stopped prior to suspending
+ * 2 = The given "watchdogNumber" does not have to be stopped prior to
+ * suspending
+ */
+#define PSERIES_WDTQL_STOPPED 1
+#define PSERIES_WDTQL_QUERY_NOT_STOPPED 2
+
+#define WDT_MIN_TIMEOUT 1 /* 1ms */
+
+static target_ulong watchdog_stop(unsigned watchdogNumber, SpaprWatchdog *w)
+{
+ target_ulong ret = H_NOOP;
+
+ if (timer_pending(&w->timer)) {
+ timer_del(&w->timer);
+ ret = H_SUCCESS;
+ }
+ trace_spapr_watchdog_stop(watchdogNumber, ret);
+
+ return ret;
+}
+
+static target_ulong watchdog_stop_all(SpaprMachineState *spapr)
+{
+ target_ulong ret = H_NOOP;
+ int i;
+
+ for (i = 1; i <= ARRAY_SIZE(spapr->wds); ++i) {
+ target_ulong r = watchdog_stop(i, &spapr->wds[i - 1]);
+
+ if (r != H_NOOP && r != H_SUCCESS) {
+ ret = r;
+ }
+ }
+
+ return ret;
+}
+
+static void watchdog_expired(void *pw)
+{
+ SpaprWatchdog *w = pw;
+ CPUState *cs;
+ SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+ unsigned num = w - spapr->wds;
+
+ g_assert(num < ARRAY_SIZE(spapr->wds));
+ trace_spapr_watchdog_expired(num, w->action);
+ switch (w->action) {
+ case PSERIES_WDTF_ACTION_HARD_POWER_OFF:
+ qemu_system_vmstop_request(RUN_STATE_SHUTDOWN);
+ break;
+ case PSERIES_WDTF_ACTION_HARD_RESTART:
+ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ break;
+ case PSERIES_WDTF_ACTION_DUMP_RESTART:
+ CPU_FOREACH(cs) {
+ async_run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL);
+ }
+ break;
+ }
+ if (!w->leave_others) {
+ watchdog_stop_all(spapr);
+ }
+}
+
+static target_ulong h_watchdog(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ target_ulong ret = H_SUCCESS;
+ target_ulong flags = args[0];
+ target_ulong watchdogNumber = args[1]; /* 1-Based per PAPR */
+ target_ulong timeoutInMs = args[2];
+ unsigned operation = FIELD_EX64(flags, PSERIES_WDTF, OP);
+ unsigned timeoutAction = FIELD_EX64(flags, PSERIES_WDTF, ACTION);
+ SpaprWatchdog *w;
+
+ if (FIELD_EX64(flags, PSERIES_WDTF, RESERVED)) {
+ return H_PARAMETER;
+ }
+
+ switch (operation) {
+ case PSERIES_WDTF_OP_START:
+ if (watchdogNumber > ARRAY_SIZE(spapr->wds)) {
+ return H_P2;
+ }
+ if (timeoutInMs <= WDT_MIN_TIMEOUT) {
+ return H_P3;
+ }
+
+ w = &spapr->wds[watchdogNumber - 1];
+ switch (timeoutAction) {
+ case PSERIES_WDTF_ACTION_HARD_POWER_OFF:
+ case PSERIES_WDTF_ACTION_HARD_RESTART:
+ case PSERIES_WDTF_ACTION_DUMP_RESTART:
+ w->action = timeoutAction;
+ break;
+ default:
+ return H_PARAMETER;
+ }
+ w->leave_others = FIELD_EX64(flags, PSERIES_WDTF, LEAVE_OTHER);
+ timer_mod(&w->timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + timeoutInMs);
+ trace_spapr_watchdog_start(flags, watchdogNumber, timeoutInMs);
+ break;
+ case PSERIES_WDTF_OP_STOP:
+ if (watchdogNumber == PSERIES_WDT_STOP_ALL) {
+ ret = watchdog_stop_all(spapr);
+ } else if (watchdogNumber <= ARRAY_SIZE(spapr->wds)) {
+ ret = watchdog_stop(watchdogNumber,
+ &spapr->wds[watchdogNumber - 1]);
+ } else {
+ return H_P2;
+ }
+ break;
+ case PSERIES_WDTF_OP_QUERY:
+ args[0] = FIELD_DP64(0, PSERIES_WDTQ, MIN_TIMEOUT, WDT_MIN_TIMEOUT);
+ args[0] = FIELD_DP64(args[0], PSERIES_WDTQ, NUM,
+ ARRAY_SIZE(spapr->wds));
+ trace_spapr_watchdog_query(args[0]);
+ break;
+ case PSERIES_WDTF_OP_QUERY_LPM:
+ if (watchdogNumber > ARRAY_SIZE(spapr->wds)) {
+ return H_P2;
+ }
+ args[0] = PSERIES_WDTQL_QUERY_NOT_STOPPED;
+ trace_spapr_watchdog_query_lpm(args[0]);
+ break;
+ default:
+ return H_PARAMETER;
+ }
+
+ return ret;
+}
+
+void spapr_watchdog_init(SpaprMachineState *spapr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(spapr->wds); ++i) {
+ char name[16];
+ SpaprWatchdog *w = &spapr->wds[i];
+
+ snprintf(name, sizeof(name) - 1, "wdt%d", i + 1);
+ object_initialize_child_with_props(OBJECT(spapr), name, w,
+ sizeof(SpaprWatchdog),
+ TYPE_SPAPR_WDT,
+ &error_fatal, NULL);
+ qdev_realize(DEVICE(w), NULL, &error_fatal);
+ }
+}
+
+static bool watchdog_needed(void *opaque)
+{
+ SpaprWatchdog *w = opaque;
+
+ return timer_pending(&w->timer);
+}
+
+static const VMStateDescription vmstate_wdt = {
+ .name = "spapr_watchdog",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = watchdog_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_TIMER(timer, SpaprWatchdog),
+ VMSTATE_UINT8(action, SpaprWatchdog),
+ VMSTATE_UINT8(leave_others, SpaprWatchdog),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void spapr_wdt_realize(DeviceState *dev, Error **errp)
+{
+ SpaprWatchdog *w = SPAPR_WDT(dev);
+ Object *o = OBJECT(dev);
+
+ timer_init_ms(&w->timer, QEMU_CLOCK_VIRTUAL, watchdog_expired, w);
+
+ object_property_add_uint64_ptr(o, "expire",
+ (uint64_t *)&w->timer.expire_time,
+ OBJ_PROP_FLAG_READ);
+ object_property_add_uint8_ptr(o, "action", &w->action, OBJ_PROP_FLAG_READ);
+ object_property_add_uint8_ptr(o, "leaveOtherWatchdogsRunningOnTimeout",
+ &w->leave_others, OBJ_PROP_FLAG_READ);
+}
+
+static void spapr_wdt_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = spapr_wdt_realize;
+ dc->vmsd = &vmstate_wdt;
+ dc->user_creatable = false;
+}
+
+static const TypeInfo spapr_wdt_info = {
+ .name = TYPE_SPAPR_WDT,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(SpaprWatchdog),
+ .class_init = spapr_wdt_class_init,
+};
+
+static void spapr_watchdog_register_types(void)
+{
+ spapr_register_hypercall(H_WATCHDOG, h_watchdog);
+ type_register_static(&spapr_wdt_info);
+}
+
+type_init(spapr_watchdog_register_types)
diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events
index e7523e2..89ccbcf 100644
--- a/hw/watchdog/trace-events
+++ b/hw/watchdog/trace-events
@@ -9,3 +9,10 @@ cmsdk_apb_watchdog_lock(uint32_t lock) "CMSDK APB watchdog: lock %" PRIu32
# wdt-aspeed.c
aspeed_wdt_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d"
aspeed_wdt_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64
+
+# spapr_watchdog.c
+spapr_watchdog_start(uint64_t flags, uint64_t num, uint64_t timeout) "Flags 0x%" PRIx64 " num=%" PRId64 " %" PRIu64 "ms"
+spapr_watchdog_stop(uint64_t num, uint64_t ret) "num=%" PRIu64 " ret=%" PRId64
+spapr_watchdog_query(uint64_t caps) "caps=0x%" PRIx64
+spapr_watchdog_query_lpm(uint64_t caps) "caps=0x%" PRIx64
+spapr_watchdog_expired(uint64_t num, unsigned action) "num=%" PRIu64 " action=%u"