Merge tag 'pull-ppc-20230707-1' of https://gitlab.com/danielhb/qemu into staging
ppc patch queue for 2023-07-07:
In this last queue for 8.1 we have a lot of fixes and improvements all
around: SMT support for powerNV, XIVE fixes, PPC440 cleanups, exception
handling cleanups and kvm_pph.h cleanups just to name a few.
Thanks everyone in the qemu-ppc community for all the contributions for
the next QEMU 8.1 release.
# -----BEGIN PGP SIGNATURE-----
#
# iIwEABYKADQWIQQX6/+ZI9AYAK8oOBk82cqW3gMxZAUCZKgihBYcZGFuaWVsaGI0
# MTNAZ21haWwuY29tAAoJEDzZypbeAzFksr0A/jrvSDSDxB5mR7bo0dNGndLXcdTo
# ZGr6k6pcMpr7RDOAAQDVeaw7f8djQ4Aaelk6v1wPs5bYfNY2ElF4NsqHJFX2Cg==
# =8lDs
# -----END PGP SIGNATURE-----
# gpg: Signature made Fri 07 Jul 2023 03:34:44 PM BST
# gpg: using EDDSA key 17EBFF9923D01800AF2838193CD9CA96DE033164
# gpg: issuer "danielhb413@gmail.com"
# 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-20230707-1' of https://gitlab.com/danielhb/qemu: (59 commits)
ppc/pnv: Add QME region for P10
target/ppc: Remove pointless checks of CONFIG_USER_ONLY in 'kvm_ppc.h'
target/ppc: Restrict 'kvm_ppc.h' to sysemu in cpu_init.c
target/ppc: Define TYPE_HOST_POWERPC_CPU in cpu-qom.h
target/ppc: Move CPU QOM definitions to cpu-qom.h
target/ppc: Reorder #ifdef'ry in kvm_ppc.h
target/ppc: Have 'kvm_ppc.h' include 'sysemu/kvm.h'
target/ppc: Machine check on invalid real address access on POWER9/10
tests/qtest: Add xscom tests for powernv10 machine
ppc/pnv: Set P10 core xscom region size to match hardware
ppc/pnv: Log all unimp warnings with similar message
ppc440_pcix: Rename QOM type define abd move it to common header
ppc4xx_pci: Add define for ppc4xx-host-bridge type name
ppc4xx_pci: Rename QOM type name define
ppc440_pcix: Stop using system io region for PCI bus
ppc440_pcix: Don't use iomem for regs
ppc/sam460ex: Remove address_space_mem local variable
ppc440: Remove ppc460ex_pcie_init legacy init function
ppc440: Add busnum property to PCIe controller model
ppc440: Stop using system io region for PCIe buses
...
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
diff --git a/docs/system/ppc/powernv.rst b/docs/system/ppc/powernv.rst
index c8f9762..09f3965 100644
--- a/docs/system/ppc/powernv.rst
+++ b/docs/system/ppc/powernv.rst
@@ -195,11 +195,6 @@
-drive file=./witherspoon.pnor,format=raw,if=mtd
-CAVEATS
--------
-
- * No support for multiple HW threads (SMT=1). Same as pseries.
-
Maintainer contact information
------------------------------
diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c
index ed438a2..bbb44a5 100644
--- a/hw/intc/pnv_xive2.c
+++ b/hw/intc/pnv_xive2.c
@@ -1590,6 +1590,18 @@
return xive->chip->chip_id << 8 | offset >> xive->ic_shift;
}
+static uint32_t pnv_xive2_ic_tm_get_hw_page_offset(PnvXive2 *xive,
+ hwaddr offset)
+{
+ /*
+ * Indirect TIMA accesses are similar to direct accesses for
+ * privilege ring 0. So remove any traces of the hw thread ID from
+ * the offset in the IC BAR as it could be interpreted as the ring
+ * privilege when calling the underlying direct access functions.
+ */
+ return offset & ((1ull << xive->ic_shift) - 1);
+}
+
static XiveTCTX *pnv_xive2_get_indirect_tctx(PnvXive2 *xive, uint32_t pir)
{
PnvChip *chip = xive->chip;
@@ -1612,14 +1624,17 @@
unsigned size)
{
PnvXive2 *xive = PNV_XIVE2(opaque);
+ XivePresenter *xptr = XIVE_PRESENTER(xive);
+ hwaddr hw_page_offset;
uint32_t pir;
XiveTCTX *tctx;
uint64_t val = -1;
pir = pnv_xive2_ic_tm_get_pir(xive, offset);
+ hw_page_offset = pnv_xive2_ic_tm_get_hw_page_offset(xive, offset);
tctx = pnv_xive2_get_indirect_tctx(xive, pir);
if (tctx) {
- val = xive_tctx_tm_read(NULL, tctx, offset, size);
+ val = xive_tctx_tm_read(xptr, tctx, hw_page_offset, size);
}
return val;
@@ -1629,13 +1644,16 @@
uint64_t val, unsigned size)
{
PnvXive2 *xive = PNV_XIVE2(opaque);
+ XivePresenter *xptr = XIVE_PRESENTER(xive);
+ hwaddr hw_page_offset;
uint32_t pir;
XiveTCTX *tctx;
pir = pnv_xive2_ic_tm_get_pir(xive, offset);
+ hw_page_offset = pnv_xive2_ic_tm_get_hw_page_offset(xive, offset);
tctx = pnv_xive2_get_indirect_tctx(xive, pir);
if (tctx) {
- xive_tctx_tm_write(NULL, tctx, offset, val, size);
+ xive_tctx_tm_write(xptr, tctx, hw_page_offset, val, size);
}
}
@@ -1644,11 +1662,11 @@
.write = pnv_xive2_ic_tm_indirect_write,
.endianness = DEVICE_BIG_ENDIAN,
.valid = {
- .min_access_size = 8,
+ .min_access_size = 1,
.max_access_size = 8,
},
.impl = {
- .min_access_size = 8,
+ .min_access_size = 1,
.max_access_size = 8,
},
};
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 5c6094c..36ff71f 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -265,8 +265,8 @@
xive_source_esb_write(uint64_t addr, uint32_t srcno, uint64_t value) "@0x%"PRIx64" IRQ 0x%x val=0x%"PRIx64
xive_router_end_notify(uint8_t end_blk, uint32_t end_idx, uint32_t end_data) "END 0x%02x/0x%04x -> enqueue 0x%08x"
xive_router_end_escalate(uint8_t end_blk, uint32_t end_idx, uint8_t esc_blk, uint32_t esc_idx, uint32_t end_data) "END 0x%02x/0x%04x -> escalate END 0x%02x/0x%04x data 0x%08x"
-xive_tctx_tm_write(uint64_t offset, unsigned int size, uint64_t value) "@0x%"PRIx64" sz=%d val=0x%" PRIx64
-xive_tctx_tm_read(uint64_t offset, unsigned int size, uint64_t value) "@0x%"PRIx64" sz=%d val=0x%" PRIx64
+xive_tctx_tm_write(uint32_t index, uint64_t offset, unsigned int size, uint64_t value) "target=%d @0x%"PRIx64" sz=%d val=0x%" PRIx64
+xive_tctx_tm_read(uint32_t index, uint64_t offset, unsigned int size, uint64_t value) "target=%d @0x%"PRIx64" sz=%d val=0x%" PRIx64
xive_presenter_notify(uint8_t nvt_blk, uint32_t nvt_idx, uint8_t ring) "found NVT 0x%x/0x%x ring=0x%x"
xive_end_source_read(uint8_t end_blk, uint32_t end_idx, uint64_t addr) "END 0x%x/0x%x @0x%"PRIx64
diff --git a/hw/intc/xive.c b/hw/intc/xive.c
index 84c079b..56670b2 100644
--- a/hw/intc/xive.c
+++ b/hw/intc/xive.c
@@ -566,7 +566,7 @@
{
const XiveTmOp *xto;
- trace_xive_tctx_tm_write(offset, size, value);
+ trace_xive_tctx_tm_write(tctx->cs->cpu_index, offset, size, value);
/*
* TODO: check V bit in Q[0-3]W2
@@ -639,7 +639,7 @@
*/
ret = xive_tm_raw_read(tctx, offset, size);
out:
- trace_xive_tctx_tm_read(offset, size, ret);
+ trace_xive_tctx_tm_read(tctx->cs->cpu_index, offset, size, ret);
return ret;
}
@@ -1175,11 +1175,11 @@
.write = xive_source_esb_write,
.endianness = DEVICE_BIG_ENDIAN,
.valid = {
- .min_access_size = 8,
+ .min_access_size = 1,
.max_access_size = 8,
},
.impl = {
- .min_access_size = 8,
+ .min_access_size = 1,
.max_access_size = 8,
},
};
@@ -1232,8 +1232,7 @@
/* Do not clear the LSI bitmap */
- /* PQs are initialized to 0b01 (Q=1) which corresponds to "ints off" */
- memset(xsrc->status, XIVE_ESB_OFF, xsrc->nr_irqs);
+ memset(xsrc->status, xsrc->reset_pq, xsrc->nr_irqs);
}
static void xive_source_realize(DeviceState *dev, Error **errp)
@@ -1287,6 +1286,11 @@
DEFINE_PROP_UINT64("flags", XiveSource, esb_flags, 0),
DEFINE_PROP_UINT32("nr-irqs", XiveSource, nr_irqs, 0),
DEFINE_PROP_UINT32("shift", XiveSource, esb_shift, XIVE_ESB_64K_2PAGE),
+ /*
+ * By default, PQs are initialized to 0b01 (Q=1) which corresponds
+ * to "ints off"
+ */
+ DEFINE_PROP_UINT8("reset-pq", XiveSource, reset_pq, XIVE_ESB_OFF),
DEFINE_PROP_LINK("xive", XiveSource, xive, TYPE_XIVE_NOTIFIER,
XiveNotifier *),
DEFINE_PROP_END_OF_LIST(),
@@ -2002,11 +2006,11 @@
.write = xive_end_source_write,
.endianness = DEVICE_BIG_ENDIAN,
.valid = {
- .min_access_size = 8,
+ .min_access_size = 1,
.max_access_size = 8,
},
.impl = {
- .min_access_size = 8,
+ .min_access_size = 1,
.max_access_size = 8,
},
};
diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c
index 4d9ff41..c37ef25 100644
--- a/hw/intc/xive2.c
+++ b/hw/intc/xive2.c
@@ -954,11 +954,11 @@
.write = xive2_end_source_write,
.endianness = DEVICE_BIG_ENDIAN,
.valid = {
- .min_access_size = 8,
+ .min_access_size = 1,
.max_access_size = 8,
},
.impl = {
- .min_access_size = 8,
+ .min_access_size = 1,
.max_access_size = 8,
},
};
diff --git a/hw/net/sungem.c b/hw/net/sungem.c
index 103376c..510b370 100644
--- a/hw/net/sungem.c
+++ b/hw/net/sungem.c
@@ -107,6 +107,15 @@
#define RXDMA_FTAG 0x0110UL /* RX FIFO Tag */
#define RXDMA_FSZ 0x0120UL /* RX FIFO Size */
+/* WOL Registers */
+#define SUNGEM_MMIO_WOL_SIZE 0x14
+
+#define WOL_MATCH0 0x0000UL
+#define WOL_MATCH1 0x0004UL
+#define WOL_MATCH2 0x0008UL
+#define WOL_MCOUNT 0x000CUL
+#define WOL_WAKECSR 0x0010UL
+
/* MAC Registers */
#define SUNGEM_MMIO_MAC_SIZE 0x200
@@ -168,6 +177,7 @@
#define SUNGEM_MMIO_PCS_SIZE 0x60
#define PCS_MIISTAT 0x0004UL /* PCS MII Status Register */
#define PCS_ISTAT 0x0018UL /* PCS Interrupt Status Reg */
+
#define PCS_SSTATE 0x005CUL /* Serialink State Register */
/* Descriptors */
@@ -200,6 +210,7 @@
MemoryRegion greg;
MemoryRegion txdma;
MemoryRegion rxdma;
+ MemoryRegion wol;
MemoryRegion mac;
MemoryRegion mif;
MemoryRegion pcs;
@@ -1062,6 +1073,43 @@
},
};
+static void sungem_mmio_wol_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ trace_sungem_mmio_wol_write(addr, val);
+
+ switch (addr) {
+ case WOL_WAKECSR:
+ if (val != 0) {
+ qemu_log_mask(LOG_UNIMP, "sungem: WOL not supported\n");
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "sungem: WOL not supported\n");
+ }
+}
+
+static uint64_t sungem_mmio_wol_read(void *opaque, hwaddr addr, unsigned size)
+{
+ uint32_t val = -1;
+
+ qemu_log_mask(LOG_UNIMP, "sungem: WOL not supported\n");
+
+ trace_sungem_mmio_wol_read(addr, val);
+
+ return val;
+}
+
+static const MemoryRegionOps sungem_mmio_wol_ops = {
+ .read = sungem_mmio_wol_read,
+ .write = sungem_mmio_wol_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
static void sungem_mmio_mac_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size)
{
@@ -1330,6 +1378,10 @@
"sungem.rxdma", SUNGEM_MMIO_RXDMA_SIZE);
memory_region_add_subregion(&s->sungem, 0x4000, &s->rxdma);
+ memory_region_init_io(&s->wol, OBJECT(s), &sungem_mmio_wol_ops, s,
+ "sungem.wol", SUNGEM_MMIO_WOL_SIZE);
+ memory_region_add_subregion(&s->sungem, 0x3000, &s->wol);
+
memory_region_init_io(&s->mac, OBJECT(s), &sungem_mmio_mac_ops, s,
"sungem.mac", SUNGEM_MMIO_MAC_SIZE);
memory_region_add_subregion(&s->sungem, 0x6000, &s->mac);
diff --git a/hw/net/trace-events b/hw/net/trace-events
index 3eeacc5..6b5ba66 100644
--- a/hw/net/trace-events
+++ b/hw/net/trace-events
@@ -351,6 +351,8 @@
sungem_mmio_txdma_read(uint64_t addr, uint64_t val) "MMIO txdma read from 0x%"PRIx64" val=0x%"PRIx64
sungem_mmio_rxdma_write(uint64_t addr, uint64_t val) "MMIO rxdma write to 0x%"PRIx64" val=0x%"PRIx64
sungem_mmio_rxdma_read(uint64_t addr, uint64_t val) "MMIO rxdma read from 0x%"PRIx64" val=0x%"PRIx64
+sungem_mmio_wol_write(uint64_t addr, uint64_t val) "MMIO wol write to 0x%"PRIx64" val=0x%"PRIx64
+sungem_mmio_wol_read(uint64_t addr, uint64_t val) "MMIO wol read from 0x%"PRIx64" val=0x%"PRIx64
sungem_mmio_mac_write(uint64_t addr, uint64_t val) "MMIO mac write to 0x%"PRIx64" val=0x%"PRIx64
sungem_mmio_mac_read(uint64_t addr, uint64_t val) "MMIO mac read from 0x%"PRIx64" val=0x%"PRIx64
sungem_mmio_mif_write(uint64_t addr, uint64_t val) "MMIO mif write to 0x%"PRIx64" val=0x%"PRIx64
diff --git a/hw/pci-host/mv64361.c b/hw/pci-host/mv64361.c
index 19e8031..01bd8c8 100644
--- a/hw/pci-host/mv64361.c
+++ b/hw/pci-host/mv64361.c
@@ -541,6 +541,12 @@
}
}
break;
+ case MV64340_ETH_PHY_ADDR:
+ ret = 0x98;
+ break;
+ case MV64340_ETH_SMI:
+ ret = BIT(27);
+ break;
case MV64340_CUNIT_ARBITER_CONTROL_REG:
ret = 0x11ff0000 | (s->gpp_int_level << 10);
break;
diff --git a/hw/pci-host/mv643xx.h b/hw/pci-host/mv643xx.h
index cd26a43..f2e1bae 100644
--- a/hw/pci-host/mv643xx.h
+++ b/hw/pci-host/mv643xx.h
@@ -656,6 +656,9 @@
/* Ethernet Unit Registers */
/****************************************/
+#define MV64340_ETH_PHY_ADDR 0x2000
+#define MV64340_ETH_SMI 0x2004
+
/*******************************************/
/* CUNIT Registers */
/*******************************************/
diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c
index af5489d..9c99441 100644
--- a/hw/ppc/pegasos2.c
+++ b/hw/ppc/pegasos2.c
@@ -44,6 +44,8 @@
#define PROM_ADDR 0xfff00000
#define PROM_SIZE 0x80000
+#define INITRD_MIN_ADDR 0x600000
+
#define KVMPPC_HCALL_BASE 0xf000
#define KVMPPC_H_RTAS (KVMPPC_HCALL_BASE + 0x0)
#define KVMPPC_H_VOF_CLIENT (KVMPPC_HCALL_BASE + 0x5)
@@ -80,6 +82,8 @@
uint64_t kernel_addr;
uint64_t kernel_entry;
uint64_t kernel_size;
+ uint64_t initrd_addr;
+ uint64_t initrd_size;
};
static void *build_fdt(MachineState *machine, int *fdt_size);
@@ -117,7 +121,8 @@
I2CBus *i2c_bus;
const char *fwname = machine->firmware ?: PROM_FILENAME;
char *filename;
- int i, sz;
+ int i;
+ ssize_t sz;
uint8_t *spd_data;
/* init CPU */
@@ -213,6 +218,20 @@
warn_report("Using Virtual OpenFirmware but no -kernel option.");
}
+ if (machine->initrd_filename) {
+ pm->initrd_addr = pm->kernel_addr + pm->kernel_size + 64 * KiB;
+ pm->initrd_addr = ROUND_UP(pm->initrd_addr, 4);
+ pm->initrd_addr = MAX(pm->initrd_addr, INITRD_MIN_ADDR);
+ sz = load_image_targphys(machine->initrd_filename, pm->initrd_addr,
+ machine->ram_size - pm->initrd_addr);
+ if (sz <= 0) {
+ error_report("Could not load initrd '%s'",
+ machine->initrd_filename);
+ exit(1);
+ }
+ pm->initrd_size = sz;
+ }
+
if (!pm->vof && machine->kernel_cmdline && machine->kernel_cmdline[0]) {
warn_report("Option -append may be ineffective with -bios.");
}
@@ -335,6 +354,11 @@
error_report("Memory for kernel is in use");
exit(1);
}
+ if (pm->initrd_size &&
+ vof_claim(pm->vof, pm->initrd_addr, pm->initrd_size, 0) == -1) {
+ error_report("Memory for initrd is in use");
+ exit(1);
+ }
fdt = build_fdt(machine, &sz);
/* FIXME: VOF assumes entry is same as load address */
d[0] = cpu_to_be64(pm->kernel_entry);
@@ -966,6 +990,12 @@
qemu_fdt_setprop_string(fdt, "/memory@0", "name", "memory");
qemu_fdt_add_subnode(fdt, "/chosen");
+ if (pm->initrd_addr && pm->initrd_size) {
+ qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
+ pm->initrd_addr + pm->initrd_size);
+ qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start",
+ pm->initrd_addr);
+ }
qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
machine->kernel_cmdline ?: "");
qemu_fdt_setprop_string(fdt, "/chosen", "name", "chosen");
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index fc08317..eb54f93 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -887,6 +887,18 @@
pnv->num_chips =
machine->smp.max_cpus / (machine->smp.cores * machine->smp.threads);
+
+ if (machine->smp.threads > 8) {
+ error_report("Cannot support more than 8 threads/core "
+ "on a powernv machine");
+ exit(1);
+ }
+ if (!is_power_of_2(machine->smp.threads)) {
+ error_report("Cannot support %d threads/core on a powernv"
+ "machine because it must be a power of 2",
+ machine->smp.threads);
+ exit(1);
+ }
/*
* TODO: should we decide on how many chips we can create based
* on #cores and Venice vs. Murano vs. Naples chip type etc...,
@@ -1429,14 +1441,15 @@
}
static void pnv_chip_quad_realize_one(PnvChip *chip, PnvQuad *eq,
- PnvCore *pnv_core)
+ PnvCore *pnv_core,
+ const char *type)
{
char eq_name[32];
int core_id = CPU_CORE(pnv_core)->core_id;
snprintf(eq_name, sizeof(eq_name), "eq[%d]", core_id);
object_initialize_child_with_props(OBJECT(chip), eq_name, eq,
- sizeof(*eq), TYPE_PNV_QUAD,
+ sizeof(*eq), type,
&error_fatal, NULL);
object_property_set_int(OBJECT(eq), "quad-id", core_id, &error_fatal);
@@ -1454,7 +1467,8 @@
for (i = 0; i < chip9->nr_quads; i++) {
PnvQuad *eq = &chip9->quads[i];
- pnv_chip_quad_realize_one(chip, eq, chip->cores[i * 4]);
+ pnv_chip_quad_realize_one(chip, eq, chip->cores[i * 4],
+ PNV_QUAD_TYPE_NAME("power9"));
pnv_xscom_add_subregion(chip, PNV9_XSCOM_EQ_BASE(eq->quad_id),
&eq->xscom_regs);
@@ -1666,10 +1680,14 @@
for (i = 0; i < chip10->nr_quads; i++) {
PnvQuad *eq = &chip10->quads[i];
- pnv_chip_quad_realize_one(chip, eq, chip->cores[i * 4]);
+ pnv_chip_quad_realize_one(chip, eq, chip->cores[i * 4],
+ PNV_QUAD_TYPE_NAME("power10"));
pnv_xscom_add_subregion(chip, PNV10_XSCOM_EQ_BASE(eq->quad_id),
&eq->xscom_regs);
+
+ pnv_xscom_add_subregion(chip, PNV10_XSCOM_QME_BASE(eq->quad_id),
+ &eq->xscom_qme_regs);
}
}
diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
index 0bc3ad4..9b39d52 100644
--- a/hw/ppc/pnv_core.c
+++ b/hw/ppc/pnv_core.c
@@ -85,8 +85,8 @@
val = 0x24f000000000000ull;
break;
default:
- qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx "\n",
- addr);
+ qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__,
+ offset);
}
return val;
@@ -95,8 +95,10 @@
static void pnv_core_power8_xscom_write(void *opaque, hwaddr addr, uint64_t val,
unsigned int width)
{
- qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx "\n",
- addr);
+ uint32_t offset = addr >> 3;
+
+ qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__,
+ offset);
}
static const MemoryRegionOps pnv_core_power8_xscom_ops = {
@@ -116,6 +118,8 @@
#define PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_HYP 0xf010d
#define PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_OTR 0xf010a
+#define PNV9_XSCOM_EC_CORE_THREAD_STATE 0x10ab3
+
static uint64_t pnv_core_power9_xscom_read(void *opaque, hwaddr addr,
unsigned int width)
{
@@ -134,9 +138,12 @@
case PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_OTR:
val = 0x0;
break;
+ case PNV9_XSCOM_EC_CORE_THREAD_STATE:
+ val = 0;
+ break;
default:
- qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx "\n",
- addr);
+ qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__,
+ offset);
}
return val;
@@ -152,8 +159,8 @@
case PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_OTR:
break;
default:
- qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx "\n",
- addr);
+ qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__,
+ offset);
}
}
@@ -167,12 +174,59 @@
.endianness = DEVICE_BIG_ENDIAN,
};
-static void pnv_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp)
+/*
+ * POWER10 core controls
+ */
+
+#define PNV10_XSCOM_EC_CORE_THREAD_STATE 0x412
+
+static uint64_t pnv_core_power10_xscom_read(void *opaque, hwaddr addr,
+ unsigned int width)
+{
+ uint32_t offset = addr >> 3;
+ uint64_t val = 0;
+
+ switch (offset) {
+ case PNV10_XSCOM_EC_CORE_THREAD_STATE:
+ val = 0;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__,
+ offset);
+ }
+
+ return val;
+}
+
+static void pnv_core_power10_xscom_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned int width)
+{
+ uint32_t offset = addr >> 3;
+
+ switch (offset) {
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__,
+ offset);
+ }
+}
+
+static const MemoryRegionOps pnv_core_power10_xscom_ops = {
+ .read = pnv_core_power10_xscom_read,
+ .write = pnv_core_power10_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_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp,
+ int thread_index)
{
CPUPPCState *env = &cpu->env;
int core_pir;
- int thread_index = 0; /* TODO: TCG supports only one thread */
ppc_spr_t *pir = &env->spr_cb[SPR_PIR];
+ ppc_spr_t *tir = &env->spr_cb[SPR_TIR];
Error *local_err = NULL;
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pc->chip);
@@ -188,11 +242,7 @@
core_pir = object_property_get_uint(OBJECT(pc), "pir", &error_abort);
- /*
- * The PIR of a thread is the core PIR + the thread index. We will
- * need to find a way to get the thread index when TCG supports
- * more than 1. We could use the object name ?
- */
+ tir->default_value = thread_index;
pir->default_value = core_pir + thread_index;
/* Set time-base frequency to 512 MHz */
@@ -241,16 +291,15 @@
}
for (j = 0; j < cc->nr_threads; j++) {
- pnv_core_cpu_realize(pc, pc->threads[j], &local_err);
+ pnv_core_cpu_realize(pc, pc->threads[j], &local_err, j);
if (local_err) {
goto err;
}
}
snprintf(name, sizeof(name), "xscom-core.%d", cc->core_id);
- /* TODO: check PNV_XSCOM_EX_SIZE for p10 */
pnv_xscom_region_init(&pc->xscom_regs, OBJECT(dev), pcc->xscom_ops,
- pc, name, PNV_XSCOM_EX_SIZE);
+ pc, name, pcc->xscom_size);
qemu_register_reset(pnv_core_reset, pc);
return;
@@ -302,6 +351,7 @@
PnvCoreClass *pcc = PNV_CORE_CLASS(oc);
pcc->xscom_ops = &pnv_core_power8_xscom_ops;
+ pcc->xscom_size = PNV_XSCOM_EX_SIZE;
}
static void pnv_core_power9_class_init(ObjectClass *oc, void *data)
@@ -309,14 +359,15 @@
PnvCoreClass *pcc = PNV_CORE_CLASS(oc);
pcc->xscom_ops = &pnv_core_power9_xscom_ops;
+ pcc->xscom_size = PNV_XSCOM_EX_SIZE;
}
static void pnv_core_power10_class_init(ObjectClass *oc, void *data)
{
PnvCoreClass *pcc = PNV_CORE_CLASS(oc);
- /* TODO: Use the P9 XSCOMs for now on P10 */
- pcc->xscom_ops = &pnv_core_power9_xscom_ops;
+ pcc->xscom_ops = &pnv_core_power10_xscom_ops;
+ pcc->xscom_size = PNV10_XSCOM_EC_SIZE;
}
static void pnv_core_class_init(ObjectClass *oc, void *data)
@@ -360,8 +411,8 @@
#define P9X_EX_NCU_SPEC_BAR 0x11010
-static uint64_t pnv_quad_xscom_read(void *opaque, hwaddr addr,
- unsigned int width)
+static uint64_t pnv_quad_power9_xscom_read(void *opaque, hwaddr addr,
+ unsigned int width)
{
uint32_t offset = addr >> 3;
uint64_t val = -1;
@@ -372,15 +423,15 @@
val = 0;
break;
default:
- qemu_log_mask(LOG_UNIMP, "%s: writing @0x%08x\n", __func__,
+ qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__,
offset);
}
return val;
}
-static void pnv_quad_xscom_write(void *opaque, hwaddr addr, uint64_t val,
- unsigned int width)
+static void pnv_quad_power9_xscom_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned int width)
{
uint32_t offset = addr >> 3;
@@ -389,14 +440,14 @@
case P9X_EX_NCU_SPEC_BAR + 0x400: /* Second EX */
break;
default:
- qemu_log_mask(LOG_UNIMP, "%s: writing @0x%08x\n", __func__,
+ qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__,
offset);
}
}
-static const MemoryRegionOps pnv_quad_xscom_ops = {
- .read = pnv_quad_xscom_read,
- .write = pnv_quad_xscom_write,
+static const MemoryRegionOps pnv_quad_power9_xscom_ops = {
+ .read = pnv_quad_power9_xscom_read,
+ .write = pnv_quad_power9_xscom_write,
.valid.min_access_size = 8,
.valid.max_access_size = 8,
.impl.min_access_size = 8,
@@ -404,14 +455,124 @@
.endianness = DEVICE_BIG_ENDIAN,
};
-static void pnv_quad_realize(DeviceState *dev, Error **errp)
+/*
+ * POWER10 Quads
+ */
+
+static uint64_t pnv_quad_power10_xscom_read(void *opaque, hwaddr addr,
+ unsigned int width)
+{
+ uint32_t offset = addr >> 3;
+ uint64_t val = -1;
+
+ switch (offset) {
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__,
+ offset);
+ }
+
+ return val;
+}
+
+static void pnv_quad_power10_xscom_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned int width)
+{
+ uint32_t offset = addr >> 3;
+
+ switch (offset) {
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__,
+ offset);
+ }
+}
+
+static const MemoryRegionOps pnv_quad_power10_xscom_ops = {
+ .read = pnv_quad_power10_xscom_read,
+ .write = pnv_quad_power10_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,
+};
+
+#define P10_QME_SPWU_HYP 0x83c
+#define P10_QME_SSH_HYP 0x82c
+
+static uint64_t pnv_qme_power10_xscom_read(void *opaque, hwaddr addr,
+ unsigned int width)
+{
+ uint32_t offset = addr >> 3;
+ uint64_t val = -1;
+
+ /*
+ * Forth nibble selects the core within a quad, mask it to process read
+ * for any core.
+ */
+ switch (offset & ~0xf000) {
+ case P10_QME_SPWU_HYP:
+ case P10_QME_SSH_HYP:
+ return 0;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__,
+ offset);
+ }
+
+ return val;
+}
+
+static void pnv_qme_power10_xscom_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned int width)
+{
+ uint32_t offset = addr >> 3;
+
+ switch (offset) {
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__,
+ offset);
+ }
+}
+
+static const MemoryRegionOps pnv_qme_power10_xscom_ops = {
+ .read = pnv_qme_power10_xscom_read,
+ .write = pnv_qme_power10_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_quad_power9_realize(DeviceState *dev, Error **errp)
{
PnvQuad *eq = PNV_QUAD(dev);
+ PnvQuadClass *pqc = PNV_QUAD_GET_CLASS(eq);
char name[32];
snprintf(name, sizeof(name), "xscom-quad.%d", eq->quad_id);
- pnv_xscom_region_init(&eq->xscom_regs, OBJECT(dev), &pnv_quad_xscom_ops,
- eq, name, PNV9_XSCOM_EQ_SIZE);
+ pnv_xscom_region_init(&eq->xscom_regs, OBJECT(dev),
+ pqc->xscom_ops,
+ eq, name,
+ pqc->xscom_size);
+}
+
+static void pnv_quad_power10_realize(DeviceState *dev, Error **errp)
+{
+ PnvQuad *eq = PNV_QUAD(dev);
+ PnvQuadClass *pqc = PNV_QUAD_GET_CLASS(eq);
+ char name[32];
+
+ snprintf(name, sizeof(name), "xscom-quad.%d", eq->quad_id);
+ pnv_xscom_region_init(&eq->xscom_regs, OBJECT(dev),
+ pqc->xscom_ops,
+ eq, name,
+ pqc->xscom_size);
+
+ snprintf(name, sizeof(name), "xscom-qme.%d", eq->quad_id);
+ pnv_xscom_region_init(&eq->xscom_qme_regs, OBJECT(dev),
+ pqc->xscom_qme_ops,
+ eq, name,
+ pqc->xscom_qme_size);
}
static Property pnv_quad_properties[] = {
@@ -419,25 +580,58 @@
DEFINE_PROP_END_OF_LIST(),
};
+static void pnv_quad_power9_class_init(ObjectClass *oc, void *data)
+{
+ PnvQuadClass *pqc = PNV_QUAD_CLASS(oc);
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = pnv_quad_power9_realize;
+
+ pqc->xscom_ops = &pnv_quad_power9_xscom_ops;
+ pqc->xscom_size = PNV9_XSCOM_EQ_SIZE;
+}
+
+static void pnv_quad_power10_class_init(ObjectClass *oc, void *data)
+{
+ PnvQuadClass *pqc = PNV_QUAD_CLASS(oc);
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = pnv_quad_power10_realize;
+
+ pqc->xscom_ops = &pnv_quad_power10_xscom_ops;
+ pqc->xscom_size = PNV10_XSCOM_EQ_SIZE;
+
+ pqc->xscom_qme_ops = &pnv_qme_power10_xscom_ops;
+ pqc->xscom_qme_size = PNV10_XSCOM_QME_SIZE;
+}
+
static void pnv_quad_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
- dc->realize = pnv_quad_realize;
device_class_set_props(dc, pnv_quad_properties);
dc->user_creatable = false;
}
-static const TypeInfo pnv_quad_info = {
- .name = TYPE_PNV_QUAD,
- .parent = TYPE_DEVICE,
- .instance_size = sizeof(PnvQuad),
- .class_init = pnv_quad_class_init,
+static const TypeInfo pnv_quad_infos[] = {
+ {
+ .name = TYPE_PNV_QUAD,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(PnvQuad),
+ .class_size = sizeof(PnvQuadClass),
+ .class_init = pnv_quad_class_init,
+ .abstract = true,
+ },
+ {
+ .parent = TYPE_PNV_QUAD,
+ .name = PNV_QUAD_TYPE_NAME("power9"),
+ .class_init = pnv_quad_power9_class_init,
+ },
+ {
+ .parent = TYPE_PNV_QUAD,
+ .name = PNV_QUAD_TYPE_NAME("power10"),
+ .class_init = pnv_quad_power10_class_init,
+ },
};
-static void pnv_core_register_types(void)
-{
- type_register_static(&pnv_quad_info);
-}
-
-type_init(pnv_core_register_types)
+DEFINE_TYPES(pnv_quad_infos);
diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c
index 8aa09ab..daaa2f0 100644
--- a/hw/ppc/pnv_psi.c
+++ b/hw/ppc/pnv_psi.c
@@ -121,8 +121,12 @@
#define PSIHB9_BAR_MASK 0x00fffffffff00000ull
#define PSIHB9_FSPBAR_MASK 0x00ffffff00000000ull
+/* mmio address to xscom address */
#define PSIHB_REG(addr) (((addr) >> 3) + PSIHB_XSCOM_BAR)
+/* xscom address to mmio address */
+#define PSIHB_MMIO(reg) ((reg - PSIHB_XSCOM_BAR) << 3)
+
static void pnv_psi_set_bar(PnvPsi *psi, uint64_t bar)
{
PnvPsiClass *ppc = PNV_PSI_GET_CLASS(psi);
@@ -769,24 +773,31 @@
static uint64_t pnv_psi_p9_xscom_read(void *opaque, hwaddr addr, unsigned size)
{
- /* No read are expected */
- qemu_log_mask(LOG_GUEST_ERROR, "PSI: xscom read at 0x%" PRIx64 "\n", addr);
- return -1;
+ uint32_t reg = addr >> 3;
+ uint64_t val = -1;
+
+ if (reg < PSIHB_XSCOM_BAR) {
+ /* FIR, not modeled */
+ qemu_log_mask(LOG_UNIMP, "PSI: xscom read at 0x%08x\n", reg);
+ } else {
+ val = pnv_psi_p9_mmio_read(opaque, PSIHB_MMIO(reg), size);
+ }
+ return val;
}
static void pnv_psi_p9_xscom_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
PnvPsi *psi = PNV_PSI(opaque);
+ uint32_t reg = addr >> 3;
- /* XSCOM is only used to set the PSIHB MMIO region */
- switch (addr >> 3) {
- case PSIHB_XSCOM_BAR:
+ if (reg < PSIHB_XSCOM_BAR) {
+ /* FIR, not modeled */
+ qemu_log_mask(LOG_UNIMP, "PSI: xscom write at 0x%08x\n", reg);
+ } else if (reg == PSIHB_XSCOM_BAR) {
pnv_psi_set_bar(psi, val);
- break;
- default:
- qemu_log_mask(LOG_GUEST_ERROR, "PSI: xscom write at 0x%" PRIx64 "\n",
- addr);
+ } else {
+ pnv_psi_p9_mmio_write(opaque, PSIHB_MMIO(reg), val, size);
}
}
@@ -852,6 +863,8 @@
object_property_set_int(OBJECT(xsrc), "nr-irqs", PSIHB9_NUM_IRQS,
&error_fatal);
object_property_set_link(OBJECT(xsrc), "xive", OBJECT(psi), &error_abort);
+ object_property_set_int(OBJECT(xsrc), "reset-pq", XIVE_ESB_RESET,
+ &error_abort);
if (!qdev_realize(DEVICE(xsrc), NULL, errp)) {
return;
}
diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
index 82e4408..0e0a3d9 100644
--- a/hw/ppc/ppc.c
+++ b/hw/ppc/ppc.c
@@ -535,23 +535,24 @@
void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value)
{
ppc_tb_t *tb_env = env->tb_env;
+ int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
uint64_t tb;
- tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset);
+ tb = cpu_ppc_get_tb(tb_env, clock, tb_env->tb_offset);
tb &= 0xFFFFFFFF00000000ULL;
- cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- &tb_env->tb_offset, tb | (uint64_t)value);
+ cpu_ppc_store_tb(tb_env, clock, &tb_env->tb_offset, tb | (uint64_t)value);
}
static inline void _cpu_ppc_store_tbu(CPUPPCState *env, uint32_t value)
{
ppc_tb_t *tb_env = env->tb_env;
+ int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
uint64_t tb;
- tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset);
+ tb = cpu_ppc_get_tb(tb_env, clock, tb_env->tb_offset);
tb &= 0x00000000FFFFFFFFULL;
- cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- &tb_env->tb_offset, ((uint64_t)value << 32) | tb);
+ cpu_ppc_store_tb(tb_env, clock, &tb_env->tb_offset,
+ ((uint64_t)value << 32) | tb);
}
void cpu_ppc_store_tbu (CPUPPCState *env, uint32_t value)
@@ -584,23 +585,24 @@
void cpu_ppc_store_atbl (CPUPPCState *env, uint32_t value)
{
ppc_tb_t *tb_env = env->tb_env;
+ int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
uint64_t tb;
- tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset);
+ tb = cpu_ppc_get_tb(tb_env, clock, tb_env->atb_offset);
tb &= 0xFFFFFFFF00000000ULL;
- cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- &tb_env->atb_offset, tb | (uint64_t)value);
+ cpu_ppc_store_tb(tb_env, clock, &tb_env->atb_offset, tb | (uint64_t)value);
}
void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value)
{
ppc_tb_t *tb_env = env->tb_env;
+ int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
uint64_t tb;
- tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset);
+ tb = cpu_ppc_get_tb(tb_env, clock, tb_env->atb_offset);
tb &= 0x00000000FFFFFFFFULL;
- cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- &tb_env->atb_offset, ((uint64_t)value << 32) | tb);
+ cpu_ppc_store_tb(tb_env, clock, &tb_env->atb_offset,
+ ((uint64_t)value << 32) | tb);
}
uint64_t cpu_ppc_load_vtb(CPUPPCState *env)
@@ -622,14 +624,13 @@
void cpu_ppc_store_tbu40(CPUPPCState *env, uint64_t value)
{
ppc_tb_t *tb_env = env->tb_env;
+ int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
uint64_t tb;
- tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- tb_env->tb_offset);
+ tb = cpu_ppc_get_tb(tb_env, clock, tb_env->tb_offset);
tb &= 0xFFFFFFUL;
tb |= (value & ~0xFFFFFFUL);
- cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- &tb_env->tb_offset, tb);
+ cpu_ppc_store_tb(tb_env, clock, &tb_env->tb_offset, tb);
}
static void cpu_ppc_tb_stop (CPUPPCState *env)
@@ -788,8 +789,8 @@
QEMUTimer *timer,
void (*raise_excp)(void *),
void (*lower_excp)(PowerPCCPU *),
- target_ulong decr, target_ulong value,
- int nr_bits)
+ uint32_t flags, target_ulong decr,
+ target_ulong value, int nr_bits)
{
CPUPPCState *env = &cpu->env;
ppc_tb_t *tb_env = env->tb_env;
@@ -819,15 +820,15 @@
* On MSB edge based DEC implementations the MSB going from 0 -> 1 triggers
* an edge interrupt, so raise it here too.
*/
- if (((tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL) && signed_value < 0) ||
- ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) && signed_value < 0
+ if (((flags & PPC_DECR_UNDERFLOW_LEVEL) && signed_value < 0) ||
+ ((flags & PPC_DECR_UNDERFLOW_TRIGGERED) && signed_value < 0
&& signed_decr >= 0)) {
(*raise_excp)(cpu);
return;
}
/* On MSB level based systems a 0 for the MSB stops interrupt delivery */
- if (signed_value >= 0 && (tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL)) {
+ if (signed_value >= 0 && (flags & PPC_DECR_UNDERFLOW_LEVEL)) {
(*lower_excp)(cpu);
}
@@ -846,8 +847,8 @@
ppc_tb_t *tb_env = cpu->env.tb_env;
__cpu_ppc_store_decr(cpu, &tb_env->decr_next, tb_env->decr_timer,
- tb_env->decr_timer->cb, &cpu_ppc_decr_lower, decr,
- value, nr_bits);
+ tb_env->decr_timer->cb, &cpu_ppc_decr_lower,
+ tb_env->flags, decr, value, nr_bits);
}
void cpu_ppc_store_decr(CPUPPCState *env, target_ulong value)
@@ -876,8 +877,10 @@
ppc_tb_t *tb_env = cpu->env.tb_env;
if (tb_env->hdecr_timer != NULL) {
+ /* HDECR (Book3S 64bit) is edge-based, not level like DECR */
__cpu_ppc_store_decr(cpu, &tb_env->hdecr_next, tb_env->hdecr_timer,
tb_env->hdecr_timer->cb, &cpu_ppc_hdecr_lower,
+ PPC_DECR_UNDERFLOW_TRIGGERED,
hdecr, value, nr_bits);
}
}
diff --git a/hw/ppc/ppc440.h b/hw/ppc/ppc440.h
index 7c24db8..909373f 100644
--- a/hw/ppc/ppc440.h
+++ b/hw/ppc/ppc440.h
@@ -18,6 +18,5 @@
void ppc4xx_sdr_init(CPUPPCState *env);
void ppc4xx_ahb_init(CPUPPCState *env);
void ppc4xx_dma_init(CPUPPCState *env, int dcr_base);
-void ppc460ex_pcie_init(CPUPPCState *env);
#endif /* PPC440_H */
diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c
index f061b8c..45f409c 100644
--- a/hw/ppc/ppc440_bamboo.c
+++ b/hw/ppc/ppc440_bamboo.c
@@ -205,8 +205,7 @@
ppc4xx_sdram_ddr_enable(PPC4xx_SDRAM_DDR(dev));
/* PCI */
- dev = sysbus_create_varargs(TYPE_PPC4xx_PCI_HOST_BRIDGE,
- PPC440EP_PCI_CONFIG,
+ dev = sysbus_create_varargs(TYPE_PPC4xx_PCI_HOST, PPC440EP_PCI_CONFIG,
qdev_get_gpio_in(uicdev, pci_irq_nrs[0]),
qdev_get_gpio_in(uicdev, pci_irq_nrs[1]),
qdev_get_gpio_in(uicdev, pci_irq_nrs[2]),
diff --git a/hw/ppc/ppc440_pcix.c b/hw/ppc/ppc440_pcix.c
index f10f93c..672090d 100644
--- a/hw/ppc/ppc440_pcix.c
+++ b/hw/ppc/ppc440_pcix.c
@@ -23,6 +23,7 @@
#include "qemu/error-report.h"
#include "qemu/log.h"
#include "qemu/module.h"
+#include "qemu/units.h"
#include "hw/irq.h"
#include "hw/ppc/ppc.h"
#include "hw/ppc/ppc4xx.h"
@@ -44,8 +45,7 @@
MemoryRegion mr;
};
-#define TYPE_PPC440_PCIX_HOST_BRIDGE "ppc440-pcix-host"
-OBJECT_DECLARE_SIMPLE_TYPE(PPC440PCIXState, PPC440_PCIX_HOST_BRIDGE)
+OBJECT_DECLARE_SIMPLE_TYPE(PPC440PCIXState, PPC440_PCIX_HOST)
#define PPC440_PCIX_NR_POMS 3
#define PPC440_PCIX_NR_PIMS 3
@@ -64,6 +64,7 @@
MemoryRegion container;
MemoryRegion iomem;
MemoryRegion busmem;
+ MemoryRegion regs;
};
#define PPC440_REG_BASE 0x80000
@@ -397,7 +398,7 @@
static void ppc440_pcix_reset(DeviceState *dev)
{
- struct PPC440PCIXState *s = PPC440_PCIX_HOST_BRIDGE(dev);
+ struct PPC440PCIXState *s = PPC440_PCIX_HOST(dev);
int i;
for (i = 0; i < PPC440_PCIX_NR_POMS; i++) {
@@ -487,15 +488,17 @@
PCIHostState *h;
h = PCI_HOST_BRIDGE(dev);
- s = PPC440_PCIX_HOST_BRIDGE(dev);
+ s = PPC440_PCIX_HOST(dev);
sysbus_init_irq(sbd, &s->irq);
- memory_region_init(&s->busmem, OBJECT(dev), "pci bus memory", UINT64_MAX);
+ memory_region_init(&s->busmem, OBJECT(dev), "pci-mem", UINT64_MAX);
+ memory_region_init(&s->iomem, OBJECT(dev), "pci-io", 64 * KiB);
h->bus = pci_register_root_bus(dev, NULL, ppc440_pcix_set_irq,
- ppc440_pcix_map_irq, &s->irq, &s->busmem,
- get_system_io(), PCI_DEVFN(0, 0), 1, TYPE_PCI_BUS);
+ ppc440_pcix_map_irq, &s->irq, &s->busmem, &s->iomem,
+ PCI_DEVFN(0, 0), 1, TYPE_PCI_BUS);
- s->dev = pci_create_simple(h->bus, PCI_DEVFN(0, 0), "ppc4xx-host-bridge");
+ s->dev = pci_create_simple(h->bus, PCI_DEVFN(0, 0),
+ TYPE_PPC4xx_HOST_BRIDGE);
memory_region_init(&s->bm, OBJECT(s), "bm-ppc440-pcix", UINT64_MAX);
memory_region_add_subregion(&s->bm, 0x0, &s->busmem);
@@ -507,12 +510,13 @@
h, "pci-conf-idx", 4);
memory_region_init_io(&h->data_mem, OBJECT(s), &pci_host_data_le_ops,
h, "pci-conf-data", 4);
- memory_region_init_io(&s->iomem, OBJECT(s), &pci_reg_ops, s,
- "pci.reg", PPC440_REG_SIZE);
+ memory_region_init_io(&s->regs, OBJECT(s), &pci_reg_ops, s, "pci-reg",
+ PPC440_REG_SIZE);
memory_region_add_subregion(&s->container, PCIC0_CFGADDR, &h->conf_mem);
memory_region_add_subregion(&s->container, PCIC0_CFGDATA, &h->data_mem);
- memory_region_add_subregion(&s->container, PPC440_REG_BASE, &s->iomem);
+ memory_region_add_subregion(&s->container, PPC440_REG_BASE, &s->regs);
sysbus_init_mmio(sbd, &s->container);
+ sysbus_init_mmio(sbd, &s->iomem);
}
static void ppc440_pcix_class_init(ObjectClass *klass, void *data)
@@ -524,7 +528,7 @@
}
static const TypeInfo ppc440_pcix_info = {
- .name = TYPE_PPC440_PCIX_HOST_BRIDGE,
+ .name = TYPE_PPC440_PCIX_HOST,
.parent = TYPE_PCI_HOST_BRIDGE,
.instance_size = sizeof(PPC440PCIXState),
.class_init = ppc440_pcix_class_init,
diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c
index 6512639..4181c84 100644
--- a/hw/ppc/ppc440_uc.c
+++ b/hw/ppc/ppc440_uc.c
@@ -17,6 +17,7 @@
#include "hw/qdev-properties.h"
#include "hw/pci/pci.h"
#include "sysemu/reset.h"
+#include "cpu.h"
#include "ppc440.h"
/*****************************************************************************/
@@ -769,15 +770,17 @@
*/
#include "hw/pci/pcie_host.h"
-#define TYPE_PPC460EX_PCIE_HOST "ppc460ex-pcie-host"
OBJECT_DECLARE_SIMPLE_TYPE(PPC460EXPCIEState, PPC460EX_PCIE_HOST)
struct PPC460EXPCIEState {
- PCIExpressHost host;
+ PCIExpressHost parent_obj;
+ MemoryRegion busmem;
MemoryRegion iomem;
qemu_irq irq[4];
+ int32_t num;
int32_t dcrn_base;
+ PowerPCCPU *cpu;
uint64_t cfg_base;
uint32_t cfg_mask;
@@ -795,9 +798,6 @@
uint32_t cfg;
};
-#define DCRN_PCIE0_BASE 0x100
-#define DCRN_PCIE1_BASE 0x120
-
enum {
PEGPL_CFGBAH = 0x0,
PEGPL_CFGBAL,
@@ -826,78 +826,78 @@
static uint32_t dcr_read_pcie(void *opaque, int dcrn)
{
- PPC460EXPCIEState *state = opaque;
+ PPC460EXPCIEState *s = opaque;
uint32_t ret = 0;
- switch (dcrn - state->dcrn_base) {
+ switch (dcrn - s->dcrn_base) {
case PEGPL_CFGBAH:
- ret = state->cfg_base >> 32;
+ ret = s->cfg_base >> 32;
break;
case PEGPL_CFGBAL:
- ret = state->cfg_base;
+ ret = s->cfg_base;
break;
case PEGPL_CFGMSK:
- ret = state->cfg_mask;
+ ret = s->cfg_mask;
break;
case PEGPL_MSGBAH:
- ret = state->msg_base >> 32;
+ ret = s->msg_base >> 32;
break;
case PEGPL_MSGBAL:
- ret = state->msg_base;
+ ret = s->msg_base;
break;
case PEGPL_MSGMSK:
- ret = state->msg_mask;
+ ret = s->msg_mask;
break;
case PEGPL_OMR1BAH:
- ret = state->omr1_base >> 32;
+ ret = s->omr1_base >> 32;
break;
case PEGPL_OMR1BAL:
- ret = state->omr1_base;
+ ret = s->omr1_base;
break;
case PEGPL_OMR1MSKH:
- ret = state->omr1_mask >> 32;
+ ret = s->omr1_mask >> 32;
break;
case PEGPL_OMR1MSKL:
- ret = state->omr1_mask;
+ ret = s->omr1_mask;
break;
case PEGPL_OMR2BAH:
- ret = state->omr2_base >> 32;
+ ret = s->omr2_base >> 32;
break;
case PEGPL_OMR2BAL:
- ret = state->omr2_base;
+ ret = s->omr2_base;
break;
case PEGPL_OMR2MSKH:
- ret = state->omr2_mask >> 32;
+ ret = s->omr2_mask >> 32;
break;
case PEGPL_OMR2MSKL:
- ret = state->omr3_mask;
+ ret = s->omr3_mask;
break;
case PEGPL_OMR3BAH:
- ret = state->omr3_base >> 32;
+ ret = s->omr3_base >> 32;
break;
case PEGPL_OMR3BAL:
- ret = state->omr3_base;
+ ret = s->omr3_base;
break;
case PEGPL_OMR3MSKH:
- ret = state->omr3_mask >> 32;
+ ret = s->omr3_mask >> 32;
break;
case PEGPL_OMR3MSKL:
- ret = state->omr3_mask;
+ ret = s->omr3_mask;
break;
case PEGPL_REGBAH:
- ret = state->reg_base >> 32;
+ ret = s->reg_base >> 32;
break;
case PEGPL_REGBAL:
- ret = state->reg_base;
+ ret = s->reg_base;
break;
case PEGPL_REGMSK:
- ret = state->reg_mask;
+ ret = s->reg_mask;
break;
case PEGPL_SPECIAL:
- ret = state->special;
+ ret = s->special;
break;
case PEGPL_CFG:
- ret = state->cfg;
+ ret = s->cfg;
break;
}
@@ -1000,37 +1000,72 @@
qemu_set_irq(s->irq[irq_num], level);
}
+#define PPC440_PCIE_DCR(s, dcrn) \
+ ppc_dcr_register(&(s)->cpu->env, (s)->dcrn_base + (dcrn), (s), \
+ &dcr_read_pcie, &dcr_write_pcie)
+
+
+static void ppc460ex_pcie_register_dcrs(PPC460EXPCIEState *s)
+{
+ PPC440_PCIE_DCR(s, PEGPL_CFGBAH);
+ PPC440_PCIE_DCR(s, PEGPL_CFGBAL);
+ PPC440_PCIE_DCR(s, PEGPL_CFGMSK);
+ PPC440_PCIE_DCR(s, PEGPL_MSGBAH);
+ PPC440_PCIE_DCR(s, PEGPL_MSGBAL);
+ PPC440_PCIE_DCR(s, PEGPL_MSGMSK);
+ PPC440_PCIE_DCR(s, PEGPL_OMR1BAH);
+ PPC440_PCIE_DCR(s, PEGPL_OMR1BAL);
+ PPC440_PCIE_DCR(s, PEGPL_OMR1MSKH);
+ PPC440_PCIE_DCR(s, PEGPL_OMR1MSKL);
+ PPC440_PCIE_DCR(s, PEGPL_OMR2BAH);
+ PPC440_PCIE_DCR(s, PEGPL_OMR2BAL);
+ PPC440_PCIE_DCR(s, PEGPL_OMR2MSKH);
+ PPC440_PCIE_DCR(s, PEGPL_OMR2MSKL);
+ PPC440_PCIE_DCR(s, PEGPL_OMR3BAH);
+ PPC440_PCIE_DCR(s, PEGPL_OMR3BAL);
+ PPC440_PCIE_DCR(s, PEGPL_OMR3MSKH);
+ PPC440_PCIE_DCR(s, PEGPL_OMR3MSKL);
+ PPC440_PCIE_DCR(s, PEGPL_REGBAH);
+ PPC440_PCIE_DCR(s, PEGPL_REGBAL);
+ PPC440_PCIE_DCR(s, PEGPL_REGMSK);
+ PPC440_PCIE_DCR(s, PEGPL_SPECIAL);
+ PPC440_PCIE_DCR(s, PEGPL_CFG);
+}
+
static void ppc460ex_pcie_realize(DeviceState *dev, Error **errp)
{
PPC460EXPCIEState *s = PPC460EX_PCIE_HOST(dev);
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
- int i, id;
- char buf[16];
+ int i;
+ char buf[20];
- switch (s->dcrn_base) {
- case DCRN_PCIE0_BASE:
- id = 0;
- break;
- case DCRN_PCIE1_BASE:
- id = 1;
- break;
- default:
- error_setg(errp, "invalid PCIe DCRN base");
+ if (!s->cpu) {
+ error_setg(errp, "cpu link property must be set");
return;
}
- snprintf(buf, sizeof(buf), "pcie%d-io", id);
- memory_region_init(&s->iomem, OBJECT(s), buf, UINT64_MAX);
+ if (s->num < 0 || s->dcrn_base < 0) {
+ error_setg(errp, "busnum and dcrn-base properties must be set");
+ return;
+ }
+ snprintf(buf, sizeof(buf), "pcie%d-mem", s->num);
+ memory_region_init(&s->busmem, OBJECT(s), buf, UINT64_MAX);
+ snprintf(buf, sizeof(buf), "pcie%d-io", s->num);
+ memory_region_init(&s->iomem, OBJECT(s), buf, 64 * KiB);
for (i = 0; i < 4; i++) {
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]);
}
- snprintf(buf, sizeof(buf), "pcie.%d", id);
+ snprintf(buf, sizeof(buf), "pcie.%d", s->num);
pci->bus = pci_register_root_bus(DEVICE(s), buf, ppc460ex_set_irq,
- pci_swizzle_map_irq_fn, s, &s->iomem,
- get_system_io(), 0, 4, TYPE_PCIE_BUS);
+ pci_swizzle_map_irq_fn, s, &s->busmem,
+ &s->iomem, 0, 4, TYPE_PCIE_BUS);
+ ppc460ex_pcie_register_dcrs(s);
}
static Property ppc460ex_pcie_props[] = {
+ DEFINE_PROP_INT32("busnum", PPC460EXPCIEState, num, -1),
DEFINE_PROP_INT32("dcrn-base", PPC460EXPCIEState, dcrn_base, -1),
+ DEFINE_PROP_LINK("cpu", PPC460EXPCIEState, cpu, TYPE_POWERPC_CPU,
+ PowerPCCPU *),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1057,68 +1092,3 @@
}
type_init(ppc460ex_pcie_register)
-
-static void ppc460ex_pcie_register_dcrs(PPC460EXPCIEState *s, CPUPPCState *env)
-{
- ppc_dcr_register(env, s->dcrn_base + PEGPL_CFGBAH, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_CFGBAL, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_CFGMSK, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_MSGBAH, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_MSGBAL, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_MSGMSK, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR1BAH, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR1BAL, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR1MSKH, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR1MSKL, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR2BAH, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR2BAL, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR2MSKH, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR2MSKL, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR3BAH, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR3BAL, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR3MSKH, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR3MSKL, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_REGBAH, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_REGBAL, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_REGMSK, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_SPECIAL, s,
- &dcr_read_pcie, &dcr_write_pcie);
- ppc_dcr_register(env, s->dcrn_base + PEGPL_CFG, s,
- &dcr_read_pcie, &dcr_write_pcie);
-}
-
-void ppc460ex_pcie_init(CPUPPCState *env)
-{
- DeviceState *dev;
-
- dev = qdev_new(TYPE_PPC460EX_PCIE_HOST);
- qdev_prop_set_int32(dev, "dcrn-base", DCRN_PCIE0_BASE);
- sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
- ppc460ex_pcie_register_dcrs(PPC460EX_PCIE_HOST(dev), env);
-
- dev = qdev_new(TYPE_PPC460EX_PCIE_HOST);
- qdev_prop_set_int32(dev, "dcrn-base", DCRN_PCIE1_BASE);
- sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
- ppc460ex_pcie_register_dcrs(PPC460EX_PCIE_HOST(dev), env);
-}
diff --git a/hw/ppc/ppc4xx_pci.c b/hw/ppc/ppc4xx_pci.c
index 1d4a50f..6652119 100644
--- a/hw/ppc/ppc4xx_pci.c
+++ b/hw/ppc/ppc4xx_pci.c
@@ -46,7 +46,7 @@
uint32_t la;
};
-OBJECT_DECLARE_SIMPLE_TYPE(PPC4xxPCIState, PPC4xx_PCI_HOST_BRIDGE)
+OBJECT_DECLARE_SIMPLE_TYPE(PPC4xxPCIState, PPC4xx_PCI_HOST)
#define PPC4xx_PCI_NR_PMMS 3
#define PPC4xx_PCI_NR_PTMS 2
@@ -321,7 +321,7 @@
int i;
h = PCI_HOST_BRIDGE(dev);
- s = PPC4xx_PCI_HOST_BRIDGE(dev);
+ s = PPC4xx_PCI_HOST(dev);
for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
sysbus_init_irq(sbd, &s->irq[i]);
@@ -333,7 +333,7 @@
TYPE_PCI_BUS);
h->bus = b;
- pci_create_simple(b, 0, "ppc4xx-host-bridge");
+ pci_create_simple(b, 0, TYPE_PPC4xx_HOST_BRIDGE);
/* XXX split into 2 memory regions, one for config space, one for regs */
memory_region_init(&s->container, OBJECT(s), "pci-container", PCI_ALL_SIZE);
@@ -367,7 +367,7 @@
}
static const TypeInfo ppc4xx_host_bridge_info = {
- .name = "ppc4xx-host-bridge",
+ .name = TYPE_PPC4xx_HOST_BRIDGE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(PCIDevice),
.class_init = ppc4xx_host_bridge_class_init,
@@ -386,7 +386,7 @@
}
static const TypeInfo ppc4xx_pcihost_info = {
- .name = TYPE_PPC4xx_PCI_HOST_BRIDGE,
+ .name = TYPE_PPC4xx_PCI_HOST,
.parent = TYPE_PCI_HOST_BRIDGE,
.instance_size = sizeof(PPC4xxPCIState),
.class_init = ppc4xx_pcihost_class_init,
diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c
index cf065aa..1e615b8 100644
--- a/hw/ppc/sam460ex.c
+++ b/hw/ppc/sam460ex.c
@@ -45,6 +45,9 @@
/* dd bs=1 skip=$(($(stat -c '%s' updater/updater-460) - 0x80000)) \
if=updater/updater-460 of=u-boot-sam460-20100605.bin */
+#define PCIE0_DCRN_BASE 0x100
+#define PCIE1_DCRN_BASE 0x120
+
/* from Sam460 U-Boot include/configs/Sam460ex.h */
#define FLASH_BASE 0xfff00000
#define FLASH_BASE_H 0x4
@@ -266,8 +269,6 @@
static void sam460ex_init(MachineState *machine)
{
- MemoryRegion *address_space_mem = get_system_memory();
- MemoryRegion *isa = g_new(MemoryRegion, 1);
MemoryRegion *l2cache_ram = g_new(MemoryRegion, 1);
DeviceState *uic[4];
int i;
@@ -406,7 +407,8 @@
/* FIXME: remove this after fixing l2sram mapping in ppc440_uc.c? */
memory_region_init_ram(l2cache_ram, NULL, "ppc440.l2cache_ram", 256 * KiB,
&error_abort);
- memory_region_add_subregion(address_space_mem, 0x400000000LL, l2cache_ram);
+ memory_region_add_subregion(get_system_memory(), 0x400000000LL,
+ l2cache_ram);
/* USB */
sysbus_create_simple(TYPE_PPC4xx_EHCI, 0x4bffd0400,
@@ -421,16 +423,25 @@
usb_create_simple(usb_bus_find(-1), "usb-kbd");
usb_create_simple(usb_bus_find(-1), "usb-mouse");
- /* PCI bus */
- ppc460ex_pcie_init(env);
- /* All PCI irqs are connected to the same UIC pin (cf. UBoot source) */
- dev = sysbus_create_simple("ppc440-pcix-host", 0xc0ec00000,
- qdev_get_gpio_in(uic[1], 0));
- pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci.0"));
+ /* PCIe buses */
+ dev = qdev_new(TYPE_PPC460EX_PCIE_HOST);
+ qdev_prop_set_int32(dev, "busnum", 0);
+ qdev_prop_set_int32(dev, "dcrn-base", PCIE0_DCRN_BASE);
+ object_property_set_link(OBJECT(dev), "cpu", OBJECT(cpu), &error_abort);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
- memory_region_init_alias(isa, NULL, "isa_mmio", get_system_io(),
- 0, 0x10000);
- memory_region_add_subregion(get_system_memory(), 0xc08000000, isa);
+ dev = qdev_new(TYPE_PPC460EX_PCIE_HOST);
+ qdev_prop_set_int32(dev, "busnum", 1);
+ qdev_prop_set_int32(dev, "dcrn-base", PCIE1_DCRN_BASE);
+ object_property_set_link(OBJECT(dev), "cpu", OBJECT(cpu), &error_abort);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+ /* PCI bus */
+ /* All PCI irqs are connected to the same UIC pin (cf. UBoot source) */
+ dev = sysbus_create_simple(TYPE_PPC440_PCIX_HOST, 0xc0ec00000,
+ qdev_get_gpio_in(uic[1], 0));
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, 0xc08000000);
+ pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci.0"));
/* PCI devices */
pci_create_simple(pci_bus, PCI_DEVFN(6, 0), "sm501");
@@ -444,13 +455,13 @@
/* SoC has 4 UARTs
* but board has only one wired and two are present in fdt */
if (serial_hd(0) != NULL) {
- serial_mm_init(address_space_mem, 0x4ef600300, 0,
+ serial_mm_init(get_system_memory(), 0x4ef600300, 0,
qdev_get_gpio_in(uic[1], 1),
PPC_SERIAL_MM_BAUDBASE, serial_hd(0),
DEVICE_BIG_ENDIAN);
}
if (serial_hd(1) != NULL) {
- serial_mm_init(address_space_mem, 0x4ef600400, 0,
+ serial_mm_init(get_system_memory(), 0x4ef600400, 0,
qdev_get_gpio_in(uic[0], 1),
PPC_SERIAL_MM_BAUDBASE, serial_hd(1),
DEVICE_BIG_ENDIAN);
diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c
index a4e3c2f..b482d97 100644
--- a/hw/ppc/spapr_cpu_core.c
+++ b/hw/ppc/spapr_cpu_core.c
@@ -270,6 +270,8 @@
env->spr_cb[SPR_PIR].default_value = cs->cpu_index;
env->spr_cb[SPR_TIR].default_value = thread_index;
+ cpu_ppc_set_1lpar(cpu);
+
/* Set time-base frequency to 512 MHz. vhyp must be set first. */
cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ);
diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h
index 3d75706..4db2122 100644
--- a/include/hw/ppc/pnv_core.h
+++ b/include/hw/ppc/pnv_core.h
@@ -46,6 +46,7 @@
DeviceClass parent_class;
const MemoryRegionOps *xscom_ops;
+ uint64_t xscom_size;
};
#define PNV_CORE_TYPE_SUFFIX "-" TYPE_PNV_CORE
@@ -60,13 +61,28 @@
return (PnvCPUState *)cpu->machine_data;
}
+struct PnvQuadClass {
+ DeviceClass parent_class;
+
+ const MemoryRegionOps *xscom_ops;
+ uint64_t xscom_size;
+
+ const MemoryRegionOps *xscom_qme_ops;
+ uint64_t xscom_qme_size;
+};
+
#define TYPE_PNV_QUAD "powernv-cpu-quad"
-OBJECT_DECLARE_SIMPLE_TYPE(PnvQuad, PNV_QUAD)
+
+#define PNV_QUAD_TYPE_SUFFIX "-" TYPE_PNV_QUAD
+#define PNV_QUAD_TYPE_NAME(cpu_model) cpu_model PNV_QUAD_TYPE_SUFFIX
+
+OBJECT_DECLARE_TYPE(PnvQuad, PnvQuadClass, PNV_QUAD)
struct PnvQuad {
DeviceState parent_obj;
uint32_t quad_id;
MemoryRegion xscom_regs;
+ MemoryRegion xscom_qme_regs;
};
#endif /* PPC_PNV_CORE_H */
diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
index cbe848d..9bc6463 100644
--- a/include/hw/ppc/pnv_xscom.h
+++ b/include/hw/ppc/pnv_xscom.h
@@ -127,13 +127,24 @@
#define PNV10_XSCOM_EC(proc) \
((0x2 << 16) | ((1 << (3 - (proc))) << 12))
+#define PNV10_XSCOM_QME(chiplet) \
+ (PNV10_XSCOM_EQ(chiplet) | (0xE << 16))
+
+/*
+ * Make the region larger by 0x1000 (instead of starting at an offset) so the
+ * modelled addresses start from 0
+ */
+#define PNV10_XSCOM_QME_BASE(core) \
+ ((uint64_t) PNV10_XSCOM_QME(PNV10_XSCOM_EQ_CHIPLET(core)))
+#define PNV10_XSCOM_QME_SIZE (0x8000 + 0x1000)
+
#define PNV10_XSCOM_EQ_BASE(core) \
((uint64_t) PNV10_XSCOM_EQ(PNV10_XSCOM_EQ_CHIPLET(core)))
-#define PNV10_XSCOM_EQ_SIZE 0x100000
+#define PNV10_XSCOM_EQ_SIZE 0x20000
#define PNV10_XSCOM_EC_BASE(core) \
((uint64_t) PNV10_XSCOM_EQ_BASE(core) | PNV10_XSCOM_EC(core & 0x3))
-#define PNV10_XSCOM_EC_SIZE 0x100000
+#define PNV10_XSCOM_EC_SIZE 0x1000
#define PNV10_XSCOM_PSIHB_BASE 0x3011D00
#define PNV10_XSCOM_PSIHB_SIZE 0x100
diff --git a/include/hw/ppc/ppc4xx.h b/include/hw/ppc/ppc4xx.h
index f8c86e0..ea77402 100644
--- a/include/hw/ppc/ppc4xx.h
+++ b/include/hw/ppc/ppc4xx.h
@@ -29,7 +29,10 @@
#include "exec/memory.h"
#include "hw/sysbus.h"
-#define TYPE_PPC4xx_PCI_HOST_BRIDGE "ppc4xx-pcihost"
+#define TYPE_PPC4xx_HOST_BRIDGE "ppc4xx-host-bridge"
+#define TYPE_PPC4xx_PCI_HOST "ppc4xx-pci-host"
+#define TYPE_PPC440_PCIX_HOST "ppc440-pcix-host"
+#define TYPE_PPC460EX_PCIE_HOST "ppc460ex-pcie-host"
/*
* Generic DCR device
diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h
index 3dfb06e..9f580a2 100644
--- a/include/hw/ppc/xive.h
+++ b/include/hw/ppc/xive.h
@@ -187,6 +187,7 @@
/* PQ bits and LSI assertion bit */
uint8_t *status;
+ uint8_t reset_pq; /* PQ state on reset */
/* ESB memory region */
uint64_t esb_flags;
diff --git a/target/ppc/arch_dump.c b/target/ppc/arch_dump.c
index f58e635..a831565 100644
--- a/target/ppc/arch_dump.c
+++ b/target/ppc/arch_dump.c
@@ -237,7 +237,7 @@
info->d_machine = PPC_ELF_MACHINE;
info->d_class = ELFCLASS;
- if (ppc_interrupts_little_endian(cpu, cpu->env.has_hv_mode)) {
+ if (ppc_interrupts_little_endian(cpu, !!(cpu->env.msr_mask & MSR_HVB))) {
info->d_endian = ELFDATA2LSB;
} else {
info->d_endian = ELFDATA2MSB;
diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h
index 9666f54..be33786 100644
--- a/target/ppc/cpu-qom.h
+++ b/target/ppc/cpu-qom.h
@@ -31,6 +31,12 @@
OBJECT_DECLARE_CPU_TYPE(PowerPCCPU, PowerPCCPUClass, POWERPC_CPU)
+#define POWERPC_CPU_TYPE_SUFFIX "-" TYPE_POWERPC_CPU
+#define POWERPC_CPU_TYPE_NAME(model) model POWERPC_CPU_TYPE_SUFFIX
+#define CPU_RESOLVING_TYPE TYPE_POWERPC_CPU
+
+#define TYPE_HOST_POWERPC_CPU POWERPC_CPU_TYPE_NAME("host")
+
ObjectClass *ppc_cpu_class_by_name(const char *name);
typedef struct CPUArchState CPUPPCState;
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index af12c93..25fac95 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -674,6 +674,8 @@
POWERPC_FLAG_SCV = 0x00200000,
/* Has >1 thread per core */
POWERPC_FLAG_SMT = 0x00400000,
+ /* Using "LPAR per core" mode (as opposed to per-thread) */
+ POWERPC_FLAG_SMT_1LPAR = 0x00800000,
};
/*
@@ -1437,6 +1439,7 @@
void ppc_tlb_invalidate_all(CPUPPCState *env);
void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr);
void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp);
+void cpu_ppc_set_1lpar(PowerPCCPU *cpu);
int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp,
target_ulong address, uint32_t pid);
int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid);
@@ -1468,10 +1471,6 @@
int ppc_dcr_read(ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp);
int ppc_dcr_write(ppc_dcr_t *dcr_env, int dcrn, uint32_t val);
-#define POWERPC_CPU_TYPE_SUFFIX "-" TYPE_POWERPC_CPU
-#define POWERPC_CPU_TYPE_NAME(model) model POWERPC_CPU_TYPE_SUFFIX
-#define CPU_RESOLVING_TYPE TYPE_POWERPC_CPU
-
#define cpu_list ppc_cpu_list
/* MMU modes definitions */
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
index aeff71d..02b7aad 100644
--- a/target/ppc/cpu_init.c
+++ b/target/ppc/cpu_init.c
@@ -21,7 +21,6 @@
#include "qemu/osdep.h"
#include "disas/dis-asm.h"
#include "gdbstub/helpers.h"
-#include "kvm_ppc.h"
#include "sysemu/cpus.h"
#include "sysemu/hw_accel.h"
#include "sysemu/tcg.h"
@@ -49,6 +48,7 @@
#ifndef CONFIG_USER_ONLY
#include "hw/boards.h"
#include "hw/intc/intc.h"
+#include "kvm_ppc.h"
#endif
/* #define PPC_DEBUG_SPR */
@@ -5370,31 +5370,6 @@
&spr_read_generic, SPR_NOACCESS,
&spr_read_generic, NULL,
0x00000000);
- spr_register_hv(env, SPR_HID0, "HID0",
- SPR_NOACCESS, SPR_NOACCESS,
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
- 0x00000000);
- spr_register_hv(env, SPR_TSCR, "TSCR",
- SPR_NOACCESS, SPR_NOACCESS,
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic32,
- 0x00000000);
- spr_register_hv(env, SPR_HMER, "HMER",
- SPR_NOACCESS, SPR_NOACCESS,
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_hmer,
- 0x00000000);
- spr_register_hv(env, SPR_HMEER, "HMEER",
- SPR_NOACCESS, SPR_NOACCESS,
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
- 0x00000000);
- spr_register_hv(env, SPR_TFMR, "TFMR",
- SPR_NOACCESS, SPR_NOACCESS,
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
- 0x00000000);
spr_register_hv(env, SPR_LPIDR, "LPIDR",
SPR_NOACCESS, SPR_NOACCESS,
SPR_NOACCESS, SPR_NOACCESS,
@@ -5656,14 +5631,60 @@
#endif
}
+/* SPRs specific to IBM POWER CPUs */
+static void register_power_common_book4_sprs(CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ spr_register_hv(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_core_write_generic,
+ 0x00000000);
+ spr_register_hv(env, SPR_TSCR, "TSCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic32,
+ 0x00000000);
+ spr_register_hv(env, SPR_HMER, "HMER",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_hmer,
+ 0x00000000);
+ spr_register_hv(env, SPR_HMEER, "HMEER",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register_hv(env, SPR_TFMR, "TFMR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_tfmr, &spr_write_tfmr,
+ 0x00000000);
+#endif
+}
+
+static void register_power9_book4_sprs(CPUPPCState *env)
+{
+ /* Add a number of P9 book4 registers */
+ register_power_common_book4_sprs(env);
+#if !defined(CONFIG_USER_ONLY)
+ spr_register_kvm(env, SPR_WORT, "WORT",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ KVM_REG_PPC_WORT, 0);
+#endif
+}
+
static void register_power8_book4_sprs(CPUPPCState *env)
{
/* Add a number of P8 book4 registers */
+ register_power_common_book4_sprs(env);
#if !defined(CONFIG_USER_ONLY)
spr_register_kvm(env, SPR_ACOP, "ACOP",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
KVM_REG_PPC_ACOP, 0);
+ /* PID is only in BookE in ISA v2.07 */
spr_register_kvm(env, SPR_BOOKS_PID, "PID",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_pidr,
@@ -5679,10 +5700,12 @@
{
/* Add a number of P7 book4 registers */
#if !defined(CONFIG_USER_ONLY)
+ register_power_common_book4_sprs(env);
spr_register_kvm(env, SPR_ACOP, "ACOP",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
KVM_REG_PPC_ACOP, 0);
+ /* PID is only in BookE in ISA v2.06 */
spr_register_kvm(env, SPR_BOOKS_PID, "PID",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic32,
@@ -5716,6 +5739,11 @@
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
0x0000000000000000);
+ /* PID is part of the BookS ISA from v3.0 */
+ spr_register_kvm(env, SPR_BOOKS_PID, "PID",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_pidr,
+ KVM_REG_PPC_PID, 0);
#endif
}
@@ -6269,7 +6297,7 @@
register_power8_dpdes_sprs(env);
register_vtb_sprs(env);
register_power8_ic_sprs(env);
- register_power8_book4_sprs(env);
+ register_power9_book4_sprs(env);
register_power8_rpr_sprs(env);
register_power9_mmu_sprs(env);
@@ -6462,7 +6490,7 @@
register_power8_dpdes_sprs(env);
register_vtb_sprs(env);
register_power8_ic_sprs(env);
- register_power8_book4_sprs(env);
+ register_power9_book4_sprs(env);
register_power8_rpr_sprs(env);
register_power9_mmu_sprs(env);
register_power10_hash_sprs(env);
@@ -6601,6 +6629,18 @@
env->msr_mask &= ~MSR_HVB;
}
+void cpu_ppc_set_1lpar(PowerPCCPU *cpu)
+{
+ CPUPPCState *env = &cpu->env;
+
+ /*
+ * pseries SMT means "LPAR per core" mode, e.g., msgsndp is usable
+ * between threads.
+ */
+ if (env->flags & POWERPC_FLAG_SMT) {
+ env->flags |= POWERPC_FLAG_SMT_1LPAR;
+ }
+}
#endif /* !defined(CONFIG_USER_ONLY) */
#endif /* defined(TARGET_PPC64) */
@@ -7295,6 +7335,7 @@
.cpu_exec_enter = ppc_cpu_exec_enter,
.cpu_exec_exit = ppc_cpu_exec_exit,
.do_unaligned_access = ppc_cpu_do_unaligned_access,
+ .do_transaction_failed = ppc_cpu_do_transaction_failed,
#endif /* !CONFIG_USER_ONLY */
};
#endif /* CONFIG_TCG */
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index 2158390..003805b 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -187,8 +187,7 @@
}
#if defined(TARGET_PPC64)
-static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp,
- target_ulong *msr)
+static int powerpc_reset_wakeup(CPUPPCState *env, int excp, target_ulong *msr)
{
/* We no longer are in a PM state */
env->resume_as_sreset = false;
@@ -223,8 +222,8 @@
*msr |= SRR1_WAKEHVI;
break;
default:
- cpu_abort(cs, "Unsupported exception %d in Power Save mode\n",
- excp);
+ cpu_abort(env_cpu(env),
+ "Unsupported exception %d in Power Save mode\n", excp);
}
return POWERPC_EXCP_RESET;
}
@@ -425,6 +424,25 @@
env->reserve_addr = -1;
}
+static void powerpc_mcheck_checkstop(CPUPPCState *env)
+{
+ CPUState *cs = env_cpu(env);
+
+ if (FIELD_EX64(env->msr, MSR, ME)) {
+ return;
+ }
+
+ /* Machine check exception is not enabled. Enter checkstop state. */
+ fprintf(stderr, "Machine check while not allowed. "
+ "Entering checkstop state\n");
+ if (qemu_log_separate()) {
+ qemu_log("Machine check while not allowed. "
+ "Entering checkstop state\n");
+ }
+ cs->halted = 1;
+ cpu_interrupt_exittb(cs);
+}
+
static void powerpc_excp_40x(PowerPCCPU *cpu, int excp)
{
CPUState *cs = CPU(cpu);
@@ -467,21 +485,7 @@
srr1 = SPR_40x_SRR3;
break;
case POWERPC_EXCP_MCHECK: /* Machine check exception */
- if (!FIELD_EX64(env->msr, MSR, ME)) {
- /*
- * Machine check exception is not enabled. Enter
- * checkstop state.
- */
- fprintf(stderr, "Machine check while not allowed. "
- "Entering checkstop state\n");
- if (qemu_log_separate()) {
- qemu_log("Machine check while not allowed. "
- "Entering checkstop state\n");
- }
- cs->halted = 1;
- cpu_interrupt_exittb(cs);
- }
-
+ powerpc_mcheck_checkstop(env);
/* machine check exceptions don't have ME set */
new_msr &= ~((target_ulong)1 << MSR_ME);
@@ -598,21 +602,7 @@
case POWERPC_EXCP_CRITICAL: /* Critical input */
break;
case POWERPC_EXCP_MCHECK: /* Machine check exception */
- if (!FIELD_EX64(env->msr, MSR, ME)) {
- /*
- * Machine check exception is not enabled. Enter
- * checkstop state.
- */
- fprintf(stderr, "Machine check while not allowed. "
- "Entering checkstop state\n");
- if (qemu_log_separate()) {
- qemu_log("Machine check while not allowed. "
- "Entering checkstop state\n");
- }
- cs->halted = 1;
- cpu_interrupt_exittb(cs);
- }
-
+ powerpc_mcheck_checkstop(env);
/* machine check exceptions don't have ME set */
new_msr &= ~((target_ulong)1 << MSR_ME);
@@ -771,21 +761,7 @@
switch (excp) {
case POWERPC_EXCP_MCHECK: /* Machine check exception */
- if (!FIELD_EX64(env->msr, MSR, ME)) {
- /*
- * Machine check exception is not enabled. Enter
- * checkstop state.
- */
- fprintf(stderr, "Machine check while not allowed. "
- "Entering checkstop state\n");
- if (qemu_log_separate()) {
- qemu_log("Machine check while not allowed. "
- "Entering checkstop state\n");
- }
- cs->halted = 1;
- cpu_interrupt_exittb(cs);
- }
-
+ powerpc_mcheck_checkstop(env);
/* machine check exceptions don't have ME set */
new_msr &= ~((target_ulong)1 << MSR_ME);
@@ -956,21 +932,7 @@
switch (excp) {
case POWERPC_EXCP_MCHECK: /* Machine check exception */
- if (!FIELD_EX64(env->msr, MSR, ME)) {
- /*
- * Machine check exception is not enabled. Enter
- * checkstop state.
- */
- fprintf(stderr, "Machine check while not allowed. "
- "Entering checkstop state\n");
- if (qemu_log_separate()) {
- qemu_log("Machine check while not allowed. "
- "Entering checkstop state\n");
- }
- cs->halted = 1;
- cpu_interrupt_exittb(cs);
- }
-
+ powerpc_mcheck_checkstop(env);
/* machine check exceptions don't have ME set */
new_msr &= ~((target_ulong)1 << MSR_ME);
@@ -1030,7 +992,7 @@
{
int lev = env->error_code;
- if ((lev == 1) && cpu->vhyp) {
+ if (lev == 1 && cpu->vhyp) {
dump_hcall(env);
} else {
dump_syscall(env);
@@ -1048,7 +1010,7 @@
* uses VOF and the 74xx CPUs, so although the 74xx don't have
* HV mode, we need to keep hypercall support.
*/
- if ((lev == 1) && cpu->vhyp) {
+ if (lev == 1 && cpu->vhyp) {
PPCVirtualHypervisorClass *vhc =
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
vhc->hypercall(cpu->vhyp, cpu);
@@ -1151,21 +1113,7 @@
srr1 = SPR_BOOKE_CSRR1;
break;
case POWERPC_EXCP_MCHECK: /* Machine check exception */
- if (!FIELD_EX64(env->msr, MSR, ME)) {
- /*
- * Machine check exception is not enabled. Enter
- * checkstop state.
- */
- fprintf(stderr, "Machine check while not allowed. "
- "Entering checkstop state\n");
- if (qemu_log_separate()) {
- qemu_log("Machine check while not allowed. "
- "Entering checkstop state\n");
- }
- cs->halted = 1;
- cpu_interrupt_exittb(cs);
- }
-
+ powerpc_mcheck_checkstop(env);
/* machine check exceptions don't have ME set */
new_msr &= ~((target_ulong)1 << MSR_ME);
@@ -1440,7 +1388,7 @@
* P7/P8/P9
*/
if (env->resume_as_sreset) {
- excp = powerpc_reset_wakeup(cs, env, excp, &msr);
+ excp = powerpc_reset_wakeup(env, excp, &msr);
}
/*
@@ -1468,20 +1416,7 @@
switch (excp) {
case POWERPC_EXCP_MCHECK: /* Machine check exception */
- if (!FIELD_EX64(env->msr, MSR, ME)) {
- /*
- * Machine check exception is not enabled. Enter
- * checkstop state.
- */
- fprintf(stderr, "Machine check while not allowed. "
- "Entering checkstop state\n");
- if (qemu_log_separate()) {
- qemu_log("Machine check while not allowed. "
- "Entering checkstop state\n");
- }
- cs->halted = 1;
- cpu_interrupt_exittb(cs);
- }
+ powerpc_mcheck_checkstop(env);
if (env->msr_mask & MSR_HVB) {
/*
* ISA specifies HV, but can be delivered to guest with HV
@@ -1493,7 +1428,9 @@
/* machine check exceptions don't have ME set */
new_msr &= ~((target_ulong)1 << MSR_ME);
+ msr |= env->error_code;
break;
+
case POWERPC_EXCP_DSI: /* Data storage exception */
trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]);
break;
@@ -1572,7 +1509,7 @@
case POWERPC_EXCP_SYSCALL: /* System call exception */
lev = env->error_code;
- if ((lev == 1) && cpu->vhyp) {
+ if (lev == 1 && cpu->vhyp) {
dump_hcall(env);
} else {
dump_syscall(env);
@@ -1585,7 +1522,7 @@
env->nip += 4;
/* "PAPR mode" built-in hypercall emulation */
- if ((lev == 1) && books_vhyp_handles_hcall(cpu)) {
+ if (lev == 1 && books_vhyp_handles_hcall(cpu)) {
PPCVirtualHypervisorClass *vhc =
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
vhc->hypercall(cpu->vhyp, cpu);
@@ -1835,8 +1772,8 @@
static int p7_next_unmasked_interrupt(CPUPPCState *env)
{
- PowerPCCPU *cpu = env_archcpu(env);
- CPUState *cs = CPU(cpu);
+ CPUState *cs = env_cpu(env);
+
/* Ignore MSR[EE] when coming out of some power management states */
bool msr_ee = FIELD_EX64(env->msr, MSR, EE) || env->resume_as_sreset;
@@ -1925,8 +1862,8 @@
static int p8_next_unmasked_interrupt(CPUPPCState *env)
{
- PowerPCCPU *cpu = env_archcpu(env);
- CPUState *cs = CPU(cpu);
+ CPUState *cs = env_cpu(env);
+
/* Ignore MSR[EE] when coming out of some power management states */
bool msr_ee = FIELD_EX64(env->msr, MSR, EE) || env->resume_as_sreset;
@@ -2046,8 +1983,8 @@
static int p9_next_unmasked_interrupt(CPUPPCState *env)
{
- PowerPCCPU *cpu = env_archcpu(env);
- CPUState *cs = CPU(cpu);
+ CPUState *cs = env_cpu(env);
+
/* Ignore MSR[EE] when coming out of some power management states */
bool msr_ee = FIELD_EX64(env->msr, MSR, EE) || env->resume_as_sreset;
@@ -2718,8 +2655,7 @@
uint32_t excp = hreg_store_msr(env, val, 0);
if (excp != 0) {
- CPUState *cs = env_cpu(env);
- cpu_interrupt_exittb(cs);
+ cpu_interrupt_exittb(env_cpu(env));
raise_exception(env, excp);
}
}
@@ -2741,9 +2677,8 @@
void helper_pminsn(CPUPPCState *env, uint32_t insn)
{
- CPUState *cs;
+ CPUState *cs = env_cpu(env);
- cs = env_cpu(env);
cs->halted = 1;
/* Condition for waking up at 0x100 */
@@ -2756,8 +2691,6 @@
static void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr)
{
- CPUState *cs = env_cpu(env);
-
/* MSR:POW cannot be set by any form of rfi */
msr &= ~(1ULL << MSR_POW);
@@ -2781,7 +2714,7 @@
* No need to raise an exception here, as rfi is always the last
* insn of a TB
*/
- cpu_interrupt_exittb(cs);
+ cpu_interrupt_exittb(env_cpu(env));
/* Reset the reservation */
env->reserve_addr = -1;
@@ -3199,6 +3132,10 @@
helper_hfscr_facility_check(env, HFSCR_MSGP, "msgsndp", HFSCR_IC_MSGP);
+ if (!(env->flags & POWERPC_FLAG_SMT_1LPAR)) {
+ nr_threads = 1; /* msgsndp behaves as 1-thread in LPAR-per-thread mode*/
+ }
+
if (!dbell_type_server(rb) || ttir >= nr_threads) {
return;
}
@@ -3253,5 +3190,52 @@
env->error_code = insn & 0x03FF0000;
cpu_loop_exit(cs);
}
+
+void ppc_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
+ vaddr vaddr, unsigned size,
+ MMUAccessType access_type,
+ int mmu_idx, MemTxAttrs attrs,
+ MemTxResult response, uintptr_t retaddr)
+{
+ CPUPPCState *env = cs->env_ptr;
+
+ switch (env->excp_model) {
+#if defined(TARGET_PPC64)
+ case POWERPC_EXCP_POWER9:
+ case POWERPC_EXCP_POWER10:
+ /*
+ * Machine check codes can be found in processor User Manual or
+ * Linux or skiboot source.
+ */
+ if (access_type == MMU_DATA_LOAD) {
+ env->spr[SPR_DAR] = vaddr;
+ env->spr[SPR_DSISR] = PPC_BIT(57);
+ env->error_code = PPC_BIT(42);
+
+ } else if (access_type == MMU_DATA_STORE) {
+ /*
+ * MCE for stores in POWER is asynchronous so hardware does
+ * not set DAR, but QEMU can do better.
+ */
+ env->spr[SPR_DAR] = vaddr;
+ env->error_code = PPC_BIT(36) | PPC_BIT(43) | PPC_BIT(45);
+ env->error_code |= PPC_BIT(42);
+
+ } else { /* Fetch */
+ env->error_code = PPC_BIT(36) | PPC_BIT(44) | PPC_BIT(45);
+ }
+ break;
+#endif
+ default:
+ /*
+ * TODO: Check behaviour for other CPUs, for now do nothing.
+ * Could add a basic MCE even if real hardware ignores.
+ */
+ return;
+ }
+
+ cs->exception_index = POWERPC_EXCP_MCHECK;
+ cpu_loop_exit_restore(cs, retaddr);
+}
#endif /* CONFIG_TCG */
#endif /* !CONFIG_USER_ONLY */
diff --git a/target/ppc/helper.h b/target/ppc/helper.h
index fda40b8..abec6fe 100644
--- a/target/ppc/helper.h
+++ b/target/ppc/helper.h
@@ -704,6 +704,7 @@
DEF_HELPER_2(load_dump_spr, void, env, i32)
DEF_HELPER_2(store_dump_spr, void, env, i32)
+DEF_HELPER_3(spr_core_write_generic, void, env, i32, tl)
DEF_HELPER_3(spr_write_CTRL, void, env, i32, tl)
DEF_HELPER_4(fscr_facility_check, void, env, i32, i32, i32)
@@ -722,6 +723,8 @@
DEF_HELPER_FLAGS_2(store_dpdes, TCG_CALL_NO_RWG, void, env, tl)
DEF_HELPER_2(book3s_msgsndp, void, env, tl)
DEF_HELPER_2(book3s_msgclrp, void, env, tl)
+DEF_HELPER_1(load_tfmr, tl, env)
+DEF_HELPER_2(store_tfmr, void, env, tl)
#endif
DEF_HELPER_2(store_sdr1, void, env, tl)
DEF_HELPER_2(store_pidr, void, env, tl)
diff --git a/target/ppc/internal.h b/target/ppc/internal.h
index 901bae6..57acb32 100644
--- a/target/ppc/internal.h
+++ b/target/ppc/internal.h
@@ -296,6 +296,11 @@
G_NORETURN void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
MMUAccessType access_type, int mmu_idx,
uintptr_t retaddr);
+void ppc_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
+ vaddr addr, unsigned size,
+ MMUAccessType access_type,
+ int mmu_idx, MemTxAttrs attrs,
+ MemTxResult response, uintptr_t retaddr);
#endif
FIELD(GER_MSK, XMSK, 0, 4)
diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h
index 611debc..6a4dd9c 100644
--- a/target/ppc/kvm_ppc.h
+++ b/target/ppc/kvm_ppc.h
@@ -9,11 +9,10 @@
#ifndef KVM_PPC_H
#define KVM_PPC_H
+#include "sysemu/kvm.h"
#include "exec/hwaddr.h"
#include "cpu.h"
-#define TYPE_HOST_POWERPC_CPU POWERPC_CPU_TYPE_NAME("host")
-
#ifdef CONFIG_KVM
uint32_t kvmppc_get_tbfreq(void);
@@ -43,7 +42,6 @@
target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu,
bool radix, bool gtse,
uint64_t proc_tbl);
-#ifndef CONFIG_USER_ONLY
bool kvmppc_spapr_use_multitce(void);
int kvmppc_spapr_enable_inkernel_multitce(void);
void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t page_shift,
@@ -53,7 +51,6 @@
int kvmppc_reset_htab(int shift_hint);
uint64_t kvmppc_vrma_limit(unsigned int hash_shift);
bool kvmppc_has_cap_spapr_vfio(void);
-#endif /* !CONFIG_USER_ONLY */
bool kvmppc_has_cap_epr(void);
int kvmppc_define_rtas_kernel_token(uint32_t token, const char *function);
int kvmppc_get_htab_fd(bool write, uint64_t index, Error **errp);
@@ -92,7 +89,34 @@
int kvm_handle_nmi(PowerPCCPU *cpu, struct kvm_run *run);
-#else
+#define kvmppc_eieio() \
+ do { \
+ if (kvm_enabled()) { \
+ asm volatile("eieio" : : : "memory"); \
+ } \
+ } while (0)
+
+/* Store data cache blocks back to memory */
+static inline void kvmppc_dcbst_range(PowerPCCPU *cpu, uint8_t *addr, int len)
+{
+ uint8_t *p;
+
+ for (p = addr; p < addr + len; p += cpu->env.dcache_line_size) {
+ asm volatile("dcbst 0,%0" : : "r"(p) : "memory");
+ }
+}
+
+/* Invalidate instruction cache blocks */
+static inline void kvmppc_icbi_range(PowerPCCPU *cpu, uint8_t *addr, int len)
+{
+ uint8_t *p;
+
+ for (p = addr; p < addr + len; p += cpu->env.icache_line_size) {
+ asm volatile("icbi 0,%0" : : "r"(p));
+ }
+}
+
+#else /* !CONFIG_KVM */
static inline uint32_t kvmppc_get_tbfreq(void)
{
@@ -236,7 +260,6 @@
{
}
-#ifndef CONFIG_USER_ONLY
static inline bool kvmppc_spapr_use_multitce(void)
{
return false;
@@ -296,8 +319,6 @@
abort();
}
-#endif /* !CONFIG_USER_ONLY */
-
static inline bool kvmppc_has_cap_epr(void)
{
return false;
@@ -439,10 +460,6 @@
return false;
}
-#endif
-
-#ifndef CONFIG_KVM
-
#define kvmppc_eieio() do { } while (0)
static inline void kvmppc_dcbst_range(PowerPCCPU *cpu, uint8_t *addr, int len)
@@ -453,35 +470,6 @@
{
}
-#else /* CONFIG_KVM */
-
-#define kvmppc_eieio() \
- do { \
- if (kvm_enabled()) { \
- asm volatile("eieio" : : : "memory"); \
- } \
- } while (0)
-
-/* Store data cache blocks back to memory */
-static inline void kvmppc_dcbst_range(PowerPCCPU *cpu, uint8_t *addr, int len)
-{
- uint8_t *p;
-
- for (p = addr; p < addr + len; p += cpu->env.dcache_line_size) {
- asm volatile("dcbst 0,%0" : : "r"(p) : "memory");
- }
-}
-
-/* Invalidate instruction cache blocks */
-static inline void kvmppc_icbi_range(PowerPCCPU *cpu, uint8_t *addr, int len)
-{
- uint8_t *p;
-
- for (p = addr; p < addr + len; p += cpu->env.icache_line_size) {
- asm volatile("icbi 0,%0" : : "r"(p));
- }
-}
-
#endif /* CONFIG_KVM */
#endif /* KVM_PPC_H */
diff --git a/target/ppc/meson.build b/target/ppc/meson.build
index a69f174..4c26350 100644
--- a/target/ppc/meson.build
+++ b/target/ppc/meson.build
@@ -28,7 +28,7 @@
extra_args: ['--static-decode=decode_insn64',
'--insnwidth=64']),
]
-ppc_ss.add(gen)
+ppc_ss.add(when: 'CONFIG_TCG', if_true: gen)
ppc_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'), if_false: files('kvm-stub.c'))
ppc_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user_only_helper.c'))
diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c
index 1f1af21..692d058 100644
--- a/target/ppc/misc_helper.c
+++ b/target/ppc/misc_helper.c
@@ -43,6 +43,27 @@
env->spr[sprn]);
}
+void helper_spr_core_write_generic(CPUPPCState *env, uint32_t sprn,
+ target_ulong val)
+{
+ CPUState *cs = env_cpu(env);
+ CPUState *ccs;
+ uint32_t nr_threads = cs->nr_threads;
+ uint32_t core_id = env->spr[SPR_PIR] & ~(nr_threads - 1);
+
+ assert(core_id == env->spr[SPR_PIR] - env->spr[SPR_TIR]);
+
+ if (nr_threads == 1) {
+ env->spr[sprn] = val;
+ return;
+ }
+
+ THREAD_SIBLING_FOREACH(cs, ccs) {
+ CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
+ cenv->spr[sprn] = val;
+ }
+}
+
void helper_spr_write_CTRL(CPUPPCState *env, uint32_t sprn,
target_ulong val)
{
@@ -191,6 +212,10 @@
helper_hfscr_facility_check(env, HFSCR_MSGP, "load DPDES", HFSCR_IC_MSGP);
+ if (!(env->flags & POWERPC_FLAG_SMT_1LPAR)) {
+ nr_threads = 1; /* DPDES behaves as 1-thread in LPAR-per-thread mode */
+ }
+
if (nr_threads == 1) {
if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) {
dpdes = 1;
@@ -222,6 +247,10 @@
helper_hfscr_facility_check(env, HFSCR_MSGP, "store DPDES", HFSCR_IC_MSGP);
+ if (!(env->flags & POWERPC_FLAG_SMT_1LPAR)) {
+ nr_threads = 1; /* DPDES behaves as 1-thread in LPAR-per-thread mode */
+ }
+
if (val & ~(nr_threads - 1)) {
qemu_log_mask(LOG_GUEST_ERROR, "Invalid DPDES register value "
TARGET_FMT_lx"\n", val);
diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h
index 4c0f2be..5995070 100644
--- a/target/ppc/spr_common.h
+++ b/target/ppc/spr_common.h
@@ -82,6 +82,7 @@
void spr_read_generic(DisasContext *ctx, int gprn, int sprn);
void spr_write_generic(DisasContext *ctx, int sprn, int gprn);
void spr_write_generic32(DisasContext *ctx, int sprn, int gprn);
+void spr_core_write_generic(DisasContext *ctx, int sprn, int gprn);
void spr_write_MMCR0(DisasContext *ctx, int sprn, int gprn);
void spr_write_MMCR1(DisasContext *ctx, int sprn, int gprn);
void spr_write_PMC(DisasContext *ctx, int sprn, int gprn);
@@ -194,6 +195,8 @@
void spr_read_ebb_upper32(DisasContext *ctx, int gprn, int sprn);
void spr_write_ebb_upper32(DisasContext *ctx, int sprn, int gprn);
void spr_write_hmer(DisasContext *ctx, int sprn, int gprn);
+void spr_read_tfmr(DisasContext *ctx, int gprn, int sprn);
+void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn);
void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn);
void spr_read_dexcr_ureg(DisasContext *ctx, int gprn, int sprn);
#endif
diff --git a/target/ppc/timebase_helper.c b/target/ppc/timebase_helper.c
index b80f56a..08a6b47 100644
--- a/target/ppc/timebase_helper.c
+++ b/target/ppc/timebase_helper.c
@@ -144,6 +144,19 @@
store_booke_tsr(env, val);
}
+#if defined(TARGET_PPC64)
+/* POWER processor Timebase Facility */
+target_ulong helper_load_tfmr(CPUPPCState *env)
+{
+ return env->spr[SPR_TFMR];
+}
+
+void helper_store_tfmr(CPUPPCState *env, target_ulong val)
+{
+ env->spr[SPR_TFMR] = val;
+}
+#endif
+
/*****************************************************************************/
/* Embedded PowerPC specific helpers */
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 372ee60..e6a0709 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -246,9 +246,9 @@
}
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
-static inline bool gen_serialize_core(DisasContext *ctx)
+static inline bool gen_serialize_core_lpar(DisasContext *ctx)
{
- if (ctx->flags & POWERPC_FLAG_SMT) {
+ if (ctx->flags & POWERPC_FLAG_SMT_1LPAR) {
return gen_serialize(ctx);
}
@@ -438,6 +438,22 @@
#endif
}
+void spr_core_write_generic(DisasContext *ctx, int sprn, int gprn)
+{
+ if (!(ctx->flags & POWERPC_FLAG_SMT)) {
+ spr_write_generic(ctx, sprn, gprn);
+ return;
+ }
+
+ if (!gen_serialize(ctx)) {
+ return;
+ }
+
+ gen_helper_spr_core_write_generic(cpu_env, tcg_constant_i32(sprn),
+ cpu_gpr[gprn]);
+ spr_store_dump_spr(sprn);
+}
+
static void spr_write_CTRL_ST(DisasContext *ctx, int sprn, int gprn)
{
/* This does not implement >1 thread */
@@ -451,7 +467,8 @@
void spr_write_CTRL(DisasContext *ctx, int sprn, int gprn)
{
- if (!(ctx->flags & POWERPC_FLAG_SMT)) {
+ if (!(ctx->flags & POWERPC_FLAG_SMT_1LPAR)) {
+ /* CTRL behaves as 1-thread in LPAR-per-thread mode */
spr_write_CTRL_ST(ctx, sprn, gprn);
goto out;
}
@@ -815,7 +832,7 @@
/* DPDES */
void spr_read_dpdes(DisasContext *ctx, int gprn, int sprn)
{
- if (!gen_serialize_core(ctx)) {
+ if (!gen_serialize_core_lpar(ctx)) {
return;
}
@@ -824,7 +841,7 @@
void spr_write_dpdes(DisasContext *ctx, int sprn, int gprn)
{
- if (!gen_serialize_core(ctx)) {
+ if (!gen_serialize_core_lpar(ctx)) {
return;
}
@@ -1175,8 +1192,19 @@
spr_store_dump_spr(sprn);
}
+void spr_read_tfmr(DisasContext *ctx, int gprn, int sprn)
+{
+ gen_helper_load_tfmr(cpu_gpr[gprn], cpu_env);
+}
+
+void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn)
+{
+ gen_helper_store_tfmr(cpu_env, cpu_gpr[gprn]);
+}
+
void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn)
{
+ translator_io_start(&ctx->base);
gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]);
}
#endif /* !defined(CONFIG_USER_ONLY) */
@@ -4002,6 +4030,7 @@
TCGv_i32 t;
CHK_HV(ctx);
+ translator_io_start(&ctx->base);
t = tcg_constant_i32(PPC_PM_DOZE);
gen_helper_pminsn(cpu_env, t);
/* Stop translation, as the CPU is supposed to sleep from now */
@@ -4017,6 +4046,7 @@
TCGv_i32 t;
CHK_HV(ctx);
+ translator_io_start(&ctx->base);
t = tcg_constant_i32(PPC_PM_NAP);
gen_helper_pminsn(cpu_env, t);
/* Stop translation, as the CPU is supposed to sleep from now */
@@ -4032,6 +4062,7 @@
TCGv_i32 t;
CHK_HV(ctx);
+ translator_io_start(&ctx->base);
t = tcg_constant_i32(PPC_PM_STOP);
gen_helper_pminsn(cpu_env, t);
/* Stop translation, as the CPU is supposed to sleep from now */
@@ -4047,6 +4078,7 @@
TCGv_i32 t;
CHK_HV(ctx);
+ translator_io_start(&ctx->base);
t = tcg_constant_i32(PPC_PM_SLEEP);
gen_helper_pminsn(cpu_env, t);
/* Stop translation, as the CPU is supposed to sleep from now */
@@ -4062,6 +4094,7 @@
TCGv_i32 t;
CHK_HV(ctx);
+ translator_io_start(&ctx->base);
t = tcg_constant_i32(PPC_PM_RVWINKLE);
gen_helper_pminsn(cpu_env, t);
/* Stop translation, as the CPU is supposed to sleep from now */
@@ -4458,6 +4491,7 @@
#else
/* Restore CPU state */
CHK_HV(ctx);
+ translator_io_start(&ctx->base);
gen_helper_hrfid(cpu_env);
ctx->base.is_jmp = DISAS_EXIT;
#endif
@@ -4469,7 +4503,6 @@
#define POWERPC_SYSCALL POWERPC_EXCP_SYSCALL_USER
#else
#define POWERPC_SYSCALL POWERPC_EXCP_SYSCALL
-#define POWERPC_SYSCALL_VECTORED POWERPC_EXCP_SYSCALL_VECTORED
#endif
static void gen_sc(DisasContext *ctx)
{
diff --git a/tests/avocado/ppc_powernv.py b/tests/avocado/ppc_powernv.py
new file mode 100644
index 0000000..d0e5c07
--- /dev/null
+++ b/tests/avocado/ppc_powernv.py
@@ -0,0 +1,87 @@
+# Test that Linux kernel boots on ppc powernv machines and check the console
+#
+# Copyright (c) 2018, 2020 Red Hat, Inc.
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+from avocado.utils import archive
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+
+class powernvMachine(QemuSystemTest):
+
+ timeout = 90
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
+ panic_message = 'Kernel panic - not syncing'
+ good_message = 'VFS: Cannot open root device'
+
+ def do_test_linux_boot(self):
+ self.require_accelerator("tcg")
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive'
+ '/fedora-secondary/releases/29/Everything/ppc64le/os'
+ '/ppc/ppc64/vmlinuz')
+ kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ self.vm.set_console()
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0'
+ self.vm.add_args('-kernel', kernel_path,
+ '-append', kernel_command_line)
+ self.vm.launch()
+
+ def test_linux_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:powernv
+ :avocado: tags=accel:tcg
+ """
+
+ self.do_test_linux_boot()
+ console_pattern = 'VFS: Cannot open root device'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+
+ def test_linux_smp_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:powernv
+ :avocado: tags=accel:tcg
+ """
+
+ self.vm.add_args('-smp', '4')
+ self.do_test_linux_boot()
+ console_pattern = 'smp: Brought up 1 node, 4 CPUs'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ wait_for_console_pattern(self, self.good_message, self.panic_message)
+
+ def test_linux_smt_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:powernv
+ :avocado: tags=accel:tcg
+ """
+
+ self.vm.add_args('-smp', '4,threads=4')
+ self.do_test_linux_boot()
+ console_pattern = 'CPU maps initialized for 4 threads per core'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ console_pattern = 'smp: Brought up 1 node, 4 CPUs'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ wait_for_console_pattern(self, self.good_message, self.panic_message)
+
+ def test_linux_big_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:powernv
+ :avocado: tags=accel:tcg
+ """
+
+ self.vm.add_args('-smp', '16,threads=4,cores=2,sockets=2')
+
+ # powernv does not support NUMA
+ self.do_test_linux_boot()
+ console_pattern = 'CPU maps initialized for 4 threads per core'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ console_pattern = 'smp: Brought up 2 nodes, 16 CPUs'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ wait_for_console_pattern(self, self.good_message, self.panic_message)
diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py
index fe1e901..79c607b 100644
--- a/tests/avocado/replay_kernel.py
+++ b/tests/avocado/replay_kernel.py
@@ -259,6 +259,23 @@ def test_ppc64_pseries(self):
console_pattern = 'Kernel command line: %s' % kernel_command_line
self.run_rr(kernel_path, kernel_command_line, console_pattern)
+ def test_ppc64_powernv(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:powernv
+ :avocado: tags=accel:tcg
+ """
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive'
+ '/fedora-secondary/releases/29/Everything/ppc64le/os'
+ '/ppc/ppc64/vmlinuz')
+ kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + \
+ 'console=tty0 console=hvc0'
+ console_pattern = 'VFS: Cannot open root device'
+ self.run_rr(kernel_path, kernel_command_line, console_pattern)
+
def test_m68k_q800(self):
"""
:avocado: tags=arch:m68k
diff --git a/tests/qtest/pnv-xscom-test.c b/tests/qtest/pnv-xscom-test.c
index 2c46d5c..8a5ac11 100644
--- a/tests/qtest/pnv-xscom-test.c
+++ b/tests/qtest/pnv-xscom-test.c
@@ -15,6 +15,7 @@
PNV_CHIP_POWER8, /* AKA Venice */
PNV_CHIP_POWER8NVL, /* AKA Naples */
PNV_CHIP_POWER9, /* AKA Nimbus */
+ PNV_CHIP_POWER10,
} PnvChipType;
typedef struct PnvChip {
@@ -46,13 +47,22 @@
.cfam_id = 0x220d104900008000ull,
.first_core = 0x0,
},
+ {
+ .chip_type = PNV_CHIP_POWER10,
+ .cpu_model = "POWER10",
+ .xscom_base = 0x000603fc00000000ull,
+ .cfam_id = 0x120da04900008000ull,
+ .first_core = 0x0,
+ },
};
static uint64_t pnv_xscom_addr(const PnvChip *chip, uint32_t pcba)
{
uint64_t addr = chip->xscom_base;
- if (chip->chip_type == PNV_CHIP_POWER9) {
+ if (chip->chip_type == PNV_CHIP_POWER10) {
+ addr |= ((uint64_t) pcba << 3);
+ } else if (chip->chip_type == PNV_CHIP_POWER9) {
addr |= ((uint64_t) pcba << 3);
} else {
addr |= (((uint64_t) pcba << 4) & ~0xffull) |
@@ -82,6 +92,8 @@
if (chip->chip_type == PNV_CHIP_POWER9) {
machine = "powernv9";
+ } else if (chip->chip_type == PNV_CHIP_POWER10) {
+ machine = "powernv10";
}
qts = qtest_initf("-M %s -accel tcg -cpu %s",
@@ -96,23 +108,36 @@
(PNV_XSCOM_EX_CORE_BASE | ((uint64_t)(core) << 24))
#define PNV_XSCOM_P9_EC_BASE(core) \
((uint64_t)(((core) & 0x1F) + 0x20) << 24)
+#define PNV_XSCOM_P10_EC_BASE(core) \
+ ((uint64_t)((((core) & ~0x3) + 0x20) << 24) + 0x20000 + \
+ (0x1000 << (3 - (core & 0x3))))
#define PNV_XSCOM_EX_DTS_RESULT0 0x50000
static void test_xscom_core(QTestState *qts, const PnvChip *chip)
{
- uint32_t first_core_dts0 = PNV_XSCOM_EX_DTS_RESULT0;
- uint64_t dts0;
+ if (chip->chip_type == PNV_CHIP_POWER10) {
+ uint32_t first_core_thread_state =
+ PNV_XSCOM_P10_EC_BASE(chip->first_core) + 0x412;
+ uint64_t thread_state;
- if (chip->chip_type != PNV_CHIP_POWER9) {
- first_core_dts0 |= PNV_XSCOM_EX_BASE(chip->first_core);
+ thread_state = pnv_xscom_read(qts, chip, first_core_thread_state);
+
+ g_assert_cmphex(thread_state, ==, 0);
} else {
- first_core_dts0 |= PNV_XSCOM_P9_EC_BASE(chip->first_core);
+ uint32_t first_core_dts0 = PNV_XSCOM_EX_DTS_RESULT0;
+ uint64_t dts0;
+
+ if (chip->chip_type == PNV_CHIP_POWER9) {
+ first_core_dts0 |= PNV_XSCOM_P9_EC_BASE(chip->first_core);
+ } else { /* POWER8 */
+ first_core_dts0 |= PNV_XSCOM_EX_BASE(chip->first_core);
+ }
+
+ dts0 = pnv_xscom_read(qts, chip, first_core_dts0);
+
+ g_assert_cmphex(dts0, ==, 0x26f024f023f0000ull);
}
-
- dts0 = pnv_xscom_read(qts, chip, first_core_dts0);
-
- g_assert_cmphex(dts0, ==, 0x26f024f023f0000ull);
}
static void test_core(const void *data)
@@ -123,6 +148,8 @@
if (chip->chip_type == PNV_CHIP_POWER9) {
machine = "powernv9";
+ } else if (chip->chip_type == PNV_CHIP_POWER10) {
+ machine = "powernv10";
}
qts = qtest_initf("-M %s -accel tcg -cpu %s",