Merge tag 'pull-ppc-for-10.0-1-20250311' of https://gitlab.com/npiggin/qemu into staging

* Next round of XIVE patches...

* tag 'pull-ppc-for-10.0-1-20250311' of https://gitlab.com/npiggin/qemu: (72 commits)
  docs/system/ppc/amigang.rst: Update for NVRAM emulation
  ppc/amigaone: Add #defines for memory map constants
  ppc/amigaone: Add kernel and initrd support
  ppc/amigaone: Add default environment
  ppc/amigaone: Implement NVRAM emulation
  ppc/amigaone: Simplify replacement dummy_fw
  spapr: Generate random HASHPKEYR for spapr machines
  target/ppc: Avoid warning message for zero process table entries
  target/ppc: Wire up BookE ATB registers for e500 family
  target/ppc: fix timebase register reset state
  spapr: nested: Add support for reporting Hostwide state counter
  ppc: spapr: Enable 2nd DAWR on Power10 pSeries machine
  ppc: Enable 2nd DAWR support on Power10 PowerNV machine
  hw/ppc/epapr: Do not swap ePAPR magic value
  hw/ppc/spapr: Convert DIRTY_HPTE() macro as hpte_set_dirty() method
  hw/ppc/spapr: Convert CLEAN_HPTE() macro as hpte_set_clean() method
  hw/ppc/spapr: Convert HPTE_DIRTY() macro as hpte_is_dirty() method
  hw/ppc/spapr: Convert HPTE_VALID() macro as hpte_is_valid() method
  hw/ppc/spapr: Convert HPTE() macro as hpte_get_ptr() method
  target/ppc: Restrict ATTN / SCV / PMINSN helpers to TCG
  ...

[Fix __packed macro redefinition on FreeBSD 14 hosts:
../hw/ppc/pnv_occ.c:397:9: error: '__packed' macro redefined [-Werror,-Wmacro-redefined]
  397 | #define __packed QEMU_PACKED
      |         ^
/usr/include/sys/cdefs.h:217:9: note: previous definition is here
  217 | #define __packed        __attribute__((__packed__))
      |         ^
--Stefan]

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi
2025-03-13 10:29:04 +08:00
76 changed files with 4203 additions and 3690 deletions
+1 -6
View File
@@ -1407,12 +1407,6 @@ F: hw/openrisc/openrisc_sim.c
PowerPC Machines
----------------
405 (ref405ep)
L: qemu-ppc@nongnu.org
S: Orphan
F: hw/ppc/ppc405*
F: tests/functional/test_ppc_405.py
Bamboo
L: qemu-ppc@nongnu.org
S: Orphan
@@ -1543,6 +1537,7 @@ F: include/hw/ppc/pnv*
F: include/hw/pci-host/pnv*
F: include/hw/ssi/pnv_spi*
F: pc-bios/skiboot.lid
F: pc-bios/pnv-pnor.bin
F: tests/qtest/pnv*
F: tests/functional/test_ppc64_powernv.py
+9 -8
View File
@@ -266,6 +266,15 @@ in the QEMU object model anymore. ``Sun-UltraSparc-IIIi+`` and
but for consistency these will get removed in a future release, too.
Use ``Sun-UltraSparc-IIIi-plus`` and ``Sun-UltraSparc-IV-plus`` instead.
PPC 405 CPUs (since 10.0)
'''''''''''''''''''''''''
The PPC 405 CPU has no known users and the ``ref405ep`` machine was
removed in QEMU 10.0. Since the IBM POWER [8-11] processors uses an
embedded 405 for power management (OCC) and other internal tasks, it
is theoretically possible to use QEMU to model them. Let's keep the
CPU implementation for a while before removing all support.
System emulator machines
------------------------
@@ -277,14 +286,6 @@ deprecated; use the new name ``dtb-randomness`` instead. The new name
better reflects the way this property affects all random data within
the device tree blob, not just the ``kaslr-seed`` node.
PPC 405 ``ref405ep`` machine (since 9.1)
''''''''''''''''''''''''''''''''''''''''
The ``ref405ep`` machine and PPC 405 CPU have no known users, firmware
images are not available, OpenWRT dropped support in 2019, U-Boot in
2017, Linux also is dropping support in 2024. It is time to let go of
this ancient hardware and focus on newer CPUs and platforms.
Big-Endian variants of MicroBlaze ``petalogix-ml605`` and ``xlnx-zynqmp-pmu`` machines (since 9.2)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+7
View File
@@ -1064,6 +1064,13 @@ for all machine types using the PXA2xx and OMAP2 SoCs. We are also
dropping the ``cheetah`` OMAP1 board, because we don't have any
test images for it and don't know of anybody who does.
ppc ``ref405ep`` machine (removed in 10.0)
''''''''''''''''''''''''''''''''''''''''''
This machine was removed because PPC 405 CPU have no known users,
firmware images are not available, OpenWRT dropped support in 2019,
U-Boot in 2017, and Linux in 2024.
linux-user mode CPUs
--------------------
+9 -8
View File
@@ -21,6 +21,7 @@ Emulated devices
* VIA VT82C686B south bridge
* PCI VGA compatible card (guests may need other card instead)
* PS/2 keyboard and mouse
* 4 KiB NVRAM (use ``-drive if=mtd,format=raw,file=nvram.bin`` to keep contents persistent)
Firmware
--------
@@ -54,14 +55,14 @@ To boot the system run:
-cdrom "A1 Linux Net Installer.iso" \
-device ati-vga,model=rv100,romfile=VGABIOS-lgpl-latest.bin
From the firmware menu that appears select ``Boot sequence``
``Amiga Multiboot Options`` and set ``Boot device 1`` to
``Onboard VIA IDE CDROM``. Then hit escape until the main screen appears again,
hit escape once more and from the exit menu that appears select either
``Save settings and exit`` or ``Use settings for this session only``. It may
take a long time loading the kernel into memory but eventually it boots and the
installer becomes visible. The ``ati-vga`` RV100 emulation is not
complete yet so only frame buffer works, DRM and 3D is not available.
If a firmware menu appears, select ``Boot sequence`` ``Amiga Multiboot Options``
and set ``Boot device 1`` to ``Onboard VIA IDE CDROM``. Then hit escape until
the main screen appears again, hit escape once more and from the exit menu that
appears select either ``Save settings and exit`` or ``Use settings for this
session only``. It may take a long time loading the kernel into memory but
eventually it boots and the installer becomes visible. The ``ati-vga`` RV100
emulation is not complete yet so only frame buffer works, DRM and 3D is not
available.
Genesi/bPlan Pegasos II (``pegasos2``)
======================================
-1
View File
@@ -4,6 +4,5 @@ Embedded family boards
- ``bamboo`` bamboo
- ``mpc8544ds`` mpc8544ds
- ``ppce500`` generic paravirt e500 platform
- ``ref405ep`` ref405ep
- ``sam460ex`` aCube Sam460ex
- ``virtex-ml507`` Xilinx Virtex ML507 reference design
+7
View File
@@ -195,6 +195,13 @@ Use a MTD drive to add a PNOR to the machine, and get a NVRAM :
-drive file=./witherspoon.pnor,format=raw,if=mtd
If no mtd drive is provided, the powernv platform will create a default
PNOR device using a tiny formatted PNOR in pc-bios/pnv-pnor.bin opened
read-only (PNOR changes will be persistent across reboots but not across
invocations of QEMU). If no defaults are used, an erased 128MB PNOR is
provided (which skiboot will probably not recognize since it is not
formatted).
Maintainer contact information
------------------------------
+5 -5
View File
@@ -1,10 +1,9 @@
/*
* QEMU PowerPC XIVE interrupt controller model
*
* Copyright (c) 2017-2019, IBM Corporation.
* Copyright (c) 2017-2024, IBM Corporation.
*
* This code is licensed under the GPL version 2 or later. See the
* COPYING file in the top-level directory.
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
@@ -473,7 +472,7 @@ static bool pnv_xive_is_cpu_enabled(PnvXive *xive, PowerPCCPU *cpu)
static int pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format,
uint8_t nvt_blk, uint32_t nvt_idx,
bool cam_ignore, uint8_t priority,
bool crowd, bool cam_ignore, uint8_t priority,
uint32_t logic_serv, XiveTCTXMatch *match)
{
PnvXive *xive = PNV_XIVE(xptr);
@@ -500,7 +499,8 @@ static int pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format,
* Check the thread context CAM lines and record matches.
*/
ring = xive_presenter_tctx_match(xptr, tctx, format, nvt_blk,
nvt_idx, cam_ignore, logic_serv);
nvt_idx, cam_ignore,
logic_serv);
/*
* Save the context and follow on to catch duplicates, that we
* don't support yet.
+136 -30
View File
@@ -1,10 +1,9 @@
/*
* QEMU PowerPC XIVE2 interrupt controller model (POWER10)
*
* Copyright (c) 2019-2022, IBM Corporation.
* Copyright (c) 2019-2024, IBM Corporation.
*
* This code is licensed under the GPL version 2 or later. See the
* COPYING file in the top-level directory.
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
@@ -625,7 +624,7 @@ static bool pnv_xive2_is_cpu_enabled(PnvXive2 *xive, PowerPCCPU *cpu)
static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format,
uint8_t nvt_blk, uint32_t nvt_idx,
bool cam_ignore, uint8_t priority,
bool crowd, bool cam_ignore, uint8_t priority,
uint32_t logic_serv, XiveTCTXMatch *match)
{
PnvXive2 *xive = PNV_XIVE2(xptr);
@@ -656,25 +655,38 @@ static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format,
logic_serv);
} else {
ring = xive2_presenter_tctx_match(xptr, tctx, format, nvt_blk,
nvt_idx, cam_ignore,
logic_serv);
nvt_idx, crowd, cam_ignore,
logic_serv);
}
/*
* Save the context and follow on to catch duplicates,
* that we don't support yet.
*/
if (ring != -1) {
if (match->tctx) {
/*
* For VP-specific match, finding more than one is a
* problem. For group notification, it's possible.
*/
if (!cam_ignore && match->tctx) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a "
"thread context NVT %x/%x\n",
nvt_blk, nvt_idx);
return false;
/* Should set a FIR if we ever model it */
return -1;
}
/*
* For a group notification, we need to know if the
* match is precluded first by checking the current
* thread priority. If the interrupt can be delivered,
* we always notify the first match (for now).
*/
if (cam_ignore &&
xive2_tm_irq_precluded(tctx, ring, priority)) {
match->precluded = true;
} else {
if (!match->tctx) {
match->ring = ring;
match->tctx = tctx;
}
count++;
}
match->ring = ring;
match->tctx = tctx;
count++;
}
}
}
@@ -693,6 +705,47 @@ static uint32_t pnv_xive2_presenter_get_config(XivePresenter *xptr)
return cfg;
}
static int pnv_xive2_broadcast(XivePresenter *xptr,
uint8_t nvt_blk, uint32_t nvt_idx,
bool crowd, bool ignore, uint8_t priority)
{
PnvXive2 *xive = PNV_XIVE2(xptr);
PnvChip *chip = xive->chip;
int i, j;
bool gen1_tima_os =
xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS;
for (i = 0; i < chip->nr_cores; i++) {
PnvCore *pc = chip->cores[i];
CPUCore *cc = CPU_CORE(pc);
for (j = 0; j < cc->nr_threads; j++) {
PowerPCCPU *cpu = pc->threads[j];
XiveTCTX *tctx;
int ring;
if (!pnv_xive2_is_cpu_enabled(xive, cpu)) {
continue;
}
tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc);
if (gen1_tima_os) {
ring = xive_presenter_tctx_match(xptr, tctx, 0, nvt_blk,
nvt_idx, ignore, 0);
} else {
ring = xive2_presenter_tctx_match(xptr, tctx, 0, nvt_blk,
nvt_idx, crowd, ignore, 0);
}
if (ring != -1) {
xive2_tm_set_lsmfb(tctx, ring, priority);
}
}
}
return 0;
}
static uint8_t pnv_xive2_get_block_id(Xive2Router *xrtr)
{
return pnv_xive2_block_id(PNV_XIVE2(xrtr));
@@ -2149,21 +2202,40 @@ static const MemoryRegionOps pnv_xive2_tm_ops = {
},
};
static uint64_t pnv_xive2_nvc_read(void *opaque, hwaddr offset,
static uint64_t pnv_xive2_nvc_read(void *opaque, hwaddr addr,
unsigned size)
{
PnvXive2 *xive = PNV_XIVE2(opaque);
XivePresenter *xptr = XIVE_PRESENTER(xive);
uint32_t page = addr >> xive->nvpg_shift;
uint16_t op = addr & 0xFFF;
uint8_t blk = pnv_xive2_block_id(xive);
xive2_error(xive, "NVC: invalid read @%"HWADDR_PRIx, offset);
return -1;
if (size != 2) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvc load size %d\n",
size);
return -1;
}
return xive2_presenter_nvgc_backlog_op(xptr, true, blk, page, op, 1);
}
static void pnv_xive2_nvc_write(void *opaque, hwaddr offset,
static void pnv_xive2_nvc_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
PnvXive2 *xive = PNV_XIVE2(opaque);
XivePresenter *xptr = XIVE_PRESENTER(xive);
uint32_t page = addr >> xive->nvc_shift;
uint16_t op = addr & 0xFFF;
uint8_t blk = pnv_xive2_block_id(xive);
xive2_error(xive, "NVC: invalid write @%"HWADDR_PRIx, offset);
if (size != 1) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvc write size %d\n",
size);
return;
}
(void)xive2_presenter_nvgc_backlog_op(xptr, true, blk, page, op, val);
}
static const MemoryRegionOps pnv_xive2_nvc_ops = {
@@ -2171,30 +2243,63 @@ static const MemoryRegionOps pnv_xive2_nvc_ops = {
.write = pnv_xive2_nvc_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,
},
};
static uint64_t pnv_xive2_nvpg_read(void *opaque, hwaddr offset,
static uint64_t pnv_xive2_nvpg_read(void *opaque, hwaddr addr,
unsigned size)
{
PnvXive2 *xive = PNV_XIVE2(opaque);
XivePresenter *xptr = XIVE_PRESENTER(xive);
uint32_t page = addr >> xive->nvpg_shift;
uint16_t op = addr & 0xFFF;
uint32_t index = page >> 1;
uint8_t blk = pnv_xive2_block_id(xive);
xive2_error(xive, "NVPG: invalid read @%"HWADDR_PRIx, offset);
return -1;
if (size != 2) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvpg load size %d\n",
size);
return -1;
}
if (page % 2) {
/* odd page - NVG */
return xive2_presenter_nvgc_backlog_op(xptr, false, blk, index, op, 1);
} else {
/* even page - NVP */
return xive2_presenter_nvp_backlog_op(xptr, blk, index, op);
}
}
static void pnv_xive2_nvpg_write(void *opaque, hwaddr offset,
static void pnv_xive2_nvpg_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
PnvXive2 *xive = PNV_XIVE2(opaque);
XivePresenter *xptr = XIVE_PRESENTER(xive);
uint32_t page = addr >> xive->nvpg_shift;
uint16_t op = addr & 0xFFF;
uint32_t index = page >> 1;
uint8_t blk = pnv_xive2_block_id(xive);
xive2_error(xive, "NVPG: invalid write @%"HWADDR_PRIx, offset);
if (size != 1) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvpg write size %d\n",
size);
return;
}
if (page % 2) {
/* odd page - NVG */
(void)xive2_presenter_nvgc_backlog_op(xptr, false, blk, index, op, val);
} else {
/* even page - NVP */
(void)xive2_presenter_nvp_backlog_op(xptr, blk, index, op);
}
}
static const MemoryRegionOps pnv_xive2_nvpg_ops = {
@@ -2202,11 +2307,11 @@ static const MemoryRegionOps pnv_xive2_nvpg_ops = {
.write = pnv_xive2_nvpg_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,
},
};
@@ -2432,6 +2537,7 @@ static void pnv_xive2_class_init(ObjectClass *klass, void *data)
xpc->match_nvt = pnv_xive2_match_nvt;
xpc->get_config = pnv_xive2_presenter_get_config;
xpc->broadcast = pnv_xive2_broadcast;
};
static const TypeInfo pnv_xive2_info = {
+4 -4
View File
@@ -1,10 +1,9 @@
/*
* QEMU PowerPC sPAPR XIVE interrupt controller model
*
* Copyright (c) 2017-2018, IBM Corporation.
* Copyright (c) 2017-2024, IBM Corporation.
*
* This code is licensed under the GPL version 2 or later. See the
* COPYING file in the top-level directory.
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
@@ -431,7 +430,8 @@ static int spapr_xive_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk,
static int spapr_xive_match_nvt(XivePresenter *xptr, uint8_t format,
uint8_t nvt_blk, uint32_t nvt_idx,
bool cam_ignore, uint8_t priority,
bool crowd, bool cam_ignore,
uint8_t priority,
uint32_t logic_serv, XiveTCTXMatch *match)
{
CPUState *cs;
+5 -1
View File
@@ -283,9 +283,13 @@ xive_router_end_notify(uint8_t end_blk, uint32_t end_idx, uint32_t end_data) "EN
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(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_presenter_notify(uint8_t nvt_blk, uint32_t nvt_idx, uint8_t ring, uint8_t group_level) "found NVT 0x%x/0x%x ring=0x%x group_level=%d"
xive_end_source_read(uint8_t end_blk, uint32_t end_idx, uint64_t addr) "END 0x%x/0x%x @0x%"PRIx64
# xive2.c
xive_nvp_backlog_op(uint8_t blk, uint32_t idx, uint8_t op, uint8_t priority, uint8_t rc) "NVP 0x%x/0x%x operation=%d priority=%d rc=%d"
xive_nvgc_backlog_op(bool c, uint8_t blk, uint32_t idx, uint8_t op, uint8_t priority, uint32_t rc) "NVGC crowd=%d 0x%x/0x%x operation=%d priority=%d rc=%d"
# pnv_xive.c
pnv_xive_ic_hw_trigger(uint64_t addr, uint64_t val) "@0x%"PRIx64" val=0x%"PRIx64
+132 -73
View File
@@ -3,8 +3,7 @@
*
* Copyright (c) 2017-2018, IBM Corporation.
*
* This code is licensed under the GPL version 2 or later. See the
* COPYING file in the top-level directory.
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
@@ -27,28 +26,6 @@
* XIVE Thread Interrupt Management context
*/
/*
* Convert an Interrupt Pending Buffer (IPB) register to a Pending
* Interrupt Priority Register (PIPR), which contains the priority of
* the most favored pending notification.
*/
static uint8_t ipb_to_pipr(uint8_t ibp)
{
return ibp ? clz32((uint32_t)ibp << 24) : 0xff;
}
static uint8_t exception_mask(uint8_t ring)
{
switch (ring) {
case TM_QW1_OS:
return TM_QW1_NSR_EO;
case TM_QW3_HV_PHYS:
return TM_QW3_NSR_HE;
default:
g_assert_not_reached();
}
}
static qemu_irq xive_tctx_output(XiveTCTX *tctx, uint8_t ring)
{
switch (ring) {
@@ -68,11 +45,10 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring)
{
uint8_t *regs = &tctx->regs[ring];
uint8_t nsr = regs[TM_NSR];
uint8_t mask = exception_mask(ring);
qemu_irq_lower(xive_tctx_output(tctx, ring));
if (regs[TM_NSR] & mask) {
if (regs[TM_NSR] != 0) {
uint8_t cppr = regs[TM_PIPR];
uint8_t alt_ring;
uint8_t *alt_regs;
@@ -87,11 +63,18 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring)
regs[TM_CPPR] = cppr;
/* Reset the pending buffer bit */
alt_regs[TM_IPB] &= ~xive_priority_to_ipb(cppr);
/*
* If the interrupt was for a specific VP, reset the pending
* buffer bit, otherwise clear the logical server indicator
*/
if (regs[TM_NSR] & TM_NSR_GRP_LVL) {
regs[TM_NSR] &= ~TM_NSR_GRP_LVL;
} else {
alt_regs[TM_IPB] &= ~xive_priority_to_ipb(cppr);
}
/* Drop Exception bit */
regs[TM_NSR] &= ~mask;
/* Drop the exception bit and any group/crowd */
regs[TM_NSR] = 0;
trace_xive_tctx_accept(tctx->cs->cpu_index, alt_ring,
alt_regs[TM_IPB], regs[TM_PIPR],
@@ -101,7 +84,7 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring)
return ((uint64_t)nsr << 8) | regs[TM_CPPR];
}
static void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring)
void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level)
{
/* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */
uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring;
@@ -111,13 +94,13 @@ static void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring)
if (alt_regs[TM_PIPR] < alt_regs[TM_CPPR]) {
switch (ring) {
case TM_QW1_OS:
regs[TM_NSR] |= TM_QW1_NSR_EO;
regs[TM_NSR] = TM_QW1_NSR_EO | (group_level & 0x3F);
break;
case TM_QW2_HV_POOL:
alt_regs[TM_NSR] = (TM_QW3_NSR_HE_POOL << 6);
alt_regs[TM_NSR] = (TM_QW3_NSR_HE_POOL << 6) | (group_level & 0x3F);
break;
case TM_QW3_HV_PHYS:
regs[TM_NSR] |= (TM_QW3_NSR_HE_PHYS << 6);
regs[TM_NSR] = (TM_QW3_NSR_HE_PHYS << 6) | (group_level & 0x3F);
break;
default:
g_assert_not_reached();
@@ -159,7 +142,7 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
* Recompute the PIPR based on local pending interrupts. The PHYS
* ring must take the minimum of both the PHYS and POOL PIPR values.
*/
pipr_min = ipb_to_pipr(regs[TM_IPB]);
pipr_min = xive_ipb_to_pipr(regs[TM_IPB]);
ring_min = ring;
/* PHYS updates also depend on POOL values */
@@ -169,7 +152,7 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
/* POOL values only matter if POOL ctx is valid */
if (pool_regs[TM_WORD2] & 0x80) {
uint8_t pool_pipr = ipb_to_pipr(pool_regs[TM_IPB]);
uint8_t pool_pipr = xive_ipb_to_pipr(pool_regs[TM_IPB]);
/*
* Determine highest priority interrupt and
@@ -185,17 +168,27 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
regs[TM_PIPR] = pipr_min;
/* CPPR has changed, check if we need to raise a pending exception */
xive_tctx_notify(tctx, ring_min);
xive_tctx_notify(tctx, ring_min, 0);
}
void xive_tctx_ipb_update(XiveTCTX *tctx, uint8_t ring, uint8_t ipb)
{
void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority,
uint8_t group_level)
{
/* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */
uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring;
uint8_t *alt_regs = &tctx->regs[alt_ring];
uint8_t *regs = &tctx->regs[ring];
regs[TM_IPB] |= ipb;
regs[TM_PIPR] = ipb_to_pipr(regs[TM_IPB]);
xive_tctx_notify(tctx, ring);
}
if (group_level == 0) {
/* VP-specific */
regs[TM_IPB] |= xive_priority_to_ipb(priority);
alt_regs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]);
} else {
/* VP-group */
alt_regs[TM_PIPR] = xive_priority_to_pipr(priority);
}
xive_tctx_notify(tctx, ring, group_level);
}
/*
* XIVE Thread Interrupt Management Area (TIMA)
@@ -411,13 +404,13 @@ static void xive_tm_set_os_lgs(XivePresenter *xptr, XiveTCTX *tctx,
}
/*
* Adjust the IPB to allow a CPU to process event queues of other
* Adjust the PIPR to allow a CPU to process event queues of other
* priorities during one physical interrupt cycle.
*/
static void xive_tm_set_os_pending(XivePresenter *xptr, XiveTCTX *tctx,
hwaddr offset, uint64_t value, unsigned size)
{
xive_tctx_ipb_update(tctx, TM_QW1_OS, xive_priority_to_ipb(value & 0xff));
xive_tctx_pipr_update(tctx, TM_QW1_OS, value & 0xff, 0);
}
static void xive_os_cam_decode(uint32_t cam, uint8_t *nvt_blk,
@@ -495,16 +488,20 @@ static void xive_tctx_need_resend(XiveRouter *xrtr, XiveTCTX *tctx,
/* Reset the NVT value */
nvt.w4 = xive_set_field32(NVT_W4_IPB, nvt.w4, 0);
xive_router_write_nvt(xrtr, nvt_blk, nvt_idx, &nvt, 4);
uint8_t *regs = &tctx->regs[TM_QW1_OS];
regs[TM_IPB] |= ipb;
}
/*
* Always call xive_tctx_ipb_update(). Even if there were no
* Always call xive_tctx_pipr_update(). Even if there were no
* escalation triggered, there could be a pending interrupt which
* was saved when the context was pulled and that we need to take
* into account by recalculating the PIPR (which is not
* saved/restored).
* It will also raise the External interrupt signal if needed.
*/
xive_tctx_ipb_update(tctx, TM_QW1_OS, ipb);
xive_tctx_pipr_update(tctx, TM_QW1_OS, 0xFF, 0); /* fxb */
}
/*
@@ -592,7 +589,7 @@ static const XiveTmOp xive2_tm_operations[] = {
* MMIOs below 2K : raw values and special operations without side
* effects
*/
{ XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive_tm_set_os_cppr,
{ XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive2_tm_set_os_cppr,
NULL },
{ XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, xive2_tm_push_os_ctx,
NULL },
@@ -600,7 +597,7 @@ static const XiveTmOp xive2_tm_operations[] = {
NULL },
{ XIVE_TM_OS_PAGE, TM_QW1_OS + TM_LGS, 1, xive_tm_set_os_lgs,
NULL },
{ XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive_tm_set_hv_cppr,
{ XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive2_tm_set_hv_cppr,
NULL },
{ XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, xive_tm_vt_push,
NULL },
@@ -841,9 +838,9 @@ void xive_tctx_reset(XiveTCTX *tctx)
* CPPR is first set.
*/
tctx->regs[TM_QW1_OS + TM_PIPR] =
ipb_to_pipr(tctx->regs[TM_QW1_OS + TM_IPB]);
xive_ipb_to_pipr(tctx->regs[TM_QW1_OS + TM_IPB]);
tctx->regs[TM_QW3_HV_PHYS + TM_PIPR] =
ipb_to_pipr(tctx->regs[TM_QW3_HV_PHYS + TM_IPB]);
xive_ipb_to_pipr(tctx->regs[TM_QW3_HV_PHYS + TM_IPB]);
}
static void xive_tctx_realize(DeviceState *dev, Error **errp)
@@ -1658,6 +1655,54 @@ static uint32_t xive_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx)
return xive_nvt_cam_line(blk, 1 << 7 | (pir & 0x7f));
}
uint32_t xive_get_vpgroup_size(uint32_t nvp_index)
{
/*
* Group size is a power of 2. The position of the first 0
* (starting with the least significant bits) in the NVP index
* gives the size of the group.
*/
return 1 << (ctz32(~nvp_index) + 1);
}
static uint8_t xive_get_group_level(bool crowd, bool ignore,
uint32_t nvp_blk, uint32_t nvp_index)
{
uint8_t level;
if (!ignore) {
g_assert(!crowd);
return 0;
}
level = (ctz32(~nvp_index) + 1) & 0b1111;
if (crowd) {
uint32_t blk;
/* crowd level is bit position of first 0 from the right in nvp_blk */
blk = ctz32(~nvp_blk) + 1;
/*
* Supported crowd sizes are 2^1, 2^2, and 2^4. 2^3 is not supported.
* HW will encode level 4 as the value 3. See xive2_pgofnext().
*/
switch (level) {
case 1:
case 2:
break;
case 4:
blk = 3;
break;
default:
g_assert_not_reached();
}
/* Crowd level bits reside in upper 2 bits of the 6 bit group level */
level |= blk << 4;
}
return level;
}
/*
* The thread context register words are in big-endian format.
*/
@@ -1724,31 +1769,41 @@ int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx,
/*
* This is our simple Xive Presenter Engine model. It is merged in the
* Router as it does not require an extra object.
*
* It receives notification requests sent by the IVRE to find one
* matching NVT (or more) dispatched on the processor threads. In case
* of a single NVT notification, the process is abbreviated and the
* thread is signaled if a match is found. In case of a logical server
* notification (bits ignored at the end of the NVT identifier), the
* IVPE and IVRE select a winning thread using different filters. This
* involves 2 or 3 exchanges on the PowerBus that the model does not
* support.
*
* The parameters represent what is sent on the PowerBus
*/
bool xive_presenter_notify(XiveFabric *xfb, uint8_t format,
uint8_t nvt_blk, uint32_t nvt_idx,
bool cam_ignore, uint8_t priority,
uint32_t logic_serv)
bool crowd, bool cam_ignore, uint8_t priority,
uint32_t logic_serv, bool *precluded)
{
XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xfb);
XiveTCTXMatch match = { .tctx = NULL, .ring = 0 };
XiveTCTXMatch match = { .tctx = NULL, .ring = 0, .precluded = false };
uint8_t group_level;
int count;
/*
* Ask the machine to scan the interrupt controllers for a match
* Ask the machine to scan the interrupt controllers for a match.
*
* For VP-specific notification, we expect at most one match and
* one call to the presenters is all we need (abbreviated notify
* sequence documented by the architecture).
*
* For VP-group notification, match_nvt() is the equivalent of the
* "histogram" and "poll" commands sent to the power bus to the
* presenters. 'count' could be more than one, but we always
* select the first match for now. 'precluded' tells if (at least)
* one thread matches but can't take the interrupt now because
* it's running at a more favored priority. We return the
* information to the router so that it can take appropriate
* actions (backlog, escalation, broadcast, etc...)
*
* If we were to implement a better way of dispatching the
* interrupt in case of multiple matches (instead of the first
* match), we would need a heuristic to elect a thread (for
* example, the hardware keeps track of an 'age' in the TIMA) and
* a new command to the presenters (the equivalent of the "assign"
* power bus command in the documented full notify sequence.
*/
count = xfc->match_nvt(xfb, format, nvt_blk, nvt_idx, cam_ignore,
count = xfc->match_nvt(xfb, format, nvt_blk, nvt_idx, crowd, cam_ignore,
priority, logic_serv, &match);
if (count < 0) {
return false;
@@ -1756,9 +1811,11 @@ bool xive_presenter_notify(XiveFabric *xfb, uint8_t format,
/* handle CPU exception delivery */
if (count) {
trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring);
xive_tctx_ipb_update(match.tctx, match.ring,
xive_priority_to_ipb(priority));
group_level = xive_get_group_level(crowd, cam_ignore, nvt_blk, nvt_idx);
trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring, group_level);
xive_tctx_pipr_update(match.tctx, match.ring, priority, group_level);
} else {
*precluded = match.precluded;
}
return !!count;
@@ -1798,7 +1855,7 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas)
uint8_t nvt_blk;
uint32_t nvt_idx;
XiveNVT nvt;
bool found;
bool found, precluded;
uint8_t end_blk = xive_get_field64(EAS_END_BLOCK, eas->w);
uint32_t end_idx = xive_get_field64(EAS_END_INDEX, eas->w);
@@ -1879,10 +1936,12 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas)
}
found = xive_presenter_notify(xrtr->xfb, format, nvt_blk, nvt_idx,
false /* crowd */,
xive_get_field32(END_W7_F0_IGNORE, end.w7),
priority,
xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7));
xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7),
&precluded);
/* we don't support VP-group notification on P9, so precluded is not used */
/* TODO: Auto EOI. */
if (found) {
+619 -71
View File
File diff suppressed because it is too large Load Diff
+54 -1
View File
@@ -197,6 +197,9 @@ static PnvPHB *pnv_pec_default_phb_realize(PnvPhb4PecState *pec,
return phb;
}
#define XPEC_P9_PCI_LANE_CFG PPC_BITMASK(10, 11)
#define XPEC_P10_PCI_LANE_CFG PPC_BITMASK(0, 1)
static void pnv_pec_realize(DeviceState *dev, Error **errp)
{
PnvPhb4PecState *pec = PNV_PHB4_PEC(dev);
@@ -211,6 +214,43 @@ static void pnv_pec_realize(DeviceState *dev, Error **errp)
pec->num_phbs = pecc->num_phbs[pec->index];
/* Pervasive chiplet */
object_initialize_child(OBJECT(pec), "nest-pervasive-common",
&pec->nest_pervasive,
TYPE_PNV_NEST_CHIPLET_PERVASIVE);
if (!qdev_realize(DEVICE(&pec->nest_pervasive), NULL, errp)) {
return;
}
/* Set up pervasive chiplet registers */
/*
* Most registers are not set up, this just sets the PCI CONF1 link-width
* field because skiboot probes it.
*/
if (pecc->version == PNV_PHB4_VERSION) {
/*
* On P9, PEC2 has configurable 1/2/3-furcation).
* Make it trifurcated (x8, x4, x4) to match pnv_pec_num_phbs.
*/
if (pec->index == 2) {
pec->nest_pervasive.control_regs.cplt_cfg1 =
SETFIELD(XPEC_P9_PCI_LANE_CFG,
pec->nest_pervasive.control_regs.cplt_cfg1,
0b10);
}
} else if (pecc->version == PNV_PHB5_VERSION) {
/*
* On P10, both PECs are configurable 1/2/3-furcation).
* Both are trifurcated to match pnv_phb5_pec_num_stacks.
*/
pec->nest_pervasive.control_regs.cplt_cfg1 =
SETFIELD(XPEC_P10_PCI_LANE_CFG,
pec->nest_pervasive.control_regs.cplt_cfg1,
0b10);
} else {
g_assert_not_reached();
}
/* Create PHBs if running with defaults */
if (defaults_enabled()) {
g_assert(pec->num_phbs <= MAX_PHBS_PER_PEC);
@@ -290,9 +330,16 @@ static const Property pnv_pec_properties[] = {
PnvChip *),
};
#define XPEC_PCI_CPLT_OFFSET 0x1000000ULL
static uint32_t pnv_pec_xscom_cplt_base(PnvPhb4PecState *pec)
{
return PNV9_XSCOM_PEC_NEST_CPLT_BASE + XPEC_PCI_CPLT_OFFSET * pec->index;
}
static uint32_t pnv_pec_xscom_pci_base(PnvPhb4PecState *pec)
{
return PNV9_XSCOM_PEC_PCI_BASE + 0x1000000 * pec->index;
return PNV9_XSCOM_PEC_PCI_BASE + XPEC_PCI_CPLT_OFFSET * pec->index;
}
static uint32_t pnv_pec_xscom_nest_base(PnvPhb4PecState *pec)
@@ -321,6 +368,7 @@ static void pnv_pec_class_init(ObjectClass *klass, void *data)
device_class_set_props(dc, pnv_pec_properties);
dc->user_creatable = false;
pecc->xscom_cplt_base = pnv_pec_xscom_cplt_base;
pecc->xscom_nest_base = pnv_pec_xscom_nest_base;
pecc->xscom_pci_base = pnv_pec_xscom_pci_base;
pecc->xscom_nest_size = PNV9_XSCOM_PEC_NEST_SIZE;
@@ -349,6 +397,10 @@ static const TypeInfo pnv_pec_type_info = {
/*
* POWER10 definitions
*/
static uint32_t pnv_phb5_pec_xscom_cplt_base(PnvPhb4PecState *pec)
{
return PNV10_XSCOM_PEC_NEST_CPLT_BASE + XPEC_PCI_CPLT_OFFSET * pec->index;
}
static uint32_t pnv_phb5_pec_xscom_pci_base(PnvPhb4PecState *pec)
{
@@ -373,6 +425,7 @@ static void pnv_phb5_pec_class_init(ObjectClass *klass, void *data)
static const char compat[] = "ibm,power10-pbcq";
static const char stk_compat[] = "ibm,power10-phb-stack";
pecc->xscom_cplt_base = pnv_phb5_pec_xscom_cplt_base;
pecc->xscom_nest_base = pnv_phb5_pec_xscom_nest_base;
pecc->xscom_pci_base = pnv_phb5_pec_xscom_pci_base;
pecc->xscom_nest_size = PNV10_XSCOM_PEC_NEST_SIZE;
-9
View File
@@ -44,15 +44,6 @@ config POWERNV
select SSI_M25P80
select PNV_SPI
config PPC405
bool
default y
depends on PPC
select M48T59
select PFLASH_CFI02
select PPC4XX
select SERIAL_MM
config PPC440
bool
default y
+271 -13
View File
@@ -21,12 +21,26 @@
#include "hw/ide/pci.h"
#include "hw/i2c/smbus_eeprom.h"
#include "hw/ppc/ppc.h"
#include "system/block-backend.h"
#include "system/qtest.h"
#include "system/reset.h"
#include "kvm_ppc.h"
#include "elf.h"
#include <zlib.h> /* for crc32 */
#define BUS_FREQ_HZ 100000000
#define INITRD_MIN_ADDR 0x600000
#define INIT_RAM_ADDR 0x40000000
#define PCI_HIGH_ADDR 0x80000000
#define PCI_HIGH_SIZE 0x7d000000
#define PCI_LOW_ADDR 0xfd000000
#define PCI_LOW_SIZE 0xe0000
#define ARTICIA_ADDR 0xfe000000
/*
* Firmware binary available at
* https://www.hyperion-entertainment.com/index.php/downloads?view=files&parent=28
@@ -41,20 +55,202 @@
/* AmigaOS calls this routine from ROM, use this if no firmware loaded */
static const char dummy_fw[] = {
0x38, 0x00, 0x00, 0x08, /* li r0,8 */
0x7c, 0x09, 0x03, 0xa6, /* mtctr r0 */
0x54, 0x63, 0xf8, 0x7e, /* srwi r3,r3,1 */
0x42, 0x00, 0xff, 0xfc, /* bdnz 0x8 */
0x54, 0x63, 0xc2, 0x3e, /* srwi r3,r3,8 */
0x7c, 0x63, 0x18, 0xf8, /* not r3,r3 */
0x4e, 0x80, 0x00, 0x20, /* blr */
};
#define NVRAM_ADDR 0xfd0e0000
#define NVRAM_SIZE (4 * KiB)
static char default_env[] =
"baudrate=115200\0"
"stdout=vga\0"
"stdin=ps2kbd\0"
"bootcmd=boota; menu; run menuboot_cmd\0"
"boot1=ide\0"
"boot2=cdrom\0"
"boota_timeout=3\0"
"ide_doreset=on\0"
"pci_irqa=9\0"
"pci_irqa_select=level\0"
"pci_irqb=10\0"
"pci_irqb_select=level\0"
"pci_irqc=11\0"
"pci_irqc_select=level\0"
"pci_irqd=7\0"
"pci_irqd_select=level\0"
"a1ide_irq=1111\0"
"a1ide_xfer=FFFF\0";
#define CRC32_DEFAULT_ENV 0xb5548481
#define CRC32_ALL_ZEROS 0x603b0489
#define TYPE_A1_NVRAM "a1-nvram"
OBJECT_DECLARE_SIMPLE_TYPE(A1NVRAMState, A1_NVRAM)
struct A1NVRAMState {
SysBusDevice parent_obj;
MemoryRegion mr;
BlockBackend *blk;
};
static uint64_t nvram_read(void *opaque, hwaddr addr, unsigned int size)
{
/* read callback not used because of romd mode */
g_assert_not_reached();
}
static void nvram_write(void *opaque, hwaddr addr, uint64_t val,
unsigned int size)
{
A1NVRAMState *s = opaque;
uint8_t *p = memory_region_get_ram_ptr(&s->mr);
p[addr] = val;
if (s->blk) {
blk_pwrite(s->blk, addr, 1, &val, 0);
}
}
static const MemoryRegionOps nvram_ops = {
.read = nvram_read,
.write = nvram_write,
.endianness = DEVICE_BIG_ENDIAN,
.impl = {
.min_access_size = 1,
.max_access_size = 1,
},
};
static void nvram_realize(DeviceState *dev, Error **errp)
{
A1NVRAMState *s = A1_NVRAM(dev);
void *p;
uint32_t crc, *c;
memory_region_init_rom_device(&s->mr, NULL, &nvram_ops, s, "nvram",
NVRAM_SIZE, &error_fatal);
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr);
c = p = memory_region_get_ram_ptr(&s->mr);
if (s->blk) {
if (blk_getlength(s->blk) != NVRAM_SIZE) {
error_setg(errp, "NVRAM backing file size must be %" PRId64 "bytes",
NVRAM_SIZE);
return;
}
blk_set_perm(s->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE,
BLK_PERM_ALL, &error_fatal);
if (blk_pread(s->blk, 0, NVRAM_SIZE, p, 0) < 0) {
error_setg(errp, "Cannot read NVRAM contents from backing file");
return;
}
}
crc = crc32(0, p + 4, NVRAM_SIZE - 4);
if (crc == CRC32_ALL_ZEROS) { /* If env is uninitialized set default */
*c = cpu_to_be32(CRC32_DEFAULT_ENV);
/* Also copies terminating \0 as env is terminated by \0\0 */
memcpy(p + 4, default_env, sizeof(default_env));
if (s->blk) {
blk_pwrite(s->blk, 0, sizeof(crc) + sizeof(default_env), p, 0);
}
return;
}
if (*c == 0) {
*c = cpu_to_be32(crc32(0, p + 4, NVRAM_SIZE - 4));
if (s->blk) {
blk_pwrite(s->blk, 0, 4, p, 0);
}
}
if (be32_to_cpu(*c) != crc) {
warn_report("NVRAM checksum mismatch");
}
}
static const Property nvram_properties[] = {
DEFINE_PROP_DRIVE("drive", A1NVRAMState, blk),
};
static void nvram_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = nvram_realize;
device_class_set_props(dc, nvram_properties);
}
static const TypeInfo nvram_types[] = {
{
.name = TYPE_A1_NVRAM,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(A1NVRAMState),
.class_init = nvram_class_init,
},
};
DEFINE_TYPES(nvram_types)
struct boot_info {
hwaddr entry;
hwaddr stack;
hwaddr bd_info;
hwaddr initrd_start;
hwaddr initrd_end;
hwaddr cmdline_start;
hwaddr cmdline_end;
};
/* Board info struct from U-Boot */
struct bd_info {
uint32_t bi_memstart;
uint32_t bi_memsize;
uint32_t bi_flashstart;
uint32_t bi_flashsize;
uint32_t bi_flashoffset;
uint32_t bi_sramstart;
uint32_t bi_sramsize;
uint32_t bi_bootflags;
uint32_t bi_ip_addr;
uint8_t bi_enetaddr[6];
uint16_t bi_ethspeed;
uint32_t bi_intfreq;
uint32_t bi_busfreq;
uint32_t bi_baudrate;
} QEMU_PACKED;
static void create_bd_info(hwaddr addr, ram_addr_t ram_size)
{
struct bd_info *bd = g_new0(struct bd_info, 1);
bd->bi_memsize = cpu_to_be32(ram_size);
bd->bi_flashstart = cpu_to_be32(PROM_ADDR);
bd->bi_flashsize = cpu_to_be32(1); /* match what U-Boot detects */
bd->bi_bootflags = cpu_to_be32(1);
bd->bi_intfreq = cpu_to_be32(11.5 * BUS_FREQ_HZ);
bd->bi_busfreq = cpu_to_be32(BUS_FREQ_HZ);
bd->bi_baudrate = cpu_to_be32(115200);
cpu_physical_memory_write(addr, bd, sizeof(*bd));
}
static void amigaone_cpu_reset(void *opaque)
{
PowerPCCPU *cpu = opaque;
CPUPPCState *env = &cpu->env;
cpu_reset(CPU(cpu));
cpu_ppc_tb_reset(&cpu->env);
if (env->load_info) {
struct boot_info *bi = env->load_info;
env->gpr[1] = bi->stack;
env->gpr[2] = 1024;
env->gpr[3] = bi->bd_info;
env->gpr[4] = bi->initrd_start;
env->gpr[5] = bi->initrd_end;
env->gpr[6] = bi->cmdline_start;
env->gpr[7] = bi->cmdline_end;
env->nip = bi->entry;
}
cpu_ppc_tb_reset(env);
}
static void fix_spd_data(uint8_t *spd)
@@ -75,7 +271,9 @@ static void amigaone_init(MachineState *machine)
DeviceState *dev;
I2CBus *i2c_bus;
uint8_t *spd_data;
int i;
DriveInfo *di;
hwaddr loadaddr;
struct boot_info *bi = NULL;
/* init CPU */
cpu = POWERPC_CPU(cpu_create(machine->cpu_type));
@@ -97,9 +295,19 @@ static void amigaone_init(MachineState *machine)
/* Firmware uses this area for startup */
mr = g_new(MemoryRegion, 1);
memory_region_init_ram(mr, NULL, "init-cache", 32 * KiB, &error_fatal);
memory_region_add_subregion(get_system_memory(), 0x40000000, mr);
memory_region_add_subregion(get_system_memory(), INIT_RAM_ADDR, mr);
}
/* nvram */
dev = qdev_new(TYPE_A1_NVRAM);
di = drive_get(IF_MTD, 0, 0);
if (di) {
qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(di));
}
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
memory_region_add_subregion(get_system_memory(), NVRAM_ADDR,
sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0));
/* allocate and load firmware */
rom = g_new(MemoryRegion, 1);
memory_region_init_rom(rom, NULL, "rom", PROM_SIZE, &error_fatal);
@@ -122,7 +330,7 @@ static void amigaone_init(MachineState *machine)
}
/* Articia S */
dev = sysbus_create_simple(TYPE_ARTICIA, 0xfe000000, NULL);
dev = sysbus_create_simple(TYPE_ARTICIA, ARTICIA_ADDR, NULL);
i2c_bus = I2C_BUS(qdev_get_child_bus(dev, "smbus"));
if (machine->ram_size > 512 * MiB) {
@@ -139,12 +347,12 @@ static void amigaone_init(MachineState *machine)
pci_mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
mr = g_new(MemoryRegion, 1);
memory_region_init_alias(mr, OBJECT(dev), "pci-mem-low", pci_mem,
0, 0x1000000);
memory_region_add_subregion(get_system_memory(), 0xfd000000, mr);
0, PCI_LOW_SIZE);
memory_region_add_subregion(get_system_memory(), PCI_LOW_ADDR, mr);
mr = g_new(MemoryRegion, 1);
memory_region_init_alias(mr, OBJECT(dev), "pci-mem-high", pci_mem,
0x80000000, 0x7d000000);
memory_region_add_subregion(get_system_memory(), 0x80000000, mr);
PCI_HIGH_ADDR, PCI_HIGH_SIZE);
memory_region_add_subregion(get_system_memory(), PCI_HIGH_ADDR, mr);
pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci.0"));
/* VIA VT82c686B South Bridge (multifunction PCI device) */
@@ -156,12 +364,62 @@ static void amigaone_init(MachineState *machine)
qdev_connect_gpio_out_named(DEVICE(via), "intr", 0,
qdev_get_gpio_in(DEVICE(cpu),
PPC6xx_INPUT_INT));
for (i = 0; i < PCI_NUM_PINS; i++) {
for (int i = 0; i < PCI_NUM_PINS; i++) {
qdev_connect_gpio_out(dev, i, qdev_get_gpio_in_named(DEVICE(via),
"pirq", i));
}
pci_ide_create_devs(PCI_DEVICE(object_resolve_path_component(via, "ide")));
pci_vga_init(pci_bus);
if (!machine->kernel_filename) {
return;
}
/* handle -kernel, -initrd, -append options and emulate U-Boot */
bi = g_new0(struct boot_info, 1);
cpu->env.load_info = bi;
loadaddr = MIN(machine->ram_size, 256 * MiB);
bi->bd_info = loadaddr - 8 * MiB;
create_bd_info(bi->bd_info, machine->ram_size);
bi->stack = bi->bd_info - 64 * KiB - 8;
if (machine->kernel_cmdline && machine->kernel_cmdline[0]) {
size_t len = strlen(machine->kernel_cmdline);
loadaddr = bi->bd_info + 1 * MiB;
cpu_physical_memory_write(loadaddr, machine->kernel_cmdline, len + 1);
bi->cmdline_start = loadaddr;
bi->cmdline_end = loadaddr + len + 1; /* including terminating '\0' */
}
sz = load_elf(machine->kernel_filename, NULL, NULL, NULL,
&bi->entry, &loadaddr, NULL, NULL,
ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0);
if (sz <= 0) {
sz = load_uimage(machine->kernel_filename, &bi->entry, &loadaddr,
NULL, NULL, NULL);
}
if (sz <= 0) {
error_report("Could not load kernel '%s'",
machine->kernel_filename);
exit(1);
}
loadaddr += sz;
if (machine->initrd_filename) {
loadaddr = ROUND_UP(loadaddr + 4 * MiB, 4 * KiB);
loadaddr = MAX(loadaddr, INITRD_MIN_ADDR);
sz = load_image_targphys(machine->initrd_filename, loadaddr,
bi->bd_info - loadaddr);
if (sz <= 0) {
error_report("Could not load initrd '%s'",
machine->initrd_filename);
exit(1);
}
bi->initrd_start = loadaddr;
bi->initrd_end = loadaddr + sz;
}
}
static void amigaone_machine_init(MachineClass *mc)
-3
View File
@@ -57,9 +57,6 @@ ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files(
'pnv_n1_chiplet.c',
))
# PowerPC 4xx boards
ppc_ss.add(when: 'CONFIG_PPC405', if_true: files(
'ppc405_boards.c',
'ppc405_uc.c'))
ppc_ss.add(when: 'CONFIG_PPC440', if_true: files(
'ppc440_bamboo.c',
'ppc440_uc.c'))
+99 -51
View File
@@ -1,7 +1,9 @@
/*
* QEMU PowerPC PowerNV machine model
*
* Copyright (c) 2016, IBM Corporation.
* Copyright (c) 2016-2024, IBM Corporation.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -64,6 +66,8 @@
#define FW_LOAD_ADDR 0x0
#define FW_MAX_SIZE (16 * MiB)
#define PNOR_FILE_NAME "pnv-pnor.bin"
#define KERNEL_LOAD_ADDR 0x20000000
#define KERNEL_MAX_SIZE (128 * MiB)
#define INITRD_LOAD_ADDR 0x28000000
@@ -941,7 +945,7 @@ static void pnv_init(MachineState *machine)
uint64_t chip_ram_start = 0;
int i;
char *chip_typename;
DriveInfo *pnor = drive_get(IF_MTD, 0, 0);
DriveInfo *pnor;
DeviceState *dev;
if (kvm_enabled()) {
@@ -971,6 +975,18 @@ static void pnv_init(MachineState *machine)
* Create our simple PNOR device
*/
dev = qdev_new(TYPE_PNV_PNOR);
pnor = drive_get(IF_MTD, 0, 0);
if (!pnor && defaults_enabled()) {
fw_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, PNOR_FILE_NAME);
if (!fw_filename) {
warn_report("Could not find PNOR '%s'", PNOR_FILE_NAME);
} else {
QemuOpts *opts;
opts = drive_add(IF_MTD, -1, fw_filename, "format=raw,readonly=on");
pnor = drive_new(opts, IF_MTD, &error_fatal);
g_free(fw_filename);
}
}
if (pnor) {
qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(pnor));
}
@@ -1555,7 +1571,21 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
return;
}
/* HOMER (must be created before OCC) */
object_property_set_link(OBJECT(&chip8->homer), "chip", OBJECT(chip),
&error_abort);
if (!qdev_realize(DEVICE(&chip8->homer), NULL, errp)) {
return;
}
/* Homer Xscom region */
pnv_xscom_add_subregion(chip, PNV_XSCOM_PBA_BASE, &chip8->homer.pba_regs);
/* Homer RAM region */
memory_region_add_subregion(get_system_memory(), chip8->homer.base,
&chip8->homer.mem);
/* Create the simplified OCC model */
object_property_set_link(OBJECT(&chip8->occ), "homer",
OBJECT(&chip8->homer), &error_abort);
if (!qdev_realize(DEVICE(&chip8->occ), NULL, errp)) {
return;
}
@@ -1567,19 +1597,6 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
memory_region_add_subregion(get_system_memory(), PNV_OCC_SENSOR_BASE(chip),
&chip8->occ.sram_regs);
/* HOMER */
object_property_set_link(OBJECT(&chip8->homer), "chip", OBJECT(chip),
&error_abort);
if (!qdev_realize(DEVICE(&chip8->homer), NULL, errp)) {
return;
}
/* Homer Xscom region */
pnv_xscom_add_subregion(chip, PNV_XSCOM_PBA_BASE, &chip8->homer.pba_regs);
/* Homer mmio region */
memory_region_add_subregion(get_system_memory(), PNV_HOMER_BASE(chip),
&chip8->homer.regs);
/* PHB controllers */
for (i = 0; i < chip8->num_phbs; i++) {
PnvPHB *phb = chip8->phbs[i];
@@ -1753,6 +1770,7 @@ static void pnv_chip_power9_pec_realize(PnvChip *chip, Error **errp)
for (i = 0; i < chip->num_pecs; i++) {
PnvPhb4PecState *pec = &chip9->pecs[i];
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
uint32_t pec_cplt_base;
uint32_t pec_nest_base;
uint32_t pec_pci_base;
@@ -1765,9 +1783,12 @@ static void pnv_chip_power9_pec_realize(PnvChip *chip, Error **errp)
return;
}
pec_cplt_base = pecc->xscom_cplt_base(pec);
pec_nest_base = pecc->xscom_nest_base(pec);
pec_pci_base = pecc->xscom_pci_base(pec);
pnv_xscom_add_subregion(chip, pec_cplt_base,
&pec->nest_pervasive.xscom_ctrl_regs_mr);
pnv_xscom_add_subregion(chip, pec_nest_base, &pec->nest_regs_mr);
pnv_xscom_add_subregion(chip, pec_pci_base, &pec->pci_regs_mr);
}
@@ -1859,18 +1880,6 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
pnv_xscom_add_subregion(chip, PNV9_XSCOM_CHIPTOD_BASE,
&chip9->chiptod.xscom_regs);
/* Create the simplified OCC model */
if (!qdev_realize(DEVICE(&chip9->occ), NULL, errp)) {
return;
}
pnv_xscom_add_subregion(chip, PNV9_XSCOM_OCC_BASE, &chip9->occ.xscom_regs);
qdev_connect_gpio_out(DEVICE(&chip9->occ), 0, qdev_get_gpio_in(
DEVICE(psi9), PSIHB9_IRQ_OCC));
/* OCC SRAM model */
memory_region_add_subregion(get_system_memory(), PNV9_OCC_SENSOR_BASE(chip),
&chip9->occ.sram_regs);
/* SBE */
if (!qdev_realize(DEVICE(&chip9->sbe), NULL, errp)) {
return;
@@ -1882,7 +1891,7 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
qdev_connect_gpio_out(DEVICE(&chip9->sbe), 0, qdev_get_gpio_in(
DEVICE(psi9), PSIHB9_IRQ_PSU));
/* HOMER */
/* HOMER (must be created before OCC) */
object_property_set_link(OBJECT(&chip9->homer), "chip", OBJECT(chip),
&error_abort);
if (!qdev_realize(DEVICE(&chip9->homer), NULL, errp)) {
@@ -1890,10 +1899,23 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
}
/* Homer Xscom region */
pnv_xscom_add_subregion(chip, PNV9_XSCOM_PBA_BASE, &chip9->homer.pba_regs);
/* Homer RAM region */
memory_region_add_subregion(get_system_memory(), chip9->homer.base,
&chip9->homer.mem);
/* Homer mmio region */
memory_region_add_subregion(get_system_memory(), PNV9_HOMER_BASE(chip),
&chip9->homer.regs);
/* Create the simplified OCC model */
object_property_set_link(OBJECT(&chip9->occ), "homer",
OBJECT(&chip9->homer), &error_abort);
if (!qdev_realize(DEVICE(&chip9->occ), NULL, errp)) {
return;
}
pnv_xscom_add_subregion(chip, PNV9_XSCOM_OCC_BASE, &chip9->occ.xscom_regs);
qdev_connect_gpio_out(DEVICE(&chip9->occ), 0, qdev_get_gpio_in(
DEVICE(psi9), PSIHB9_IRQ_OCC));
/* OCC SRAM model */
memory_region_add_subregion(get_system_memory(), PNV9_OCC_SENSOR_BASE(chip),
&chip9->occ.sram_regs);
/* PEC PHBs */
pnv_chip_power9_pec_realize(chip, &local_err);
@@ -2027,6 +2049,7 @@ static void pnv_chip_power10_phb_realize(PnvChip *chip, Error **errp)
for (i = 0; i < chip->num_pecs; i++) {
PnvPhb4PecState *pec = &chip10->pecs[i];
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
uint32_t pec_cplt_base;
uint32_t pec_nest_base;
uint32_t pec_pci_base;
@@ -2039,9 +2062,12 @@ static void pnv_chip_power10_phb_realize(PnvChip *chip, Error **errp)
return;
}
pec_cplt_base = pecc->xscom_cplt_base(pec);
pec_nest_base = pecc->xscom_nest_base(pec);
pec_pci_base = pecc->xscom_pci_base(pec);
pnv_xscom_add_subregion(chip, pec_cplt_base,
&pec->nest_pervasive.xscom_ctrl_regs_mr);
pnv_xscom_add_subregion(chip, pec_nest_base, &pec->nest_regs_mr);
pnv_xscom_add_subregion(chip, pec_pci_base, &pec->pci_regs_mr);
}
@@ -2136,7 +2162,22 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp)
pnv_xscom_add_subregion(chip, PNV10_XSCOM_CHIPTOD_BASE,
&chip10->chiptod.xscom_regs);
/* HOMER (must be created before OCC) */
object_property_set_link(OBJECT(&chip10->homer), "chip", OBJECT(chip),
&error_abort);
if (!qdev_realize(DEVICE(&chip10->homer), NULL, errp)) {
return;
}
/* Homer Xscom region */
pnv_xscom_add_subregion(chip, PNV10_XSCOM_PBA_BASE,
&chip10->homer.pba_regs);
/* Homer RAM region */
memory_region_add_subregion(get_system_memory(), chip10->homer.base,
&chip10->homer.mem);
/* Create the simplified OCC model */
object_property_set_link(OBJECT(&chip10->occ), "homer",
OBJECT(&chip10->homer), &error_abort);
if (!qdev_realize(DEVICE(&chip10->occ), NULL, errp)) {
return;
}
@@ -2161,20 +2202,6 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp)
qdev_connect_gpio_out(DEVICE(&chip10->sbe), 0, qdev_get_gpio_in(
DEVICE(&chip10->psi), PSIHB9_IRQ_PSU));
/* HOMER */
object_property_set_link(OBJECT(&chip10->homer), "chip", OBJECT(chip),
&error_abort);
if (!qdev_realize(DEVICE(&chip10->homer), NULL, errp)) {
return;
}
/* Homer Xscom region */
pnv_xscom_add_subregion(chip, PNV10_XSCOM_PBA_BASE,
&chip10->homer.pba_regs);
/* Homer mmio region */
memory_region_add_subregion(get_system_memory(), PNV10_HOMER_BASE(chip),
&chip10->homer.regs);
/* N1 chiplet */
if (!qdev_realize(DEVICE(&chip10->n1_chiplet), NULL, errp)) {
return;
@@ -2225,6 +2252,8 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp)
/* pib_spic[2] connected to 25csm04 which implements 1 byte transfer */
object_property_set_int(OBJECT(&chip10->pib_spic[i]), "transfer_len",
(i == 2) ? 1 : 4, &error_fatal);
object_property_set_int(OBJECT(&chip10->pib_spic[i]), "chip-id",
chip->chip_id, &error_fatal);
if (!sysbus_realize(SYS_BUS_DEVICE(OBJECT
(&chip10->pib_spic[i])), errp)) {
return;
@@ -2581,7 +2610,7 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, GString *buf)
static int pnv_match_nvt(XiveFabric *xfb, uint8_t format,
uint8_t nvt_blk, uint32_t nvt_idx,
bool cam_ignore, uint8_t priority,
bool crowd, bool cam_ignore, uint8_t priority,
uint32_t logic_serv,
XiveTCTXMatch *match)
{
@@ -2595,8 +2624,8 @@ static int pnv_match_nvt(XiveFabric *xfb, uint8_t format,
XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr);
int count;
count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, cam_ignore,
priority, logic_serv, match);
count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd,
cam_ignore, priority, logic_serv, match);
if (count < 0) {
return count;
@@ -2610,7 +2639,7 @@ static int pnv_match_nvt(XiveFabric *xfb, uint8_t format,
static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format,
uint8_t nvt_blk, uint32_t nvt_idx,
bool cam_ignore, uint8_t priority,
bool crowd, bool cam_ignore, uint8_t priority,
uint32_t logic_serv,
XiveTCTXMatch *match)
{
@@ -2624,8 +2653,8 @@ static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format,
XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr);
int count;
count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, cam_ignore,
priority, logic_serv, match);
count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd,
cam_ignore, priority, logic_serv, match);
if (count < 0) {
return count;
@@ -2637,6 +2666,24 @@ static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format,
return total_count;
}
static int pnv10_xive_broadcast(XiveFabric *xfb,
uint8_t nvt_blk, uint32_t nvt_idx,
bool crowd, bool cam_ignore,
uint8_t priority)
{
PnvMachineState *pnv = PNV_MACHINE(xfb);
int i;
for (i = 0; i < pnv->num_chips; i++) {
Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]);
XivePresenter *xptr = XIVE_PRESENTER(&chip10->xive);
XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr);
xpc->broadcast(xptr, nvt_blk, nvt_idx, crowd, cam_ignore, priority);
}
return 0;
}
static bool pnv_machine_get_big_core(Object *obj, Error **errp)
{
PnvMachineState *pnv = PNV_MACHINE(obj);
@@ -2770,6 +2817,7 @@ static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data)
pmc->dt_power_mgt = pnv_dt_power_mgt;
xfc->match_nvt = pnv10_xive_match_nvt;
xfc->broadcast = pnv10_xive_broadcast;
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB);
}
+28
View File
@@ -251,10 +251,38 @@ static const IPMINetfn hiomap_netfn = {
void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor)
{
uint32_t pnor_size = pnor->size;
uint32_t pnor_addr = PNOR_SPI_OFFSET;
if (!pnv_bmc_is_simulator(bmc)) {
return;
}
/*
* The HIOMAP protocol uses block units and 16-bit addressing.
* Prevent overflow or misalign.
*/
if (pnor_addr >= 1U << (BLOCK_SHIFT + 16)) {
warn_report("PNOR address is larger than 2^%d, disabling PNOR",
BLOCK_SHIFT + 16);
return;
}
if (pnor_addr & ((1U << BLOCK_SHIFT) - 1)) {
warn_report("PNOR address is not aligned to 2^%d, disabling PNOR",
BLOCK_SHIFT);
return;
}
if (pnor_size > 1U << (BLOCK_SHIFT + 16)) {
warn_report("PNOR size is larger than 2^%d, disabling PNOR",
BLOCK_SHIFT + 16);
return;
}
if (pnor_size & ((1U << BLOCK_SHIFT) - 1)) {
warn_report("PNOR size is not aligned to 2^%d, disabling PNOR",
BLOCK_SHIFT);
return;
}
object_ref(OBJECT(pnor));
object_property_add_const_link(OBJECT(bmc), "pnor", OBJECT(pnor));
+40 -190
View File
@@ -29,94 +29,6 @@
#include "hw/ppc/pnv_homer.h"
#include "hw/ppc/pnv_xscom.h"
static bool core_max_array(PnvHomer *homer, hwaddr addr)
{
int i;
PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer);
for (i = 0; i <= homer->chip->nr_cores; i++) {
if (addr == (hmrc->core_max_base + i)) {
return true;
}
}
return false;
}
/* P8 Pstate table */
#define PNV8_OCC_PSTATE_VERSION 0x1f8001
#define PNV8_OCC_PSTATE_MIN 0x1f8003
#define PNV8_OCC_PSTATE_VALID 0x1f8000
#define PNV8_OCC_PSTATE_THROTTLE 0x1f8002
#define PNV8_OCC_PSTATE_NOM 0x1f8004
#define PNV8_OCC_PSTATE_TURBO 0x1f8005
#define PNV8_OCC_PSTATE_ULTRA_TURBO 0x1f8006
#define PNV8_OCC_PSTATE_DATA 0x1f8008
#define PNV8_OCC_PSTATE_ID_ZERO 0x1f8010
#define PNV8_OCC_PSTATE_ID_ONE 0x1f8018
#define PNV8_OCC_PSTATE_ID_TWO 0x1f8020
#define PNV8_OCC_VDD_VOLTAGE_IDENTIFIER 0x1f8012
#define PNV8_OCC_VCS_VOLTAGE_IDENTIFIER 0x1f8013
#define PNV8_OCC_PSTATE_ZERO_FREQUENCY 0x1f8014
#define PNV8_OCC_PSTATE_ONE_FREQUENCY 0x1f801c
#define PNV8_OCC_PSTATE_TWO_FREQUENCY 0x1f8024
#define PNV8_CORE_MAX_BASE 0x1f8810
static uint64_t pnv_power8_homer_read(void *opaque, hwaddr addr,
unsigned size)
{
PnvHomer *homer = PNV_HOMER(opaque);
switch (addr) {
case PNV8_OCC_PSTATE_VERSION:
case PNV8_OCC_PSTATE_MIN:
case PNV8_OCC_PSTATE_ID_ZERO:
return 0;
case PNV8_OCC_PSTATE_VALID:
case PNV8_OCC_PSTATE_THROTTLE:
case PNV8_OCC_PSTATE_NOM:
case PNV8_OCC_PSTATE_TURBO:
case PNV8_OCC_PSTATE_ID_ONE:
case PNV8_OCC_VDD_VOLTAGE_IDENTIFIER:
case PNV8_OCC_VCS_VOLTAGE_IDENTIFIER:
return 1;
case PNV8_OCC_PSTATE_ULTRA_TURBO:
case PNV8_OCC_PSTATE_ID_TWO:
return 2;
case PNV8_OCC_PSTATE_DATA:
return 0x1000000000000000;
/* P8 frequency for 0, 1, and 2 pstates */
case PNV8_OCC_PSTATE_ZERO_FREQUENCY:
case PNV8_OCC_PSTATE_ONE_FREQUENCY:
case PNV8_OCC_PSTATE_TWO_FREQUENCY:
return 3000;
}
/* pstate table core max array */
if (core_max_array(homer, addr)) {
return 1;
}
return 0;
}
static void pnv_power8_homer_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
/* callback function defined to homer write */
return;
}
static const MemoryRegionOps pnv_power8_homer_ops = {
.read = pnv_power8_homer_read,
.write = pnv_power8_homer_write,
.valid.min_access_size = 1,
.valid.max_access_size = 8,
.impl.min_access_size = 1,
.impl.max_access_size = 8,
.endianness = DEVICE_BIG_ENDIAN,
};
/* P8 PBA BARs */
#define PBA_BAR0 0x00
#define PBA_BAR1 0x01
@@ -131,16 +43,16 @@ static uint64_t pnv_homer_power8_pba_read(void *opaque, hwaddr addr,
unsigned size)
{
PnvHomer *homer = PNV_HOMER(opaque);
PnvChip *chip = homer->chip;
PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer);
uint32_t reg = addr >> 3;
uint64_t val = 0;
switch (reg) {
case PBA_BAR0:
val = PNV_HOMER_BASE(chip);
val = homer->base;
break;
case PBA_BARMASK0: /* P8 homer region mask */
val = (PNV_HOMER_SIZE - 1) & 0x300000;
val = (hmrc->size - 1) & 0x300000;
break;
case PBA_BAR3: /* P8 occ common area */
val = PNV_OCC_COMMON_AREA_BASE;
@@ -172,15 +84,19 @@ static const MemoryRegionOps pnv_homer_power8_pba_ops = {
.endianness = DEVICE_BIG_ENDIAN,
};
static hwaddr pnv_homer_power8_get_base(PnvChip *chip)
{
return PNV_HOMER_BASE(chip);
}
static void pnv_homer_power8_class_init(ObjectClass *klass, void *data)
{
PnvHomerClass *homer = PNV_HOMER_CLASS(klass);
homer->get_base = pnv_homer_power8_get_base;
homer->size = PNV_HOMER_SIZE;
homer->pba_size = PNV_XSCOM_PBA_SIZE;
homer->pba_ops = &pnv_homer_power8_pba_ops;
homer->homer_size = PNV_HOMER_SIZE;
homer->homer_ops = &pnv_power8_homer_ops;
homer->core_max_base = PNV8_CORE_MAX_BASE;
}
static const TypeInfo pnv_homer_power8_type_info = {
@@ -190,100 +106,20 @@ static const TypeInfo pnv_homer_power8_type_info = {
.class_init = pnv_homer_power8_class_init,
};
/* P9 Pstate table */
#define PNV9_OCC_PSTATE_ID_ZERO 0xe2018
#define PNV9_OCC_PSTATE_ID_ONE 0xe2020
#define PNV9_OCC_PSTATE_ID_TWO 0xe2028
#define PNV9_OCC_PSTATE_DATA 0xe2000
#define PNV9_OCC_PSTATE_DATA_AREA 0xe2008
#define PNV9_OCC_PSTATE_MIN 0xe2003
#define PNV9_OCC_PSTATE_NOM 0xe2004
#define PNV9_OCC_PSTATE_TURBO 0xe2005
#define PNV9_OCC_PSTATE_ULTRA_TURBO 0xe2818
#define PNV9_OCC_MAX_PSTATE_ULTRA_TURBO 0xe2006
#define PNV9_OCC_PSTATE_MAJOR_VERSION 0xe2001
#define PNV9_OCC_OPAL_RUNTIME_DATA 0xe2b85
#define PNV9_CHIP_HOMER_IMAGE_POINTER 0x200008
#define PNV9_CHIP_HOMER_BASE 0x0
#define PNV9_OCC_PSTATE_ZERO_FREQUENCY 0xe201c
#define PNV9_OCC_PSTATE_ONE_FREQUENCY 0xe2024
#define PNV9_OCC_PSTATE_TWO_FREQUENCY 0xe202c
#define PNV9_OCC_ROLE_MASTER_OR_SLAVE 0xe2002
#define PNV9_CORE_MAX_BASE 0xe2819
static uint64_t pnv_power9_homer_read(void *opaque, hwaddr addr,
unsigned size)
{
PnvHomer *homer = PNV_HOMER(opaque);
switch (addr) {
case PNV9_OCC_MAX_PSTATE_ULTRA_TURBO:
case PNV9_OCC_PSTATE_ID_ZERO:
return 0;
case PNV9_OCC_PSTATE_DATA:
case PNV9_OCC_ROLE_MASTER_OR_SLAVE:
case PNV9_OCC_PSTATE_NOM:
case PNV9_OCC_PSTATE_TURBO:
case PNV9_OCC_PSTATE_ID_ONE:
case PNV9_OCC_PSTATE_ULTRA_TURBO:
case PNV9_OCC_OPAL_RUNTIME_DATA:
return 1;
case PNV9_OCC_PSTATE_MIN:
case PNV9_OCC_PSTATE_ID_TWO:
return 2;
/* 3000 khz frequency for 0, 1, and 2 pstates */
case PNV9_OCC_PSTATE_ZERO_FREQUENCY:
case PNV9_OCC_PSTATE_ONE_FREQUENCY:
case PNV9_OCC_PSTATE_TWO_FREQUENCY:
return 3000;
case PNV9_OCC_PSTATE_MAJOR_VERSION:
return 0x90;
case PNV9_CHIP_HOMER_BASE:
case PNV9_OCC_PSTATE_DATA_AREA:
case PNV9_CHIP_HOMER_IMAGE_POINTER:
return 0x1000000000000000;
}
/* pstate table core max array */
if (core_max_array(homer, addr)) {
return 1;
}
return 0;
}
static void pnv_power9_homer_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
/* callback function defined to homer write */
return;
}
static const MemoryRegionOps pnv_power9_homer_ops = {
.read = pnv_power9_homer_read,
.write = pnv_power9_homer_write,
.valid.min_access_size = 1,
.valid.max_access_size = 8,
.impl.min_access_size = 1,
.impl.max_access_size = 8,
.endianness = DEVICE_BIG_ENDIAN,
};
static uint64_t pnv_homer_power9_pba_read(void *opaque, hwaddr addr,
unsigned size)
{
PnvHomer *homer = PNV_HOMER(opaque);
PnvChip *chip = homer->chip;
PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer);
uint32_t reg = addr >> 3;
uint64_t val = 0;
switch (reg) {
case PBA_BAR0:
val = PNV9_HOMER_BASE(chip);
val = homer->base;
break;
case PBA_BARMASK0: /* P9 homer region mask */
val = (PNV9_HOMER_SIZE - 1) & 0x300000;
val = (hmrc->size - 1) & 0x300000;
break;
case PBA_BAR2: /* P9 occ common area */
val = PNV9_OCC_COMMON_AREA_BASE;
@@ -315,15 +151,19 @@ static const MemoryRegionOps pnv_homer_power9_pba_ops = {
.endianness = DEVICE_BIG_ENDIAN,
};
static hwaddr pnv_homer_power9_get_base(PnvChip *chip)
{
return PNV9_HOMER_BASE(chip);
}
static void pnv_homer_power9_class_init(ObjectClass *klass, void *data)
{
PnvHomerClass *homer = PNV_HOMER_CLASS(klass);
homer->get_base = pnv_homer_power9_get_base;
homer->size = PNV_HOMER_SIZE;
homer->pba_size = PNV9_XSCOM_PBA_SIZE;
homer->pba_ops = &pnv_homer_power9_pba_ops;
homer->homer_size = PNV9_HOMER_SIZE;
homer->homer_ops = &pnv_power9_homer_ops;
homer->core_max_base = PNV9_CORE_MAX_BASE;
}
static const TypeInfo pnv_homer_power9_type_info = {
@@ -337,16 +177,16 @@ static uint64_t pnv_homer_power10_pba_read(void *opaque, hwaddr addr,
unsigned size)
{
PnvHomer *homer = PNV_HOMER(opaque);
PnvChip *chip = homer->chip;
PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer);
uint32_t reg = addr >> 3;
uint64_t val = 0;
switch (reg) {
case PBA_BAR0:
val = PNV10_HOMER_BASE(chip);
val = homer->base;
break;
case PBA_BARMASK0: /* P10 homer region mask */
val = (PNV10_HOMER_SIZE - 1) & 0x300000;
val = (hmrc->size - 1) & 0x300000;
break;
case PBA_BAR2: /* P10 occ common area */
val = PNV10_OCC_COMMON_AREA_BASE;
@@ -378,15 +218,19 @@ static const MemoryRegionOps pnv_homer_power10_pba_ops = {
.endianness = DEVICE_BIG_ENDIAN,
};
static hwaddr pnv_homer_power10_get_base(PnvChip *chip)
{
return PNV10_HOMER_BASE(chip);
}
static void pnv_homer_power10_class_init(ObjectClass *klass, void *data)
{
PnvHomerClass *homer = PNV_HOMER_CLASS(klass);
homer->get_base = pnv_homer_power10_get_base;
homer->size = PNV_HOMER_SIZE;
homer->pba_size = PNV10_XSCOM_PBA_SIZE;
homer->pba_ops = &pnv_homer_power10_pba_ops;
homer->homer_size = PNV10_HOMER_SIZE;
homer->homer_ops = &pnv_power9_homer_ops; /* TODO */
homer->core_max_base = PNV9_CORE_MAX_BASE;
}
static const TypeInfo pnv_homer_power10_type_info = {
@@ -400,16 +244,22 @@ static void pnv_homer_realize(DeviceState *dev, Error **errp)
{
PnvHomer *homer = PNV_HOMER(dev);
PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer);
char homer_str[32];
assert(homer->chip);
pnv_xscom_region_init(&homer->pba_regs, OBJECT(dev), hmrc->pba_ops,
homer, "xscom-pba", hmrc->pba_size);
/* homer region */
memory_region_init_io(&homer->regs, OBJECT(dev),
hmrc->homer_ops, homer, "homer-main-memory",
hmrc->homer_size);
/* Homer RAM region */
homer->base = hmrc->get_base(homer->chip);
snprintf(homer_str, sizeof(homer_str), "homer-chip%d-memory",
homer->chip->chip_id);
if (!memory_region_init_ram(&homer->mem, OBJECT(homer),
homer_str, hmrc->size, errp)) {
return;
}
}
static const Property pnv_homer_properties[] = {
+50 -39
View File
@@ -85,7 +85,7 @@ enum {
#define ISA_IO_SIZE 0x00010000
#define ISA_MEM_SIZE 0x10000000
#define ISA_FW_SIZE 0x10000000
#define ISA_FW_SIZE 0x100000000
#define LPC_IO_OPB_ADDR 0xd0010000
#define LPC_IO_OPB_SIZE 0x00010000
#define LPC_MEM_OPB_ADDR 0xe0000000
@@ -353,6 +353,8 @@ static const MemoryRegionOps pnv_lpc_xscom_ops = {
.endianness = DEVICE_BIG_ENDIAN,
};
static void pnv_lpc_opb_noresponse(PnvLpcController *lpc);
static uint64_t pnv_lpc_mmio_read(void *opaque, hwaddr addr, unsigned size)
{
PnvLpcController *lpc = PNV_LPC(opaque);
@@ -376,6 +378,7 @@ static uint64_t pnv_lpc_mmio_read(void *opaque, hwaddr addr, unsigned size)
}
if (result != MEMTX_OK) {
pnv_lpc_opb_noresponse(lpc);
qemu_log_mask(LOG_GUEST_ERROR, "OPB read failed at @0x%"
HWADDR_PRIx "\n", addr);
}
@@ -406,6 +409,7 @@ static void pnv_lpc_mmio_write(void *opaque, hwaddr addr,
}
if (result != MEMTX_OK) {
pnv_lpc_opb_noresponse(lpc);
qemu_log_mask(LOG_GUEST_ERROR, "OPB write failed at @0x%"
HWADDR_PRIx "\n", addr);
}
@@ -456,46 +460,18 @@ static void pnv_lpc_eval_irqs(PnvLpcController *lpc)
{
uint32_t active_irqs = 0;
if (lpc->lpc_hc_irqstat & PPC_BITMASK32(16, 31)) {
qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented irqs in IRQSTAT: "
"0x%08"PRIx32"\n", lpc->lpc_hc_irqstat);
}
if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) {
active_irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask;
active_irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask;
if (!(lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN)) {
active_irqs &= ~LPC_HC_IRQ_SERIRQ_ALL;
}
/* Reflect the interrupt */
if (!lpc->psi_has_serirq) {
if (lpc->psi_has_serirq) {
/*
* POWER8 ORs all irqs together (also with LPCHC internal interrupt
* sources) and outputs a single line that raises the PSI LPCHC irq
* which then latches an OPB IRQ status register that sends the irq
* to PSI.
*
* We don't honor the polarity register, it's pointless and unused
* anyway
*/
if (active_irqs) {
lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC;
} else {
lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC;
}
/* Update OPB internal latch */
lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask;
qemu_set_irq(lpc->psi_irq_lpchc, lpc->opb_irq_stat != 0);
} else {
/*
* POWER9 and POWER10 have routing fields in OPB master registers that
* POWER9 and later have routing fields in OPB master registers that
* send LPC irqs to 4 output lines that raise the PSI SERIRQ irqs.
* These don't appear to get latched into an OPB register like the
* LPCHC irqs.
*
* POWER9 LPC controller internal irqs still go via the OPB
* and LPCHC PSI irqs like P8, but we have no such internal sources
* modelled yet.
*/
bool serirq_out[4] = { false, false, false, false };
int irq;
@@ -510,7 +486,39 @@ static void pnv_lpc_eval_irqs(PnvLpcController *lpc)
qemu_set_irq(lpc->psi_irq_serirq[1], serirq_out[1]);
qemu_set_irq(lpc->psi_irq_serirq[2], serirq_out[2]);
qemu_set_irq(lpc->psi_irq_serirq[3], serirq_out[3]);
/*
* POWER9 and later LPC controller internal irqs still go via the OPB
* and LPCHC PSI irqs like P8, so take the SERIRQs out and continue.
*/
active_irqs &= ~LPC_HC_IRQ_SERIRQ_ALL;
}
/*
* POWER8 ORs all irqs together (also with LPCHC internal interrupt
* sources) and outputs a single line that raises the PSI LPCHC irq
* which then latches an OPB IRQ status register that sends the irq
* to PSI.
*
* We don't honor the polarity register, it's pointless and unused
* anyway
*/
if (active_irqs) {
lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC;
} else {
lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC;
}
/* Update OPB internal latch */
lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask;
qemu_set_irq(lpc->psi_irq_lpchc, lpc->opb_irq_stat != 0);
}
static void pnv_lpc_opb_noresponse(PnvLpcController *lpc)
{
lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SYNC_NORESP_ERR;
pnv_lpc_eval_irqs(lpc);
}
static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size)
@@ -553,10 +561,13 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val,
switch (addr) {
case LPC_HC_FW_SEG_IDSEL:
/* XXX Actually figure out how that works as this impact
* memory regions/aliases
/*
* ISA FW "devices" are modeled as 16x256MB windows into a
* 4GB LPC FW address space.
*/
val &= 0xf; /* Selects device 0-15 */
lpc->lpc_hc_fw_seg_idsel = val;
memory_region_set_alias_offset(&lpc->opb_isa_fw, val * LPC_FW_OPB_SIZE);
break;
case LPC_HC_FW_RD_ACC_SIZE:
lpc->lpc_hc_fw_rd_acc_size = val;
@@ -790,9 +801,9 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp)
memory_region_init(&lpc->opb_mr, OBJECT(dev), "lpc-opb", 0x100000000ull);
address_space_init(&lpc->opb_as, &lpc->opb_mr, "lpc-opb");
/* Create ISA IO and Mem space regions which are the root of
* the ISA bus (ie, ISA address spaces). We don't create a
* separate one for FW which we alias to memory.
/*
* Create ISA IO, Mem, and FW space regions which are the root of
* the ISA bus (ie, ISA address spaces).
*/
memory_region_init(&lpc->isa_io, OBJECT(dev), "isa-io", ISA_IO_SIZE);
memory_region_init(&lpc->isa_mem, OBJECT(dev), "isa-mem", ISA_MEM_SIZE);

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