All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space
@ 2016-10-03  7:24 Cédric Le Goater
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 01/20] ppc/pnv: add skeleton PowerNV platform Cédric Le Goater
                   ` (20 more replies)
  0 siblings, 21 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

Hello,

Here is a v4 addressing all the comments from v3 and adding a couple
more models. Most important changes are :

 - a rework of the XSCOM model which now dispatches addresses to the
   memory regions using pcb_addr << 3. 
 - the model for the native Interrupt Presentation Controllers based
   on MMIOS, borrowed from Nikunj v3 patchset, plus some extras to
   support HW CPU ids,
 - a minimal PSI HB (Processor Service Interface Host Bus) model to
   handle the external interrupt, 
 - initial support for the POWER9 LPC controller.


The initial patch provides a minimal platform with some RAM to load
the ROMs : firmware, kernel and initrd. The device tree is built with
what is available at reset time. Then, comes the PnvChip object acting
as a container for other devices required to run a system. The cores
are added to each chip with some restrictions on the number and the
ids. Next is the XSCOM model, the sideband bus which gives controls to
all the units in the POWER8 chip, the LPC controller for the console,
the native interrupt controller and the PSI HB model to handle the
external interrupt.

Last is an initial LPC controller model for the POWER9. We have a
console ! But, more important, it gives us an idea of what changes we
should consider. The LPC controller is a little different for POWER9
as it is accessed through MMIOs but it is not too complex to adapt the
code from POWER8. PSIHB has different XSCOMs addresses, we should be
OK with the current framework. The POWER9 interrupt controller is
another story for later.

The next step is a PHB3 model to include some PCI devices. Nevertheless,
the patchset has now enough support to be usable as a real machine. If
you still feel adventurous, you can grab skiboot and kernel images
here :

  https://openpower.xyz/job/openpower-op-build/distro=ubuntu,target=palmetto/lastSuccessfulBuild/artifact/images/skiboot.lid
  https://openpower.xyz/job/openpower-op-build/distro=ubuntu,target=palmetto/lastSuccessfulBuild/artifact/images/zImage.epapr
  https://openpower.xyz/job/openpower-op-build/distro=ubuntu,target=palmetto/lastSuccessfulBuild/artifact/images/rootfs.cpio.xz

The code is available here (beware this is a wip branch)

   https://github.com/legoater/qemu/commits/powernv-ipmi-2.8

Thanks,

C. 

Benjamin Herrenschmidt (9):
  ppc/pnv: add skeleton PowerNV platform
  ppc/pnv: add a LPC controller
  ppc/xics: Make the ICSState a list
  ppc/xics: Split ICS into ics-base and ics class
  ppc/xics: Add xics to the monitor "info pic" command
  ppc/xics: Add "native" XICS subclass
  ppc/pnv: Add cut down PSI bridge model and hookup external interrupt
  ppc/pnv: Add OCC model stub with interrupt support
  ppc/pnv: Add Naples chip support for LPC interrupts

Cédric Le Goater (11):
  ppc/pnv: add a PnvChip object
  ppc/pnv: add a core mask to PnvChip
  ppc/pnv: add a PIR handler to PnvChip
  ppc/pnv: add a PnvCore object
  ppc/pnv: add XSCOM infrastructure
  ppc/pnv: add XSCOM handlers to PnvCore
  ppc/pnv: add a ISA bus
  ppc/xics: introduce helpers to find an ICP from some (CPU) index
  ppc/xics: introduce a helper to insert a new ics
  ppc/pnv: add a XICS native to each PowerNV chip
  ppc/pnv: add support for POWER9 LPC Controller

 default-configs/ppc64-softmmu.mak |   4 +-
 hmp-commands-info.hx              |   2 +
 hw/intc/Makefile.objs             |   1 +
 hw/intc/trace-events              |  15 +-
 hw/intc/xics.c                    | 337 ++++++++++-----
 hw/intc/xics_kvm.c                |  44 +-
 hw/intc/xics_native.c             | 327 ++++++++++++++
 hw/intc/xics_spapr.c              | 128 +++---
 hw/ppc/Makefile.objs              |   2 +
 hw/ppc/pnv.c                      | 878 ++++++++++++++++++++++++++++++++++++++
 hw/ppc/pnv_core.c                 | 253 +++++++++++
 hw/ppc/pnv_lpc.c                  | 745 ++++++++++++++++++++++++++++++++
 hw/ppc/pnv_occ.c                  | 135 ++++++
 hw/ppc/pnv_psi.c                  | 598 ++++++++++++++++++++++++++
 hw/ppc/pnv_xscom.c                | 262 ++++++++++++
 hw/ppc/ppc.c                      |  14 +
 hw/ppc/spapr_events.c             |   2 +-
 hw/ppc/spapr_pci.c                |   5 +-
 hw/ppc/spapr_vio.c                |   2 +-
 include/hw/ppc/pnv.h              | 164 +++++++
 include/hw/ppc/pnv_core.h         |  50 +++
 include/hw/ppc/pnv_lpc.h          | 107 +++++
 include/hw/ppc/pnv_occ.h          |  38 ++
 include/hw/ppc/pnv_psi.h          |  64 +++
 include/hw/ppc/pnv_xscom.h        |  75 ++++
 include/hw/ppc/ppc.h              |   2 +
 include/hw/ppc/xics.h             |  69 ++-
 monitor.c                         |   4 +
 28 files changed, 4129 insertions(+), 198 deletions(-)
 create mode 100644 hw/intc/xics_native.c
 create mode 100644 hw/ppc/pnv.c
 create mode 100644 hw/ppc/pnv_core.c
 create mode 100644 hw/ppc/pnv_lpc.c
 create mode 100644 hw/ppc/pnv_occ.c
 create mode 100644 hw/ppc/pnv_psi.c
 create mode 100644 hw/ppc/pnv_xscom.c
 create mode 100644 include/hw/ppc/pnv.h
 create mode 100644 include/hw/ppc/pnv_core.h
 create mode 100644 include/hw/ppc/pnv_lpc.h
 create mode 100644 include/hw/ppc/pnv_occ.h
 create mode 100644 include/hw/ppc/pnv_psi.h
 create mode 100644 include/hw/ppc/pnv_xscom.h

-- 
2.7.4

^ permalink raw reply	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 01/20] ppc/pnv: add skeleton PowerNV platform
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-07  4:14   ` David Gibson
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 02/20] ppc/pnv: add a PnvChip object Cédric Le Goater
                   ` (19 subsequent siblings)
  20 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

The goal is to emulate a PowerNV system at the level of the skiboot
firmware, which loads the OS and provides some runtime services. Power
Systems have a lower firmware (HostBoot) that does low level system
initialization, like DRAM training. This is beyond the scope of what
qemu will address in a PowerNV guest.

No devices yet, not even an interrupt controller. Just to get started,
some RAM to load the skiboot firmware, the kernel and initrd. The
device tree is fully created in the machine reset op.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
[clg: - updated for qemu-2.7
      - replaced fprintf by error_report
      - used a common definition of _FDT macro
      - removed VMStateDescription as migration is not yet supported
      - added IBM Copyright statements
      - reworked kernel_filename handling
      - merged PnvSystem and sPowerNVMachineState
      - removed PHANDLE_XICP
      - added ppc_create_page_sizes_prop helper
      - removed nmi support
      - removed kvm support
      - updated powernv machine to version 2.8
      - removed chips and cpus, They will be provided in another patches
      - added a machine reset routine to initialize the device tree (also)
      - french has a squelette and english a skeleton.
      - improved commit log.
      - reworked prototypes parameters
      - added a check on the ram size (thanks to Michael Ellerman)
      - fixed chip-id cell
      - changed MAX_CPUS to 2048
      - simplified memory node creation to one node only
      - removed machine version
      - rewrote the device tree creation with the fdt "rw" routines
      - s/sPowerNVMachineState/PnvMachineState/
      - etc.]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 Changes since v3:

 - fixed printf format for hwaddr
 - used fdt_pack() before writing the tree in memory 
 - removed the requirement on having a kernel loaded as running with
   just a firmware is fine. We will need to discuss the inclusion of
   the file skiboot.lid under qemu.
 
 Changes since v2:

 - some more copyright header cleanups
 - remove fdt_addr field from PnvMachineState

 Changes since v1:

 - changed MAX_CPUS to 2048
 - simplified memory node creation to one node only
 - removed machine version 
 - rewrote the device tree creation with the fdt "rw" routines
 - s/sPowerNVMachineState/PnvMachineState/
 - block_default_type is back to IF_IDE because of the AHCI device

 default-configs/ppc64-softmmu.mak |   1 +
 hw/ppc/Makefile.objs              |   2 +
 hw/ppc/pnv.c                      | 223 ++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h              |  38 +++++++
 4 files changed, 264 insertions(+)
 create mode 100644 hw/ppc/pnv.c
 create mode 100644 include/hw/ppc/pnv.h

diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak
index db5a4d6f5eea..67a9bcaa67fa 100644
--- a/default-configs/ppc64-softmmu.mak
+++ b/default-configs/ppc64-softmmu.mak
@@ -39,6 +39,7 @@ CONFIG_I8259=y
 CONFIG_XILINX=y
 CONFIG_XILINX_ETHLITE=y
 CONFIG_PSERIES=y
+CONFIG_POWERNV=y
 CONFIG_PREP=y
 CONFIG_MAC=y
 CONFIG_E500=y
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index 99a0d4e581bf..8105db7d5600 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -5,6 +5,8 @@ obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o
 obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
 obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
 obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
+# IBM PowerNV
+obj-$(CONFIG_POWERNV) += pnv.o
 ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
 obj-y += spapr_pci_vfio.o
 endif
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
new file mode 100644
index 000000000000..02fc4826baa4
--- /dev/null
+++ b/hw/ppc/pnv.c
@@ -0,0 +1,223 @@
+/*
+ * QEMU PowerPC PowerNV machine model
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/numa.h"
+#include "hw/hw.h"
+#include "target-ppc/cpu.h"
+#include "qemu/log.h"
+#include "hw/ppc/fdt.h"
+#include "hw/ppc/ppc.h"
+#include "hw/ppc/pnv.h"
+#include "hw/loader.h"
+#include "exec/address-spaces.h"
+#include "qemu/cutils.h"
+
+#include <libfdt.h>
+
+#define FDT_MAX_SIZE            0x00100000
+
+#define FW_FILE_NAME            "skiboot.lid"
+#define FW_LOAD_ADDR            0x0
+#define FW_MAX_SIZE             0x00400000
+
+#define KERNEL_LOAD_ADDR        0x20000000
+#define INITRD_LOAD_ADDR        0x40000000
+
+/*
+ * On Power Systems E880, the max cpus (threads) should be :
+ *     4 * 4 sockets * 12 cores * 8 threads = 1536
+ * Let's make it 2^11
+ */
+#define MAX_CPUS                2048
+
+/*
+ * Memory nodes are created by hostboot, one for each range of memory
+ * that has a different "affinity". In practice, it means one range
+ * per chip.
+ */
+static void powernv_populate_memory_node(void *fdt, int chip_id, hwaddr start,
+                                         hwaddr size)
+{
+    char *mem_name;
+    uint64_t mem_reg_property[2];
+    int off;
+
+    mem_reg_property[0] = cpu_to_be64(start);
+    mem_reg_property[1] = cpu_to_be64(size);
+
+    mem_name = g_strdup_printf("memory@%"HWADDR_PRIx, start);
+    off = fdt_add_subnode(fdt, 0, mem_name);
+    g_free(mem_name);
+
+    _FDT((fdt_setprop_string(fdt, off, "device_type", "memory")));
+    _FDT((fdt_setprop(fdt, off, "reg", mem_reg_property,
+                       sizeof(mem_reg_property))));
+    _FDT((fdt_setprop_cell(fdt, off, "ibm,chip-id", chip_id)));
+}
+
+static void *powernv_create_fdt(PnvMachineState *pnv,
+                                const char *kernel_cmdline)
+{
+    void *fdt;
+    char *buf;
+    const char plat_compat[] = "qemu,powernv\0ibm,powernv";
+    int off;
+
+    fdt = g_malloc0(FDT_MAX_SIZE);
+    _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE)));
+
+    /* Root node */
+    _FDT((fdt_setprop_cell(fdt, 0, "#address-cells", 0x2)));
+    _FDT((fdt_setprop_cell(fdt, 0, "#size-cells", 0x2)));
+    _FDT((fdt_setprop_string(fdt, 0, "model",
+                             "IBM PowerNV (emulated by qemu)")));
+    _FDT((fdt_setprop(fdt, 0, "compatible", plat_compat,
+                      sizeof(plat_compat))));
+
+    buf =  qemu_uuid_unparse_strdup(&qemu_uuid);
+    _FDT((fdt_setprop_string(fdt, 0, "vm,uuid", buf)));
+    if (qemu_uuid_set) {
+        _FDT((fdt_property_string(fdt, "system-id", buf)));
+    }
+    g_free(buf);
+
+    off = fdt_add_subnode(fdt, 0, "chosen");
+    if (kernel_cmdline) {
+        _FDT((fdt_setprop_string(fdt, off, "bootargs", kernel_cmdline)));
+    }
+
+    if (pnv->initrd_size) {
+        uint32_t start_prop = cpu_to_be32(pnv->initrd_base);
+        uint32_t end_prop = cpu_to_be32(pnv->initrd_base + pnv->initrd_size);
+
+        _FDT((fdt_setprop(fdt, off, "linux,initrd-start",
+                               &start_prop, sizeof(start_prop))));
+        _FDT((fdt_setprop(fdt, off, "linux,initrd-end",
+                               &end_prop, sizeof(end_prop))));
+    }
+
+    /* Put all the memory in one node on chip 0 until we find a way to
+     * specify different ranges for each chip
+     */
+    powernv_populate_memory_node(fdt, 0, 0, ram_size);
+
+    return fdt;
+}
+
+static void ppc_powernv_reset(void)
+{
+    MachineState *machine = MACHINE(qdev_get_machine());
+    PnvMachineState *pnv = POWERNV_MACHINE(machine);
+    void *fdt;
+
+    qemu_devices_reset();
+
+    fdt = powernv_create_fdt(pnv, machine->kernel_cmdline);
+
+    /* Pack resulting tree */
+    _FDT((fdt_pack(fdt)));
+
+    cpu_physical_memory_write(POWERNV_FDT_ADDR, fdt, fdt_totalsize(fdt));
+}
+
+static void ppc_powernv_init(MachineState *machine)
+{
+    PnvMachineState *pnv = POWERNV_MACHINE(machine);
+    ram_addr_t ram_size = machine->ram_size;
+    MemoryRegion *ram;
+    char *fw_filename;
+    long fw_size;
+    long kernel_size;
+
+    /* allocate RAM */
+    if (ram_size < (1 * G_BYTE)) {
+        error_report("Warning: skiboot may not work with < 1GB of RAM");
+    }
+
+    ram = g_new(MemoryRegion, 1);
+    memory_region_allocate_system_memory(ram, NULL, "ppc_powernv.ram",
+                                         ram_size);
+    memory_region_add_subregion(get_system_memory(), 0, ram);
+
+    /* load skiboot firmware  */
+    if (bios_name == NULL) {
+        bios_name = FW_FILE_NAME;
+    }
+
+    fw_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+
+    fw_size = load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE);
+    if (fw_size < 0) {
+        hw_error("qemu: could not load OPAL '%s'\n", fw_filename);
+        exit(1);
+    }
+    g_free(fw_filename);
+
+    /* load kernel */
+    kernel_size = load_image_targphys(machine->kernel_filename,
+                                      KERNEL_LOAD_ADDR, 0x2000000);
+    if (kernel_size < 0) {
+        hw_error("qemu: could not load kernel'%s'\n", machine->kernel_filename);
+        exit(1);
+    }
+
+    /* load initrd */
+    if (machine->initrd_filename) {
+        pnv->initrd_base = INITRD_LOAD_ADDR;
+        pnv->initrd_size = load_image_targphys(machine->initrd_filename,
+                                  pnv->initrd_base, 0x10000000); /* 128MB max */
+        if (pnv->initrd_size < 0) {
+            error_report("qemu: could not load initial ram disk '%s'",
+                         machine->initrd_filename);
+            exit(1);
+        }
+    }
+}
+
+static void powernv_machine_class_init(ObjectClass *oc, void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    mc->desc = "IBM PowerNV (Non-Virtualized)";
+    mc->init = ppc_powernv_init;
+    mc->reset = ppc_powernv_reset;
+    mc->max_cpus = MAX_CPUS;
+    mc->block_default_type = IF_IDE; /* Pnv provides a AHCI device for
+                                      * storage */
+    mc->no_parallel = 1;
+    mc->default_boot_order = NULL;
+    mc->default_ram_size = 1 * G_BYTE;
+}
+
+static const TypeInfo powernv_machine_info = {
+    .name          = TYPE_POWERNV_MACHINE,
+    .parent        = TYPE_MACHINE,
+    .instance_size = sizeof(PnvMachineState),
+    .class_init    = powernv_machine_class_init,
+};
+
+static void powernv_machine_register_types(void)
+{
+    type_register_static(&powernv_machine_info);
+}
+
+type_init(powernv_machine_register_types)
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
new file mode 100644
index 000000000000..c8a73bc74267
--- /dev/null
+++ b/include/hw/ppc/pnv.h
@@ -0,0 +1,38 @@
+/*
+ * QEMU PowerPC PowerNV various definitions
+ *
+ * Copyright (c) 2014-2016 BenH, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _PPC_PNV_H
+#define _PPC_PNV_H
+
+#include "hw/boards.h"
+
+#define TYPE_POWERNV_MACHINE       MACHINE_TYPE_NAME("powernv")
+#define POWERNV_MACHINE(obj) \
+    OBJECT_CHECK(PnvMachineState, (obj), TYPE_POWERNV_MACHINE)
+
+typedef struct PnvMachineState {
+    /*< private >*/
+    MachineState parent_obj;
+
+    uint32_t initrd_base;
+    long initrd_size;
+} PnvMachineState;
+
+#define POWERNV_FDT_ADDR                0x01000000
+
+#endif /* _PPC_PNV_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 02/20] ppc/pnv: add a PnvChip object
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 01/20] ppc/pnv: add skeleton PowerNV platform Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-07  4:26   ` David Gibson
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 03/20] ppc/pnv: add a core mask to PnvChip Cédric Le Goater
                   ` (18 subsequent siblings)
  20 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

This is is an abstraction of a POWER8 chip which is a set of cores
plus other 'units', like the pervasive unit, the interrupt controller,
the memory controller, the on-chip microcontroller, etc. The whole can
be seen as a socket. It depends on a cpu model and its characteristics:
max cores, specific inits are defined in a PnvChipClass.

We start with an near empty PnvChip with only a few cpu constants
which we will grow in the subsequent patches with the controllers
required to run the system.

The Chip CFAM (Common FRU Access Module) ID gives the model of the
chip and its version number. It is generally the first thing firmwares
fetch, available at XSCOM PCB address 0xf000f, to start initialization.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---

 chip_type could possibly be removed or calculated from the attribute
 chip_cfam_id. Let's keep it for now and see how the patchset evolves.
 This is getting big, maybe should move the code to hw/ppc/pnv_chip.c ?

 Changes since v3:

 - removed PnvChipPower* types
 - removed realize ops of PnvChip
 - replaced scanf by qemu_strtoul

 Changes since v2:

 - forced a POWER8 cpu model if none is specified and check that a
   PnvChip type exist for it
 - did some renaming to be consistent with the cpu model names
 - added POWER9 chip
 - removed empty realize op
 - renamed atribute chip_f000f in chip_cfam_id
 - used error_fatal instead of error_abort when setting the chip
   properties
 - introduced a powernv_populate_chip() routine

 Changes since v1:
 
 - introduced a PnvChipClass depending on the cpu model. It also
   provides some chip constants used by devices, like the cpu model hw
   id (f000f), a enum type (not sure this is useful yet), a custom
   realize ops for customization.
 - the num-chips property can be configured on the command line.
 
 hw/ppc/pnv.c         | 194 +++++++++++++++++++++++++++++++++++++++++++++++++--
 include/hw/ppc/pnv.h |  61 ++++++++++++++++
 2 files changed, 250 insertions(+), 5 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 02fc4826baa4..08f72dbdca97 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -74,6 +74,16 @@ static void powernv_populate_memory_node(void *fdt, int chip_id, hwaddr start,
     _FDT((fdt_setprop_cell(fdt, off, "ibm,chip-id", chip_id)));
 }
 
+static void powernv_populate_chip(PnvChip *chip, void *fdt)
+{
+    /* Put all the memory in one node on chip 0 until we find a way to
+     * specify different ranges for each chip
+     */
+    if (chip->chip_id == 0) {
+        powernv_populate_memory_node(fdt, chip->chip_id, 0, ram_size);
+    }
+}
+
 static void *powernv_create_fdt(PnvMachineState *pnv,
                                 const char *kernel_cmdline)
 {
@@ -81,6 +91,7 @@ static void *powernv_create_fdt(PnvMachineState *pnv,
     char *buf;
     const char plat_compat[] = "qemu,powernv\0ibm,powernv";
     int off;
+    int i;
 
     fdt = g_malloc0(FDT_MAX_SIZE);
     _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE)));
@@ -115,11 +126,10 @@ static void *powernv_create_fdt(PnvMachineState *pnv,
                                &end_prop, sizeof(end_prop))));
     }
 
-    /* Put all the memory in one node on chip 0 until we find a way to
-     * specify different ranges for each chip
-     */
-    powernv_populate_memory_node(fdt, 0, 0, ram_size);
-
+    /* Populate device tree for each chip */
+    for (i = 0; i < pnv->num_chips; i++) {
+        powernv_populate_chip(pnv->chips[i], fdt);
+    }
     return fdt;
 }
 
@@ -147,6 +157,8 @@ static void ppc_powernv_init(MachineState *machine)
     char *fw_filename;
     long fw_size;
     long kernel_size;
+    int i;
+    char *chip_typename;
 
     /* allocate RAM */
     if (ram_size < (1 * G_BYTE)) {
@@ -191,6 +203,172 @@ static void ppc_powernv_init(MachineState *machine)
             exit(1);
         }
     }
+
+    /* We need some cpu model to instantiate the PnvChip class */
+    if (machine->cpu_model == NULL) {
+        machine->cpu_model = "POWER8";
+    }
+
+    /* Create the processor chips */
+    chip_typename = g_strdup_printf(TYPE_PNV_CHIP "-%s", machine->cpu_model);
+    if (!object_class_by_name(chip_typename)) {
+        error_report("qemu: invalid CPU model '%s' for %s machine",
+                     machine->cpu_model, MACHINE_GET_CLASS(machine)->name);
+        exit(1);
+    }
+
+    pnv->chips = g_new0(PnvChip *, pnv->num_chips);
+    for (i = 0; i < pnv->num_chips; i++) {
+        char chip_name[32];
+        Object *chip = object_new(chip_typename);
+
+        pnv->chips[i] = PNV_CHIP(chip);
+
+        snprintf(chip_name, sizeof(chip_name), "chip[%d]", CHIP_HWID(i));
+        object_property_add_child(OBJECT(pnv), chip_name, chip, &error_fatal);
+        object_property_set_int(chip, CHIP_HWID(i), "chip-id", &error_fatal);
+        object_property_set_bool(chip, true, "realized", &error_fatal);
+    }
+    g_free(chip_typename);
+}
+
+static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PnvChipClass *k = PNV_CHIP_CLASS(klass);
+
+    k->cpu_model = "POWER8E";
+    k->chip_type = PNV_CHIP_POWER8E;
+    k->chip_cfam_id = 0x221ef04980000000ull;  /* P8 Murano DD2.1 */
+    dc->desc = "PowerNV Chip POWER8E";
+}
+
+static const TypeInfo pnv_chip_power8e_info = {
+    .name          = TYPE_PNV_CHIP_POWER8E,
+    .parent        = TYPE_PNV_CHIP,
+    .instance_size = sizeof(PnvChip),
+    .class_init    = pnv_chip_power8e_class_init,
+};
+
+static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PnvChipClass *k = PNV_CHIP_CLASS(klass);
+
+    k->cpu_model = "POWER8";
+    k->chip_type = PNV_CHIP_POWER8;
+    k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */
+    dc->desc = "PowerNV Chip POWER8";
+}
+
+static const TypeInfo pnv_chip_power8_info = {
+    .name          = TYPE_PNV_CHIP_POWER8,
+    .parent        = TYPE_PNV_CHIP,
+    .instance_size = sizeof(PnvChip),
+    .class_init    = pnv_chip_power8_class_init,
+};
+
+static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PnvChipClass *k = PNV_CHIP_CLASS(klass);
+
+    k->cpu_model = "POWER8NVL";
+    k->chip_type = PNV_CHIP_POWER8NVL;
+    k->chip_cfam_id = 0x120d304980000000ull;  /* P8 Naples DD1.0 */
+    dc->desc = "PowerNV Chip POWER8NVL";
+}
+
+static const TypeInfo pnv_chip_power8nvl_info = {
+    .name          = TYPE_PNV_CHIP_POWER8NVL,
+    .parent        = TYPE_PNV_CHIP,
+    .instance_size = sizeof(PnvChip),
+    .class_init    = pnv_chip_power8nvl_class_init,
+};
+
+static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PnvChipClass *k = PNV_CHIP_CLASS(klass);
+
+    k->cpu_model = "POWER9";
+    k->chip_type = PNV_CHIP_POWER9;
+    k->chip_cfam_id = 0x100d104980000000ull; /* P9 Nimbus DD1.0 */
+    dc->desc = "PowerNV Chip POWER9";
+}
+
+static const TypeInfo pnv_chip_power9_info = {
+    .name          = TYPE_PNV_CHIP_POWER9,
+    .parent        = TYPE_PNV_CHIP,
+    .instance_size = sizeof(PnvChip),
+    .class_init    = pnv_chip_power9_class_init,
+};
+
+static void pnv_chip_realize(DeviceState *dev, Error **errp)
+{
+    /* left purposely empty */
+}
+
+static Property pnv_chip_properties[] = {
+    DEFINE_PROP_UINT32("chip-id", PnvChip, chip_id, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pnv_chip_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = pnv_chip_realize;
+    dc->props = pnv_chip_properties;
+    dc->desc = "PowerNV Chip";
+}
+
+static const TypeInfo pnv_chip_info = {
+    .name          = TYPE_PNV_CHIP,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .class_init    = pnv_chip_class_init,
+    .class_size    = sizeof(PnvChipClass),
+    .abstract      = true,
+};
+
+
+static char *pnv_get_num_chips(Object *obj, Error **errp)
+{
+    return g_strdup_printf("%d", POWERNV_MACHINE(obj)->num_chips);
+}
+
+static void pnv_set_num_chips(Object *obj, const char *value, Error **errp)
+{
+    PnvMachineState *pnv = POWERNV_MACHINE(obj);
+    unsigned long num_chips;
+
+    if (qemu_strtoul(value, NULL, 10, &num_chips) < 0) {
+        error_setg(errp, "invalid num_chips property: '%s'", value);
+        return;
+    }
+
+    /*
+     * TODO: should we decide on how many chips we can create based
+     * on #cores and Venice vs. Murano vs. Naples chip type etc...,
+     */
+    if (num_chips < 1 || num_chips > 4) {
+        error_setg(errp, "invalid number of chips: '%s'", value);
+        return;
+    }
+
+    pnv->num_chips = num_chips;
+}
+
+static void powernv_machine_initfn(Object *obj)
+{
+    PnvMachineState *pnv = POWERNV_MACHINE(obj);
+    pnv->num_chips = 1;
+
+    object_property_add_str(obj, "num-chips", pnv_get_num_chips,
+                            pnv_set_num_chips, NULL);
+    object_property_set_description(obj, "num-chips",
+                                    "Specifies the number of processor chips",
+                                    NULL);
 }
 
 static void powernv_machine_class_init(ObjectClass *oc, void *data)
@@ -212,12 +390,18 @@ static const TypeInfo powernv_machine_info = {
     .name          = TYPE_POWERNV_MACHINE,
     .parent        = TYPE_MACHINE,
     .instance_size = sizeof(PnvMachineState),
+    .instance_init = powernv_machine_initfn,
     .class_init    = powernv_machine_class_init,
 };
 
 static void powernv_machine_register_types(void)
 {
     type_register_static(&powernv_machine_info);
+    type_register_static(&pnv_chip_info);
+    type_register_static(&pnv_chip_power8e_info);
+    type_register_static(&pnv_chip_power8_info);
+    type_register_static(&pnv_chip_power8nvl_info);
+    type_register_static(&pnv_chip_power9_info);
 }
 
 type_init(powernv_machine_register_types)
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index c8a73bc74267..da543ed81636 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -20,6 +20,64 @@
 #define _PPC_PNV_H
 
 #include "hw/boards.h"
+#include "hw/sysbus.h"
+
+#define TYPE_PNV_CHIP "powernv-chip"
+#define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
+#define PNV_CHIP_CLASS(klass) \
+     OBJECT_CLASS_CHECK(PnvChipClass, (klass), TYPE_PNV_CHIP)
+#define PNV_CHIP_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(PnvChipClass, (obj), TYPE_PNV_CHIP)
+
+typedef enum PnvChipType {
+    PNV_CHIP_POWER8E,     /* AKA Murano (default) */
+    PNV_CHIP_POWER8,      /* AKA Venice */
+    PNV_CHIP_POWER8NVL,   /* AKA Naples */
+    PNV_CHIP_POWER9,      /* AKA Nimbus */
+} PnvChipType;
+
+typedef struct PnvChip {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    uint32_t     chip_id;
+} PnvChip;
+
+typedef struct PnvChipClass {
+    /*< private >*/
+    SysBusDeviceClass parent_class;
+
+    /*< public >*/
+    const char *cpu_model;
+    PnvChipType  chip_type;
+    uint64_t     chip_cfam_id;
+} PnvChipClass;
+
+#define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E"
+#define PNV_CHIP_POWER8E(obj) \
+    OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8E)
+
+#define TYPE_PNV_CHIP_POWER8 TYPE_PNV_CHIP "-POWER8"
+#define PNV_CHIP_POWER8(obj) \
+    OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8)
+
+#define TYPE_PNV_CHIP_POWER8NVL TYPE_PNV_CHIP "-POWER8NVL"
+#define PNV_CHIP_POWER8NVL(obj) \
+    OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8NVL)
+
+#define TYPE_PNV_CHIP_POWER9 TYPE_PNV_CHIP "-POWER9"
+#define PNV_CHIP_POWER9(obj) \
+    OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER9)
+
+/*
+ * This generates a HW chip id depending on an index:
+ *
+ *    0x0, 0x1, 0x10, 0x11
+ *
+ * 4 chips should be the maximum
+ */
+#define CHIP_HWID(i) ((((i) & 0x3e) << 3) | ((i) & 0x1))
 
 #define TYPE_POWERNV_MACHINE       MACHINE_TYPE_NAME("powernv")
 #define POWERNV_MACHINE(obj) \
@@ -31,6 +89,9 @@ typedef struct PnvMachineState {
 
     uint32_t initrd_base;
     long initrd_size;
+
+    uint32_t  num_chips;
+    PnvChip   **chips;
 } PnvMachineState;
 
 #define POWERNV_FDT_ADDR                0x01000000
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 03/20] ppc/pnv: add a core mask to PnvChip
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 01/20] ppc/pnv: add skeleton PowerNV platform Cédric Le Goater
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 02/20] ppc/pnv: add a PnvChip object Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-07  4:32   ` David Gibson
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 04/20] ppc/pnv: add a PIR handler " Cédric Le Goater
                   ` (17 subsequent siblings)
  20 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

This will be used to build real HW ids for the cores and enforce some
limits on the available cores per chip.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---

 Changes since v3 :

 - reworked pnv_chip_core_sanitize() to return errors and to check the
   maximum of cores against the instance cores_mask
  
 Changes since v2 :

 - added POWER9 support
 - removed cores_max 
 - introduces a pnv_chip_core_sanitize() helper to check the core
   ids_mask and the maximum number of cores

 hw/ppc/pnv.c         | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 include/hw/ppc/pnv.h |  4 +++
 2 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 08f72dbdca97..fc930be94f53 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -227,11 +227,44 @@ static void ppc_powernv_init(MachineState *machine)
         snprintf(chip_name, sizeof(chip_name), "chip[%d]", CHIP_HWID(i));
         object_property_add_child(OBJECT(pnv), chip_name, chip, &error_fatal);
         object_property_set_int(chip, CHIP_HWID(i), "chip-id", &error_fatal);
+        object_property_set_int(chip, smp_cores, "nr-cores", &error_fatal);
+        /*
+         * We could customize cores_mask for the chip here. May be
+         * using a powernv machine property, like 'num-chips'. Let the
+         * chip choose the default for now.
+         */
+        object_property_set_int(chip, 0x0, "cores-mask", &error_fatal);
         object_property_set_bool(chip, true, "realized", &error_fatal);
     }
     g_free(chip_typename);
 }
 
+/* Allowed core identifiers on a POWER8 Processor Chip :
+ *
+ * <EX0 reserved>
+ *  EX1  - Venice only
+ *  EX2  - Venice only
+ *  EX3  - Venice only
+ *  EX4
+ *  EX5
+ *  EX6
+ * <EX7,8 reserved> <reserved>
+ *  EX9  - Venice only
+ *  EX10 - Venice only
+ *  EX11 - Venice only
+ *  EX12
+ *  EX13
+ *  EX14
+ * <EX15 reserved>
+ */
+#define POWER8E_CORE_MASK  (0x7070ull)
+#define POWER8_CORE_MASK   (0x7e7eull)
+
+/*
+ * POWER9 has 24 cores, ids starting at 0x20
+ */
+#define POWER9_CORE_MASK   (0xffffff00000000ull)
+
 static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
@@ -240,6 +273,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
     k->cpu_model = "POWER8E";
     k->chip_type = PNV_CHIP_POWER8E;
     k->chip_cfam_id = 0x221ef04980000000ull;  /* P8 Murano DD2.1 */
+    k->cores_mask = POWER8E_CORE_MASK;
     dc->desc = "PowerNV Chip POWER8E";
 }
 
@@ -258,6 +292,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
     k->cpu_model = "POWER8";
     k->chip_type = PNV_CHIP_POWER8;
     k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */
+    k->cores_mask = POWER8_CORE_MASK;
     dc->desc = "PowerNV Chip POWER8";
 }
 
@@ -276,6 +311,7 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
     k->cpu_model = "POWER8NVL";
     k->chip_type = PNV_CHIP_POWER8NVL;
     k->chip_cfam_id = 0x120d304980000000ull;  /* P8 Naples DD1.0 */
+    k->cores_mask = POWER8_CORE_MASK;
     dc->desc = "PowerNV Chip POWER8NVL";
 }
 
@@ -294,6 +330,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
     k->cpu_model = "POWER9";
     k->chip_type = PNV_CHIP_POWER9;
     k->chip_cfam_id = 0x100d104980000000ull; /* P9 Nimbus DD1.0 */
+    k->cores_mask = POWER9_CORE_MASK;
     dc->desc = "PowerNV Chip POWER9";
 }
 
@@ -304,13 +341,52 @@ static const TypeInfo pnv_chip_power9_info = {
     .class_init    = pnv_chip_power9_class_init,
 };
 
+static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp)
+{
+    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+    int cores_max;
+
+    /*
+     * No custom mask for this chip, let's use the default one from *
+     * the chip class
+     */
+    if (!chip->cores_mask) {
+        chip->cores_mask = pcc->cores_mask;
+    }
+
+    /* filter alien core ids ! some are reserved */
+    if ((chip->cores_mask & pcc->cores_mask) != chip->cores_mask) {
+        error_setg(errp, "warning: invalid core mask for chip !");
+        return;
+    }
+    chip->cores_mask &= pcc->cores_mask;
+
+    /* now that we have a sane layout, let check the number of cores */
+    cores_max = hweight_long(chip->cores_mask);
+    if (chip->nr_cores > cores_max) {
+        error_setg(errp, "warning: too many cores for chip ! Limit is %d",
+                   cores_max);
+        return;
+    }
+}
+
 static void pnv_chip_realize(DeviceState *dev, Error **errp)
 {
-    /* left purposely empty */
+    PnvChip *chip = PNV_CHIP(dev);
+    Error *error = NULL;
+
+    /* Early checks on the core settings */
+    pnv_chip_core_sanitize(chip, &error);
+    if (error) {
+        error_propagate(errp, error);
+        return;
+    }
 }
 
 static Property pnv_chip_properties[] = {
     DEFINE_PROP_UINT32("chip-id", PnvChip, chip_id, 0),
+    DEFINE_PROP_UINT32("nr-cores", PnvChip, nr_cores, 1),
+    DEFINE_PROP_UINT64("cores-mask", PnvChip, cores_mask, 0x0),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index da543ed81636..2c225c928974 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -42,6 +42,9 @@ typedef struct PnvChip {
 
     /*< public >*/
     uint32_t     chip_id;
+
+    uint32_t  nr_cores;
+    uint64_t  cores_mask;
 } PnvChip;
 
 typedef struct PnvChipClass {
@@ -52,6 +55,7 @@ typedef struct PnvChipClass {
     const char *cpu_model;
     PnvChipType  chip_type;
     uint64_t     chip_cfam_id;
+    uint64_t     cores_mask;
 } PnvChipClass;
 
 #define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E"
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 04/20] ppc/pnv: add a PIR handler to PnvChip
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (2 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 03/20] ppc/pnv: add a core mask to PnvChip Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-07  4:34   ` David Gibson
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 05/20] ppc/pnv: add a PnvCore object Cédric Le Goater
                   ` (16 subsequent siblings)
  20 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

The Processor Identification Register (PIR) is a register that holds a
processor identifier which is used for bus transactions (XSCOM) and
for processor differentiation in multiprocessor systems. It also used
in the interrupt vector entries (IVE) to identify the thread serving
the interrupts.

P9 and P8 have some differences in the CPU PIR encoding.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---

 Changes since v3 :

 - added a couple more comments on the bits definition
  
 hw/ppc/pnv.c         | 30 ++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h |  2 ++
 2 files changed, 32 insertions(+)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index fc930be94f53..758c849702a0 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -239,6 +239,32 @@ static void ppc_powernv_init(MachineState *machine)
     g_free(chip_typename);
 }
 
+/*
+ *    0:21  Reserved - Read as zeros
+ *   22:24  Chip ID
+ *   25:28  Core number
+ *   29:31  Thread ID
+ */
+static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t core_id)
+{
+    return (chip->chip_id << 7) | (core_id << 3);
+}
+
+/*
+ *    0:48  Reserved - Read as zeroes
+ *   49:52  Node ID
+ *   53:55  Chip ID
+ *   56     Reserved - Read as zero
+ *   57:61  Core number
+ *   62:63  Thread ID
+ *
+ * We only care about the lower bits. uint32_t is fine for the moment.
+ */
+static uint32_t pnv_chip_core_pir_p9(PnvChip *chip, uint32_t core_id)
+{
+    return (chip->chip_id << 8) | (core_id << 2);
+}
+
 /* Allowed core identifiers on a POWER8 Processor Chip :
  *
  * <EX0 reserved>
@@ -274,6 +300,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
     k->chip_type = PNV_CHIP_POWER8E;
     k->chip_cfam_id = 0x221ef04980000000ull;  /* P8 Murano DD2.1 */
     k->cores_mask = POWER8E_CORE_MASK;
+    k->core_pir = pnv_chip_core_pir_p8;
     dc->desc = "PowerNV Chip POWER8E";
 }
 
@@ -293,6 +320,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
     k->chip_type = PNV_CHIP_POWER8;
     k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */
     k->cores_mask = POWER8_CORE_MASK;
+    k->core_pir = pnv_chip_core_pir_p8;
     dc->desc = "PowerNV Chip POWER8";
 }
 
@@ -312,6 +340,7 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
     k->chip_type = PNV_CHIP_POWER8NVL;
     k->chip_cfam_id = 0x120d304980000000ull;  /* P8 Naples DD1.0 */
     k->cores_mask = POWER8_CORE_MASK;
+    k->core_pir = pnv_chip_core_pir_p8;
     dc->desc = "PowerNV Chip POWER8NVL";
 }
 
@@ -331,6 +360,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
     k->chip_type = PNV_CHIP_POWER9;
     k->chip_cfam_id = 0x100d104980000000ull; /* P9 Nimbus DD1.0 */
     k->cores_mask = POWER9_CORE_MASK;
+    k->core_pir = pnv_chip_core_pir_p9;
     dc->desc = "PowerNV Chip POWER9";
 }
 
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index 2c225c928974..c676f800e28e 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -56,6 +56,8 @@ typedef struct PnvChipClass {
     PnvChipType  chip_type;
     uint64_t     chip_cfam_id;
     uint64_t     cores_mask;
+
+    uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
 } PnvChipClass;
 
 #define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E"
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 05/20] ppc/pnv: add a PnvCore object
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (3 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 04/20] ppc/pnv: add a PIR handler " Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-07  4:52   ` David Gibson
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 06/20] ppc/pnv: add XSCOM infrastructure Cédric Le Goater
                   ` (15 subsequent siblings)
  20 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

This is largy inspired by sPAPRCPUCore with some simplification, no
hotplug for instance. But the differences are small and the objects
could possibly be merged.

A set of PnvCore objects is added to the PnvChip and the device
tree is populated looping on these cores.

Real HW cpu ids are now generated depending on the chip cpu model, the
chip id and a core mask.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---

 I did not introduce a single table to construct both the chip types
 and the corresponding core types yet. Keeping the idea for later as
 there might be other types to construct with P9 support.

 Changes since v3:

 - removed the usage of cpu_index
 - removed the setting of the msr_mask
 
 Changes since v2:

 - added P9 support
 - used error_fatal instead of error_abort when setting the chip
   properties
 - replaced num_cores by nr_cores
 - removed gservers properties that were unused on powernv. 
 - used a 'void *' instead of a 'PnvCore *' to hold core Objects of
   potentially different size.
 - qom: linked the core Objects to the chip 
 - moved device tree creation under powernv_populate_chip()
 - added a 'pir' property' for ease of use

 Changes since v1:

 - changed name to PnvCore
 - changed PnvChip core array type to a 'PnvCore *cores'
 - introduced real cpu hw ids using a core mask from the chip
 - reworked powernv_create_core_node() which populates the device tree
 - added missing "ibm,pa-features" property 
 - smp_cpus representing threads, used smp_cores instead to create the
   cores in the chip.
 - removed the use of ppc_get_vcpu_dt_id() 
 - added "POWER8E" and "POWER8NVL" cpu models to exercice the
   PnvChipClass

 hw/ppc/Makefile.objs      |   2 +-
 hw/ppc/pnv.c              | 187 ++++++++++++++++++++++++++++++++++++++++++++++
 hw/ppc/pnv_core.c         | 186 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h      |   3 +
 include/hw/ppc/pnv_core.h |  48 ++++++++++++
 5 files changed, 425 insertions(+), 1 deletion(-)
 create mode 100644 hw/ppc/pnv_core.c
 create mode 100644 include/hw/ppc/pnv_core.h

diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index 8105db7d5600..f8c7d1db9ade 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
 obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
 obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
 # IBM PowerNV
-obj-$(CONFIG_POWERNV) += pnv.o
+obj-$(CONFIG_POWERNV) += pnv.o pnv_core.o
 ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
 obj-y += spapr_pci_vfio.o
 endif
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 758c849702a0..2376bb222918 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -27,6 +27,7 @@
 #include "hw/ppc/fdt.h"
 #include "hw/ppc/ppc.h"
 #include "hw/ppc/pnv.h"
+#include "hw/ppc/pnv_core.h"
 #include "hw/loader.h"
 #include "exec/address-spaces.h"
 #include "qemu/cutils.h"
@@ -74,14 +75,162 @@ static void powernv_populate_memory_node(void *fdt, int chip_id, hwaddr start,
     _FDT((fdt_setprop_cell(fdt, off, "ibm,chip-id", chip_id)));
 }
 
+static int get_cpus_node(void *fdt)
+{
+    int cpus_offset = fdt_path_offset(fdt, "/cpus");
+
+    if (cpus_offset < 0) {
+        cpus_offset = fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"),
+                                      "cpus");
+        if (cpus_offset) {
+            _FDT((fdt_setprop_cell(fdt, cpus_offset, "#address-cells", 0x1)));
+            _FDT((fdt_setprop_cell(fdt, cpus_offset, "#size-cells", 0x0)));
+        }
+    }
+    _FDT(cpus_offset);
+    return cpus_offset;
+}
+
+/*
+ * The PowerNV cores (and threads) need to use real HW ids and not an
+ * incremental index like it has been done on other platforms. This HW
+ * id is stored in the CPU PIR, it is used to create cpu nodes in the
+ * device tree, used in XSCOM to address cores and in interrupt
+ * servers.
+ */
+static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt)
+{
+    CPUState *cs = CPU(DEVICE(pc->threads));
+    DeviceClass *dc = DEVICE_GET_CLASS(cs);
+    PowerPCCPU *cpu = POWERPC_CPU(cs);
+    int smt_threads = ppc_get_compat_smt_threads(cpu);
+    CPUPPCState *env = &cpu->env;
+    PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
+    uint32_t servers_prop[smt_threads];
+    int i;
+    uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40),
+                       0xffffffff, 0xffffffff};
+    uint32_t tbfreq = PNV_TIMEBASE_FREQ;
+    uint32_t cpufreq = 1000000000;
+    uint32_t page_sizes_prop[64];
+    size_t page_sizes_prop_size;
+    const uint8_t pa_features[] = { 24, 0,
+                                    0xf6, 0x3f, 0xc7, 0xc0, 0x80, 0xf0,
+                                    0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                    0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
+                                    0x80, 0x00, 0x80, 0x00, 0x80, 0x00 };
+    int offset;
+    char *nodename;
+    int cpus_offset = get_cpus_node(fdt);
+
+    nodename = g_strdup_printf("%s@%x", dc->fw_name, pc->pir);
+    offset = fdt_add_subnode(fdt, cpus_offset, nodename);
+    _FDT(offset);
+    g_free(nodename);
+
+    _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", chip->chip_id)));
+
+    _FDT((fdt_setprop_cell(fdt, offset, "reg", pc->pir)));
+    _FDT((fdt_setprop_cell(fdt, offset, "ibm,pir", pc->pir)));
+    _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu")));
+
+    _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", env->spr[SPR_PVR])));
+    _FDT((fdt_setprop_cell(fdt, offset, "d-cache-block-size",
+                            env->dcache_line_size)));
+    _FDT((fdt_setprop_cell(fdt, offset, "d-cache-line-size",
+                            env->dcache_line_size)));
+    _FDT((fdt_setprop_cell(fdt, offset, "i-cache-block-size",
+                            env->icache_line_size)));
+    _FDT((fdt_setprop_cell(fdt, offset, "i-cache-line-size",
+                            env->icache_line_size)));
+
+    if (pcc->l1_dcache_size) {
+        _FDT((fdt_setprop_cell(fdt, offset, "d-cache-size",
+                               pcc->l1_dcache_size)));
+    } else {
+        error_report("Warning: Unknown L1 dcache size for cpu");
+    }
+    if (pcc->l1_icache_size) {
+        _FDT((fdt_setprop_cell(fdt, offset, "i-cache-size",
+                               pcc->l1_icache_size)));
+    } else {
+        error_report("Warning: Unknown L1 icache size for cpu");
+    }
+
+    _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq)));
+    _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq)));
+    _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", env->slb_nr)));
+    _FDT((fdt_setprop_string(fdt, offset, "status", "okay")));
+    _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0)));
+
+    if (env->spr_cb[SPR_PURR].oea_read) {
+        _FDT((fdt_setprop(fdt, offset, "ibm,purr", NULL, 0)));
+    }
+
+    if (env->mmu_model & POWERPC_MMU_1TSEG) {
+        _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes",
+                           segs, sizeof(segs))));
+    }
+
+    /* Advertise VMX/VSX (vector extensions) if available
+     *   0 / no property == no vector extensions
+     *   1               == VMX / Altivec available
+     *   2               == VSX available */
+    if (env->insns_flags & PPC_ALTIVEC) {
+        uint32_t vmx = (env->insns_flags2 & PPC2_VSX) ? 2 : 1;
+
+        _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", vmx)));
+    }
+
+    /* Advertise DFP (Decimal Floating Point) if available
+     *   0 / no property == no DFP
+     *   1               == DFP available */
+    if (env->insns_flags2 & PPC2_DFP) {
+        _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1)));
+    }
+
+    page_sizes_prop_size = ppc_create_page_sizes_prop(env, page_sizes_prop,
+                                                  sizeof(page_sizes_prop));
+    if (page_sizes_prop_size) {
+        _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes",
+                           page_sizes_prop, page_sizes_prop_size)));
+    }
+
+    _FDT((fdt_setprop(fdt, offset, "ibm,pa-features",
+                       pa_features, sizeof(pa_features))));
+
+    if (cpu->cpu_version) {
+        _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", cpu->cpu_version)));
+    }
+
+    /* Build interrupt servers properties */
+    for (i = 0; i < smt_threads; i++) {
+        servers_prop[i] = cpu_to_be32(pc->pir + i);
+    }
+    _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s",
+                       servers_prop, sizeof(servers_prop))));
+}
+
 static void powernv_populate_chip(PnvChip *chip, void *fdt)
 {
+    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+    char *typename = pnv_core_typename(pcc->cpu_model);
+    size_t typesize = object_type_get_instance_size(typename);
+    int i;
+
+    for (i = 0; i < chip->nr_cores; i++) {
+        PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
+
+        powernv_create_core_node(chip, pnv_core, fdt);
+    }
+
     /* Put all the memory in one node on chip 0 until we find a way to
      * specify different ranges for each chip
      */
     if (chip->chip_id == 0) {
         powernv_populate_memory_node(fdt, chip->chip_id, 0, ram_size);
     }
+    g_free(typename);
 }
 
 static void *powernv_create_fdt(PnvMachineState *pnv,
@@ -404,6 +553,15 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
 {
     PnvChip *chip = PNV_CHIP(dev);
     Error *error = NULL;
+    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+    char *typename = pnv_core_typename(pcc->cpu_model);
+    size_t typesize = object_type_get_instance_size(typename);
+    int i, core_hwid;
+
+    if (!object_class_by_name(typename)) {
+        error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename);
+        return;
+    }
 
     /* Early checks on the core settings */
     pnv_chip_core_sanitize(chip, &error);
@@ -411,6 +569,35 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
         error_propagate(errp, error);
         return;
     }
+
+    chip->cores = g_malloc0(typesize * chip->nr_cores);
+
+    for (i = 0, core_hwid = 0; (core_hwid < sizeof(chip->cores_mask) * 8)
+             && (i < chip->nr_cores); core_hwid++) {
+        char core_name[32];
+        void *pnv_core = chip->cores + i * typesize;
+
+        if (!(chip->cores_mask & (1ull << core_hwid))) {
+            continue;
+        }
+
+        object_initialize(pnv_core, typesize, typename);
+        snprintf(core_name, sizeof(core_name), "core[%d]", core_hwid);
+        object_property_add_child(OBJECT(chip), core_name, OBJECT(pnv_core),
+                                  &error_fatal);
+        object_property_set_int(OBJECT(pnv_core), smp_threads, "nr-threads",
+                                &error_fatal);
+        object_property_set_int(OBJECT(pnv_core), core_hwid,
+                                CPU_CORE_PROP_CORE_ID, &error_fatal);
+        object_property_set_int(OBJECT(pnv_core),
+                                pcc->core_pir(chip, core_hwid),
+                                "pir", &error_fatal);
+        object_property_set_bool(OBJECT(pnv_core), true, "realized",
+                                 &error_fatal);
+        object_unref(OBJECT(pnv_core));
+        i++;
+    }
+    g_free(typename);
 }
 
 static Property pnv_chip_properties[] = {
diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
new file mode 100644
index 000000000000..d37788f142f4
--- /dev/null
+++ b/hw/ppc/pnv_core.c
@@ -0,0 +1,186 @@
+/*
+ * QEMU PowerPC PowerNV CPU Core model
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "qemu/osdep.h"
+#include "sysemu/sysemu.h"
+#include "qapi/error.h"
+#include "target-ppc/cpu.h"
+#include "hw/ppc/ppc.h"
+#include "hw/ppc/pnv.h"
+#include "hw/ppc/pnv_core.h"
+
+static void powernv_cpu_reset(void *opaque)
+{
+    PowerPCCPU *cpu = opaque;
+    CPUState *cs = CPU(cpu);
+    CPUPPCState *env = &cpu->env;
+    int core_pir;
+    int thread_index = 0; /* TODO: TCG supports only one thread */
+
+    cpu_reset(cs);
+
+    core_pir = object_property_get_int(OBJECT(cpu), "core-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 ?
+     */
+    env->spr[SPR_PIR] = core_pir + thread_index;
+
+    env->spr[SPR_HIOR] = 0;
+    /*
+     * the skiboot firmware elects a primary thread to initialize the
+     * system and it can be any.
+     */
+    env->gpr[3] = POWERNV_FDT_ADDR;
+    env->nip = 0x10;
+    env->msr |= MSR_HVB; /* Hypervisor mode */
+}
+
+static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
+{
+    CPUPPCState *env = &cpu->env;
+
+    /* Set time-base frequency to 512 MHz */
+    cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ);
+
+    qemu_register_reset(powernv_cpu_reset, cpu);
+    powernv_cpu_reset(cpu);
+}
+
+static void pnv_core_realize_child(Object *child, Error **errp)
+{
+    Error *local_err = NULL;
+    CPUState *cs = CPU(child);
+    PowerPCCPU *cpu = POWERPC_CPU(cs);
+
+    object_property_set_bool(child, true, "realized", &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    powernv_cpu_init(cpu, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+}
+
+static void pnv_core_realize(DeviceState *dev, Error **errp)
+{
+    PnvCore *pc = PNV_CORE(OBJECT(dev));
+    CPUCore *cc = CPU_CORE(OBJECT(dev));
+    PnvCoreClass *pcc = PNV_CORE_GET_CLASS(OBJECT(dev));
+    const char *typename = object_class_get_name(pcc->cpu_oc);
+    size_t size = object_type_get_instance_size(typename);
+    Error *local_err = NULL;
+    void *obj;
+    int i, j;
+    char name[32];
+
+    pc->threads = g_malloc0(size * cc->nr_threads);
+    for (i = 0; i < cc->nr_threads; i++) {
+        obj = pc->threads + i * size;
+
+        object_initialize(obj, size, typename);
+
+        snprintf(name, sizeof(name), "thread[%d]", i);
+        object_property_add_child(OBJECT(pc), name, obj, &local_err);
+        object_property_add_alias(obj, "core-pir", OBJECT(pc),
+                                  "pir", &local_err);
+        if (local_err) {
+            goto err;
+        }
+        object_unref(obj);
+    }
+
+    for (j = 0; j < cc->nr_threads; j++) {
+        obj = pc->threads + j * size;
+
+        pnv_core_realize_child(obj, &local_err);
+        if (local_err) {
+            goto err;
+        }
+    }
+    return;
+
+err:
+    while (--i >= 0) {
+        obj = pc->threads + i * size;
+        object_unparent(obj);
+    }
+    g_free(pc->threads);
+    error_propagate(errp, local_err);
+}
+
+static Property pnv_core_properties[] = {
+    DEFINE_PROP_UINT32("pir", PnvCore, pir, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pnv_core_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    PnvCoreClass *pcc = PNV_CORE_CLASS(oc);
+
+    dc->realize = pnv_core_realize;
+    dc->props = pnv_core_properties;
+    pcc->cpu_oc = cpu_class_by_name(TYPE_POWERPC_CPU, data);
+}
+
+static const TypeInfo pnv_core_info = {
+    .name           = TYPE_PNV_CORE,
+    .parent         = TYPE_CPU_CORE,
+    .instance_size  = sizeof(PnvCore),
+    .class_size     = sizeof(PnvCoreClass),
+    .abstract       = true,
+};
+
+/*
+ * Grow this list or merge with SPAPRCoreInfo which is very similar
+ */
+static const char *pnv_core_models[] = {
+    "POWER8E", "POWER8", "POWER8NVL", "POWER9"
+};
+
+static void pnv_core_register_types(void)
+{
+    int i ;
+
+    type_register_static(&pnv_core_info);
+    for (i = 0; i < ARRAY_SIZE(pnv_core_models); ++i) {
+        TypeInfo ti = {
+            .parent = TYPE_PNV_CORE,
+            .instance_size = sizeof(PnvCore),
+            .class_init = pnv_core_class_init,
+            .class_data = (void *) pnv_core_models[i],
+        };
+        ti.name = pnv_core_typename(pnv_core_models[i]);
+        type_register(&ti);
+        g_free((void *)ti.name);
+    }
+}
+
+type_init(pnv_core_register_types)
+
+char *pnv_core_typename(const char *model)
+{
+    return g_strdup_printf(TYPE_PNV_CORE "-%s", model);
+}
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index c676f800e28e..ed4a360cde3b 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -45,6 +45,7 @@ typedef struct PnvChip {
 
     uint32_t  nr_cores;
     uint64_t  cores_mask;
+    void      *cores;
 } PnvChip;
 
 typedef struct PnvChipClass {
@@ -102,4 +103,6 @@ typedef struct PnvMachineState {
 
 #define POWERNV_FDT_ADDR                0x01000000
 
+#define PNV_TIMEBASE_FREQ           512000000ULL
+
 #endif /* _PPC_PNV_H */
diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h
new file mode 100644
index 000000000000..a151e281c017
--- /dev/null
+++ b/include/hw/ppc/pnv_core.h
@@ -0,0 +1,48 @@
+/*
+ * QEMU PowerPC PowerNV CPU Core model
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _PPC_PNV_CORE_H
+#define _PPC_PNV_CORE_H
+
+#include "hw/cpu/core.h"
+
+#define TYPE_PNV_CORE "powernv-cpu-core"
+#define PNV_CORE(obj) \
+    OBJECT_CHECK(PnvCore, (obj), TYPE_PNV_CORE)
+#define PNV_CORE_CLASS(klass) \
+     OBJECT_CLASS_CHECK(PnvCoreClass, (klass), TYPE_PNV_CORE)
+#define PNV_CORE_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(PnvCoreClass, (obj), TYPE_PNV_CORE)
+
+typedef struct PnvCore {
+    /*< private >*/
+    CPUCore parent_obj;
+
+    /*< public >*/
+    void *threads;
+    uint32_t pir;
+} PnvCore;
+
+typedef struct PnvCoreClass {
+    DeviceClass parent_class;
+    ObjectClass *cpu_oc;
+} PnvCoreClass;
+
+extern char *pnv_core_typename(const char *model);
+
+#endif /* _PPC_PNV_CORE_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 06/20] ppc/pnv: add XSCOM infrastructure
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (4 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 05/20] ppc/pnv: add a PnvCore object Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-13  0:41   ` David Gibson
  2016-11-07  8:26   ` Olaf Hering
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 07/20] ppc/pnv: add XSCOM handlers to PnvCore Cédric Le Goater
                   ` (14 subsequent siblings)
  20 siblings, 2 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

On a real POWER8 system, the Pervasive Interconnect Bus (PIB) serves
as a backbone to connect different units of the system. The host
firmware connects to the PIB through a bridge unit, the
Alter-Display-Unit (ADU), which gives him access to all the chiplets
on the PCB network (Pervasive Connect Bus), the PIB acting as the root
of this network.

XSCOM (serial communication) is the interface to the sideband bus
provided by the POWER8 pervasive unit to read and write to chiplets
resources. This is needed by the host firmware, OPAL and to a lesser
extent, Linux. This is among others how the PCI Host bridges get
configured at boot or how the LPC bus is accessed.

To represent the ADU of a real system, we introduce a specific
AddressSpace to dispatch XSCOM accesses to the targeted chiplets. The
translation of an XSCOM address into a PCB register address is
slightly different between the P9 and the P8. This is handled before
the dispatch using a 8byte alignment for all.

To customize the device tree, a QOM InterfaceClass, PnvXScomInterface,
is provided with a populate() handler. The chip populates the device
tree by simply looping on its children. Therefore, each model needing
custom nodes should not forget to declare itself as a child at
instantiation time.

Based on previous work done by :
      Benjamin Herrenschmidt <benh@kernel.crashing.org>

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---

 Changes since v3:

 - reworked the model to dispatch addresses to the memory regions
   using pcb_addr << 3, which is a no-op for the P9. The benefit is
   that all the address translation work can be done before dispatch
   and the conversion handlers in the chip and in the xscom interface
   are gone.
   
 - removed the proxy PnnXscom object and extended the PnvChip object
   with an address space for XSCOM and its associated memory region.
   
 - changed the read/write handlers in the address space to use
   address_space_stq() and address_space_ldq()
   
 - introduced 'fake' default read/write handlers to handle 'core'
   registers. We can add a real device model when more work needs to
   be done under these.
   
 - fixed an issue with the monitor doing read/write in the XSCOM
   address space. When under the monitor, we don't have a cpu to
   update the HMER SPR. That might need more work in the long term.
   
 - introduced a xscom base field to hold the xscom base address as
   it is different on P9

 - renamed the devnode() handler to populate()

 Changes since v2:

 - QOMified the model.
 
 - all mappings in main memory space are now gathered in
   pnv_chip_realize() as done on other architectures.
   
 - removed XScomBus. The parenthood is established through the QOM
   model
   
 - replaced the XScomDevice with an InterfaceClass : PnvXScomInterface. 
 - introduced an XSCOM address space to dispatch accesses to the
   chiplets

 hw/ppc/Makefile.objs       |   2 +-
 hw/ppc/pnv.c               |  25 +++++
 hw/ppc/pnv_xscom.c         | 262 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h       |  15 +++
 include/hw/ppc/pnv_xscom.h |  47 ++++++++
 5 files changed, 350 insertions(+), 1 deletion(-)
 create mode 100644 hw/ppc/pnv_xscom.c
 create mode 100644 include/hw/ppc/pnv_xscom.h

diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index f8c7d1db9ade..08c213c40684 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
 obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
 obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
 # IBM PowerNV
-obj-$(CONFIG_POWERNV) += pnv.o pnv_core.o
+obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o
 ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
 obj-y += spapr_pci_vfio.o
 endif
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 2376bb222918..5e19b6880387 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -32,6 +32,8 @@
 #include "exec/address-spaces.h"
 #include "qemu/cutils.h"
 
+#include "hw/ppc/pnv_xscom.h"
+
 #include <libfdt.h>
 
 #define FDT_MAX_SIZE            0x00100000
@@ -218,6 +220,8 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
     size_t typesize = object_type_get_instance_size(typename);
     int i;
 
+    pnv_xscom_populate(chip, fdt, 0);
+
     for (i = 0; i < chip->nr_cores; i++) {
         PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
 
@@ -450,6 +454,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
     k->chip_cfam_id = 0x221ef04980000000ull;  /* P8 Murano DD2.1 */
     k->cores_mask = POWER8E_CORE_MASK;
     k->core_pir = pnv_chip_core_pir_p8;
+    k->xscom_base = 0x003fc0000000000ull;
     dc->desc = "PowerNV Chip POWER8E";
 }
 
@@ -470,6 +475,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
     k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */
     k->cores_mask = POWER8_CORE_MASK;
     k->core_pir = pnv_chip_core_pir_p8;
+    k->xscom_base = 0x003fc0000000000ull;
     dc->desc = "PowerNV Chip POWER8";
 }
 
@@ -490,6 +496,7 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
     k->chip_cfam_id = 0x120d304980000000ull;  /* P8 Naples DD1.0 */
     k->cores_mask = POWER8_CORE_MASK;
     k->core_pir = pnv_chip_core_pir_p8;
+    k->xscom_base = 0x003fc0000000000ull;
     dc->desc = "PowerNV Chip POWER8NVL";
 }
 
@@ -510,6 +517,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
     k->chip_cfam_id = 0x100d104980000000ull; /* P9 Nimbus DD1.0 */
     k->cores_mask = POWER9_CORE_MASK;
     k->core_pir = pnv_chip_core_pir_p9;
+    k->xscom_base = 0x00603fc00000000ull;
     dc->desc = "PowerNV Chip POWER9";
 }
 
@@ -549,6 +557,14 @@ static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp)
     }
 }
 
+static void pnv_chip_init(Object *obj)
+{
+    PnvChip *chip = PNV_CHIP(obj);
+    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+
+    chip->xscom_base = pcc->xscom_base;
+}
+
 static void pnv_chip_realize(DeviceState *dev, Error **errp)
 {
     PnvChip *chip = PNV_CHIP(dev);
@@ -563,6 +579,14 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
         return;
     }
 
+    /* XSCOM bridge */
+    pnv_xscom_realize(chip, &error);
+    if (error) {
+        error_propagate(errp, error);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip));
+
     /* Early checks on the core settings */
     pnv_chip_core_sanitize(chip, &error);
     if (error) {
@@ -620,6 +644,7 @@ static const TypeInfo pnv_chip_info = {
     .name          = TYPE_PNV_CHIP,
     .parent        = TYPE_SYS_BUS_DEVICE,
     .class_init    = pnv_chip_class_init,
+    .instance_init = pnv_chip_init,
     .class_size    = sizeof(PnvChipClass),
     .abstract      = true,
 };
diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c
new file mode 100644
index 000000000000..ce1182d9a13e
--- /dev/null
+++ b/hw/ppc/pnv_xscom.c
@@ -0,0 +1,262 @@
+/*
+ * QEMU PowerPC PowerNV XSCOM bus
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "qemu/log.h"
+#include "sysemu/kvm.h"
+#include "target-ppc/cpu.h"
+#include "hw/sysbus.h"
+
+#include "hw/ppc/fdt.h"
+#include "hw/ppc/pnv_xscom.h"
+#include "hw/ppc/pnv.h"
+
+#include <libfdt.h>
+
+static void xscom_complete(uint64_t hmer_bits)
+{
+    CPUState *cs = current_cpu;
+
+    /*
+     * TODO: When the read/write comes from the monitor, we do not
+     * have a cpu.
+     */
+    if (cs) {
+        PowerPCCPU *cpu = POWERPC_CPU(cs);
+        CPUPPCState *env = &cpu->env;
+
+        /*
+         * TODO: Need a CPU helper to set HMER, also handle generation
+         * of HMIs
+         */
+        cpu_synchronize_state(cs);
+        env->spr[SPR_HMER] |= hmer_bits;
+    }
+}
+
+static uint32_t pnv_xscom_pcba(PnvChip *chip, uint64_t addr)
+{
+    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+
+    addr &= (PNV_XSCOM_SIZE - 1);
+    if (pcc->chip_type == PNV_CHIP_POWER9) {
+        return addr >> 3;
+    } else {
+        return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf);
+    }
+}
+
+static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba)
+{
+    switch (pcba) {
+    case 0xf000f:
+        return PNV_CHIP_GET_CLASS(chip)->chip_cfam_id;
+    case 0x1010c00:     /* PIBAM FIR */
+    case 0x1010c03:     /* PIBAM FIR MASK */
+    case 0x2020007:     /* ADU stuff */
+    case 0x2020009:     /* ADU stuff */
+    case 0x202000f:     /* ADU stuff */
+        return 0;
+    case 0x2013f00:     /* PBA stuff */
+    case 0x2013f01:     /* PBA stuff */
+    case 0x2013f02:     /* PBA stuff */
+    case 0x2013f03:     /* PBA stuff */
+    case 0x2013f04:     /* PBA stuff */
+    case 0x2013f05:     /* PBA stuff */
+    case 0x2013f06:     /* PBA stuff */
+    case 0x2013f07:     /* PBA stuff */
+        return 0;
+    case 0x2013028:     /* CAPP stuff */
+    case 0x201302a:     /* CAPP stuff */
+    case 0x2013801:     /* CAPP stuff */
+    case 0x2013802:     /* CAPP stuff */
+        return 0;
+    default:
+        return -1;
+    }
+}
+
+static bool xscom_write_default(PnvChip *chip, uint32_t pcba, uint64_t val)
+{
+    /* We ignore writes to these */
+    switch (pcba) {
+    case 0xf000f:       /* chip id is RO */
+    case 0x1010c00:     /* PIBAM FIR */
+    case 0x1010c01:     /* PIBAM FIR */
+    case 0x1010c02:     /* PIBAM FIR */
+    case 0x1010c03:     /* PIBAM FIR MASK */
+    case 0x1010c04:     /* PIBAM FIR MASK */
+    case 0x1010c05:     /* PIBAM FIR MASK */
+    case 0x2020007:     /* ADU stuff */
+    case 0x2020009:     /* ADU stuff */
+    case 0x202000f:     /* ADU stuff */
+        return true;
+    default:
+        return false;
+    }
+}
+
+static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)
+{
+    PnvChip *chip = opaque;
+    uint32_t pcba = pnv_xscom_pcba(chip, addr);
+    uint64_t val = 0;
+    MemTxResult result;
+
+    /* Handle some SCOMs here before dispatch */
+    val = xscom_read_default(chip, pcba);
+    if (val != -1) {
+        goto complete;
+    }
+
+    val = address_space_ldq(&chip->xscom_as, pcba << 3, MEMTXATTRS_UNSPECIFIED,
+                            &result);
+    if (result != MEMTX_OK) {
+        qemu_log_mask(LOG_GUEST_ERROR, "XSCOM read failed at @0x%"
+                      HWADDR_PRIx " pcba=0x%08x\n", addr, pcba);
+        xscom_complete(HMER_XSCOM_FAIL | HMER_XSCOM_DONE);
+        return 0;
+    }
+
+complete:
+    xscom_complete(HMER_XSCOM_DONE);
+    return val;
+}
+
+static void xscom_write(void *opaque, hwaddr addr, uint64_t val,
+                        unsigned width)
+{
+    PnvChip *chip = opaque;
+    uint32_t pcba = pnv_xscom_pcba(chip, addr);
+    MemTxResult result;
+
+    /* Handle some SCOMs here before dispatch */
+    if (xscom_write_default(chip, pcba, val)) {
+        goto complete;
+    }
+
+    address_space_stq(&chip->xscom_as, pcba << 3, val, MEMTXATTRS_UNSPECIFIED,
+                      &result);
+    if (result != MEMTX_OK) {
+        qemu_log_mask(LOG_GUEST_ERROR, "XSCOM write failed at @0x%"
+                      HWADDR_PRIx " pcba=0x%08x data=0x%" PRIx64 "\n",
+                      addr, pcba, val);
+        xscom_complete(HMER_XSCOM_FAIL | HMER_XSCOM_DONE);
+        return;
+    }
+
+complete:
+    xscom_complete(HMER_XSCOM_DONE);
+}
+
+const MemoryRegionOps pnv_xscom_ops = {
+    .read = xscom_read,
+    .write = 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,
+};
+
+void pnv_xscom_realize(PnvChip *chip, Error **errp)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(chip);
+    char *name;
+
+    name = g_strdup_printf("xscom-%x", chip->chip_id);
+    memory_region_init_io(&chip->xscom_mmio, OBJECT(chip), &pnv_xscom_ops,
+                          chip, name, PNV_XSCOM_SIZE);
+    sysbus_init_mmio(sbd, &chip->xscom_mmio);
+
+    memory_region_init(&chip->xscom, OBJECT(chip), name, PNV_XSCOM_SIZE);
+    address_space_init(&chip->xscom_as, &chip->xscom, name);
+    g_free(name);
+}
+
+static const TypeInfo pnv_xscom_interface_info = {
+    .name = TYPE_PNV_XSCOM_INTERFACE,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(PnvXScomInterfaceClass),
+};
+
+static void pnv_xscom_register_types(void)
+{
+    type_register_static(&pnv_xscom_interface_info);
+}
+
+type_init(pnv_xscom_register_types)
+
+typedef struct ForeachPopulateArgs {
+    void *fdt;
+    int xscom_offset;
+} ForeachPopulateArgs;
+
+static int xscom_populate_child(Object *child, void *opaque)
+{
+    if (object_dynamic_cast(child, TYPE_PNV_XSCOM_INTERFACE)) {
+        ForeachPopulateArgs *args = opaque;
+        PnvXScomInterface *xd = PNV_XSCOM_INTERFACE(child);
+        PnvXScomInterfaceClass *xc = PNV_XSCOM_INTERFACE_GET_CLASS(xd);
+
+        if (xc->populate) {
+            _FDT((xc->populate(xd, args->fdt, args->xscom_offset)));
+        }
+    }
+    return 0;
+}
+
+static const char compat_p8[] = "ibm,power8-xscom\0ibm,xscom";
+static const char compat_p9[] = "ibm,power9-xscom\0ibm,xscom";
+
+int pnv_xscom_populate(PnvChip *chip, void *fdt, int root_offset)
+{
+    uint64_t reg[] = { cpu_to_be64(PNV_XSCOM_BASE(chip)),
+                       cpu_to_be64(PNV_XSCOM_SIZE) };
+    int xscom_offset;
+    ForeachPopulateArgs args;
+    char *name;
+    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+
+    name = g_strdup_printf("xscom@%" PRIx64, be64_to_cpu(reg[0]));
+    xscom_offset = fdt_add_subnode(fdt, root_offset, name);
+    _FDT(xscom_offset);
+    g_free(name);
+    _FDT((fdt_setprop_cell(fdt, xscom_offset, "ibm,chip-id", chip->chip_id)));
+    _FDT((fdt_setprop_cell(fdt, xscom_offset, "#address-cells", 1)));
+    _FDT((fdt_setprop_cell(fdt, xscom_offset, "#size-cells", 1)));
+    _FDT((fdt_setprop(fdt, xscom_offset, "reg", reg, sizeof(reg))));
+
+    if (pcc->chip_type == PNV_CHIP_POWER9) {
+        _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat_p9,
+                          sizeof(compat_p9))));
+    } else {
+        _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat_p8,
+                          sizeof(compat_p8))));
+    }
+
+    _FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0)));
+
+    args.fdt = fdt;
+    args.xscom_offset = xscom_offset;
+
+    object_child_foreach(OBJECT(chip), xscom_populate_child, &args);
+    return 0;
+}
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index ed4a360cde3b..f89eddb6e5e0 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -21,6 +21,7 @@
 
 #include "hw/boards.h"
 #include "hw/sysbus.h"
+#include "hw/ppc/pnv_xscom.h"
 
 #define TYPE_PNV_CHIP "powernv-chip"
 #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
@@ -43,6 +44,11 @@ typedef struct PnvChip {
     /*< public >*/
     uint32_t     chip_id;
 
+    hwaddr       xscom_base;
+    MemoryRegion xscom_mmio;
+    MemoryRegion xscom;
+    AddressSpace xscom_as;
+
     uint32_t  nr_cores;
     uint64_t  cores_mask;
     void      *cores;
@@ -58,6 +64,8 @@ typedef struct PnvChipClass {
     uint64_t     chip_cfam_id;
     uint64_t     cores_mask;
 
+    hwaddr       xscom_base;
+
     uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
 } PnvChipClass;
 
@@ -105,4 +113,11 @@ typedef struct PnvMachineState {
 
 #define PNV_TIMEBASE_FREQ           512000000ULL
 
+/*
+ * POWER8 MMIO base addresses
+ */
+#define PNV_XSCOM_SIZE        0x800000000ull
+#define PNV_XSCOM_BASE(chip)                                            \
+    (chip->xscom_base + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE)
+
 #endif /* _PPC_PNV_H */
diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
new file mode 100644
index 000000000000..f50eb0bc4099
--- /dev/null
+++ b/include/hw/ppc/pnv_xscom.h
@@ -0,0 +1,47 @@
+/*
+ * QEMU PowerPC PowerNV XSCOM bus definitions
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _PPC_PNV_XSCOM_H
+#define _PPC_PNV_XSCOM_H
+
+#include "qom/object.h"
+
+typedef struct PnvChip PnvChip;
+
+typedef struct PnvXScomInterface {
+    Object parent;
+} PnvXScomInterface;
+
+#define TYPE_PNV_XSCOM_INTERFACE "pnv-xscom-interface"
+#define PNV_XSCOM_INTERFACE(obj) \
+     OBJECT_CHECK(PnvXScomInterface, (obj), TYPE_PNV_XSCOM_INTERFACE)
+#define PNV_XSCOM_INTERFACE_CLASS(klass)                \
+    OBJECT_CLASS_CHECK(PnvXScomInterfaceClass, (klass), \
+                       TYPE_PNV_XSCOM_INTERFACE)
+#define PNV_XSCOM_INTERFACE_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(PnvXScomInterfaceClass, (obj), TYPE_PNV_XSCOM_INTERFACE)
+
+typedef struct PnvXScomInterfaceClass {
+    InterfaceClass parent;
+    int (*populate)(PnvXScomInterface *dev, void *fdt, int offset);
+} PnvXScomInterfaceClass;
+
+extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
+extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
+
+#endif /* _PPC_PNV_XSCOM_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 07/20] ppc/pnv: add XSCOM handlers to PnvCore
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (5 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 06/20] ppc/pnv: add XSCOM infrastructure Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-13  0:51   ` David Gibson
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 08/20] ppc/pnv: add a LPC controller Cédric Le Goater
                   ` (13 subsequent siblings)
  20 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

Now that we are using real HW ids for the cores in PowerNV chips, we
can route the XSCOM accesses to them. We just need to attach a
specific XSCOM memory region to each core in the appropriate window
for the core number.

To start with, let's install the DTS (Digital Thermal Sensor) handlers
which should return 38°C for each core.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---

 Changes since v3:

 - moved to new XSCOM model
 - kept the write op on the XSCOM memory region for later use

 Changes since v2:

 - added a XSCOM memory region to handle access to the EX core
   registers   
 - extended the PnvCore object with a XSCOM_INTERFACE so that we can
   use pnv_xscom_pcba() and pnv_xscom_addr() to handle XSCOM address
   translation.

 hw/ppc/pnv.c               |  4 ++++
 hw/ppc/pnv_core.c          | 50 ++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv_core.h  |  2 ++
 include/hw/ppc/pnv_xscom.h | 19 ++++++++++++++++++
 4 files changed, 75 insertions(+)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 5e19b6880387..ffe245fe59d2 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -620,6 +620,10 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
                                  &error_fatal);
         object_unref(OBJECT(pnv_core));
         i++;
+
+        memory_region_add_subregion(&chip->xscom,
+                         PNV_XSCOM_EX_CORE_BASE(core_hwid) << 3,
+                         &PNV_CORE(pnv_core)->xscom_regs);
     }
     g_free(typename);
 }
diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
index d37788f142f4..a1c8a14f06b6 100644
--- a/hw/ppc/pnv_core.c
+++ b/hw/ppc/pnv_core.c
@@ -19,6 +19,7 @@
 #include "qemu/osdep.h"
 #include "sysemu/sysemu.h"
 #include "qapi/error.h"
+#include "qemu/log.h"
 #include "target-ppc/cpu.h"
 #include "hw/ppc/ppc.h"
 #include "hw/ppc/pnv.h"
@@ -64,6 +65,51 @@ static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
     powernv_cpu_reset(cpu);
 }
 
+/*
+ * These values are read by the PowerNV HW monitors under Linux
+ */
+#define PNV_XSCOM_EX_DTS_RESULT0     0x50000
+#define PNV_XSCOM_EX_DTS_RESULT1     0x50001
+
+static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr,
+                                    unsigned int width)
+{
+    uint32_t offset = addr >> 3;
+    uint64_t val = 0;
+
+    /* The result should be 38 C */
+    switch (offset) {
+    case PNV_XSCOM_EX_DTS_RESULT0:
+        val = 0x26f024f023f0000ull;
+        break;
+    case PNV_XSCOM_EX_DTS_RESULT1:
+        val = 0x24f000000000000ull;
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx,
+                  addr);
+    }
+
+    return val;
+}
+
+static void pnv_core_xscom_write(void *opaque, hwaddr addr, uint64_t val,
+                                 unsigned int width)
+{
+    qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx,
+                  addr);
+}
+
+static const MemoryRegionOps pnv_core_xscom_ops = {
+    .read = pnv_core_xscom_read,
+    .write = pnv_core_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_realize_child(Object *child, Error **errp)
 {
     Error *local_err = NULL;
@@ -119,6 +165,10 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
             goto err;
         }
     }
+
+    snprintf(name, sizeof(name), "xscom-core.%d", cc->core_id);
+    memory_region_init_io(&pc->xscom_regs, OBJECT(dev), &pnv_core_xscom_ops,
+                          pc, name,  PNV_XSCOM_EX_CORE_SIZE << 3);
     return;
 
 err:
diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h
index a151e281c017..2955a41c901f 100644
--- a/include/hw/ppc/pnv_core.h
+++ b/include/hw/ppc/pnv_core.h
@@ -36,6 +36,8 @@ typedef struct PnvCore {
     /*< public >*/
     void *threads;
     uint32_t pir;
+
+    MemoryRegion xscom_regs;
 } PnvCore;
 
 typedef struct PnvCoreClass {
diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
index f50eb0bc4099..79975a6cbe46 100644
--- a/include/hw/ppc/pnv_xscom.h
+++ b/include/hw/ppc/pnv_xscom.h
@@ -41,6 +41,25 @@ typedef struct PnvXScomInterfaceClass {
     int (*populate)(PnvXScomInterface *dev, void *fdt, int offset);
 } PnvXScomInterfaceClass;
 
+/*
+ * Layout of the XSCOM PCB addresses of EX core 1
+ *
+ *   GPIO        0x1100xxxx
+ *   SCOM        0x1101xxxx
+ *   OHA         0x1102xxxx
+ *   CLOCK CTL   0x1103xxxx
+ *   FIR         0x1104xxxx
+ *   THERM       0x1105xxxx
+ *   <reserved>  0x1106xxxx
+ *               ..
+ *               0x110Exxxx
+ *   PCB SLAVE   0x110Fxxxx
+ */
+
+#define PNV_XSCOM_EX_BASE         0x10000000
+#define PNV_XSCOM_EX_CORE_BASE(i) (PNV_XSCOM_EX_BASE | (((uint64_t)i) << 24))
+#define PNV_XSCOM_EX_CORE_SIZE    0x100000
+
 extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
 extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 08/20] ppc/pnv: add a LPC controller
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (6 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 07/20] ppc/pnv: add XSCOM handlers to PnvCore Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-13  2:52   ` David Gibson
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 09/20] ppc/pnv: add a ISA bus Cédric Le Goater
                   ` (12 subsequent siblings)
  20 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

The LPC (Low Pin Count) interface on a POWER8 is made accessible to
the system through the ADU (XSCOM interface). This interface is part
of set of units connected together via a local OPB (On-Chip Peripheral
Bus) which act as a bridge between the ADU and the off chip LPC
endpoints, like external flash modules.

The most important units of this OPB are :
 - OPB Master: contains the ADU slave logic, a set of internal
   registers and the logic to control the OPB.
 - LPCHC (LPC HOST Controller): which implements a OPB Slave, a set of
   internal registers and the LPC HOST Controller to control the LPC
   interface.

Four address spaces are provided to the ADU :
 - LPC Bus Firmware Memory
 - LPC Bus Memory
 - LPC Bus I/O (ISA bus)
 - and the registers for the OPB Master and the LPC Host Controller

On POWER8, an intermediate hop is necessary to reach the OPB, through
a unit called the ECCB. OPB commands are simply mangled in ECCB write
commands.

On POWER9, the OPB master address space can be accessed via MMIO. The
logic is same but the code will be simpler as the XSCOM and ECCB hops
are not necessary anymore.

This version of the LPC controller model doesn't yet implement support
for the SerIRQ deserializer present in the Naples version of the chip
though some preliminary work is there.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
[clg: - updated for qemu-2.7
      - ported on latest PowerNV patchset
      - changed the XSCOM interface to fit new model
      - QOMified the model
      - moved the ISA hunks in another patch
      - removed printf logging
      - added a couple of UNIMP logging
      - rewrote commit log ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---

 Todo:

 - rework the address_space read/write ops as they should be shared
   with the P9 support.

 Changes since v3:

 - rewrote commit log
 - fixed appendprop
 
 hw/ppc/Makefile.objs       |   2 +-
 hw/ppc/pnv.c               |   9 +
 hw/ppc/pnv_lpc.c           | 471 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h       |   3 +
 include/hw/ppc/pnv_lpc.h   |  67 +++++++
 include/hw/ppc/pnv_xscom.h |   3 +
 6 files changed, 554 insertions(+), 1 deletion(-)
 create mode 100644 hw/ppc/pnv_lpc.c
 create mode 100644 include/hw/ppc/pnv_lpc.h

diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index 08c213c40684..ebc72af0a7c6 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
 obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
 obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
 # IBM PowerNV
-obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o
+obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o
 ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
 obj-y += spapr_pci_vfio.o
 endif
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index ffe245fe59d2..e41244294435 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -563,6 +563,9 @@ static void pnv_chip_init(Object *obj)
     PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
 
     chip->xscom_base = pcc->xscom_base;
+
+    object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
+    object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
 }
 
 static void pnv_chip_realize(DeviceState *dev, Error **errp)
@@ -626,6 +629,12 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
                          &PNV_CORE(pnv_core)->xscom_regs);
     }
     g_free(typename);
+
+    /* Create LPC controller */
+    object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
+                             &error_fatal);
+    memory_region_add_subregion(&chip->xscom, PNV_XSCOM_LPC_BASE << 3,
+                                &chip->lpc.xscom_regs);
 }
 
 static Property pnv_chip_properties[] = {
diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
new file mode 100644
index 000000000000..210cc1cff167
--- /dev/null
+++ b/hw/ppc/pnv_lpc.c
@@ -0,0 +1,471 @@
+/*
+ * QEMU PowerPC PowerNV LPC controller
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "sysemu/sysemu.h"
+#include "target-ppc/cpu.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+
+#include "hw/ppc/pnv_lpc.h"
+#include "hw/ppc/pnv.h"
+#include "hw/ppc/fdt.h"
+
+#include <libfdt.h>
+
+enum {
+    ECCB_CTL    = 0,
+    ECCB_RESET  = 1,
+    ECCB_STAT   = 2,
+    ECCB_DATA   = 3,
+};
+
+/* OPB Master LS registers */
+#define OPB_MASTER_LS_IRQ_STAT  0x50
+#define   OPB_MASTER_IRQ_LPC            0x00000800
+#define OPB_MASTER_LS_IRQ_MASK  0x54
+#define OPB_MASTER_LS_IRQ_POL   0x58
+#define OPB_MASTER_LS_IRQ_INPUT 0x5c
+
+/* LPC HC registers */
+#define LPC_HC_FW_SEG_IDSEL     0x24
+#define LPC_HC_FW_RD_ACC_SIZE   0x28
+#define   LPC_HC_FW_RD_1B               0x00000000
+#define   LPC_HC_FW_RD_2B               0x01000000
+#define   LPC_HC_FW_RD_4B               0x02000000
+#define   LPC_HC_FW_RD_16B              0x04000000
+#define   LPC_HC_FW_RD_128B             0x07000000
+#define LPC_HC_IRQSER_CTRL      0x30
+#define   LPC_HC_IRQSER_EN              0x80000000
+#define   LPC_HC_IRQSER_QMODE           0x40000000
+#define   LPC_HC_IRQSER_START_MASK      0x03000000
+#define   LPC_HC_IRQSER_START_4CLK      0x00000000
+#define   LPC_HC_IRQSER_START_6CLK      0x01000000
+#define   LPC_HC_IRQSER_START_8CLK      0x02000000
+#define LPC_HC_IRQMASK          0x34    /* same bit defs as LPC_HC_IRQSTAT */
+#define LPC_HC_IRQSTAT          0x38
+#define   LPC_HC_IRQ_SERIRQ0            0x80000000 /* all bits down to ... */
+#define   LPC_HC_IRQ_SERIRQ16           0x00008000 /* IRQ16=IOCHK#, IRQ2=SMI# */
+#define   LPC_HC_IRQ_SERIRQ_ALL         0xffff8000
+#define   LPC_HC_IRQ_LRESET             0x00000400
+#define   LPC_HC_IRQ_SYNC_ABNORM_ERR    0x00000080
+#define   LPC_HC_IRQ_SYNC_NORESP_ERR    0x00000040
+#define   LPC_HC_IRQ_SYNC_NORM_ERR      0x00000020
+#define   LPC_HC_IRQ_SYNC_TIMEOUT_ERR   0x00000010
+#define   LPC_HC_IRQ_SYNC_TARG_TAR_ERR  0x00000008
+#define   LPC_HC_IRQ_SYNC_BM_TAR_ERR    0x00000004
+#define   LPC_HC_IRQ_SYNC_BM0_REQ       0x00000002
+#define   LPC_HC_IRQ_SYNC_BM1_REQ       0x00000001
+#define LPC_HC_ERROR_ADDRESS    0x40
+
+#define LPC_OPB_SIZE            0x100000000ull
+
+#define ISA_IO_SIZE             0x00010000
+#define ISA_MEM_SIZE            0x10000000
+#define LPC_IO_OPB_ADDR         0xd0010000
+#define LPC_IO_OPB_SIZE         0x00010000
+#define LPC_MEM_OPB_ADDR        0xe0010000
+#define LPC_MEM_OPB_SIZE        0x10000000
+#define LPC_FW_OPB_ADDR         0xf0000000
+#define LPC_FW_OPB_SIZE         0x10000000
+
+#define LPC_OPB_REGS_OPB_ADDR   0xc0010000
+#define LPC_OPB_REGS_OPB_SIZE   0x00002000
+#define LPC_HC_REGS_OPB_ADDR    0xc0012000
+#define LPC_HC_REGS_OPB_SIZE    0x00001000
+
+
+/*
+ * TODO: the "primary" cell should only be added on chip 0. This is
+ * how skiboot chooses the default LPC controller on multichip
+ * systems.
+ *
+ * It would be easly done if we can change the populate() interface to
+ * replace the PnvXScomInterface parameter by a PnvChip one
+ */
+static int pnv_lpc_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset)
+{
+    const char compat[] = "ibm,power8-lpc\0ibm,lpc";
+    char *name;
+    int offset;
+    uint32_t lpc_pcba = PNV_XSCOM_LPC_BASE;
+    uint32_t reg[] = {
+        cpu_to_be32(lpc_pcba),
+        cpu_to_be32(PNV_XSCOM_LPC_SIZE)
+    };
+
+    name = g_strdup_printf("isa@%x", lpc_pcba);
+    offset = fdt_add_subnode(fdt, xscom_offset, name);
+    _FDT(offset);
+    g_free(name);
+
+    _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
+    _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2)));
+    _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1)));
+    _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0)));
+    _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat))));
+    return 0;
+}
+
+/*
+ * These read/write handlers of the OPB address space should be common
+ * with the P9 LPC Controller which uses direct MMIOs.
+ *
+ * TODO: rework to use address_space_stq() and address_space_ldq()
+ * instead.
+ */
+static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data,
+                     int sz)
+{
+    bool success;
+
+    /* XXX Handle access size limits and FW read caching here */
+    success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED,
+                                data, sz, false);
+
+    return success;
+}
+
+static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data,
+                      int sz)
+{
+    bool success;
+
+    /* XXX Handle access size limits here */
+    success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED,
+                                data, sz, true);
+
+    return success;
+}
+
+#define ECCB_CTL_READ           (1ull << (63 - 15))
+#define ECCB_CTL_SZ_LSH         (63 - 7)
+#define ECCB_CTL_SZ_MASK        (0xfull << ECCB_CTL_SZ_LSH)
+#define ECCB_CTL_ADDR_MASK      0xffffffffu;
+
+#define ECCB_STAT_OP_DONE       (1ull << (63 - 52))
+#define ECCB_STAT_OP_ERR        (1ull << (63 - 52))
+#define ECCB_STAT_RD_DATA_LSH   (63 - 37)
+#define ECCB_STAT_RD_DATA_MASK  (0xffffffff << ECCB_STAT_RD_DATA_LSH)
+
+static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd)
+{
+    /* XXX Check for magic bits at the top, addr size etc... */
+    unsigned int sz = (cmd & ECCB_CTL_SZ_MASK) >> ECCB_CTL_SZ_LSH;
+    uint32_t opb_addr = cmd & ECCB_CTL_ADDR_MASK;
+    uint8_t data[4];
+    bool success;
+
+    if (cmd & ECCB_CTL_READ) {
+        success = opb_read(lpc, opb_addr, data, sz);
+        if (success) {
+            lpc->eccb_stat_reg = ECCB_STAT_OP_DONE |
+                    (((uint64_t)data[0]) << 24 |
+                     ((uint64_t)data[1]) << 16 |
+                     ((uint64_t)data[2]) <<  8 |
+                     ((uint64_t)data[3])) << ECCB_STAT_RD_DATA_LSH;
+        } else {
+            lpc->eccb_stat_reg = ECCB_STAT_OP_DONE |
+                    (0xffffffffull << ECCB_STAT_RD_DATA_LSH);
+        }
+    } else {
+        data[0] = lpc->eccb_data_reg >> 24;
+        data[1] = lpc->eccb_data_reg >> 16;
+        data[2] = lpc->eccb_data_reg >>  8;
+        data[3] = lpc->eccb_data_reg;
+
+        success = opb_write(lpc, opb_addr, data, sz);
+        lpc->eccb_stat_reg = ECCB_STAT_OP_DONE;
+    }
+    /* XXX Which error bit (if any) to signal OPB error ? */
+}
+
+static uint64_t pnv_lpc_xscom_read(void *opaque, hwaddr addr, unsigned size)
+{
+    PnvLpcController *lpc = PNV_LPC(opaque);
+    uint32_t offset = addr >> 3;
+    uint64_t val = 0;
+
+    switch (offset & 3) {
+    case ECCB_CTL:
+    case ECCB_RESET:
+        val = 0;
+        break;
+    case ECCB_STAT:
+        val = lpc->eccb_stat_reg;
+        lpc->eccb_stat_reg = 0;
+        break;
+    case ECCB_DATA:
+        val = ((uint64_t)lpc->eccb_data_reg) << 32;
+        break;
+    }
+    return val;
+}
+
+static void pnv_lpc_xscom_write(void *opaque, hwaddr addr,
+                                uint64_t val, unsigned size)
+{
+    PnvLpcController *lpc = PNV_LPC(opaque);
+    uint32_t offset = addr >> 3;
+
+    switch (offset & 3) {
+    case ECCB_CTL:
+        pnv_lpc_do_eccb(lpc, val);
+        break;
+    case ECCB_RESET:
+        /*  XXXX  */
+        break;
+    case ECCB_STAT:
+        break;
+    case ECCB_DATA:
+        lpc->eccb_data_reg = val >> 32;
+        break;
+    }
+}
+
+static const MemoryRegionOps pnv_lpc_xscom_ops = {
+    .read = pnv_lpc_xscom_read,
+    .write = pnv_lpc_xscom_write,
+    .valid.min_access_size = 8,
+    .valid.max_access_size = 8,
+    .impl.min_access_size = 8,
+    .impl.max_access_size = 8,
+    .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size)
+{
+    PnvLpcController *lpc = opaque;
+    uint64_t val = 0xfffffffffffffffful;
+
+    switch (addr) {
+    case LPC_HC_FW_SEG_IDSEL:
+        val =  lpc->lpc_hc_fw_seg_idsel;
+        break;
+    case LPC_HC_FW_RD_ACC_SIZE:
+        val =  lpc->lpc_hc_fw_rd_acc_size;
+        break;
+    case LPC_HC_IRQSER_CTRL:
+        val =  lpc->lpc_hc_irqser_ctrl;
+        break;
+    case LPC_HC_IRQMASK:
+        val =  lpc->lpc_hc_irqmask;
+        break;
+    case LPC_HC_IRQSTAT:
+        val =  lpc->lpc_hc_irqstat;
+        break;
+    case LPC_HC_ERROR_ADDRESS:
+        val =  lpc->lpc_hc_error_addr;
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%"
+                      HWADDR_PRIx "\n", addr);
+    }
+    return val;
+}
+
+static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val,
+                         unsigned size)
+{
+    PnvLpcController *lpc = opaque;
+
+    /* XXX Filter out reserved bits */
+
+    switch (addr) {
+    case LPC_HC_FW_SEG_IDSEL:
+        /* XXX Actually figure out how that works as this impact
+         * memory regions/aliases
+         */
+        lpc->lpc_hc_fw_seg_idsel = val;
+        break;
+    case LPC_HC_FW_RD_ACC_SIZE:
+        lpc->lpc_hc_fw_rd_acc_size = val;
+        break;
+    case LPC_HC_IRQSER_CTRL:
+        lpc->lpc_hc_irqser_ctrl = val;
+        break;
+    case LPC_HC_IRQMASK:
+        lpc->lpc_hc_irqmask = val;
+        break;
+    case LPC_HC_IRQSTAT:
+        lpc->lpc_hc_irqstat &= ~val;
+        break;
+    case LPC_HC_ERROR_ADDRESS:
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%"
+                      HWADDR_PRIx "\n", addr);
+    }
+}
+
+static const MemoryRegionOps lpc_hc_ops = {
+    .read = lpc_hc_read,
+    .write = lpc_hc_write,
+    .endianness = DEVICE_BIG_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size)
+{
+    PnvLpcController *lpc = opaque;
+    uint64_t val = 0xfffffffffffffffful;
+
+    switch (addr) {
+    case OPB_MASTER_LS_IRQ_STAT:
+        val = lpc->opb_irq_stat;
+        break;
+    case OPB_MASTER_LS_IRQ_MASK:
+        val = lpc->opb_irq_mask;
+        break;
+    case OPB_MASTER_LS_IRQ_POL:
+        val = lpc->opb_irq_pol;
+        break;
+    case OPB_MASTER_LS_IRQ_INPUT:
+        val = lpc->opb_irq_input;
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%"
+                      HWADDR_PRIx "\n", addr);
+    }
+
+    return val;
+}
+
+static void opb_master_write(void *opaque, hwaddr addr,
+                             uint64_t val, unsigned size)
+{
+    PnvLpcController *lpc = opaque;
+
+    switch (addr) {
+    case OPB_MASTER_LS_IRQ_STAT:
+        lpc->opb_irq_stat &= ~val;
+        break;
+    case OPB_MASTER_LS_IRQ_MASK:
+        /* XXX Filter out reserved bits */
+        lpc->opb_irq_mask = val;
+        break;
+    case OPB_MASTER_LS_IRQ_POL:
+        /* XXX Filter out reserved bits */
+        lpc->opb_irq_pol = val;
+        break;
+    case OPB_MASTER_LS_IRQ_INPUT:
+        /* Read only */
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%"
+                      HWADDR_PRIx "\n", addr);
+    }
+}
+
+static const MemoryRegionOps opb_master_ops = {
+    .read = opb_master_read,
+    .write = opb_master_write,
+    .endianness = DEVICE_BIG_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static void pnv_lpc_realize(DeviceState *dev, Error **errp)
+{
+    PnvLpcController *lpc = PNV_LPC(dev);
+
+    /* Reg inits */
+    lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B;
+
+    /* Create address space and backing MR for the OPB bus */
+    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.
+     */
+    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);
+
+    /* Create windows from the OPB space to the ISA space */
+    memory_region_init_alias(&lpc->opb_isa_io, OBJECT(dev), "lpc-isa-io",
+                             &lpc->isa_io, 0, LPC_IO_OPB_SIZE);
+    memory_region_add_subregion(&lpc->opb_mr, LPC_IO_OPB_ADDR,
+                                &lpc->opb_isa_io);
+    memory_region_init_alias(&lpc->opb_isa_mem, OBJECT(dev), "lpc-isa-mem",
+                             &lpc->isa_mem, 0, LPC_MEM_OPB_SIZE);
+    memory_region_add_subregion(&lpc->opb_mr, LPC_MEM_OPB_ADDR,
+                                &lpc->opb_isa_mem);
+    memory_region_init_alias(&lpc->opb_isa_fw, OBJECT(dev), "lpc-isa-fw",
+                             &lpc->isa_mem, 0, LPC_FW_OPB_SIZE);
+    memory_region_add_subregion(&lpc->opb_mr, LPC_FW_OPB_ADDR,
+                                &lpc->opb_isa_fw);
+
+    /* Create MMIO regions for LPC HC and OPB registers */
+    memory_region_init_io(&lpc->opb_master_regs, OBJECT(dev), &opb_master_ops,
+                          lpc, "lpc-opb-master", LPC_OPB_REGS_OPB_SIZE);
+    memory_region_add_subregion(&lpc->opb_mr, LPC_OPB_REGS_OPB_ADDR,
+                                &lpc->opb_master_regs);
+    memory_region_init_io(&lpc->lpc_hc_regs, OBJECT(dev), &lpc_hc_ops, lpc,
+                          "lpc-hc", LPC_HC_REGS_OPB_SIZE);
+    memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR,
+                                &lpc->lpc_hc_regs);
+
+    /* XScom region for LPC registers */
+    memory_region_init_io(&lpc->xscom_regs, OBJECT(dev),
+                          &pnv_lpc_xscom_ops, lpc, "xscom-lpc",
+                          PNV_XSCOM_LPC_SIZE << 3);
+}
+
+static void pnv_lpc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
+
+    xdc->populate = pnv_lpc_populate;
+
+    dc->realize = pnv_lpc_realize;
+}
+
+static const TypeInfo pnv_lpc_info = {
+    .name          = TYPE_PNV_LPC,
+    .parent        = TYPE_DEVICE,
+    .instance_size = sizeof(PnvLpcController),
+    .class_init    = pnv_lpc_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_PNV_XSCOM_INTERFACE },
+        { }
+    }
+};
+
+static void pnv_lpc_register_types(void)
+{
+    type_register_static(&pnv_lpc_info);
+}
+
+type_init(pnv_lpc_register_types)
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index f89eddb6e5e0..e586ff4e735e 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -22,6 +22,7 @@
 #include "hw/boards.h"
 #include "hw/sysbus.h"
 #include "hw/ppc/pnv_xscom.h"
+#include "hw/ppc/pnv_lpc.h"
 
 #define TYPE_PNV_CHIP "powernv-chip"
 #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
@@ -52,6 +53,8 @@ typedef struct PnvChip {
     uint32_t  nr_cores;
     uint64_t  cores_mask;
     void      *cores;
+
+    PnvLpcController lpc;
 } PnvChip;
 
 typedef struct PnvChipClass {
diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h
new file mode 100644
index 000000000000..38e5506975aa
--- /dev/null
+++ b/include/hw/ppc/pnv_lpc.h
@@ -0,0 +1,67 @@
+/*
+ * QEMU PowerPC PowerNV LPC controller
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _PPC_PNV_LPC_H
+#define _PPC_PNV_LPC_H
+
+#define TYPE_PNV_LPC "pnv-lpc"
+#define PNV_LPC(obj) \
+     OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC)
+
+typedef struct PnvLpcController {
+    DeviceState parent;
+
+    uint64_t eccb_stat_reg;
+    uint32_t eccb_data_reg;
+
+    /* OPB bus */
+    MemoryRegion opb_mr;
+    AddressSpace opb_as;
+
+    /* ISA IO and Memory space */
+    MemoryRegion isa_io;
+    MemoryRegion isa_mem;
+
+    /* Windows from OPB to ISA (aliases) */
+    MemoryRegion opb_isa_io;
+    MemoryRegion opb_isa_mem;
+    MemoryRegion opb_isa_fw;
+
+    /* Registers */
+    MemoryRegion lpc_hc_regs;
+    MemoryRegion opb_master_regs;
+
+    /* OPB Master LS registers */
+    uint32_t opb_irq_stat;
+    uint32_t opb_irq_mask;
+    uint32_t opb_irq_pol;
+    uint32_t opb_irq_input;
+
+    /* LPC HC registers */
+    uint32_t lpc_hc_fw_seg_idsel;
+    uint32_t lpc_hc_fw_rd_acc_size;
+    uint32_t lpc_hc_irqser_ctrl;
+    uint32_t lpc_hc_irqmask;
+    uint32_t lpc_hc_irqstat;
+    uint32_t lpc_hc_error_addr;
+
+    /* XSCOM registers */
+    MemoryRegion xscom_regs;
+} PnvLpcController;
+
+#endif /* _PPC_PNV_LPC_H */
diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
index 79975a6cbe46..3b8a8b4fc6ff 100644
--- a/include/hw/ppc/pnv_xscom.h
+++ b/include/hw/ppc/pnv_xscom.h
@@ -60,6 +60,9 @@ typedef struct PnvXScomInterfaceClass {
 #define PNV_XSCOM_EX_CORE_BASE(i) (PNV_XSCOM_EX_BASE | (((uint64_t)i) << 24))
 #define PNV_XSCOM_EX_CORE_SIZE    0x100000
 
+#define PNV_XSCOM_LPC_BASE        0xb0020
+#define PNV_XSCOM_LPC_SIZE        0x4
+
 extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
 extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 09/20] ppc/pnv: add a ISA bus
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (7 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 08/20] ppc/pnv: add a LPC controller Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-13  2:58   ` David Gibson
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 10/20] ppc/xics: Make the ICSState a list Cédric Le Goater
                   ` (11 subsequent siblings)
  20 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

As Qemu only supports a single instance of the ISA bus, we use the LPC
controller of chip 0 to create one and plug in a couple of useful
devices, like an UART and RTC. An IPMI BT device, which is also an ISA
device, can be defined on the command line to connect an external BMC.
That is for later.

The PowerNV machine now has a console. Skiboot should load a kernel
and jump into it but execution will stop quite early because we lack a
model for the native XICS controller for the moment :

    [    0.000000] NR_IRQS:512 nr_irqs:512 16
    [    0.000000] XICS: Cannot find a Presentation Controller !
    [    0.000000] ------------[ cut here ]------------
    [    0.000000] WARNING: at arch/powerpc/platforms/powernv/setup.c:81
    ...
    [    0.000000] NIP [c00000000079d65c] pnv_init_IRQ+0x30/0x44

You can still do a few things under xmon.

Based on previous work from :
      Benjamin Herrenschmidt <benh@kernel.crashing.org>

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/ppc/pnv.c         | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h |  2 ++
 2 files changed, 67 insertions(+)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index e41244294435..4a71b18bf38b 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -34,6 +34,10 @@
 
 #include "hw/ppc/pnv_xscom.h"
 
+#include "hw/isa/isa.h"
+#include "hw/char/serial.h"
+#include "hw/timer/mc146818rtc.h"
+
 #include <libfdt.h>
 
 #define FDT_MAX_SIZE            0x00100000
@@ -302,6 +306,58 @@ static void ppc_powernv_reset(void)
     cpu_physical_memory_write(POWERNV_FDT_ADDR, fdt, fdt_totalsize(fdt));
 }
 
+/* If we don't use the built-in LPC interrupt deserializer, we need
+ * to provide a set of qirqs for the ISA bus or things will go bad.
+ *
+ * Most machines using pre-Naples chips (without said deserializer)
+ * have a CPLD that will collect the SerIRQ and shoot them as a
+ * single level interrupt to the P8 chip. So let's setup a hook
+ * for doing just that.
+ *
+ * Note: The actual interrupt input isn't emulated yet, this will
+ * come with the PSI bridge model.
+ */
+static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
+{
+    /* We don't yet emulate the PSI bridge which provides the external
+     * interrupt, so just drop interrupts on the floor
+     */
+}
+
+static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
+{
+     /* XXX TODO */
+}
+
+static ISABus *pnv_isa_create(PnvChip *chip)
+{
+    PnvLpcController *lpc = &chip->lpc;
+    ISABus *isa_bus;
+    qemu_irq *irqs;
+    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+
+    /* let isa_bus_new() create its own bridge on SysBus otherwise
+     * devices speficied on the command line won't find the bus and
+     * will fail to create.
+     */
+    isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io,
+                          &error_fatal);
+
+    /* Not all variants have a working serial irq decoder. If not,
+     * handling of LPC interrupts becomes a platform issue (some
+     * platforms have a CPLD to do it).
+     */
+    if (pcc->chip_type == PNV_CHIP_POWER8NVL) {
+        irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler, lpc, ISA_NUM_IRQS);
+    } else {
+        irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, NULL,
+                                  ISA_NUM_IRQS);
+    }
+
+    isa_bus_irqs(isa_bus, irqs);
+    return isa_bus;
+}
+
 static void ppc_powernv_init(MachineState *machine)
 {
     PnvMachineState *pnv = POWERNV_MACHINE(machine);
@@ -390,6 +446,15 @@ static void ppc_powernv_init(MachineState *machine)
         object_property_set_bool(chip, true, "realized", &error_fatal);
     }
     g_free(chip_typename);
+
+    /* Instantiate ISA bus on chip 0 */
+    pnv->isa_bus = pnv_isa_create(pnv->chips[0]);
+
+    /* Create serial port */
+    serial_hds_isa_init(pnv->isa_bus, MAX_SERIAL_PORTS);
+
+    /* Create an RTC ISA device too */
+    rtc_init(pnv->isa_bus, 2000, NULL);
 }
 
 /*
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index e586ff4e735e..617c3fdd4f06 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -110,6 +110,8 @@ typedef struct PnvMachineState {
 
     uint32_t  num_chips;
     PnvChip   **chips;
+
+    ISABus *isa_bus;
 } PnvMachineState;
 
 #define POWERNV_FDT_ADDR                0x01000000
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 10/20] ppc/xics: Make the ICSState a list
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (8 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 09/20] ppc/pnv: add a ISA bus Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-14  5:32   ` David Gibson
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 11/20] ppc/xics: Split ICS into ics-base and ics class Cédric Le Goater
                   ` (10 subsequent siblings)
  20 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel,
	Cedric Le Goater, Nikunj A Dadhania

From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

Instead of an array of fixed sized blocks, use a list, as we will need
to have sources with variable number of interrupts. SPAPR only uses
a single entry. Native will create more. If performance becomes an
issue we can add some hashed lookup but for now this will do fine.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
[ move the initialization of list to xics_common_initfn,
  restore xirr_owner after migration and move restoring to
  icp_post_load]
Signed-off-by: Nikunj A Dadhania <nikunj@linux.vnet.ibm.com>
[ clg: removed the icp_post_load() changes from nikunj patchset v3:
       http://patchwork.ozlabs.org/patch/646008/ ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/intc/trace-events  |  5 +--
 hw/intc/xics.c        | 83 ++++++++++++++++++++++++++++--------------------
 hw/intc/xics_kvm.c    | 27 +++++++++++-----
 hw/intc/xics_spapr.c  | 88 +++++++++++++++++++++++++++++++++------------------
 hw/ppc/spapr_events.c |  2 +-
 hw/ppc/spapr_pci.c    |  5 ++-
 hw/ppc/spapr_vio.c    |  2 +-
 include/hw/ppc/xics.h | 13 ++++----
 8 files changed, 139 insertions(+), 86 deletions(-)

diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index f12192c082b5..aa342a86fa6c 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -56,10 +56,11 @@ xics_set_irq_lsi(int srcno, int nr) "set_irq_lsi: srcno %d [irq %#x]"
 xics_ics_write_xive(int nr, int srcno, int server, uint8_t priority) "ics_write_xive: irq %#x [src %d] server %#x prio %#x"
 xics_ics_reject(int nr, int srcno) "reject irq %#x [src %d]"
 xics_ics_eoi(int nr) "ics_eoi: irq %#x"
-xics_alloc(int src, int irq) "source#%d, irq %d"
-xics_alloc_block(int src, int first, int num, bool lsi, int align) "source#%d, first irq %d, %d irqs, lsi=%d, alignnum %d"
+xics_alloc(int irq) "irq %d"
+xics_alloc_block(int first, int num, bool lsi, int align) "first irq %d, %d irqs, lsi=%d, alignnum %d"
 xics_ics_free(int src, int irq, int num) "Source#%d, first irq %d, %d irqs"
 xics_ics_free_warn(int src, int irq) "Source#%d, irq %d is already free"
+xics_icp_post_load(uint32_t server_no, uint32_t xirr, uint64_t addr, uint8_t pend) "server_no %d, xirr %#x, xirr_owner 0x%" PRIx64 ", pending %d"
 
 # hw/intc/s390_flic_kvm.c
 flic_create_device(int err) "flic: create device failed %d"
diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index 69162f0328fa..433af93254fb 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -96,13 +96,16 @@ void xics_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
 static void xics_common_reset(DeviceState *d)
 {
     XICSState *xics = XICS_COMMON(d);
+    ICSState *ics;
     int i;
 
     for (i = 0; i < xics->nr_servers; i++) {
         device_reset(DEVICE(&xics->ss[i]));
     }
 
-    device_reset(DEVICE(xics->ics));
+    QLIST_FOREACH(ics, &xics->ics, list) {
+        device_reset(DEVICE(ics));
+    }
 }
 
 static void xics_prop_get_nr_irqs(Object *obj, Visitor *v, const char *name,
@@ -134,7 +137,6 @@ static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, const char *name,
     }
 
     assert(info->set_nr_irqs);
-    assert(xics->ics);
     info->set_nr_irqs(xics, value, errp);
 }
 
@@ -174,6 +176,9 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
 
 static void xics_common_initfn(Object *obj)
 {
+    XICSState *xics = XICS_COMMON(obj);
+
+    QLIST_INIT(&xics->ics);
     object_property_add(obj, "nr_irqs", "int",
                         xics_prop_get_nr_irqs, xics_prop_set_nr_irqs,
                         NULL, NULL, NULL);
@@ -212,33 +217,35 @@ static void ics_reject(ICSState *ics, int nr);
 static void ics_resend(ICSState *ics);
 static void ics_eoi(ICSState *ics, int nr);
 
-static void icp_check_ipi(XICSState *xics, int server)
+static void icp_check_ipi(ICPState *ss)
 {
-    ICPState *ss = xics->ss + server;
-
     if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) {
         return;
     }
 
-    trace_xics_icp_check_ipi(server, ss->mfrr);
+    trace_xics_icp_check_ipi(ss->cs->cpu_index, ss->mfrr);
 
-    if (XISR(ss)) {
-        ics_reject(xics->ics, XISR(ss));
+    if (XISR(ss) && ss->xirr_owner) {
+        ics_reject(ss->xirr_owner, XISR(ss));
     }
 
     ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI;
     ss->pending_priority = ss->mfrr;
+    ss->xirr_owner = NULL;
     qemu_irq_raise(ss->output);
 }
 
 static void icp_resend(XICSState *xics, int server)
 {
     ICPState *ss = xics->ss + server;
+    ICSState *ics;
 
     if (ss->mfrr < CPPR(ss)) {
-        icp_check_ipi(xics, server);
+        icp_check_ipi(ss);
+    }
+    QLIST_FOREACH(ics, &xics->ics, list) {
+        ics_resend(ics);
     }
-    ics_resend(xics->ics);
 }
 
 void icp_set_cppr(XICSState *xics, int server, uint8_t cppr)
@@ -256,7 +263,10 @@ void icp_set_cppr(XICSState *xics, int server, uint8_t cppr)
             ss->xirr &= ~XISR_MASK; /* Clear XISR */
             ss->pending_priority = 0xff;
             qemu_irq_lower(ss->output);
-            ics_reject(xics->ics, old_xisr);
+            if (ss->xirr_owner) {
+                ics_reject(ss->xirr_owner, old_xisr);
+                ss->xirr_owner = NULL;
+            }
         }
     } else {
         if (!XISR(ss)) {
@@ -271,7 +281,7 @@ void icp_set_mfrr(XICSState *xics, int server, uint8_t mfrr)
 
     ss->mfrr = mfrr;
     if (mfrr < CPPR(ss)) {
-        icp_check_ipi(xics, server);
+        icp_check_ipi(ss);
     }
 }
 
@@ -282,6 +292,7 @@ uint32_t icp_accept(ICPState *ss)
     qemu_irq_lower(ss->output);
     ss->xirr = ss->pending_priority << 24;
     ss->pending_priority = 0xff;
+    ss->xirr_owner = NULL;
 
     trace_xics_icp_accept(xirr, ss->xirr);
 
@@ -299,30 +310,40 @@ uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr)
 void icp_eoi(XICSState *xics, int server, uint32_t xirr)
 {
     ICPState *ss = xics->ss + server;
+    ICSState *ics;
+    uint32_t irq;
 
     /* Send EOI -> ICS */
     ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK);
     trace_xics_icp_eoi(server, xirr, ss->xirr);
-    ics_eoi(xics->ics, xirr & XISR_MASK);
+    irq = xirr & XISR_MASK;
+    QLIST_FOREACH(ics, &xics->ics, list) {
+        if (ics_valid_irq(ics, irq)) {
+            ics_eoi(ics, irq);
+        }
+    }
     if (!XISR(ss)) {
         icp_resend(xics, server);
     }
 }
 
-static void icp_irq(XICSState *xics, int server, int nr, uint8_t priority)
+static void icp_irq(ICSState *ics, int server, int nr, uint8_t priority)
 {
+    XICSState *xics = ics->xics;
     ICPState *ss = xics->ss + server;
 
     trace_xics_icp_irq(server, nr, priority);
 
     if ((priority >= CPPR(ss))
         || (XISR(ss) && (ss->pending_priority <= priority))) {
-        ics_reject(xics->ics, nr);
+        ics_reject(ics, nr);
     } else {
-        if (XISR(ss)) {
-            ics_reject(xics->ics, XISR(ss));
+        if (XISR(ss) && ss->xirr_owner) {
+            ics_reject(ss->xirr_owner, XISR(ss));
+            ss->xirr_owner = NULL;
         }
         ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK);
+        ss->xirr_owner = ics;
         ss->pending_priority = priority;
         trace_xics_icp_raise(ss->xirr, ss->pending_priority);
         qemu_irq_raise(ss->output);
@@ -405,8 +426,7 @@ static void resend_msi(ICSState *ics, int srcno)
     if (irq->status & XICS_STATUS_REJECTED) {
         irq->status &= ~XICS_STATUS_REJECTED;
         if (irq->priority != 0xff) {
-            icp_irq(ics->xics, irq->server, srcno + ics->offset,
-                    irq->priority);
+            icp_irq(ics, irq->server, srcno + ics->offset, irq->priority);
         }
     }
 }
@@ -419,7 +439,7 @@ static void resend_lsi(ICSState *ics, int srcno)
         && (irq->status & XICS_STATUS_ASSERTED)
         && !(irq->status & XICS_STATUS_SENT)) {
         irq->status |= XICS_STATUS_SENT;
-        icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
+        icp_irq(ics, irq->server, srcno + ics->offset, irq->priority);
     }
 }
 
@@ -434,7 +454,7 @@ static void set_irq_msi(ICSState *ics, int srcno, int val)
             irq->status |= XICS_STATUS_MASKED_PENDING;
             trace_xics_masked_pending();
         } else  {
-            icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
+            icp_irq(ics, irq->server, srcno + ics->offset, irq->priority);
         }
     }
 }
@@ -473,7 +493,7 @@ static void write_xive_msi(ICSState *ics, int srcno)
     }
 
     irq->status &= ~XICS_STATUS_MASKED_PENDING;
-    icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
+    icp_irq(ics, irq->server, srcno + ics->offset, irq->priority);
 }
 
 static void write_xive_lsi(ICSState *ics, int srcno)
@@ -662,28 +682,23 @@ static const TypeInfo ics_info = {
 /*
  * Exported functions
  */
-int xics_find_source(XICSState *xics, int irq)
+ICSState *xics_find_source(XICSState *xics, int irq)
 {
-    int sources = 1;
-    int src;
+    ICSState *ics;
 
-    /* FIXME: implement multiple sources */
-    for (src = 0; src < sources; ++src) {
-        ICSState *ics = &xics->ics[src];
+    QLIST_FOREACH(ics, &xics->ics, list) {
         if (ics_valid_irq(ics, irq)) {
-            return src;
+            return ics;
         }
     }
-
-    return -1;
+    return NULL;
 }
 
 qemu_irq xics_get_qirq(XICSState *xics, int irq)
 {
-    int src = xics_find_source(xics, irq);
+    ICSState *ics = xics_find_source(xics, irq);
 
-    if (src >= 0) {
-        ICSState *ics = &xics->ics[src];
+    if (ics) {
         return ics->qirqs[irq - ics->offset];
     }
 
diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c
index c9caefcf2b0b..843905cfbd37 100644
--- a/hw/intc/xics_kvm.c
+++ b/hw/intc/xics_kvm.c
@@ -361,7 +361,13 @@ static void xics_kvm_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
 static void xics_kvm_set_nr_irqs(XICSState *xics, uint32_t nr_irqs,
                                  Error **errp)
 {
-    xics->nr_irqs = xics->ics->nr_irqs = nr_irqs;
+    ICSState *ics = QLIST_FIRST(&xics->ics);
+
+    /* This needs to be deprecated ... */
+    xics->nr_irqs = nr_irqs;
+    if (ics) {
+        ics->nr_irqs = nr_irqs;
+    }
 }
 
 static void xics_kvm_set_nr_servers(XICSState *xics, uint32_t nr_servers,
@@ -394,6 +400,7 @@ static void xics_kvm_realize(DeviceState *dev, Error **errp)
 {
     KVMXICSState *xicskvm = XICS_SPAPR_KVM(dev);
     XICSState *xics = XICS_COMMON(dev);
+    ICSState *ics;
     int i, rc;
     Error *error = NULL;
     struct kvm_create_device xics_create_device = {
@@ -445,10 +452,12 @@ static void xics_kvm_realize(DeviceState *dev, Error **errp)
 
     xicskvm->kernel_xics_fd = xics_create_device.fd;
 
-    object_property_set_bool(OBJECT(xics->ics), true, "realized", &error);
-    if (error) {
-        error_propagate(errp, error);
-        goto fail;
+    QLIST_FOREACH(ics, &xics->ics, list) {
+        object_property_set_bool(OBJECT(ics), true, "realized", &error);
+        if (error) {
+            error_propagate(errp, error);
+            goto fail;
+        }
     }
 
     assert(xics->nr_servers);
@@ -477,10 +486,12 @@ fail:
 static void xics_kvm_initfn(Object *obj)
 {
     XICSState *xics = XICS_COMMON(obj);
+    ICSState *ics;
 
-    xics->ics = ICS(object_new(TYPE_KVM_ICS));
-    object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
-    xics->ics->xics = xics;
+    ics = ICS(object_new(TYPE_KVM_ICS));
+    object_property_add_child(obj, "ics", OBJECT(ics), NULL);
+    ics->xics = xics;
+    QLIST_INSERT_HEAD(&xics->ics, ics, list);
 }
 
 static void xics_kvm_class_init(ObjectClass *oc, void *data)
diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c
index 618826dacf0b..0b0845d6280f 100644
--- a/hw/intc/xics_spapr.c
+++ b/hw/intc/xics_spapr.c
@@ -113,13 +113,17 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                           uint32_t nargs, target_ulong args,
                           uint32_t nret, target_ulong rets)
 {
-    ICSState *ics = spapr->xics->ics;
+    ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
     uint32_t nr, server, priority;
 
     if ((nargs != 3) || (nret != 1)) {
         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
         return;
     }
+    if (!ics) {
+        rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
+        return;
+    }
 
     nr = rtas_ld(args, 0);
     server = xics_get_cpu_index_by_dt_id(rtas_ld(args, 1));
@@ -141,13 +145,17 @@ static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                           uint32_t nargs, target_ulong args,
                           uint32_t nret, target_ulong rets)
 {
-    ICSState *ics = spapr->xics->ics;
+    ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
     uint32_t nr;
 
     if ((nargs != 1) || (nret != 3)) {
         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
         return;
     }
+    if (!ics) {
+        rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
+        return;
+    }
 
     nr = rtas_ld(args, 0);
 
@@ -166,13 +174,17 @@ static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                          uint32_t nargs, target_ulong args,
                          uint32_t nret, target_ulong rets)
 {
-    ICSState *ics = spapr->xics->ics;
+    ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
     uint32_t nr;
 
     if ((nargs != 1) || (nret != 1)) {
         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
         return;
     }
+    if (!ics) {
+        rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
+        return;
+    }
 
     nr = rtas_ld(args, 0);
 
@@ -192,13 +204,17 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                         uint32_t nargs, target_ulong args,
                         uint32_t nret, target_ulong rets)
 {
-    ICSState *ics = spapr->xics->ics;
+    ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
     uint32_t nr;
 
     if ((nargs != 1) || (nret != 1)) {
         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
         return;
     }
+    if (!ics) {
+        rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
+        return;
+    }
 
     nr = rtas_ld(args, 0);
 
@@ -217,7 +233,13 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr,
 static void xics_spapr_set_nr_irqs(XICSState *xics, uint32_t nr_irqs,
                                    Error **errp)
 {
-    xics->nr_irqs = xics->ics->nr_irqs = nr_irqs;
+    ICSState *ics = QLIST_FIRST(&xics->ics);
+
+    /* This needs to be deprecated ... */
+    xics->nr_irqs = nr_irqs;
+    if (ics) {
+        ics->nr_irqs = nr_irqs;
+    }
 }
 
 static void xics_spapr_set_nr_servers(XICSState *xics, uint32_t nr_servers,
@@ -240,6 +262,7 @@ static void xics_spapr_set_nr_servers(XICSState *xics, uint32_t nr_servers,
 static void xics_spapr_realize(DeviceState *dev, Error **errp)
 {
     XICSState *xics = XICS_SPAPR(dev);
+    ICSState *ics;
     Error *error = NULL;
     int i;
 
@@ -261,10 +284,12 @@ static void xics_spapr_realize(DeviceState *dev, Error **errp)
     spapr_register_hypercall(H_EOI, h_eoi);
     spapr_register_hypercall(H_IPOLL, h_ipoll);
 
-    object_property_set_bool(OBJECT(xics->ics), true, "realized", &error);
-    if (error) {
-        error_propagate(errp, error);
-        return;
+    QLIST_FOREACH(ics, &xics->ics, list) {
+        object_property_set_bool(OBJECT(ics), true, "realized", &error);
+        if (error) {
+            error_propagate(errp, error);
+            return;
+        }
     }
 
     for (i = 0; i < xics->nr_servers; i++) {
@@ -280,10 +305,12 @@ static void xics_spapr_realize(DeviceState *dev, Error **errp)
 static void xics_spapr_initfn(Object *obj)
 {
     XICSState *xics = XICS_SPAPR(obj);
+    ICSState *ics;
 
-    xics->ics = ICS(object_new(TYPE_ICS));
-    object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
-    xics->ics->xics = xics;
+    ics = ICS(object_new(TYPE_ICS));
+    object_property_add_child(obj, "ics", OBJECT(ics), NULL);
+    ics->xics = xics;
+    QLIST_INSERT_HEAD(&xics->ics, ics, list);
 }
 
 static void xics_spapr_class_init(ObjectClass *oc, void *data)
@@ -329,14 +356,15 @@ static int ics_find_free_block(ICSState *ics, int num, int alignnum)
     return -1;
 }
 
-int xics_spapr_alloc(XICSState *xics, int src, int irq_hint, bool lsi,
-                     Error **errp)
+int xics_spapr_alloc(XICSState *xics, int irq_hint, bool lsi, Error **errp)
 {
-    ICSState *ics = &xics->ics[src];
+    ICSState *ics = QLIST_FIRST(&xics->ics);
     int irq;
 
+    if (!ics) {
+        return -1;
+    }
     if (irq_hint) {
-        assert(src == xics_find_source(xics, irq_hint));
         if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) {
             error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint);
             return -1;
@@ -352,7 +380,7 @@ int xics_spapr_alloc(XICSState *xics, int src, int irq_hint, bool lsi,
     }
 
     ics_set_irq_type(ics, irq - ics->offset, lsi);
-    trace_xics_alloc(src, irq);
+    trace_xics_alloc(irq);
 
     return irq;
 }
@@ -361,13 +389,16 @@ int xics_spapr_alloc(XICSState *xics, int src, int irq_hint, bool lsi,
  * Allocate block of consecutive IRQs, and return the number of the first IRQ in
  * the block. If align==true, aligns the first IRQ number to num.
  */
-int xics_spapr_alloc_block(XICSState *xics, int src, int num, bool lsi,
-                           bool align, Error **errp)
+int xics_spapr_alloc_block(XICSState *xics, int num, bool lsi, bool align,
+                           Error **errp)
 {
+    ICSState *ics = QLIST_FIRST(&xics->ics);
     int i, first = -1;
-    ICSState *ics = &xics->ics[src];
 
-    assert(src == 0);
+    if (!ics) {
+        return -1;
+    }
+
     /*
      * MSIMesage::data is used for storing VIRQ so
      * it has to be aligned to num to support multiple
@@ -394,7 +425,7 @@ int xics_spapr_alloc_block(XICSState *xics, int src, int num, bool lsi,
     }
     first += ics->offset;
 
-    trace_xics_alloc_block(src, first, num, lsi, align);
+    trace_xics_alloc_block(first, num, lsi, align);
 
     return first;
 }
@@ -405,7 +436,7 @@ static void ics_free(ICSState *ics, int srcno, int num)
 
     for (i = srcno; i < srcno + num; ++i) {
         if (ICS_IRQ_FREE(ics, i)) {
-            trace_xics_ics_free_warn(ics - ics->xics->ics, i + ics->offset);
+            trace_xics_ics_free_warn(0, i + ics->offset);
         }
         memset(&ics->irqs[i], 0, sizeof(ICSIRQState));
     }
@@ -413,15 +444,10 @@ static void ics_free(ICSState *ics, int srcno, int num)
 
 void xics_spapr_free(XICSState *xics, int irq, int num)
 {
-    int src = xics_find_source(xics, irq);
-
-    if (src >= 0) {
-        ICSState *ics = &xics->ics[src];
-
-        /* FIXME: implement multiple sources */
-        assert(src == 0);
+    ICSState *ics = xics_find_source(xics, irq);
 
-        trace_xics_ics_free(ics - xics->ics, irq, num);
+    if (ics) {
+        trace_xics_ics_free(0, irq, num);
         ics_free(ics, irq - ics->offset, num);
     }
 }
diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c
index 4c7b6aeab630..6d3534541c85 100644
--- a/hw/ppc/spapr_events.c
+++ b/hw/ppc/spapr_events.c
@@ -594,7 +594,7 @@ out_no_events:
 void spapr_events_init(sPAPRMachineState *spapr)
 {
     QTAILQ_INIT(&spapr->pending_events);
-    spapr->check_exception_irq = xics_spapr_alloc(spapr->xics, 0, 0, false,
+    spapr->check_exception_irq = xics_spapr_alloc(spapr->xics, 0, false,
                                             &error_fatal);
     spapr->epow_notifier.notify = spapr_powerdown_req;
     qemu_register_powerdown_notifier(&spapr->epow_notifier);
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 4f008654d673..a7ca98838799 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -363,7 +363,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
     }
 
     /* Allocate MSIs */
-    irq = xics_spapr_alloc_block(spapr->xics, 0, req_num, false,
+    irq = xics_spapr_alloc_block(spapr->xics, req_num, false,
                            ret_intr_type == RTAS_TYPE_MSI, &err);
     if (err) {
         error_reportf_err(err, "Can't allocate MSIs for device %x: ",
@@ -1445,8 +1445,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
         uint32_t irq;
         Error *local_err = NULL;
 
-        irq = xics_spapr_alloc_block(spapr->xics, 0, 1, true, false,
-                                     &local_err);
+        irq = xics_spapr_alloc_block(spapr->xics, 1, true, false, &local_err);
         if (local_err) {
             error_propagate(errp, local_err);
             error_prepend(errp, "can't allocate LSIs: ");
diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c
index d68dd35679bd..3648aa596044 100644
--- a/hw/ppc/spapr_vio.c
+++ b/hw/ppc/spapr_vio.c
@@ -453,7 +453,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
         dev->qdev.id = id;
     }
 
-    dev->irq = xics_spapr_alloc(spapr->xics, 0, dev->irq, false, &local_err);
+    dev->irq = xics_spapr_alloc(spapr->xics, dev->irq, false, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
index 5aac67ad89b8..e49a2dab934a 100644
--- a/include/hw/ppc/xics.h
+++ b/include/hw/ppc/xics.h
@@ -85,7 +85,7 @@ struct XICSState {
     uint32_t nr_servers;
     uint32_t nr_irqs;
     ICPState *ss;
-    ICSState *ics;
+    QLIST_HEAD(, ICSState) ics;
 };
 
 #define TYPE_ICP "icp"
@@ -111,6 +111,7 @@ struct ICPState {
     DeviceState parent_obj;
     /*< public >*/
     CPUState *cs;
+    ICSState *xirr_owner;
     uint32_t xirr;
     uint8_t pending_priority;
     uint8_t mfrr;
@@ -145,6 +146,7 @@ struct ICSState {
     qemu_irq *qirqs;
     ICSIRQState *irqs;
     XICSState *xics;
+    QLIST_ENTRY(ICSState) list;
 };
 
 static inline bool ics_valid_irq(ICSState *ics, uint32_t nr)
@@ -172,10 +174,9 @@ struct ICSIRQState {
 #define XICS_IRQS_SPAPR               1024
 
 qemu_irq xics_get_qirq(XICSState *icp, int irq);
-int xics_spapr_alloc(XICSState *icp, int src, int irq_hint, bool lsi,
-                     Error **errp);
-int xics_spapr_alloc_block(XICSState *icp, int src, int num, bool lsi,
-                           bool align, Error **errp);
+int xics_spapr_alloc(XICSState *icp, int irq_hint, bool lsi, Error **errp);
+int xics_spapr_alloc_block(XICSState *icp, int num, bool lsi, bool align,
+                           Error **errp);
 void xics_spapr_free(XICSState *icp, int irq, int num);
 
 void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu);
@@ -195,6 +196,6 @@ void ics_write_xive(ICSState *ics, int nr, int server,
 
 void ics_set_irq_type(ICSState *ics, int srcno, bool lsi);
 
-int xics_find_source(XICSState *icp, int irq);
+ICSState *xics_find_source(XICSState *icp, int irq);
 
 #endif /* XICS_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 11/20] ppc/xics: Split ICS into ics-base and ics class
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (9 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 10/20] ppc/xics: Make the ICSState a list Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 12/20] ppc/xics: Add xics to the monitor "info pic" command Cédric Le Goater
                   ` (9 subsequent siblings)
  20 siblings, 0 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel,
	Cedric Le Goater, Nikunj A Dadhania

From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

The existing implementation remains same and ics-base is introduced. The
type name "ics" is retained, and all the related functions renamed as
ics_simple_*

This will allow different implementations for the source controllers
such as the MSI support of PHB3 on Power8 which uses in-memory state
tables for example.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Nikunj A Dadhania <nikunj@linux.vnet.ibm.com>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
[ clg: added ICS_BASE_GET_CLASS and related fixes, based on :
       http://patchwork.ozlabs.org/patch/646010/ ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/intc/trace-events  |  10 ++--
 hw/intc/xics.c        | 150 +++++++++++++++++++++++++++++++-------------------
 hw/intc/xics_kvm.c    |  12 ++--
 hw/intc/xics_spapr.c  |  30 +++++-----
 include/hw/ppc/xics.h |  27 +++++----
 5 files changed, 138 insertions(+), 91 deletions(-)

diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index aa342a86fa6c..a367b46b1c20 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -50,12 +50,12 @@ xics_icp_accept(uint32_t old_xirr, uint32_t new_xirr) "icp_accept: XIRR %#"PRIx3
 xics_icp_eoi(int server, uint32_t xirr, uint32_t new_xirr) "icp_eoi: server %d given XIRR %#"PRIx32" new XIRR %#"PRIx32
 xics_icp_irq(int server, int nr, uint8_t priority) "cpu %d trying to deliver irq %#"PRIx32" priority %#x"
 xics_icp_raise(uint32_t xirr, uint8_t pending_priority) "raising IRQ new XIRR=%#x new pending priority=%#x"
-xics_set_irq_msi(int srcno, int nr) "set_irq_msi: srcno %d [irq %#x]"
+xics_ics_simple_set_irq_msi(int srcno, int nr) "set_irq_msi: srcno %d [irq %#x]"
 xics_masked_pending(void) "set_irq_msi: masked pending"
-xics_set_irq_lsi(int srcno, int nr) "set_irq_lsi: srcno %d [irq %#x]"
-xics_ics_write_xive(int nr, int srcno, int server, uint8_t priority) "ics_write_xive: irq %#x [src %d] server %#x prio %#x"
-xics_ics_reject(int nr, int srcno) "reject irq %#x [src %d]"
-xics_ics_eoi(int nr) "ics_eoi: irq %#x"
+xics_ics_simple_set_irq_lsi(int srcno, int nr) "set_irq_lsi: srcno %d [irq %#x]"
+xics_ics_simple_write_xive(int nr, int srcno, int server, uint8_t priority) "ics_write_xive: irq %#x [src %d] server %#x prio %#x"
+xics_ics_simple_reject(int nr, int srcno) "reject irq %#x [src %d]"
+xics_ics_simple_eoi(int nr) "ics_eoi: irq %#x"
 xics_alloc(int irq) "irq %d"
 xics_alloc_block(int first, int num, bool lsi, int align) "first irq %d, %d irqs, lsi=%d, alignnum %d"
 xics_ics_free(int src, int irq, int num) "Source#%d, first irq %d, %d irqs"
diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index 433af93254fb..f40b00003a45 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -213,9 +213,32 @@ static const TypeInfo xics_common_info = {
 #define XISR(ss)   (((ss)->xirr) & XISR_MASK)
 #define CPPR(ss)   (((ss)->xirr) >> 24)
 
-static void ics_reject(ICSState *ics, int nr);
-static void ics_resend(ICSState *ics);
-static void ics_eoi(ICSState *ics, int nr);
+static void ics_reject(ICSState *ics, uint32_t nr)
+{
+    ICSStateClass *k = ICS_BASE_GET_CLASS(ics);
+
+    if (k->reject) {
+        k->reject(ics, nr);
+    }
+}
+
+static void ics_resend(ICSState *ics)
+{
+    ICSStateClass *k = ICS_BASE_GET_CLASS(ics);
+
+    if (k->resend) {
+        k->resend(ics);
+    }
+}
+
+static void ics_eoi(ICSState *ics, int nr)
+{
+    ICSStateClass *k = ICS_BASE_GET_CLASS(ics);
+
+    if (k->eoi) {
+        k->eoi(ics, nr);
+    }
+}
 
 static void icp_check_ipi(ICPState *ss)
 {
@@ -418,7 +441,7 @@ static const TypeInfo icp_info = {
 /*
  * ICS: Source layer
  */
-static void resend_msi(ICSState *ics, int srcno)
+static void ics_simple_resend_msi(ICSState *ics, int srcno)
 {
     ICSIRQState *irq = ics->irqs + srcno;
 
@@ -431,7 +454,7 @@ static void resend_msi(ICSState *ics, int srcno)
     }
 }
 
-static void resend_lsi(ICSState *ics, int srcno)
+static void ics_simple_resend_lsi(ICSState *ics, int srcno)
 {
     ICSIRQState *irq = ics->irqs + srcno;
 
@@ -443,11 +466,11 @@ static void resend_lsi(ICSState *ics, int srcno)
     }
 }
 
-static void set_irq_msi(ICSState *ics, int srcno, int val)
+static void ics_simple_set_irq_msi(ICSState *ics, int srcno, int val)
 {
     ICSIRQState *irq = ics->irqs + srcno;
 
-    trace_xics_set_irq_msi(srcno, srcno + ics->offset);
+    trace_xics_ics_simple_set_irq_msi(srcno, srcno + ics->offset);
 
     if (val) {
         if (irq->priority == 0xff) {
@@ -459,31 +482,31 @@ static void set_irq_msi(ICSState *ics, int srcno, int val)
     }
 }
 
-static void set_irq_lsi(ICSState *ics, int srcno, int val)
+static void ics_simple_set_irq_lsi(ICSState *ics, int srcno, int val)
 {
     ICSIRQState *irq = ics->irqs + srcno;
 
-    trace_xics_set_irq_lsi(srcno, srcno + ics->offset);
+    trace_xics_ics_simple_set_irq_lsi(srcno, srcno + ics->offset);
     if (val) {
         irq->status |= XICS_STATUS_ASSERTED;
     } else {
         irq->status &= ~XICS_STATUS_ASSERTED;
     }
-    resend_lsi(ics, srcno);
+    ics_simple_resend_lsi(ics, srcno);
 }
 
-static void ics_set_irq(void *opaque, int srcno, int val)
+static void ics_simple_set_irq(void *opaque, int srcno, int val)
 {
     ICSState *ics = (ICSState *)opaque;
 
     if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_LSI) {
-        set_irq_lsi(ics, srcno, val);
+        ics_simple_set_irq_lsi(ics, srcno, val);
     } else {
-        set_irq_msi(ics, srcno, val);
+        ics_simple_set_irq_msi(ics, srcno, val);
     }
 }
 
-static void write_xive_msi(ICSState *ics, int srcno)
+static void ics_simple_write_xive_msi(ICSState *ics, int srcno)
 {
     ICSIRQState *irq = ics->irqs + srcno;
 
@@ -496,35 +519,35 @@ static void write_xive_msi(ICSState *ics, int srcno)
     icp_irq(ics, irq->server, srcno + ics->offset, irq->priority);
 }
 
-static void write_xive_lsi(ICSState *ics, int srcno)
+static void ics_simple_write_xive_lsi(ICSState *ics, int srcno)
 {
-    resend_lsi(ics, srcno);
+    ics_simple_resend_lsi(ics, srcno);
 }
 
-void ics_write_xive(ICSState *ics, int nr, int server,
-                    uint8_t priority, uint8_t saved_priority)
+void ics_simple_write_xive(ICSState *ics, int srcno, int server,
+                           uint8_t priority, uint8_t saved_priority)
 {
-    int srcno = nr - ics->offset;
     ICSIRQState *irq = ics->irqs + srcno;
 
     irq->server = server;
     irq->priority = priority;
     irq->saved_priority = saved_priority;
 
-    trace_xics_ics_write_xive(nr, srcno, server, priority);
+    trace_xics_ics_simple_write_xive(ics->offset + srcno, srcno, server,
+                                     priority);
 
     if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_LSI) {
-        write_xive_lsi(ics, srcno);
+        ics_simple_write_xive_lsi(ics, srcno);
     } else {
-        write_xive_msi(ics, srcno);
+        ics_simple_write_xive_msi(ics, srcno);
     }
 }
 
-static void ics_reject(ICSState *ics, int nr)
+static void ics_simple_reject(ICSState *ics, uint32_t nr)
 {
     ICSIRQState *irq = ics->irqs + nr - ics->offset;
 
-    trace_xics_ics_reject(nr, nr - ics->offset);
+    trace_xics_ics_simple_reject(nr, nr - ics->offset);
     if (irq->flags & XICS_FLAGS_IRQ_MSI) {
         irq->status |= XICS_STATUS_REJECTED;
     } else if (irq->flags & XICS_FLAGS_IRQ_LSI) {
@@ -532,35 +555,35 @@ static void ics_reject(ICSState *ics, int nr)
     }
 }
 
-static void ics_resend(ICSState *ics)
+static void ics_simple_resend(ICSState *ics)
 {
     int i;
 
     for (i = 0; i < ics->nr_irqs; i++) {
         /* FIXME: filter by server#? */
         if (ics->irqs[i].flags & XICS_FLAGS_IRQ_LSI) {
-            resend_lsi(ics, i);
+            ics_simple_resend_lsi(ics, i);
         } else {
-            resend_msi(ics, i);
+            ics_simple_resend_msi(ics, i);
         }
     }
 }
 
-static void ics_eoi(ICSState *ics, int nr)
+static void ics_simple_eoi(ICSState *ics, uint32_t nr)
 {
     int srcno = nr - ics->offset;
     ICSIRQState *irq = ics->irqs + srcno;
 
-    trace_xics_ics_eoi(nr);
+    trace_xics_ics_simple_eoi(nr);
 
     if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_LSI) {
         irq->status &= ~XICS_STATUS_SENT;
     }
 }
 
-static void ics_reset(DeviceState *dev)
+static void ics_simple_reset(DeviceState *dev)
 {
-    ICSState *ics = ICS(dev);
+    ICSState *ics = ICS_SIMPLE(dev);
     int i;
     uint8_t flags[ics->nr_irqs];
 
@@ -577,7 +600,7 @@ static void ics_reset(DeviceState *dev)
     }
 }
 
-static int ics_post_load(ICSState *ics, int version_id)
+static int ics_simple_post_load(ICSState *ics, int version_id)
 {
     int i;
 
@@ -588,20 +611,20 @@ static int ics_post_load(ICSState *ics, int version_id)
     return 0;
 }
 
-static void ics_dispatch_pre_save(void *opaque)
+static void ics_simple_dispatch_pre_save(void *opaque)
 {
     ICSState *ics = opaque;
-    ICSStateClass *info = ICS_GET_CLASS(ics);
+    ICSStateClass *info = ICS_BASE_GET_CLASS(ics);
 
     if (info->pre_save) {
         info->pre_save(ics);
     }
 }
 
-static int ics_dispatch_post_load(void *opaque, int version_id)
+static int ics_simple_dispatch_post_load(void *opaque, int version_id)
 {
     ICSState *ics = opaque;
-    ICSStateClass *info = ICS_GET_CLASS(ics);
+    ICSStateClass *info = ICS_BASE_GET_CLASS(ics);
 
     if (info->post_load) {
         return info->post_load(ics, version_id);
@@ -610,7 +633,7 @@ static int ics_dispatch_post_load(void *opaque, int version_id)
     return 0;
 }
 
-static const VMStateDescription vmstate_ics_irq = {
+static const VMStateDescription vmstate_ics_simple_irq = {
     .name = "ics/irq",
     .version_id = 2,
     .minimum_version_id = 1,
@@ -624,59 +647,71 @@ static const VMStateDescription vmstate_ics_irq = {
     },
 };
 
-static const VMStateDescription vmstate_ics = {
+static const VMStateDescription vmstate_ics_simple = {
     .name = "ics",
     .version_id = 1,
     .minimum_version_id = 1,
-    .pre_save = ics_dispatch_pre_save,
-    .post_load = ics_dispatch_post_load,
+    .pre_save = ics_simple_dispatch_pre_save,
+    .post_load = ics_simple_dispatch_post_load,
     .fields = (VMStateField[]) {
         /* Sanity check */
         VMSTATE_UINT32_EQUAL(nr_irqs, ICSState),
 
         VMSTATE_STRUCT_VARRAY_POINTER_UINT32(irqs, ICSState, nr_irqs,
-                                             vmstate_ics_irq, ICSIRQState),
+                                             vmstate_ics_simple_irq,
+                                             ICSIRQState),
         VMSTATE_END_OF_LIST()
     },
 };
 
-static void ics_initfn(Object *obj)
+static void ics_simple_initfn(Object *obj)
 {
-    ICSState *ics = ICS(obj);
+    ICSState *ics = ICS_SIMPLE(obj);
 
     ics->offset = XICS_IRQ_BASE;
 }
 
-static void ics_realize(DeviceState *dev, Error **errp)
+static void ics_simple_realize(DeviceState *dev, Error **errp)
 {
-    ICSState *ics = ICS(dev);
+    ICSState *ics = ICS_SIMPLE(dev);
 
     if (!ics->nr_irqs) {
         error_setg(errp, "Number of interrupts needs to be greater 0");
         return;
     }
     ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState));
-    ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, ics->nr_irqs);
+    ics->qirqs = qemu_allocate_irqs(ics_simple_set_irq, ics, ics->nr_irqs);
 }
 
-static void ics_class_init(ObjectClass *klass, void *data)
+static void ics_simple_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
-    ICSStateClass *isc = ICS_CLASS(klass);
+    ICSStateClass *isc = ICS_BASE_CLASS(klass);
 
-    dc->realize = ics_realize;
-    dc->vmsd = &vmstate_ics;
-    dc->reset = ics_reset;
-    isc->post_load = ics_post_load;
+    dc->realize = ics_simple_realize;
+    dc->vmsd = &vmstate_ics_simple;
+    dc->reset = ics_simple_reset;
+    isc->post_load = ics_simple_post_load;
+    isc->reject = ics_simple_reject;
+    isc->resend = ics_simple_resend;
+    isc->eoi = ics_simple_eoi;
 }
 
-static const TypeInfo ics_info = {
-    .name = TYPE_ICS,
+static const TypeInfo ics_simple_info = {
+    .name = TYPE_ICS_SIMPLE,
+    .parent = TYPE_ICS_BASE,
+    .instance_size = sizeof(ICSState),
+    .class_init = ics_simple_class_init,
+    .class_size = sizeof(ICSStateClass),
+    .instance_init = ics_simple_initfn,
+};
+
+static const TypeInfo ics_base_info = {
+    .name = TYPE_ICS_BASE,
     .parent = TYPE_DEVICE,
+    .abstract = true,
     .instance_size = sizeof(ICSState),
-    .class_init = ics_class_init,
     .class_size = sizeof(ICSStateClass),
-    .instance_init = ics_initfn,
 };
 
 /*
@@ -716,7 +751,8 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
 static void xics_register_types(void)
 {
     type_register_static(&xics_common_info);
-    type_register_static(&ics_info);
+    type_register_static(&ics_simple_info);
+    type_register_static(&ics_base_info);
     type_register_static(&icp_info);
 }
 
diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c
index 843905cfbd37..9c2f198fd142 100644
--- a/hw/intc/xics_kvm.c
+++ b/hw/intc/xics_kvm.c
@@ -272,7 +272,7 @@ static void ics_kvm_set_irq(void *opaque, int srcno, int val)
 
 static void ics_kvm_reset(DeviceState *dev)
 {
-    ICSState *ics = ICS(dev);
+    ICSState *ics = ICS_SIMPLE(dev);
     int i;
     uint8_t flags[ics->nr_irqs];
 
@@ -293,7 +293,7 @@ static void ics_kvm_reset(DeviceState *dev)
 
 static void ics_kvm_realize(DeviceState *dev, Error **errp)
 {
-    ICSState *ics = ICS(dev);
+    ICSState *ics = ICS_SIMPLE(dev);
 
     if (!ics->nr_irqs) {
         error_setg(errp, "Number of interrupts needs to be greater 0");
@@ -306,7 +306,7 @@ static void ics_kvm_realize(DeviceState *dev, Error **errp)
 static void ics_kvm_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
-    ICSStateClass *icsc = ICS_CLASS(klass);
+    ICSStateClass *icsc = ICS_BASE_CLASS(klass);
 
     dc->realize = ics_kvm_realize;
     dc->reset = ics_kvm_reset;
@@ -315,8 +315,8 @@ static void ics_kvm_class_init(ObjectClass *klass, void *data)
 }
 
 static const TypeInfo ics_kvm_info = {
-    .name = TYPE_KVM_ICS,
-    .parent = TYPE_ICS,
+    .name = TYPE_ICS_KVM,
+    .parent = TYPE_ICS_SIMPLE,
     .instance_size = sizeof(ICSState),
     .class_init = ics_kvm_class_init,
 };
@@ -488,7 +488,7 @@ static void xics_kvm_initfn(Object *obj)
     XICSState *xics = XICS_COMMON(obj);
     ICSState *ics;
 
-    ics = ICS(object_new(TYPE_KVM_ICS));
+    ics = ICS_SIMPLE(object_new(TYPE_ICS_KVM));
     object_property_add_child(obj, "ics", OBJECT(ics), NULL);
     ics->xics = xics;
     QLIST_INSERT_HEAD(&xics->ics, ics, list);
diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c
index 0b0845d6280f..e8d0623c2cb5 100644
--- a/hw/intc/xics_spapr.c
+++ b/hw/intc/xics_spapr.c
@@ -114,7 +114,7 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                           uint32_t nret, target_ulong rets)
 {
     ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
-    uint32_t nr, server, priority;
+    uint32_t nr, srcno, server, priority;
 
     if ((nargs != 3) || (nret != 1)) {
         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
@@ -135,7 +135,8 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
         return;
     }
 
-    ics_write_xive(ics, nr, server, priority, priority);
+    srcno = nr - ics->offset;
+    ics_simple_write_xive(ics, srcno, server, priority, priority);
 
     rtas_st(rets, 0, RTAS_OUT_SUCCESS);
 }
@@ -146,7 +147,7 @@ static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                           uint32_t nret, target_ulong rets)
 {
     ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
-    uint32_t nr;
+    uint32_t nr, srcno;
 
     if ((nargs != 1) || (nret != 3)) {
         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
@@ -165,8 +166,9 @@ static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
     }
 
     rtas_st(rets, 0, RTAS_OUT_SUCCESS);
-    rtas_st(rets, 1, ics->irqs[nr - ics->offset].server);
-    rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority);
+    srcno = nr - ics->offset;
+    rtas_st(rets, 1, ics->irqs[srcno].server);
+    rtas_st(rets, 2, ics->irqs[srcno].priority);
 }
 
 static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr,
@@ -175,7 +177,7 @@ static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                          uint32_t nret, target_ulong rets)
 {
     ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
-    uint32_t nr;
+    uint32_t nr, srcno;
 
     if ((nargs != 1) || (nret != 1)) {
         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
@@ -193,8 +195,9 @@ static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr,
         return;
     }
 
-    ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff,
-                   ics->irqs[nr - ics->offset].priority);
+    srcno = nr - ics->offset;
+    ics_simple_write_xive(ics, srcno, ics->irqs[srcno].server, 0xff,
+                          ics->irqs[srcno].priority);
 
     rtas_st(rets, 0, RTAS_OUT_SUCCESS);
 }
@@ -205,7 +208,7 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                         uint32_t nret, target_ulong rets)
 {
     ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
-    uint32_t nr;
+    uint32_t nr, srcno;
 
     if ((nargs != 1) || (nret != 1)) {
         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
@@ -223,9 +226,10 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr,
         return;
     }
 
-    ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server,
-                   ics->irqs[nr - ics->offset].saved_priority,
-                   ics->irqs[nr - ics->offset].saved_priority);
+    srcno = nr - ics->offset;
+    ics_simple_write_xive(ics, srcno, ics->irqs[srcno].server,
+                          ics->irqs[srcno].saved_priority,
+                          ics->irqs[srcno].saved_priority);
 
     rtas_st(rets, 0, RTAS_OUT_SUCCESS);
 }
@@ -307,7 +311,7 @@ static void xics_spapr_initfn(Object *obj)
     XICSState *xics = XICS_SPAPR(obj);
     ICSState *ics;
 
-    ics = ICS(object_new(TYPE_ICS));
+    ics = ICS_SIMPLE(object_new(TYPE_ICS_SIMPLE));
     object_property_add_child(obj, "ics", OBJECT(ics), NULL);
     ics->xics = xics;
     QLIST_INSERT_HEAD(&xics->ics, ics, list);
diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
index e49a2dab934a..66ae55ded387 100644
--- a/include/hw/ppc/xics.h
+++ b/include/hw/ppc/xics.h
@@ -119,22 +119,29 @@ struct ICPState {
     bool cap_irq_xics_enabled;
 };
 
-#define TYPE_ICS "ics"
-#define ICS(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS)
+#define TYPE_ICS_BASE "ics-base"
+#define ICS_BASE(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS_BASE)
 
-#define TYPE_KVM_ICS "icskvm"
-#define KVM_ICS(obj) OBJECT_CHECK(ICSState, (obj), TYPE_KVM_ICS)
+/* Retain ics for sPAPR for migration from existing sPAPR guests */
+#define TYPE_ICS_SIMPLE "ics"
+#define ICS_SIMPLE(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS_SIMPLE)
 
-#define ICS_CLASS(klass) \
-     OBJECT_CLASS_CHECK(ICSStateClass, (klass), TYPE_ICS)
-#define ICS_GET_CLASS(obj) \
-     OBJECT_GET_CLASS(ICSStateClass, (obj), TYPE_ICS)
+#define TYPE_ICS_KVM "icskvm"
+#define ICS_KVM(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS_KVM)
+
+#define ICS_BASE_CLASS(klass) \
+     OBJECT_CLASS_CHECK(ICSStateClass, (klass), TYPE_ICS_BASE)
+#define ICS_BASE_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(ICSStateClass, (obj), TYPE_ICS_BASE)
 
 struct ICSStateClass {
     DeviceClass parent_class;
 
     void (*pre_save)(ICSState *s);
     int (*post_load)(ICSState *s, int version_id);
+    void (*reject)(ICSState *s, uint32_t irq);
+    void (*resend)(ICSState *s);
+    void (*eoi)(ICSState *s, uint32_t irq);
 };
 
 struct ICSState {
@@ -191,8 +198,8 @@ uint32_t icp_accept(ICPState *ss);
 uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr);
 void icp_eoi(XICSState *icp, int server, uint32_t xirr);
 
-void ics_write_xive(ICSState *ics, int nr, int server,
-                    uint8_t priority, uint8_t saved_priority);
+void ics_simple_write_xive(ICSState *ics, int nr, int server,
+                           uint8_t priority, uint8_t saved_priority);
 
 void ics_set_irq_type(ICSState *ics, int srcno, bool lsi);
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 12/20] ppc/xics: Add xics to the monitor "info pic" command
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (10 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 11/20] ppc/xics: Split ICS into ics-base and ics class Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-14  5:30   ` David Gibson
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 13/20] ppc/xics: introduce helpers to find an ICP from some (CPU) index Cédric Le Goater
                   ` (8 subsequent siblings)
  20 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

Useful to debug interrupt problems.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
[clg: - updated for qemu-2.7
      - added a test on ->irqs as it is not necessarily allocated
        (PHB3_MSI)
      - removed static variable g_xics and replace with a loop on all
        children to find the xics objects. ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hmp-commands-info.hx  |  2 ++
 hw/intc/xics.c        | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ppc/ppc.c          | 14 ++++++++++++++
 include/hw/ppc/ppc.h  |  2 ++
 include/hw/ppc/xics.h |  2 ++
 monitor.c             |  4 ++++
 6 files changed, 76 insertions(+)

diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
index 19729e55aea2..ab11eaf54348 100644
--- a/hmp-commands-info.hx
+++ b/hmp-commands-info.hx
@@ -203,6 +203,8 @@ ETEXI
         .cmd        = sun4m_hmp_info_pic,
 #elif defined(TARGET_LM32)
         .cmd        = lm32_hmp_info_pic,
+#elif defined(TARGET_PPC)
+        .cmd        = ppc_hmp_info_pic,
 #else
         .cmd        = hmp_info_pic,
 #endif
diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index f40b00003a45..3bbbcc847791 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -35,6 +35,7 @@
 #include "hw/ppc/xics.h"
 #include "qemu/error-report.h"
 #include "qapi/visitor.h"
+#include "monitor/monitor.h"
 
 int xics_get_cpu_index_by_dt_id(int cpu_dt_id)
 {
@@ -633,6 +634,57 @@ static int ics_simple_dispatch_post_load(void *opaque, int version_id)
     return 0;
 }
 
+static int xics_hmp_info_pic_child(Object *child, void *opaque)
+{
+    Monitor *mon = opaque;
+
+    if (object_dynamic_cast(child, TYPE_XICS_COMMON)) {
+        XICSState *xics = XICS_COMMON(child);
+        ICSState *ics;
+        uint32_t i;
+
+        for (i = 0; i < xics->nr_servers; i++) {
+            ICPState *icp = &xics->ss[i];
+
+            if (!icp->output) {
+                continue;
+            }
+            monitor_printf(mon, "CPU %d XIRR=%08x (%p) PP=%02x MFRR=%02x\n",
+                           i, icp->xirr, icp->xirr_owner,
+                           icp->pending_priority, icp->mfrr);
+        }
+
+        QLIST_FOREACH(ics, &xics->ics, list) {
+            monitor_printf(mon, "ICS %4x..%4x %p\n",
+                           ics->offset, ics->offset + ics->nr_irqs - 1, ics);
+
+            if (!ics->irqs) {
+                continue;
+            }
+
+            for (i = 0; i < ics->nr_irqs; i++) {
+                ICSIRQState *irq = ics->irqs + i;
+
+                if (!(irq->flags & XICS_FLAGS_IRQ_MASK)) {
+                    continue;
+                }
+                monitor_printf(mon, "  %4x %s %02x %02x\n",
+                               ics->offset + i,
+                               (irq->flags & XICS_FLAGS_IRQ_LSI) ?
+                               "LSI" : "MSI",
+                               irq->priority, irq->status);
+            }
+        }
+    }
+    return 0;
+}
+
+void xics_hmp_info_pic(Monitor *mon, const QDict *qdict)
+{
+    object_child_foreach_recursive(OBJECT(qdev_get_machine()),
+                                   xics_hmp_info_pic_child, mon);
+}
+
 static const VMStateDescription vmstate_ics_simple_irq = {
     .name = "ics/irq",
     .version_id = 2,
diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
index 89458690097f..bc734281f509 100644
--- a/hw/ppc/ppc.c
+++ b/hw/ppc/ppc.c
@@ -27,6 +27,7 @@
 #include "hw/hw.h"
 #include "hw/ppc/ppc.h"
 #include "hw/ppc/ppc_e500.h"
+#include "hw/i386/pc.h"
 #include "qemu/timer.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/cpus.h"
@@ -39,6 +40,10 @@
 #include "kvm_ppc.h"
 #include "trace.h"
 
+#if defined(TARGET_PPC64)
+#include "hw/ppc/xics.h"
+#endif
+
 //#define PPC_DEBUG_IRQ
 //#define PPC_DEBUG_TB
 
@@ -1376,3 +1381,12 @@ void ppc_cpu_parse_features(const char *cpu_model)
     cc->parse_features(typename, model_pieces[1], &error_fatal);
     g_strfreev(model_pieces);
 }
+
+void ppc_hmp_info_pic(Monitor *mon, const QDict *qdict)
+{
+    /* Call in turn every PIC around. OpenPIC doesn't have one yet */
+#ifdef TARGET_PPC64
+    xics_hmp_info_pic(mon, qdict);
+#endif
+    hmp_info_pic(mon, qdict);
+}
diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h
index 00c1fb1e720a..b36024a1213c 100644
--- a/include/hw/ppc/ppc.h
+++ b/include/hw/ppc/ppc.h
@@ -3,6 +3,8 @@
 
 #include "target-ppc/cpu-qom.h"
 
+void ppc_hmp_info_pic(Monitor *mon, const QDict *qdict);
+
 void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level);
 
 /* PowerPC hardware exceptions management helpers */
diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
index 66ae55ded387..ca9f8da542e0 100644
--- a/include/hw/ppc/xics.h
+++ b/include/hw/ppc/xics.h
@@ -205,4 +205,6 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi);
 
 ICSState *xics_find_source(XICSState *icp, int irq);
 
+void xics_hmp_info_pic(Monitor *mon, const QDict *qdict);
+
 #endif /* XICS_H */
diff --git a/monitor.c b/monitor.c
index 83c4edfce08e..70e17fa9f1fd 100644
--- a/monitor.c
+++ b/monitor.c
@@ -91,6 +91,10 @@
 #include "hw/s390x/storage-keys.h"
 #endif
 
+#if defined(TARGET_PPC)
+#include "hw/ppc/ppc.h"
+#endif
+
 /*
  * Supported types:
  *
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 13/20] ppc/xics: introduce helpers to find an ICP from some (CPU) index
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (11 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 12/20] ppc/xics: Add xics to the monitor "info pic" command Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-14  5:34   ` David Gibson
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 14/20] ppc/xics: introduce a helper to insert a new ics Cédric Le Goater
                   ` (7 subsequent siblings)
  20 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

Today, the Interrupt Presentation Controllers (ICP) are stored in an
array under the base class XICSState. They are simply indexed using
the CPU index field of CPUState. This made sense for the current
derived classes, spapr and kvm, as the CPU index are contiguous.
Nevertheless some problems have started to occur with CPU hotplug.

With the PowerNV platform CPUs, this is not true anymore. Real HW ids
are being used and they are not contiguous. So we need a way to
customize the lookups in the array. Below is a proposal for this
purpose. A couple of helpers are introduced to hide the nature of the
underlying ICP array and also a new XICSStateClass method 'find_icp'
to let the derived classes customize the ICP lookups.

A followup patch would be to let the derived class decide on the ICP
storage. They could use a hash table for instance. We would need to
introduce a new class method 'get_icp' for that. Or simply, change the
ICP array for a hash table and let the derived class decide on the key
to use.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/intc/xics.c        | 48 ++++++++++++++++++++++++++++++++++++------------
 hw/intc/xics_kvm.c    |  7 ++-----
 hw/intc/xics_spapr.c  | 12 ++++++------
 include/hw/ppc/xics.h |  2 ++
 4 files changed, 46 insertions(+), 23 deletions(-)

diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index 3bbbcc847791..876c472aaa69 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -48,12 +48,32 @@ int xics_get_cpu_index_by_dt_id(int cpu_dt_id)
     return -1;
 }
 
+ICPState *xics_find_icp(XICSState *xics, int cpu_index)
+{
+    XICSStateClass *xsc = XICS_COMMON_GET_CLASS(xics);
+    ICPState *icp = xsc->find_icp(xics, cpu_index);
+
+    assert(icp);
+
+    return icp;
+}
+
+static ICPState *xics_get_icp(XICSState *xics, CPUState *cs)
+{
+    ICPState *ss;
+
+    assert(cs->cpu_index < xics->nr_servers);
+
+    ss = &xics->ss[cs->cpu_index];
+    ss->cs = cs;
+    return ss;
+}
+
 void xics_cpu_destroy(XICSState *xics, PowerPCCPU *cpu)
 {
     CPUState *cs = CPU(cpu);
-    ICPState *ss = &xics->ss[cs->cpu_index];
+    ICPState *ss = xics_find_icp(xics, cs->cpu_index);
 
-    assert(cs->cpu_index < xics->nr_servers);
     assert(cs == ss->cs);
 
     ss->output = NULL;
@@ -64,13 +84,9 @@ void xics_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
 {
     CPUState *cs = CPU(cpu);
     CPUPPCState *env = &cpu->env;
-    ICPState *ss = &xics->ss[cs->cpu_index];
+    ICPState *ss = xics_get_icp(xics, cs);
     XICSStateClass *info = XICS_COMMON_GET_CLASS(xics);
 
-    assert(cs->cpu_index < xics->nr_servers);
-
-    ss->cs = cs;
-
     if (info->cpu_setup) {
         info->cpu_setup(xics, cpu);
     }
@@ -94,6 +110,12 @@ void xics_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
 /*
  * XICS Common class - parent for emulated XICS and KVM-XICS
  */
+
+static ICPState *xics_common_find_icp(XICSState *xics, int cpu_index)
+{
+    return &xics->ss[cpu_index];
+}
+
 static void xics_common_reset(DeviceState *d)
 {
     XICSState *xics = XICS_COMMON(d);
@@ -191,8 +213,10 @@ static void xics_common_initfn(Object *obj)
 static void xics_common_class_init(ObjectClass *oc, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(oc);
+    XICSStateClass *xsc = XICS_COMMON_CLASS(oc);
 
     dc->reset = xics_common_reset;
+    xsc->find_icp = xics_common_find_icp;
 }
 
 static const TypeInfo xics_common_info = {
@@ -261,7 +285,7 @@ static void icp_check_ipi(ICPState *ss)
 
 static void icp_resend(XICSState *xics, int server)
 {
-    ICPState *ss = xics->ss + server;
+    ICPState *ss = xics_find_icp(xics, server);
     ICSState *ics;
 
     if (ss->mfrr < CPPR(ss)) {
@@ -274,7 +298,7 @@ static void icp_resend(XICSState *xics, int server)
 
 void icp_set_cppr(XICSState *xics, int server, uint8_t cppr)
 {
-    ICPState *ss = xics->ss + server;
+    ICPState *ss = xics_find_icp(xics, server);
     uint8_t old_cppr;
     uint32_t old_xisr;
 
@@ -301,7 +325,7 @@ void icp_set_cppr(XICSState *xics, int server, uint8_t cppr)
 
 void icp_set_mfrr(XICSState *xics, int server, uint8_t mfrr)
 {
-    ICPState *ss = xics->ss + server;
+    ICPState *ss = xics_find_icp(xics, server);
 
     ss->mfrr = mfrr;
     if (mfrr < CPPR(ss)) {
@@ -333,7 +357,7 @@ uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr)
 
 void icp_eoi(XICSState *xics, int server, uint32_t xirr)
 {
-    ICPState *ss = xics->ss + server;
+    ICPState *ss = xics_find_icp(xics, server);
     ICSState *ics;
     uint32_t irq;
 
@@ -354,7 +378,7 @@ void icp_eoi(XICSState *xics, int server, uint32_t xirr)
 static void icp_irq(ICSState *ics, int server, int nr, uint8_t priority)
 {
     XICSState *xics = ics->xics;
-    ICPState *ss = xics->ss + server;
+    ICPState *ss = xics_find_icp(xics, server);
 
     trace_xics_icp_irq(server, nr, priority);
 
diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c
index 9c2f198fd142..b666bb59fc24 100644
--- a/hw/intc/xics_kvm.c
+++ b/hw/intc/xics_kvm.c
@@ -326,14 +326,11 @@ static const TypeInfo ics_kvm_info = {
  */
 static void xics_kvm_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
 {
-    CPUState *cs;
-    ICPState *ss;
+    CPUState *cs = CPU(cpu);
+    ICPState *ss = xics_find_icp(xics, cs->cpu_index);
     KVMXICSState *xicskvm = XICS_SPAPR_KVM(xics);
     int ret;
 
-    cs = CPU(cpu);
-    ss = &xics->ss[cs->cpu_index];
-
     assert(cs->cpu_index < xics->nr_servers);
     if (xicskvm->kernel_xics_fd == -1) {
         abort();
diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c
index e8d0623c2cb5..af29998b1255 100644
--- a/hw/intc/xics_spapr.c
+++ b/hw/intc/xics_spapr.c
@@ -67,9 +67,9 @@ static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                            target_ulong opcode, target_ulong *args)
 {
     CPUState *cs = CPU(cpu);
-    uint32_t xirr = icp_accept(spapr->xics->ss + cs->cpu_index);
+    ICPState *ss = xics_find_icp(spapr->xics, cs->cpu_index);
 
-    args[0] = xirr;
+    args[0] = icp_accept(ss);
     return H_SUCCESS;
 }
 
@@ -77,10 +77,9 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                              target_ulong opcode, target_ulong *args)
 {
     CPUState *cs = CPU(cpu);
-    ICPState *ss = &spapr->xics->ss[cs->cpu_index];
-    uint32_t xirr = icp_accept(ss);
+    ICPState *ss = xics_find_icp(spapr->xics, cs->cpu_index);
 
-    args[0] = xirr;
+    args[0] = icp_accept(ss);
     args[1] = cpu_get_host_ticks();
     return H_SUCCESS;
 }
@@ -99,8 +98,9 @@ static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                             target_ulong opcode, target_ulong *args)
 {
     CPUState *cs = CPU(cpu);
+    ICPState *ss = xics_find_icp(spapr->xics, cs->cpu_index);
     uint32_t mfrr;
-    uint32_t xirr = icp_ipoll(spapr->xics->ss + cs->cpu_index, &mfrr);
+    uint32_t xirr = icp_ipoll(ss, &mfrr);
 
     args[0] = xirr;
     args[1] = mfrr;
diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
index ca9f8da542e0..52c426d409c9 100644
--- a/include/hw/ppc/xics.h
+++ b/include/hw/ppc/xics.h
@@ -76,6 +76,7 @@ struct XICSStateClass {
     void (*cpu_setup)(XICSState *icp, PowerPCCPU *cpu);
     void (*set_nr_irqs)(XICSState *icp, uint32_t nr_irqs, Error **errp);
     void (*set_nr_servers)(XICSState *icp, uint32_t nr_servers, Error **errp);
+    ICPState *(*find_icp)(XICSState *xics, int cpu_index);
 };
 
 struct XICSState {
@@ -206,5 +207,6 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi);
 ICSState *xics_find_source(XICSState *icp, int irq);
 
 void xics_hmp_info_pic(Monitor *mon, const QDict *qdict);
+ICPState *xics_find_icp(XICSState *xics, int cpu_index);
 
 #endif /* XICS_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 14/20] ppc/xics: introduce a helper to insert a new ics
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (12 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 13/20] ppc/xics: introduce helpers to find an ICP from some (CPU) index Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 15/20] ppc/xics: Add "native" XICS subclass Cédric Le Goater
                   ` (6 subsequent siblings)
  20 siblings, 0 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

Interrupt Control Sources (ICS) are now maintained under a list.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/intc/xics.c        | 6 ++++++
 include/hw/ppc/xics.h | 1 +
 2 files changed, 7 insertions(+)

diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index 876c472aaa69..3c250fca85c0 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -131,6 +131,12 @@ static void xics_common_reset(DeviceState *d)
     }
 }
 
+void xics_insert_ics(XICSState *xics, ICSState *ics)
+{
+    ics->xics = xics;
+    QLIST_INSERT_HEAD(&xics->ics, ics, list);
+}
+
 static void xics_prop_get_nr_irqs(Object *obj, Visitor *v, const char *name,
                                   void *opaque, Error **errp)
 {
diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
index 52c426d409c9..dea9b92d4726 100644
--- a/include/hw/ppc/xics.h
+++ b/include/hw/ppc/xics.h
@@ -208,5 +208,6 @@ ICSState *xics_find_source(XICSState *icp, int irq);
 
 void xics_hmp_info_pic(Monitor *mon, const QDict *qdict);
 ICPState *xics_find_icp(XICSState *xics, int cpu_index);
+void xics_insert_ics(XICSState *xics, ICSState *ics);
 
 #endif /* XICS_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 15/20] ppc/xics: Add "native" XICS subclass
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (13 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 14/20] ppc/xics: introduce a helper to insert a new ics Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-14  6:10   ` David Gibson
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 16/20] ppc/pnv: add a XICS native to each PowerNV chip Cédric Le Goater
                   ` (5 subsequent siblings)
  20 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

This provides access to the MMIO based Interrupt Presentation
Controllers (ICP) as found on a POWER8 system.

A new XICSNative class is introduced to hold the MMIO region of the
ICPs. It also makes use of a hash table to associate the ICPState of a
CPU with a HW processor id, as this is the server number presented in
the XIVEs.

The class routine 'find_icp' provide the way to do the lookups when
needed in the XICS base class.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
[clg: - naming cleanups
      - replaced the use of xics_get_cpu_index_by_dt_id() by xics_find_icp()
      - added some qemu logging in case of error      
      - introduced a xics_native_find_icp routine to map icp index to
        cpu index      
      - moved sysbus mapping to chip ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---

 checkpatch complains on this one, but it seems to be a false positive :
 
 ERROR: spaces required around that '&' (ctx:WxV)
 #314: FILE: hw/intc/xics_native.c:246:
 +                        (gpointer) &xics->ss[cs->cpu_index]);

 default-configs/ppc64-softmmu.mak |   3 +-
 hw/intc/Makefile.objs             |   1 +
 hw/intc/xics_native.c             | 327 ++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h              |  19 +++
 include/hw/ppc/xics.h             |  24 +++
 5 files changed, 373 insertions(+), 1 deletion(-)
 create mode 100644 hw/intc/xics_native.c

diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak
index 67a9bcaa67fa..a22c93a48686 100644
--- a/default-configs/ppc64-softmmu.mak
+++ b/default-configs/ppc64-softmmu.mak
@@ -48,8 +48,9 @@ CONFIG_PLATFORM_BUS=y
 CONFIG_ETSEC=y
 CONFIG_LIBDECNUMBER=y
 # For pSeries
-CONFIG_XICS=$(CONFIG_PSERIES)
+CONFIG_XICS=$(or $(CONFIG_PSERIES),$(CONFIG_POWERNV))
 CONFIG_XICS_SPAPR=$(CONFIG_PSERIES)
+CONFIG_XICS_NATIVE=$(CONFIG_POWERNV)
 CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM))
 # For PReP
 CONFIG_MC146818RTC=y
diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index 05ec21b21e0e..7be5dfc8347b 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -31,6 +31,7 @@ obj-$(CONFIG_RASPI) += bcm2835_ic.o bcm2836_control.o
 obj-$(CONFIG_SH4) += sh_intc.o
 obj-$(CONFIG_XICS) += xics.o
 obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o
+obj-$(CONFIG_XICS_NATIVE) += xics_native.o
 obj-$(CONFIG_XICS_KVM) += xics_kvm.o
 obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
 obj-$(CONFIG_S390_FLIC) += s390_flic.o
diff --git a/hw/intc/xics_native.c b/hw/intc/xics_native.c
new file mode 100644
index 000000000000..16413d807f65
--- /dev/null
+++ b/hw/intc/xics_native.c
@@ -0,0 +1,327 @@
+/*
+ * QEMU PowerPC PowerNV machine model
+ *
+ * Native version of ICS/ICP
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "hw/hw.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+
+#include "hw/ppc/fdt.h"
+#include "hw/ppc/xics.h"
+#include "hw/ppc/pnv.h"
+
+#include <libfdt.h>
+
+static void xics_native_reset(void *opaque)
+{
+    device_reset(DEVICE(opaque));
+}
+
+static void xics_native_initfn(Object *obj)
+{
+    XICSState *xics = XICS_COMMON(obj);
+
+    QLIST_INIT(&xics->ics);
+
+    /*
+     * Let's not forget to register a reset handler else the ICPs
+     * won't be initialized with the correct values. Trouble ahead !
+     */
+    qemu_register_reset(xics_native_reset, xics);
+}
+
+static uint64_t xics_native_read(void *opaque, hwaddr addr, unsigned width)
+{
+    XICSState *s = opaque;
+    uint32_t cpu_id = (addr & (PNV_XICS_SIZE - 1)) >> 12;
+    bool byte0 = (width == 1 && (addr & 0x3) == 0);
+    uint64_t val = 0xffffffff;
+    ICPState *ss;
+
+    ss = xics_find_icp(s, cpu_id);
+    if (!ss) {
+        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP server %d\n", cpu_id);
+        return val;
+    }
+
+    switch (addr & 0xffc) {
+    case 0: /* poll */
+        val = icp_ipoll(ss, NULL);
+        if (byte0) {
+            val >>= 24;
+        } else if (width != 4) {
+            goto bad_access;
+        }
+        break;
+    case 4: /* xirr */
+        if (byte0) {
+            val = icp_ipoll(ss, NULL) >> 24;
+        } else if (width == 4) {
+            val = icp_accept(ss);
+        } else {
+            goto bad_access;
+        }
+        break;
+    case 12:
+        if (byte0) {
+            val = ss->mfrr;
+        } else {
+            goto bad_access;
+        }
+        break;
+    case 16:
+        if (width == 4) {
+            val = ss->links[0];
+        } else {
+            goto bad_access;
+        }
+        break;
+    case 20:
+        if (width == 4) {
+            val = ss->links[1];
+        } else {
+            goto bad_access;
+        }
+        break;
+    case 24:
+        if (width == 4) {
+            val = ss->links[2];
+        } else {
+            goto bad_access;
+        }
+        break;
+    default:
+bad_access:
+        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
+                      HWADDR_PRIx"/%d\n", addr, width);
+    }
+
+    return val;
+}
+
+static void xics_native_write(void *opaque, hwaddr addr, uint64_t val,
+                        unsigned width)
+{
+    XICSState *s = opaque;
+    uint32_t cpu_id = (addr & (PNV_XICS_SIZE - 1)) >> 12;
+    bool byte0 = (width == 1 && (addr & 0x3) == 0);
+    ICPState *ss;
+
+    ss = xics_find_icp(s, cpu_id);
+    if (!ss) {
+        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP server %d\n", cpu_id);
+        return;
+    }
+
+    switch (addr & 0xffc) {
+    case 4: /* xirr */
+        if (byte0) {
+            icp_set_cppr(s, cpu_id, val);
+        } else if (width == 4) {
+            icp_eoi(s, cpu_id, val);
+        } else {
+            goto bad_access;
+        }
+        break;
+    case 12:
+        if (byte0) {
+            icp_set_mfrr(s, cpu_id, val);
+        } else {
+            goto bad_access;
+        }
+        break;
+    case 16:
+        if (width == 4) {
+            ss->links[0] = val;
+        } else {
+            goto bad_access;
+        }
+        break;
+    case 20:
+        if (width == 4) {
+            ss->links[1] = val;
+        } else {
+            goto bad_access;
+        }
+        break;
+    case 24:
+        if (width == 4) {
+            ss->links[2] = val;
+        } else {
+            goto bad_access;
+        }
+        break;
+    default:
+bad_access:
+        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
+                      HWADDR_PRIx"/%d\n", addr, width);
+    }
+}
+
+static const MemoryRegionOps xics_native_ops = {
+    .read = xics_native_read,
+    .write = xics_native_write,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 4,
+    .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers,
+                                Error **errp)
+{
+    int i;
+
+    icp->nr_servers = nr_servers;
+
+    icp->ss = g_malloc0(icp->nr_servers * sizeof(ICPState));
+    for (i = 0; i < icp->nr_servers; i++) {
+        char name[32];
+        object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP);
+        snprintf(name, sizeof(name), "icp[%d]", i);
+        object_property_add_child(OBJECT(icp), name, OBJECT(&icp->ss[i]),
+                                  errp);
+    }
+}
+
+static void xics_native_realize(DeviceState *dev, Error **errp)
+{
+    XICSState *xics = XICS_COMMON(dev);
+    XICSNative *xicsn = XICS_NATIVE(dev);
+    Error *error = NULL;
+    int i;
+
+    if (!xics->nr_servers) {
+        error_setg(errp, "Number of servers needs to be greater than 0");
+        return;
+    }
+
+    for (i = 0; i < xics->nr_servers; i++) {
+        object_property_set_bool(OBJECT(&xics->ss[i]), true, "realized",
+                                 &error);
+        if (error) {
+            error_propagate(errp, error);
+            return;
+        }
+    }
+
+    xicsn->pir_table = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+    /* Register MMIO regions */
+    memory_region_init_io(&xicsn->icp_mmio, OBJECT(dev), &xics_native_ops,
+                          xicsn, "xics", PNV_XICS_SIZE);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xicsn->icp_mmio);
+}
+
+static void xics_native_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
+{
+    CPUState *cs = CPU(cpu);
+    CPUPPCState *env = &cpu->env;
+    XICSNative *xicsn = XICS_NATIVE(xics);
+
+    assert(cs->cpu_index < xics->nr_servers);
+    g_hash_table_insert(xicsn->pir_table, GINT_TO_POINTER(env->spr[SPR_PIR]),
+                        (gpointer) &xics->ss[cs->cpu_index]);
+}
+
+static ICPState *xics_native_find_icp(XICSState *xics, int pir)
+{
+    XICSNative *xicsn = XICS_NATIVE(xics);
+    gpointer key, value;
+    gboolean found = g_hash_table_lookup_extended(xicsn->pir_table,
+                                GINT_TO_POINTER(pir), &key, &value);
+
+    assert(found);
+
+    return (ICPState *) value;
+}
+
+static void xics_native_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    XICSStateClass *xsc = XICS_NATIVE_CLASS(oc);
+
+    dc->realize = xics_native_realize;
+    xsc->set_nr_servers = xics_set_nr_servers;
+    xsc->cpu_setup = xics_native_cpu_setup;
+    xsc->find_icp = xics_native_find_icp;
+}
+
+static const TypeInfo xics_native_info = {
+    .name          = TYPE_XICS_NATIVE,
+    .parent        = TYPE_XICS_COMMON,
+    .instance_size = sizeof(XICSNative),
+    .class_size = sizeof(XICSStateClass),
+    .class_init    = xics_native_class_init,
+    .instance_init = xics_native_initfn,
+};
+
+static void xics_native_register_types(void)
+{
+    type_register_static(&xics_native_info);
+}
+
+type_init(xics_native_register_types)
+
+void xics_native_populate_icp(PnvChip *chip, void *fdt, int offset,
+                              uint32_t pir, uint32_t count)
+{
+    uint64_t addr;
+    char *name;
+    const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp";
+    uint32_t irange[2], i, rsize;
+    uint64_t *reg;
+
+    /*
+     * TODO: add multichip ICP BAR
+     */
+    addr = PNV_XICS_BASE | (pir << 12);
+
+    irange[0] = cpu_to_be32(pir);
+    irange[1] = cpu_to_be32(count);
+
+    rsize = sizeof(uint64_t) * 2 * count;
+    reg = g_malloc(rsize);
+    for (i = 0; i < count; i++) {
+        reg[i * 2] = cpu_to_be64(addr | ((pir + i) * 0x1000));
+        reg[i * 2 + 1] = cpu_to_be64(0x1000);
+    }
+
+    name = g_strdup_printf("interrupt-controller@%"PRIX64, addr);
+    offset = fdt_add_subnode(fdt, offset, name);
+    _FDT(offset);
+    g_free(name);
+
+    _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat))));
+    _FDT((fdt_setprop(fdt, offset, "reg", reg, rsize)));
+    _FDT((fdt_setprop_string(fdt, offset, "device_type",
+                              "PowerPC-External-Interrupt-Presentation")));
+    _FDT((fdt_setprop(fdt, offset, "interrupt-controller", NULL, 0)));
+    _FDT((fdt_setprop(fdt, offset, "ibm,interrupt-server-ranges",
+                       irange, sizeof(irange))));
+    _FDT((fdt_setprop_cell(fdt, offset, "#interrupt-cells", 1)));
+    _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0)));
+    g_free(reg);
+}
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index 617c3fdd4f06..3f24b87d199b 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -125,4 +125,23 @@ typedef struct PnvMachineState {
 #define PNV_XSCOM_BASE(chip)                                            \
     (chip->xscom_base + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE)
 
+/*
+ * XSCOM 0x20109CA defines the ICP BAR:
+ *
+ * 0:29   : bits 14 to 43 of address to define 1 MB region.
+ * 30     : 1 to enable ICP to receive loads/stores against its BAR region
+ * 31:63  : Constant 0
+ *
+ * Usually defined as :
+ *
+ *      0xffffe00200000000 -> 0x0003ffff80000000
+ *      0xffffe00600000000 -> 0x0003ffff80100000
+ *      0xffffe02200000000 -> 0x0003ffff80800000
+ *      0xffffe02600000000 -> 0x0003ffff80900000
+ *
+ * TODO: make a macro using the chip hw id ?
+ */
+#define PNV_XICS_BASE         0x0003ffff80000000ull
+#define PNV_XICS_SIZE         0x0000000000100000ull
+
 #endif /* _PPC_PNV_H */
diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
index dea9b92d4726..77ce786f000e 100644
--- a/include/hw/ppc/xics.h
+++ b/include/hw/ppc/xics.h
@@ -118,8 +118,27 @@ struct ICPState {
     uint8_t mfrr;
     qemu_irq output;
     bool cap_irq_xics_enabled;
+
+    /*
+     * for XICSNative (not used by Linux).
+     */
+    uint32_t links[3];
 };
 
+#define TYPE_XICS_NATIVE "xics-native"
+#define XICS_NATIVE(obj) OBJECT_CHECK(XICSNative, (obj), TYPE_XICS_NATIVE)
+#define XICS_NATIVE_CLASS(klass) \
+     OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS_NATIVE)
+#define XICS_NATIVE_GET_CLASS(obj) \
+     OBJECT_CLASS_CHECK(XICSStateClass, (obj), TYPE_XICS_NATIVE)
+
+typedef struct XICSNative {
+    XICSState parent_obj;
+
+    GHashTable *pir_table;
+    MemoryRegion icp_mmio;
+} XICSNative;
+
 #define TYPE_ICS_BASE "ics-base"
 #define ICS_BASE(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS_BASE)
 
@@ -210,4 +229,9 @@ void xics_hmp_info_pic(Monitor *mon, const QDict *qdict);
 ICPState *xics_find_icp(XICSState *xics, int cpu_index);
 void xics_insert_ics(XICSState *xics, ICSState *ics);
 
+typedef struct PnvChip PnvChip;
+
+void xics_native_populate_icp(PnvChip *chip, void *fdt, int offset,
+                              uint32_t base, uint32_t count);
+
 #endif /* XICS_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 16/20] ppc/pnv: add a XICS native to each PowerNV chip
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (14 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 15/20] ppc/xics: Add "native" XICS subclass Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-14  6:18   ` David Gibson
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 17/20] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt Cédric Le Goater
                   ` (4 subsequent siblings)
  20 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

and also link the XICS object to each core as it is needed to do the
CPU setup.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/ppc/pnv.c         | 18 ++++++++++++++++++
 hw/ppc/pnv_core.c    | 25 +++++++++++++++++++++----
 include/hw/ppc/pnv.h |  2 ++
 3 files changed, 41 insertions(+), 4 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 4a71b18bf38b..6335ca11efe7 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -32,6 +32,7 @@
 #include "exec/address-spaces.h"
 #include "qemu/cutils.h"
 
+#include "hw/ppc/xics.h"
 #include "hw/ppc/pnv_xscom.h"
 
 #include "hw/isa/isa.h"
@@ -223,6 +224,7 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
     char *typename = pnv_core_typename(pcc->cpu_model);
     size_t typesize = object_type_get_instance_size(typename);
     int i;
+    int smt = 1; /* TCG does not support more for the moment */
 
     pnv_xscom_populate(chip, fdt, 0);
 
@@ -230,6 +232,9 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
         PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
 
         powernv_create_core_node(chip, pnv_core, fdt);
+
+        /* Interrupt presentation controllers (ICP). One per core. */
+        xics_native_populate_icp(chip, fdt, 0, pnv_core->pir, smt);
     }
 
     /* Put all the memory in one node on chip 0 until we find a way to
@@ -631,6 +636,9 @@ static void pnv_chip_init(Object *obj)
 
     object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
     object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
+
+    object_initialize(&chip->xics, sizeof(chip->xics), TYPE_XICS_NATIVE);
+    object_property_add_child(obj, "xics", OBJECT(&chip->xics), NULL);
 }
 
 static void pnv_chip_realize(DeviceState *dev, Error **errp)
@@ -641,6 +649,7 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
     char *typename = pnv_core_typename(pcc->cpu_model);
     size_t typesize = object_type_get_instance_size(typename);
     int i, core_hwid;
+    int smt = 1; /* TCG does not support more for the moment */
 
     if (!object_class_by_name(typename)) {
         error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename);
@@ -662,6 +671,13 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
         return;
     }
 
+    /* Set up Interrupt Controller before we create the VCPUs */
+    object_property_set_int(OBJECT(&chip->xics), smp_cpus * smt / smp_threads,
+                            "nr_servers",  &error_fatal);
+    object_property_set_bool(OBJECT(&chip->xics), true, "realized",
+                             &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&chip->xics), 0, PNV_XICS_BASE);
+
     chip->cores = g_malloc0(typesize * chip->nr_cores);
 
     for (i = 0, core_hwid = 0; (core_hwid < sizeof(chip->cores_mask) * 8)
@@ -684,6 +700,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
         object_property_set_int(OBJECT(pnv_core),
                                 pcc->core_pir(chip, core_hwid),
                                 "pir", &error_fatal);
+        object_property_add_const_link(OBJECT(pnv_core), "xics",
+                                       OBJECT(&chip->xics), &error_fatal);
         object_property_set_bool(OBJECT(pnv_core), true, "realized",
                                  &error_fatal);
         object_unref(OBJECT(pnv_core));
diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
index a1c8a14f06b6..fe18e3150f78 100644
--- a/hw/ppc/pnv_core.c
+++ b/hw/ppc/pnv_core.c
@@ -24,6 +24,7 @@
 #include "hw/ppc/ppc.h"
 #include "hw/ppc/pnv.h"
 #include "hw/ppc/pnv_core.h"
+#include "hw/ppc/xics.h"
 
 static void powernv_cpu_reset(void *opaque)
 {
@@ -54,7 +55,7 @@ static void powernv_cpu_reset(void *opaque)
     env->msr |= MSR_HVB; /* Hypervisor mode */
 }
 
-static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
+static void powernv_cpu_init(PowerPCCPU *cpu, XICSState *xics, Error **errp)
 {
     CPUPPCState *env = &cpu->env;
 
@@ -63,6 +64,12 @@ static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
 
     qemu_register_reset(powernv_cpu_reset, cpu);
     powernv_cpu_reset(cpu);
+
+    /*
+     * XICS native cpu_setup() expects SPR_PIR to be set. So it needs
+     * to run after powernv_cpu_reset()
+     */
+    xics_cpu_setup(xics, cpu);
 }
 
 /*
@@ -110,7 +117,7 @@ static const MemoryRegionOps pnv_core_xscom_ops = {
     .endianness = DEVICE_BIG_ENDIAN,
 };
 
-static void pnv_core_realize_child(Object *child, Error **errp)
+static void pnv_core_realize_child(Object *child, XICSState *xics, Error **errp)
 {
     Error *local_err = NULL;
     CPUState *cs = CPU(child);
@@ -122,7 +129,7 @@ static void pnv_core_realize_child(Object *child, Error **errp)
         return;
     }
 
-    powernv_cpu_init(cpu, &local_err);
+    powernv_cpu_init(cpu, xics, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -140,6 +147,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
     void *obj;
     int i, j;
     char name[32];
+    XICSState *xics;
 
     pc->threads = g_malloc0(size * cc->nr_threads);
     for (i = 0; i < cc->nr_threads; i++) {
@@ -157,10 +165,19 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
         object_unref(obj);
     }
 
+    /* get XICS object from chip */
+    obj = object_property_get_link(OBJECT(dev), "xics", &local_err);
+    if (!obj) {
+        error_setg(errp, "%s: required link 'xics' not found: %s",
+                   __func__, error_get_pretty(local_err));
+        return;
+    }
+    xics = XICS_COMMON(obj);
+
     for (j = 0; j < cc->nr_threads; j++) {
         obj = pc->threads + j * size;
 
-        pnv_core_realize_child(obj, &local_err);
+        pnv_core_realize_child(obj, xics, &local_err);
         if (local_err) {
             goto err;
         }
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index 3f24b87d199b..73d26c55d993 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -23,6 +23,7 @@
 #include "hw/sysbus.h"
 #include "hw/ppc/pnv_xscom.h"
 #include "hw/ppc/pnv_lpc.h"
+#include "hw/ppc/xics.h"
 
 #define TYPE_PNV_CHIP "powernv-chip"
 #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
@@ -55,6 +56,7 @@ typedef struct PnvChip {
     void      *cores;
 
     PnvLpcController lpc;
+    XICSNative   xics;
 } PnvChip;
 
 typedef struct PnvChipClass {
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 17/20] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (15 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 16/20] ppc/pnv: add a XICS native to each PowerNV chip Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-14  6:32   ` David Gibson
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 18/20] ppc/pnv: Add OCC model stub with interrupt support Cédric Le Goater
                   ` (3 subsequent siblings)
  20 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

The PSI (Processor Service Interface) is one of the engines of the
"Bridge" unit which connects the different interfaces to the Power
Processor.

This adds just enough of the PSI bridge to handle various on-chip and
the one external interrupt. The rest of PSI has to do with the link to
the IBM FSP service processor which we don't plan to emulate (not used
on OpenPower machines).

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
[clg: - updated for qemu-2.7
      - changed the XSCOM interface to fit new model
      - QOMified the model ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/ppc/Makefile.objs       |   2 +-
 hw/ppc/pnv.c               |  38 ++-
 hw/ppc/pnv_psi.c           | 598 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h       |   6 +
 include/hw/ppc/pnv_psi.h   |  64 +++++
 include/hw/ppc/pnv_xscom.h |   3 +
 6 files changed, 702 insertions(+), 9 deletions(-)
 create mode 100644 hw/ppc/pnv_psi.c
 create mode 100644 include/hw/ppc/pnv_psi.h

diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index ebc72af0a7c6..4feb15b360c8 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
 obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
 obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
 # IBM PowerNV
-obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o
+obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o
 ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
 obj-y += spapr_pci_vfio.o
 endif
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 6335ca11efe7..b17e205c74db 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -318,15 +318,24 @@ static void ppc_powernv_reset(void)
  * have a CPLD that will collect the SerIRQ and shoot them as a
  * single level interrupt to the P8 chip. So let's setup a hook
  * for doing just that.
- *
- * Note: The actual interrupt input isn't emulated yet, this will
- * come with the PSI bridge model.
  */
 static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
 {
-    /* We don't yet emulate the PSI bridge which provides the external
-     * interrupt, so just drop interrupts on the floor
-     */
+    static uint32_t irqstate;
+    uint32_t old_state = irqstate;
+    PnvPsiController *psi = opaque;
+
+    if (n >= ISA_NUM_IRQS) {
+        return;
+    }
+    if (level) {
+        irqstate |= 1u << n;
+    } else {
+        irqstate &= ~(1u << n);
+    }
+    if (irqstate != old_state) {
+        pnv_psi_irq_set(psi, PSIHB_IRQ_EXTERNAL, irqstate != 0);
+    }
 }
 
 static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
@@ -355,8 +364,8 @@ static ISABus *pnv_isa_create(PnvChip *chip)
     if (pcc->chip_type == PNV_CHIP_POWER8NVL) {
         irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler, lpc, ISA_NUM_IRQS);
     } else {
-        irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, NULL,
-                                  ISA_NUM_IRQS);
+        irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, &chip->psi,
+                                 ISA_NUM_IRQS);
     }
 
     isa_bus_irqs(isa_bus, irqs);
@@ -639,6 +648,9 @@ static void pnv_chip_init(Object *obj)
 
     object_initialize(&chip->xics, sizeof(chip->xics), TYPE_XICS_NATIVE);
     object_property_add_child(obj, "xics", OBJECT(&chip->xics), NULL);
+
+    object_initialize(&chip->psi, sizeof(chip->psi), TYPE_PNV_PSI);
+    object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL);
 }
 
 static void pnv_chip_realize(DeviceState *dev, Error **errp)
@@ -713,6 +725,16 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
     }
     g_free(typename);
 
+
+    /* Create PSI */
+    object_property_set_bool(OBJECT(&chip->psi), true, "realized",
+                             &error_fatal);
+    memory_region_add_subregion(&chip->xscom, PNV_XSCOM_PSI_BASE << 3,
+                                &chip->psi.xscom_regs);
+
+    /* link in the PSI ICS */
+    xics_insert_ics(XICS_COMMON(&chip->xics), &chip->psi.ics);
+
     /* Create LPC controller */
     object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
                              &error_fatal);
diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c
new file mode 100644
index 000000000000..4f903b9985e9
--- /dev/null
+++ b/hw/ppc/pnv_psi.c
@@ -0,0 +1,598 @@
+/*
+ * QEMU PowerNV PowerPC PSI interface
+ *
+ * Copyright (c) 2016, IBM Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "target-ppc/cpu.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+
+#include "exec/address-spaces.h"
+
+#include "hw/ppc/fdt.h"
+#include "hw/ppc/pnv.h"
+#include "hw/ppc/pnv_psi.h"
+
+#include <libfdt.h>
+
+#define PSIHB_XSCOM_FIR_RW      0x00
+#define PSIHB_XSCOM_FIR_AND     0x01
+#define PSIHB_XSCOM_FIR_OR      0x02
+#define PSIHB_XSCOM_FIRMASK_RW  0x03
+#define PSIHB_XSCOM_FIRMASK_AND 0x04
+#define PSIHB_XSCOM_FIRMASK_OR  0x05
+#define PSIHB_XSCOM_FIRACT0     0x06
+#define PSIHB_XSCOM_FIRACT1     0x07
+#define PSIHB_XSCOM_BAR         0x0a
+#define   PSIHB_BAR_EN                  0x0000000000000001ull
+#define PSIHB_XSCOM_FSPBAR      0x0b
+#define PSIHB_XSCOM_CR          0x0e
+#define   PSIHB_CR_FSP_CMD_ENABLE       0x8000000000000000ull
+#define   PSIHB_CR_FSP_MMIO_ENABLE      0x4000000000000000ull
+#define   PSIHB_CR_FSP_IRQ_ENABLE       0x1000000000000000ull
+#define   PSIHB_CR_FSP_ERR_RSP_ENABLE   0x0800000000000000ull
+#define   PSIHB_CR_PSI_LINK_ENABLE      0x0400000000000000ull
+#define   PSIHB_CR_FSP_RESET            0x0200000000000000ull
+#define   PSIHB_CR_PSIHB_RESET          0x0100000000000000ull
+#define   PSIHB_CR_PSI_IRQ              0x0000800000000000ull
+#define   PSIHB_CR_FSP_IRQ              0x0000400000000000ull
+#define   PSIHB_CR_FSP_LINK_ACTIVE      0x0000200000000000ull
+          /* and more ... */
+#define PSIHB_XSCOM_SEMR        0x0f
+#define PSIHB_XSCOM_XIVR_PSI    0x10
+#define   PSIHB_XIVR_SERVER_SH  40
+#define   PSIHB_XIVR_SERVER_MSK (0xffffull << PSIHB_XIVR_SERVER_SH)
+#define   PSIHB_XIVR_PRIO_SH    32
+#define   PSIHB_XIVR_PRIO_MSK   (0xffull << PSIHB_XIVR_PRIO_SH)
+#define   PSIHB_XIVR_SRC_SH             29
+#define   PSIHB_XIVR_SRC_MSK    (0x7ull << PSIHB_XIVR_SRC_SH)
+#define   PSIHB_XIVR_PENDING    0x01000000ull
+#define PSIHB_XSCOM_SCR         0x12
+#define PSIHB_XSCOM_CCR         0x13
+#define PSIHB_XSCOM_DMA_UPADD   0x14
+#define PSIHB_XSCOM_IRQ_STAT    0x15
+#define  PSIHB_IRQ_STAT_OCC             0x0000001000000000ull
+#define  PSIHB_IRQ_STAT_FSI             0x0000000800000000ull
+#define  PSIHB_IRQ_STAT_LPCI2C          0x0000000400000000ull
+#define  PSIHB_IRQ_STAT_LOCERR          0x0000000200000000ull
+#define  PSIHB_IRQ_STAT_EXT             0x0000000100000000ull
+#define PSIHB_XSCOM_XIVR_OCC    0x16
+#define PSIHB_XSCOM_XIVR_FSI    0x17
+#define PSIHB_XSCOM_XIVR_LPCI2C 0x18
+#define PSIHB_XSCOM_XIVR_LOCERR 0x19
+#define PSIHB_XSCOM_XIVR_EXT    0x1a
+#define PSIHB_XSCOM_IRSN        0x1b
+#define   PSIHB_IRSN_COMP_SH            45
+#define   PSIHB_IRSN_COMP_MSK           (0x7ffffull << PSIHB_IRSN_COMP_SH)
+#define   PSIHB_IRSN_IRQ_MUX            0x0000000800000000ull
+#define   PSIHB_IRSN_IRQ_RESET          0x0000000400000000ull
+#define   PSIHB_IRSN_DOWNSTREAM_EN      0x0000000200000000ull
+#define   PSIHB_IRSN_UPSTREAM_EN        0x0000000100000000ull
+#define   PSIHB_IRSN_COMPMASK_SH        13
+#define   PSIHB_IRSN_COMPMASK_MSK       (0x7ffffull << PSIHB_IRSN_COMPMASK_SH)
+
+#define PSIHB_MMIO_BAR          0x00
+#define PSIHB_MMIO_FSPBAR       0x08
+#define PSIHB_MMIO_CR           0x20
+#define PSIHB_MMIO_SEMR         0x28
+#define PSIHB_MMIO_XIVR_PSI     0x30
+#define PSIHB_MMIO_SCR          0x40
+#define PSIHB_MMIO_CCR          0x48
+#define PSIHB_MMIO_DMA_UPADD    0x50
+#define PSIHB_MMIO_IRQ_STAT     0x58
+#define PSIHB_MMIO_XIVR_OCC     0x60
+#define PSIHB_MMIO_XIVR_FSI     0x68
+#define PSIHB_MMIO_XIVR_LPCI2C  0x70
+#define PSIHB_MMIO_XIVR_LOCERR  0x78
+#define PSIHB_MMIO_XIVR_EXT     0x80
+#define PSIHB_MMIO_IRSN         0x88
+#define PSIHB_MMIO_MAX          0x100
+
+static const uint32_t psi_mmio_to_xscom[PSIHB_MMIO_MAX / 8] = {
+        [PSIHB_MMIO_BAR / 8]         = PSIHB_XSCOM_BAR,
+        [PSIHB_MMIO_FSPBAR / 8]      = PSIHB_XSCOM_FSPBAR,
+        [PSIHB_MMIO_CR / 8]          = PSIHB_XSCOM_CR,
+        [PSIHB_MMIO_SCR / 8]         = PSIHB_XSCOM_SCR,
+        [PSIHB_MMIO_CCR / 8]         = PSIHB_XSCOM_CCR,
+        [PSIHB_MMIO_SEMR / 8]        = PSIHB_XSCOM_SEMR,
+        [PSIHB_MMIO_XIVR_PSI / 8]    = PSIHB_XSCOM_XIVR_PSI,
+        [PSIHB_MMIO_XIVR_OCC / 8]    = PSIHB_XSCOM_XIVR_OCC,
+        [PSIHB_MMIO_XIVR_FSI / 8]    = PSIHB_XSCOM_XIVR_FSI,
+        [PSIHB_MMIO_XIVR_LPCI2C / 8] = PSIHB_XSCOM_XIVR_LPCI2C,
+        [PSIHB_MMIO_XIVR_LOCERR / 8] = PSIHB_XSCOM_XIVR_LOCERR,
+        [PSIHB_MMIO_XIVR_EXT / 8]    = PSIHB_XSCOM_XIVR_EXT,
+        [PSIHB_MMIO_IRQ_STAT / 8]    = PSIHB_XSCOM_IRQ_STAT,
+        [PSIHB_MMIO_DMA_UPADD / 8]   = PSIHB_XSCOM_DMA_UPADD,
+        [PSIHB_MMIO_IRSN / 8]        = PSIHB_XSCOM_IRSN,
+};
+
+static void pnv_psi_set_bar(PnvPsiController *psi, uint64_t bar)
+{
+    MemoryRegion *sysmem = get_system_memory();
+    uint64_t old = psi->regs[PSIHB_XSCOM_BAR];
+
+    psi->regs[PSIHB_XSCOM_BAR] = bar & 0x0003fffffff00001;
+
+    /* Update MR, always remove it first */
+    if (old & PSIHB_BAR_EN) {
+        memory_region_del_subregion(sysmem, &psi->regs_mr);
+    }
+    /* Then add it back if needed */
+    if (bar & PSIHB_BAR_EN) {
+        uint64_t addr = bar & 0x0003fffffff00000;
+        memory_region_add_subregion(sysmem, addr, &psi->regs_mr);
+    }
+}
+
+static void pnv_psi_update_fsp_mr(PnvPsiController *psi)
+{
+    /* XXX Update FSP MR if/when we support FSP BAR */
+}
+
+static void pnv_psi_set_cr(PnvPsiController *psi, uint64_t cr)
+{
+    uint64_t old = psi->regs[PSIHB_XSCOM_CR];
+
+    psi->regs[PSIHB_XSCOM_CR] = cr & 0x0003ffff00000000;
+
+    /* Check some bit changes */
+    if ((old ^ psi->regs[PSIHB_XSCOM_CR]) & PSIHB_CR_FSP_MMIO_ENABLE) {
+        pnv_psi_update_fsp_mr(psi);
+    }
+}
+
+static void pnv_psi_set_irsn(PnvPsiController *psi, uint64_t val)
+{
+    uint32_t offset;
+    ICSState *ics = &psi->ics;
+
+    /* In this model we ignore the up/down enable bits for now
+     * as SW doesn't use them (other than setting them at boot).
+     * We ignore IRQ_MUX, its meaning isn't clear and we don't use
+     * it and finally we ignore reset (XXX fix that ?)
+     */
+    psi->regs[PSIHB_XSCOM_IRSN] = val & (PSIHB_IRSN_COMP_MSK |
+                                         PSIHB_IRSN_IRQ_MUX |
+                                         PSIHB_IRSN_DOWNSTREAM_EN |
+                                         PSIHB_IRSN_DOWNSTREAM_EN |
+                                         PSIHB_IRSN_DOWNSTREAM_EN);
+
+    /* We ignore the compare mask as well, our ICS emulation is too
+     * simplistic to make any use if it, and we extract the offset
+     * from the compare value
+     */
+    offset = (val & PSIHB_IRSN_COMP_MSK) >> PSIHB_IRSN_COMP_SH;
+    ics->offset = offset;
+}
+
+static bool pnv_psi_irq_bits(PnvPsiController *psi, PnvPsiIrq irq,
+                             uint32_t *out_xivr_reg,
+                             uint32_t *out_stat_reg,
+                             uint64_t *out_stat_bit)
+{
+    switch (irq) {
+    case PSIHB_IRQ_PSI:
+        *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI;
+        *out_stat_reg = PSIHB_XSCOM_CR;
+        *out_stat_bit = PSIHB_CR_PSI_IRQ;
+        break;
+    case PSIHB_IRQ_FSP:
+        *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI;
+        *out_stat_reg = PSIHB_XSCOM_CR;
+        *out_stat_bit = PSIHB_CR_FSP_IRQ;
+        break;
+    case PSIHB_IRQ_OCC:
+        *out_xivr_reg = PSIHB_XSCOM_XIVR_OCC;
+        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
+        *out_stat_bit = PSIHB_IRQ_STAT_OCC;
+        break;
+    case PSIHB_IRQ_FSI:
+        *out_xivr_reg = PSIHB_XSCOM_XIVR_FSI;
+        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
+        *out_stat_bit = PSIHB_IRQ_STAT_FSI;
+        break;
+    case PSIHB_IRQ_LPC_I2C:
+        *out_xivr_reg = PSIHB_XSCOM_XIVR_LPCI2C;
+        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
+        *out_stat_bit = PSIHB_IRQ_STAT_LPCI2C;
+        break;
+    case PSIHB_IRQ_LOCAL_ERR:
+        *out_xivr_reg = PSIHB_XSCOM_XIVR_LOCERR;
+        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
+        *out_stat_bit = PSIHB_IRQ_STAT_LOCERR;
+        break;
+    case PSIHB_IRQ_EXTERNAL:
+        *out_xivr_reg = PSIHB_XSCOM_XIVR_EXT;
+        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
+        *out_stat_bit = PSIHB_IRQ_STAT_EXT;
+        break;
+    default:
+        return false;
+    }
+    return true;
+}
+
+void pnv_psi_irq_set(PnvPsiController *psi, PnvPsiIrq irq, bool state)
+{
+    ICSState *ics = &psi->ics;
+    uint32_t xivr_reg;
+    uint32_t stat_reg;
+    uint64_t stat_bit;
+    uint32_t src;
+    bool masked;
+
+    if (!pnv_psi_irq_bits(psi, irq, &xivr_reg, &stat_reg, &stat_bit)) {
+        qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", irq);
+        return;
+    }
+
+    src = (psi->regs[xivr_reg] & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH;
+    masked = (psi->regs[xivr_reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK;
+    if (state) {
+        psi->regs[stat_reg] |= stat_bit;
+        /* XXX optimization: check mask here. That means re-evaluating
+         * when unmasking, thus TODO
+         */
+        qemu_irq_raise(ics->qirqs[src]);
+    } else {
+        psi->regs[stat_reg] &= ~stat_bit;
+
+        /* FSP and PSI are muxed so don't lower if either still set */
+        if (stat_reg != PSIHB_XSCOM_CR ||
+            !(psi->regs[stat_reg] & (PSIHB_CR_PSI_IRQ | PSIHB_CR_FSP_IRQ))) {
+            qemu_irq_lower(ics->qirqs[src]);
+        } else {
+            state = true;
+        }
+    }
+
+    /* XXX Note about the emulation of the pending bit: This isn't
+     * entirely correct. The pending bit should be cleared when the
+     * EOI has been received. However, we don't have callbacks on
+     * EOI (especially not under KVM) so no way to emulate that
+     * properly, so instead we just set that bit as the logical
+     * "output" of the XIVR (ie pending & !masked)
+     * XXX TODO: Also update it on set_xivr
+     */
+    if (state && !masked) {
+        psi->regs[xivr_reg] |= PSIHB_XIVR_PENDING;
+    } else {
+        psi->regs[xivr_reg] &= ~PSIHB_XIVR_PENDING;
+    }
+}
+
+static void pnv_psi_set_xivr(PnvPsiController *psi, uint32_t reg, uint64_t val)
+{
+    ICSState *ics = &psi->ics;
+    uint16_t server;
+    uint8_t prio;
+    uint8_t src;
+
+    psi->regs[reg] = (psi->regs[reg] & PSIHB_XIVR_PENDING) |
+            (val & (PSIHB_XIVR_SERVER_MSK |
+                    PSIHB_XIVR_PRIO_MSK |
+                    PSIHB_XIVR_SRC_MSK));
+    val = psi->regs[reg];
+    server = (val & PSIHB_XIVR_SERVER_MSK) >> PSIHB_XIVR_SERVER_SH;
+    prio = (val & PSIHB_XIVR_PRIO_MSK) >> PSIHB_XIVR_PRIO_SH;
+    src = (val & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH;
+    if (src > PSIHB_IRQ_EXTERNAL) {
+        /* XXX Generate error ? */
+        return;
+    }
+
+    /*
+     * Linux fills the irq xivr with the hw processor id plus the link
+     * bits. shift back to get something valid.
+     */
+    server >>= 2;
+
+    /* Now because of source remapping, weird things can happen
+     * if you change the source number dynamically, our simple ICS
+     * doesn't deal with remapping. So we just poke a different
+     * ICS entry based on what source number was written. This will
+     * do for now but a more accurate implementation would instead
+     * use a fixed server/prio and a remapper of the generated irq.
+     */
+    ics_simple_write_xive(ics, src, server, prio, prio);
+}
+
+static bool pnv_psi_reg_read(PnvPsiController *psi, uint32_t offset,
+                             uint64_t *out_val, bool mmio)
+{
+    switch (offset) {
+    case PSIHB_XSCOM_FIR_RW:
+    case PSIHB_XSCOM_FIRACT0:
+    case PSIHB_XSCOM_FIRACT1:
+    case PSIHB_XSCOM_BAR:
+    case PSIHB_XSCOM_FSPBAR:
+    case PSIHB_XSCOM_CR:
+    case PSIHB_XSCOM_XIVR_PSI:
+    case PSIHB_XSCOM_XIVR_OCC:
+    case PSIHB_XSCOM_XIVR_FSI:
+    case PSIHB_XSCOM_XIVR_LPCI2C:
+    case PSIHB_XSCOM_XIVR_LOCERR:
+    case PSIHB_XSCOM_XIVR_EXT:
+    case PSIHB_XSCOM_IRQ_STAT:
+    case PSIHB_XSCOM_SEMR:
+    case PSIHB_XSCOM_DMA_UPADD:
+    case PSIHB_XSCOM_IRSN:
+        *out_val = psi->regs[offset];
+        return true;
+    }
+    return false;
+}
+
+static bool pnv_psi_reg_write(PnvPsiController *psi, uint32_t offset,
+                              uint64_t val, bool mmio)
+{
+    switch (offset) {
+    case PSIHB_XSCOM_FIR_RW:
+    case PSIHB_XSCOM_FIRACT0:
+    case PSIHB_XSCOM_FIRACT1:
+    case PSIHB_XSCOM_SEMR:
+    case PSIHB_XSCOM_DMA_UPADD:
+        psi->regs[offset] = val;
+        return true;
+    case PSIHB_XSCOM_FIR_OR:
+        psi->regs[PSIHB_XSCOM_FIR_RW] |= val;
+        return true;
+    case PSIHB_XSCOM_FIR_AND:
+        psi->regs[PSIHB_XSCOM_FIR_RW] &= val;
+        return true;
+    case PSIHB_XSCOM_BAR:
+        /* Only XSCOM can write this one */
+        if (!mmio) {
+            pnv_psi_set_bar(psi, val);
+        }
+        return true;
+    case PSIHB_XSCOM_FSPBAR:
+        psi->regs[PSIHB_XSCOM_BAR] = val & 0x0003ffff00000000;
+        pnv_psi_update_fsp_mr(psi);
+        return true;
+    case PSIHB_XSCOM_CR:
+        pnv_psi_set_cr(psi, val);
+        return true;
+    case PSIHB_XSCOM_SCR:
+        pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] | val);
+        return true;
+    case PSIHB_XSCOM_CCR:
+        pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] & ~val);
+        return true;
+    case PSIHB_XSCOM_XIVR_PSI:
+    case PSIHB_XSCOM_XIVR_OCC:
+    case PSIHB_XSCOM_XIVR_FSI:
+    case PSIHB_XSCOM_XIVR_LPCI2C:
+    case PSIHB_XSCOM_XIVR_LOCERR:
+    case PSIHB_XSCOM_XIVR_EXT:
+        pnv_psi_set_xivr(psi, offset, val);
+        return true;
+    case PSIHB_XSCOM_IRQ_STAT:
+        /* Read only, should we generate an error ? */
+        return true;
+    case PSIHB_XSCOM_IRSN:
+        pnv_psi_set_irsn(psi, val);
+        return true;
+    }
+    return false;
+}
+
+static uint64_t pnv_psi_mmio_read(void *opaque, hwaddr addr, unsigned size)
+{
+    PnvPsiController *psi = opaque;
+    uint32_t xscom_off;
+    uint64_t val;
+
+    if (size != 8) {
+        goto fail;
+    }
+
+    addr &= (PNV_PSIHB_BAR_SIZE - 1);
+
+    if (addr >= PSIHB_MMIO_MAX) {
+        goto fail;
+    }
+    xscom_off = psi_mmio_to_xscom[addr / 8];
+    if (xscom_off == 0) {
+        goto fail;
+    }
+    if (pnv_psi_reg_read(psi, xscom_off, &val, true)) {
+        return val;
+    }
+ fail:
+    return 0xffffffffffffffffull;
+}
+
+static void pnv_psi_mmio_write(void *opaque, hwaddr addr,
+                              uint64_t val, unsigned size)
+{
+    PnvPsiController *psi = opaque;
+    uint32_t xscom_off;
+
+    if (size != 8) {
+        return;
+    }
+
+    addr &= (PNV_PSIHB_BAR_SIZE - 1);
+
+    if (addr >= PSIHB_MMIO_MAX) {
+        return;
+    }
+    xscom_off = psi_mmio_to_xscom[addr / 8];
+    if (xscom_off == 0) {
+        return;
+    }
+    pnv_psi_reg_write(psi, xscom_off, val, true);
+}
+
+static const MemoryRegionOps psi_mmio_ops = {
+    .read = pnv_psi_mmio_read,
+    .write = pnv_psi_mmio_write,
+    .endianness = DEVICE_BIG_ENDIAN,
+    .valid = {
+        .min_access_size = 8,
+        .max_access_size = 8,
+    },
+    .impl = {
+        .min_access_size = 8,
+        .max_access_size = 8,
+    },
+};
+
+static uint64_t pnv_psi_xscom_read(void *opaque, hwaddr addr, unsigned size)
+{
+    PnvPsiController *psi = PNV_PSI(opaque);
+    uint32_t offset = addr >> 3;
+    uint64_t val = 0;
+
+    pnv_psi_reg_read(psi, offset, &val, false);
+
+    return val;
+}
+
+static void pnv_psi_xscom_write(void *opaque, hwaddr addr,
+                                uint64_t val, unsigned size)
+{
+    PnvPsiController *psi = PNV_PSI(opaque);
+    uint32_t offset = addr >> 3;
+
+    pnv_psi_reg_write(psi, offset, val, false);
+}
+
+static const MemoryRegionOps pnv_psi_xscom_ops = {
+    .read = pnv_psi_xscom_read,
+    .write = pnv_psi_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_psi_init(Object *obj)
+{
+    PnvPsiController *psi = PNV_PSI(obj);
+
+    object_initialize(&psi->ics, sizeof(psi->ics), TYPE_ICS_SIMPLE);
+    object_property_add_child(obj, "ics-psi", OBJECT(&psi->ics), NULL);
+}
+
+static void pnv_psi_realize(DeviceState *dev, Error **errp)
+{
+    PnvPsiController *psi = PNV_PSI(dev);
+    ICSState *ics = &psi->ics;
+    Error *error = NULL;
+    unsigned int i;
+
+    /* Initialize MMIO region */
+    memory_region_init_io(&psi->regs_mr, OBJECT(dev), &psi_mmio_ops, psi,
+                          "psihb", PNV_PSIHB_BAR_SIZE);
+
+    /* Default BAR. Use object properties ? */
+    pnv_psi_set_bar(psi, PNV_PSIHB_BAR | PSIHB_BAR_EN);
+
+    /* Default sources in XIVR */
+    psi->regs[PSIHB_XSCOM_XIVR_PSI] = PSIHB_XIVR_PRIO_MSK |
+            (0ull << PSIHB_XIVR_SRC_SH);
+    psi->regs[PSIHB_XSCOM_XIVR_OCC] = PSIHB_XIVR_PRIO_MSK |
+            (1ull << PSIHB_XIVR_SRC_SH);
+    psi->regs[PSIHB_XSCOM_XIVR_FSI] = PSIHB_XIVR_PRIO_MSK |
+            (2ull << PSIHB_XIVR_SRC_SH);
+    psi->regs[PSIHB_XSCOM_XIVR_LPCI2C] = PSIHB_XIVR_PRIO_MSK |
+            (3ull << PSIHB_XIVR_SRC_SH);
+    psi->regs[PSIHB_XSCOM_XIVR_LOCERR] = PSIHB_XIVR_PRIO_MSK |
+            (4ull << PSIHB_XIVR_SRC_SH);
+    psi->regs[PSIHB_XSCOM_XIVR_EXT] = PSIHB_XIVR_PRIO_MSK |
+            (5ull << PSIHB_XIVR_SRC_SH);
+
+    /*
+     * PSI interrupt control source
+     * FIXME: nr_irqs should a property
+     */
+    ics->nr_irqs = PSI_NUM_INTERRUPTS;
+    object_property_set_bool(OBJECT(ics), true, "realized", &error);
+    if (error) {
+        error_propagate(errp, error);
+        return;
+    }
+
+    for (i = 0; i < ics->nr_irqs; i++) {
+        ics_set_irq_type(ics, i, true);
+    }
+
+    /* XScom region for PSI registers */
+    memory_region_init_io(&psi->xscom_regs, OBJECT(dev), &pnv_psi_xscom_ops,
+                psi, "xscom-psi", PNV_XSCOM_PSI_SIZE << 3);
+}
+
+static int pnv_psi_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset)
+{
+    const char compat[] = "ibm,power8-psihb-x\0ibm,psihb-x";
+    char *name;
+    int offset;
+    uint32_t lpc_pcba = PNV_XSCOM_PSI_BASE;
+    uint32_t reg[] = {
+        cpu_to_be32(lpc_pcba),
+        cpu_to_be32(PNV_XSCOM_PSI_SIZE)
+    };
+
+    name = g_strdup_printf("psihb@%x", lpc_pcba);
+    offset = fdt_add_subnode(fdt, xscom_offset, name);
+    _FDT(offset);
+    g_free(name);
+
+    _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
+
+    _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2)));
+    _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1)));
+    _FDT((fdt_setprop(fdt, offset, "compatible", compat,
+                      sizeof(compat))));
+    return 0;
+}
+
+
+static void pnv_psi_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
+
+    xdc->populate = pnv_psi_populate;
+
+    dc->realize = pnv_psi_realize;
+}
+
+static const TypeInfo pnv_psi_info = {
+    .name          = TYPE_PNV_PSI,
+    .parent        = TYPE_DEVICE,
+    .instance_size = sizeof(PnvPsiController),
+    .instance_init = pnv_psi_init,
+    .class_init    = pnv_psi_class_init,
+    .interfaces    = (InterfaceInfo[]) {
+        { TYPE_PNV_XSCOM_INTERFACE },
+        { }
+    }
+};
+
+static void pnv_psi_register_types(void)
+{
+    type_register_static(&pnv_psi_info);
+}
+
+type_init(pnv_psi_register_types)
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index 73d26c55d993..473fd2318d87 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -24,6 +24,7 @@
 #include "hw/ppc/pnv_xscom.h"
 #include "hw/ppc/pnv_lpc.h"
 #include "hw/ppc/xics.h"
+#include "hw/ppc/pnv_psi.h"
 
 #define TYPE_PNV_CHIP "powernv-chip"
 #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
@@ -57,6 +58,7 @@ typedef struct PnvChip {
 
     PnvLpcController lpc;
     XICSNative   xics;
+    PnvPsiController psi;
 } PnvChip;
 
 typedef struct PnvChipClass {
@@ -146,4 +148,8 @@ typedef struct PnvMachineState {
 #define PNV_XICS_BASE         0x0003ffff80000000ull
 #define PNV_XICS_SIZE         0x0000000000100000ull
 
+#define PNV_PSIHB_BAR         0x0003fffe80000000ull
+#define PNV_PSIHB_BAR_SIZE    0x0000000000100000ull
+
+
 #endif /* _PPC_PNV_H */
diff --git a/include/hw/ppc/pnv_psi.h b/include/hw/ppc/pnv_psi.h
new file mode 100644
index 000000000000..30bb9bc71839
--- /dev/null
+++ b/include/hw/ppc/pnv_psi.h
@@ -0,0 +1,64 @@
+/*
+ * QEMU PowerPC PowerNV PSI controller
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _PPC_PNV_PSI_H
+#define _PPC_PNV_PSI_H
+
+#include "hw/ppc/pnv_xscom.h"
+#include "hw/ppc/pnv_psi.h"
+
+#define TYPE_PNV_PSI "pnv-psi"
+#define PNV_PSI(obj) \
+     OBJECT_CHECK(PnvPsiController, (obj), TYPE_PNV_PSI)
+
+#define PSIHB_XSCOM_MAX         0x20
+
+typedef struct XICSState XICSState;
+
+typedef struct PnvPsiController {
+    DeviceState parent;
+
+    MemoryRegion regs_mr;
+
+    /* FSP region not supported */
+    /* MemoryRegion fsp_mr; */
+
+    /* Interrupt generation */
+    ICSState ics;
+
+    /* Registers */
+    uint64_t regs[PSIHB_XSCOM_MAX];
+
+    MemoryRegion xscom_regs;
+} PnvPsiController;
+
+typedef enum PnvPsiIrq {
+    PSIHB_IRQ_PSI, /* internal use only */
+    PSIHB_IRQ_FSP, /* internal use only */
+    PSIHB_IRQ_OCC,
+    PSIHB_IRQ_FSI,
+    PSIHB_IRQ_LPC_I2C,
+    PSIHB_IRQ_LOCAL_ERR,
+    PSIHB_IRQ_EXTERNAL,
+} PnvPsiIrq;
+
+#define PSI_NUM_INTERRUPTS 6
+
+extern void pnv_psi_irq_set(PnvPsiController *psi, PnvPsiIrq irq, bool state);
+
+#endif /* _PPC_PNV_PSI_H */
diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
index 3b8a8b4fc6ff..28fbec8ee8e0 100644
--- a/include/hw/ppc/pnv_xscom.h
+++ b/include/hw/ppc/pnv_xscom.h
@@ -63,6 +63,9 @@ typedef struct PnvXScomInterfaceClass {
 #define PNV_XSCOM_LPC_BASE        0xb0020
 #define PNV_XSCOM_LPC_SIZE        0x4
 
+#define PNV_XSCOM_PSI_BASE        0x2010900
+#define PNV_XSCOM_PSI_SIZE        0x20
+
 extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
 extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 18/20] ppc/pnv: Add OCC model stub with interrupt support
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (16 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 17/20] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-14  6:34   ` David Gibson
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 19/20] ppc/pnv: Add Naples chip support for LPC interrupts Cédric Le Goater
                   ` (2 subsequent siblings)
  20 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

The OCC is an on-chip microcontroller based on a ppc405 core used
for various power management tasks. It comes with a pile of additional
hardware sitting on the PIB (aka XSCOM bus). At this point we don't
emulate it (nor plan to do so). However there is one facility which
is provided by the surrounding hardware that we do need, which is the
interrupt generation facility. OPAL uses it to send itself interrupts
under some circumstances and there are other uses around the corner.

So this implement just enough to support this.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
[clg: - updated for qemu-2.7
      - changed the XSCOM interface to fit new model
      - QOMified the model ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/ppc/Makefile.objs       |   2 +-
 hw/ppc/pnv.c               |  11 ++++
 hw/ppc/pnv_occ.c           | 135 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h       |   2 +
 include/hw/ppc/pnv_occ.h   |  38 +++++++++++++
 include/hw/ppc/pnv_xscom.h |   3 +
 6 files changed, 190 insertions(+), 1 deletion(-)
 create mode 100644 hw/ppc/pnv_occ.c
 create mode 100644 include/hw/ppc/pnv_occ.h

diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index 4feb15b360c8..35b11cf887d5 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
 obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
 obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
 # IBM PowerNV
-obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o
+obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o
 ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
 obj-y += spapr_pci_vfio.o
 endif
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index b17e205c74db..e805e97d4d87 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -651,6 +651,11 @@ static void pnv_chip_init(Object *obj)
 
     object_initialize(&chip->psi, sizeof(chip->psi), TYPE_PNV_PSI);
     object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL);
+
+    object_initialize(&chip->occ, sizeof(chip->occ), TYPE_PNV_OCC);
+    object_property_add_child(obj, "occ", OBJECT(&chip->occ), NULL);
+    object_property_add_const_link(OBJECT(&chip->occ), "psi",
+                                   OBJECT(&chip->psi), &error_abort);
 }
 
 static void pnv_chip_realize(DeviceState *dev, Error **errp)
@@ -740,6 +745,12 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
                              &error_fatal);
     memory_region_add_subregion(&chip->xscom, PNV_XSCOM_LPC_BASE << 3,
                                 &chip->lpc.xscom_regs);
+
+    /* Create the simplified OCC model */
+    object_property_set_bool(OBJECT(&chip->occ), true, "realized",
+                             &error_fatal);
+    memory_region_add_subregion(&chip->xscom, PNV_XSCOM_OCC_BASE << 3,
+                                &chip->occ.xscom_regs);
 }
 
 static Property pnv_chip_properties[] = {
diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c
new file mode 100644
index 000000000000..250517cca0ef
--- /dev/null
+++ b/hw/ppc/pnv_occ.c
@@ -0,0 +1,135 @@
+/*
+ * QEMU PowerNV Emulation of a few OCC related registers
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "sysemu/sysemu.h"
+#include "target-ppc/cpu.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+
+#include "hw/ppc/pnv.h"
+#include "hw/ppc/pnv_occ.h"
+
+#define OCB_OCI_OCCMISC         0x4020
+#define OCB_OCI_OCCMISC_AND     0x4021
+#define OCB_OCI_OCCMISC_OR      0x4022
+
+static void pnv_occ_set_misc(PnvOCC *occ, uint64_t val)
+{
+    bool irq_state;
+
+    val &= 0xffff000000000000ull;
+
+    occ->occmisc = val;
+    irq_state = !!(val >> 63);
+    pnv_psi_irq_set(occ->psi, PSIHB_IRQ_OCC, irq_state);
+}
+
+static uint64_t pnv_occ_xscom_read(void *opaque, hwaddr addr, unsigned size)
+{
+    PnvOCC *occ = PNV_OCC(opaque);
+    uint32_t offset = addr >> 3;
+    uint64_t val = 0;
+
+    switch (offset) {
+    case OCB_OCI_OCCMISC:
+        val = occ->occmisc;
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%"
+                      HWADDR_PRIx "\n", addr);
+    }
+    return val;
+}
+
+static void pnv_occ_xscom_write(void *opaque, hwaddr addr,
+                                uint64_t val, unsigned size)
+{
+    PnvOCC *occ = PNV_OCC(opaque);
+    uint32_t offset = addr >> 3;
+
+    switch (offset) {
+    case OCB_OCI_OCCMISC_AND:
+        pnv_occ_set_misc(occ, occ->occmisc & val);
+        break;
+    case OCB_OCI_OCCMISC_OR:
+        pnv_occ_set_misc(occ, occ->occmisc | val);
+        break;
+    case OCB_OCI_OCCMISC:
+        pnv_occ_set_misc(occ, val);
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%"
+                      HWADDR_PRIx "\n", addr);
+    }
+}
+
+static const MemoryRegionOps pnv_occ_xscom_ops = {
+    .read = pnv_occ_xscom_read,
+    .write = pnv_occ_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_occ_realize(DeviceState *dev, Error **errp)
+{
+    PnvOCC *occ = PNV_OCC(dev);
+    Object *obj;
+    Error *error = NULL;
+
+    occ->occmisc = 0;
+
+    /* get PSI object from chip */
+    obj = object_property_get_link(OBJECT(dev), "psi", &error);
+    if (!obj) {
+        error_setg(errp, "%s: required link 'psi' not found: %s",
+                   __func__, error_get_pretty(error));
+        return;
+    }
+    occ->psi = PNV_PSI(obj);
+
+    /* XScom region for OCC registers */
+    memory_region_init_io(&occ->xscom_regs, OBJECT(dev), &pnv_occ_xscom_ops,
+                  occ, "xscom-occ", PNV_XSCOM_OCC_SIZE << 3);
+}
+
+static void pnv_occ_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = pnv_occ_realize;
+}
+
+static const TypeInfo pnv_occ_type_info = {
+    .name          = TYPE_PNV_OCC,
+    .parent        = TYPE_DEVICE,
+    .instance_size = sizeof(PnvOCC),
+    .class_init    = pnv_occ_class_init,
+};
+
+static void pnv_occ_register_types(void)
+{
+    type_register_static(&pnv_occ_type_info);
+}
+
+type_init(pnv_occ_register_types)
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index 473fd2318d87..ed3316501326 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -25,6 +25,7 @@
 #include "hw/ppc/pnv_lpc.h"
 #include "hw/ppc/xics.h"
 #include "hw/ppc/pnv_psi.h"
+#include "hw/ppc/pnv_occ.h"
 
 #define TYPE_PNV_CHIP "powernv-chip"
 #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
@@ -59,6 +60,7 @@ typedef struct PnvChip {
     PnvLpcController lpc;
     XICSNative   xics;
     PnvPsiController psi;
+    PnvOCC           occ;
 } PnvChip;
 
 typedef struct PnvChipClass {
diff --git a/include/hw/ppc/pnv_occ.h b/include/hw/ppc/pnv_occ.h
new file mode 100644
index 000000000000..54e760df7c4f
--- /dev/null
+++ b/include/hw/ppc/pnv_occ.h
@@ -0,0 +1,38 @@
+/*
+ * QEMU PowerNV Emulation of a few OCC related registers
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _PPC_PNV_OCC_H
+#define _PPC_PNV_OCC_H
+
+#define TYPE_PNV_OCC "pnv-occ"
+#define PNV_OCC(obj) OBJECT_CHECK(PnvOCC, (obj), TYPE_PNV_OCC)
+
+typedef struct PnvPsiController PnvPsiController;
+
+typedef struct PnvOCC {
+    DeviceState xd;
+
+    /* OCC Misc interrupt */
+    uint64_t occmisc;
+
+    PnvPsiController *psi;
+
+    MemoryRegion xscom_regs;
+} PnvOCC;
+
+#endif /* _PPC_PNV_OCC_H */
diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
index 28fbec8ee8e0..f22906dc2fff 100644
--- a/include/hw/ppc/pnv_xscom.h
+++ b/include/hw/ppc/pnv_xscom.h
@@ -66,6 +66,9 @@ typedef struct PnvXScomInterfaceClass {
 #define PNV_XSCOM_PSI_BASE        0x2010900
 #define PNV_XSCOM_PSI_SIZE        0x20
 
+#define PNV_XSCOM_OCC_BASE        0x0066000
+#define PNV_XSCOM_OCC_SIZE        0x6000
+
 extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
 extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 19/20] ppc/pnv: Add Naples chip support for LPC interrupts
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (17 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 18/20] ppc/pnv: Add OCC model stub with interrupt support Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-14  6:36   ` David Gibson
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 20/20] ppc/pnv: add support for POWER9 LPC Controller Cédric Le Goater
  2016-10-03  7:59 ` [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space no-reply
  20 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

It adds the Naples chip which supports proper LPC interrupts via the
LPC controller rather than via an external CPLD.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
[clg: - updated for qemu-2.7
      - ported on latest PowerNV patchset (v3) ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/ppc/pnv.c             | 18 +++++++++++++++++-
 hw/ppc/pnv_lpc.c         | 47 +++++++++++++++++++++++++++++++++++++++++++++--
 include/hw/ppc/pnv_lpc.h |  7 +++++++
 3 files changed, 69 insertions(+), 3 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index e805e97d4d87..5b70ccf66fac 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -340,7 +340,17 @@ static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
 
 static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
 {
-     /* XXX TODO */
+    PnvLpcController *lpc = opaque;
+
+    if (n >= ISA_NUM_IRQS) {
+        return;
+    }
+
+    /* The Naples HW latches the 1 levels, clearing is done by SW */
+    if (level) {
+        lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SERIRQ0 >> n;
+        pnv_lpc_eval_irqs(lpc);
+    }
 }
 
 static ISABus *pnv_isa_create(PnvChip *chip)
@@ -656,6 +666,12 @@ static void pnv_chip_init(Object *obj)
     object_property_add_child(obj, "occ", OBJECT(&chip->occ), NULL);
     object_property_add_const_link(OBJECT(&chip->occ), "psi",
                                    OBJECT(&chip->psi), &error_abort);
+
+    /*
+     * The LPC controller needs PSI to generate interrupts
+     */
+    object_property_add_const_link(OBJECT(&chip->lpc), "psi",
+                                   OBJECT(&chip->psi), &error_abort);
 }
 
 static void pnv_chip_realize(DeviceState *dev, Error **errp)
diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
index 210cc1cff167..8b78b0a1e414 100644
--- a/hw/ppc/pnv_lpc.c
+++ b/hw/ppc/pnv_lpc.c
@@ -249,6 +249,34 @@ static const MemoryRegionOps pnv_lpc_xscom_ops = {
     .endianness = DEVICE_BIG_ENDIAN,
 };
 
+void pnv_lpc_eval_irqs(PnvLpcController *lpc)
+{
+    bool lpc_to_opb_irq = false;
+
+    /* Update LPC controller to OPB line */
+    if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) {
+        uint32_t irqs;
+
+        irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask;
+        lpc_to_opb_irq = (irqs != 0);
+    }
+
+    /* We don't honor the polarity register, it's pointless and unused
+     * anyway
+     */
+    if (lpc_to_opb_irq) {
+        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;
+
+    /* Reflect the interrupt */
+    pnv_psi_irq_set(lpc->psi, PSIHB_IRQ_LPC_I2C, lpc->opb_irq_stat != 0);
+}
+
 static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size)
 {
     PnvLpcController *lpc = opaque;
@@ -299,12 +327,15 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val,
         break;
     case LPC_HC_IRQSER_CTRL:
         lpc->lpc_hc_irqser_ctrl = val;
+        pnv_lpc_eval_irqs(lpc);
         break;
     case LPC_HC_IRQMASK:
         lpc->lpc_hc_irqmask = val;
+        pnv_lpc_eval_irqs(lpc);
         break;
     case LPC_HC_IRQSTAT:
         lpc->lpc_hc_irqstat &= ~val;
+        pnv_lpc_eval_irqs(lpc);
         break;
     case LPC_HC_ERROR_ADDRESS:
         break;
@@ -362,14 +393,15 @@ static void opb_master_write(void *opaque, hwaddr addr,
     switch (addr) {
     case OPB_MASTER_LS_IRQ_STAT:
         lpc->opb_irq_stat &= ~val;
+        pnv_lpc_eval_irqs(lpc);
         break;
     case OPB_MASTER_LS_IRQ_MASK:
-        /* XXX Filter out reserved bits */
         lpc->opb_irq_mask = val;
+        pnv_lpc_eval_irqs(lpc);
         break;
     case OPB_MASTER_LS_IRQ_POL:
-        /* XXX Filter out reserved bits */
         lpc->opb_irq_pol = val;
+        pnv_lpc_eval_irqs(lpc);
         break;
     case OPB_MASTER_LS_IRQ_INPUT:
         /* Read only */
@@ -397,6 +429,8 @@ static const MemoryRegionOps opb_master_ops = {
 static void pnv_lpc_realize(DeviceState *dev, Error **errp)
 {
     PnvLpcController *lpc = PNV_LPC(dev);
+    Object *obj;
+    Error *error = NULL;
 
     /* Reg inits */
     lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B;
@@ -440,6 +474,15 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp)
     memory_region_init_io(&lpc->xscom_regs, OBJECT(dev),
                           &pnv_lpc_xscom_ops, lpc, "xscom-lpc",
                           PNV_XSCOM_LPC_SIZE << 3);
+
+    /* get PSI object from chip */
+    obj = object_property_get_link(OBJECT(dev), "psi", &error);
+    if (!obj) {
+        error_setg(errp, "%s: required link 'psi' not found: %s",
+                   __func__, error_get_pretty(error));
+        return;
+    }
+    lpc->psi = PNV_PSI(obj);
 }
 
 static void pnv_lpc_class_init(ObjectClass *klass, void *data)
diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h
index 38e5506975aa..fc348dca50ca 100644
--- a/include/hw/ppc/pnv_lpc.h
+++ b/include/hw/ppc/pnv_lpc.h
@@ -23,9 +23,13 @@
 #define PNV_LPC(obj) \
      OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC)
 
+typedef struct PnvPsiController PnvPsiController;
+
 typedef struct PnvLpcController {
     DeviceState parent;
 
+    PnvPsiController *psi;
+
     uint64_t eccb_stat_reg;
     uint32_t eccb_data_reg;
 
@@ -64,4 +68,7 @@ typedef struct PnvLpcController {
     MemoryRegion xscom_regs;
 } PnvLpcController;
 
+#define   LPC_HC_IRQ_SERIRQ0            0x80000000 /* all bits down to ... */
+void pnv_lpc_eval_irqs(PnvLpcController *lpc);
+
 #endif /* _PPC_PNV_LPC_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH v4 20/20] ppc/pnv: add support for POWER9 LPC Controller
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (18 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 19/20] ppc/pnv: Add Naples chip support for LPC interrupts Cédric Le Goater
@ 2016-10-03  7:24 ` Cédric Le Goater
  2016-10-14  6:43   ` David Gibson
  2016-10-03  7:59 ` [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space no-reply
  20 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  7:24 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

The LPC Controller on POWER9 is very similar to the one found on
POWER8 but accesses are now done via on MMIOs, without the XSCOM and
ECCB logic. The device tree is populated differently so we add a
PnvLpcClass and a couple of methods for the purpose.

LPC interrupts routing is missing. This is Work In Progress but it
gives us a P9 console and, more important, a first idea of what
changes we should consider for the following models.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/ppc/pnv.c             |  32 +++---
 hw/ppc/pnv_lpc.c         | 255 ++++++++++++++++++++++++++++++++++++++++++++---
 include/hw/ppc/pnv.h     |   9 +-
 include/hw/ppc/pnv_lpc.h |  33 ++++++
 4 files changed, 304 insertions(+), 25 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 5b70ccf66fac..d5ef4f8ddb53 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -237,6 +237,8 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
         xics_native_populate_icp(chip, fdt, 0, pnv_core->pir, smt);
     }
 
+    pnv_lpc_populate(chip, fdt, 0);
+
     /* Put all the memory in one node on chip 0 until we find a way to
      * specify different ranges for each chip
      */
@@ -355,7 +357,7 @@ static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
 
 static ISABus *pnv_isa_create(PnvChip *chip)
 {
-    PnvLpcController *lpc = &chip->lpc;
+    PnvLpcController *lpc = chip->lpc;
     ISABus *isa_bus;
     qemu_irq *irqs;
     PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
@@ -653,9 +655,6 @@ static void pnv_chip_init(Object *obj)
 
     chip->xscom_base = pcc->xscom_base;
 
-    object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
-    object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
-
     object_initialize(&chip->xics, sizeof(chip->xics), TYPE_XICS_NATIVE);
     object_property_add_child(obj, "xics", OBJECT(&chip->xics), NULL);
 
@@ -667,11 +666,6 @@ static void pnv_chip_init(Object *obj)
     object_property_add_const_link(OBJECT(&chip->occ), "psi",
                                    OBJECT(&chip->psi), &error_abort);
 
-    /*
-     * The LPC controller needs PSI to generate interrupts
-     */
-    object_property_add_const_link(OBJECT(&chip->lpc), "psi",
-                                   OBJECT(&chip->psi), &error_abort);
 }
 
 static void pnv_chip_realize(DeviceState *dev, Error **errp)
@@ -757,10 +751,24 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
     xics_insert_ics(XICS_COMMON(&chip->xics), &chip->psi.ics);
 
     /* Create LPC controller */
-    object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
+    typename = g_strdup_printf(TYPE_PNV_LPC "-%s", pcc->cpu_model);
+    chip->lpc = PNV_LPC(object_new(typename));
+    object_property_add_child(OBJECT(chip), "lpc", OBJECT(chip->lpc), NULL);
+
+    /* The LPC controller needs PSI to generate interrupts  */
+    object_property_add_const_link(OBJECT(chip->lpc), "psi",
+                                   OBJECT(&chip->psi), &error_abort);
+    object_property_set_bool(OBJECT(chip->lpc), true, "realized",
                              &error_fatal);
-    memory_region_add_subregion(&chip->xscom, PNV_XSCOM_LPC_BASE << 3,
-                                &chip->lpc.xscom_regs);
+
+    if (PNV_CHIP_GET_CLASS(chip)->chip_type != PNV_CHIP_POWER9) {
+        memory_region_add_subregion(&chip->xscom, PNV_XSCOM_LPC_BASE << 3,
+                                    &chip->lpc->xscom_regs);
+    } else {
+        memory_region_add_subregion(get_system_memory(), PNV_POWER9_LPCM_BASE,
+                                    &chip->lpc->xscom_regs);
+    }
+
 
     /* Create the simplified OCC model */
     object_property_set_bool(OBJECT(&chip->occ), true, "realized",
diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
index 8b78b0a1e414..93ccc656120c 100644
--- a/hw/ppc/pnv_lpc.c
+++ b/hw/ppc/pnv_lpc.c
@@ -28,6 +28,7 @@
 #include "hw/ppc/fdt.h"
 
 #include <libfdt.h>
+#include "exec/address-spaces.h"
 
 enum {
     ECCB_CTL    = 0,
@@ -91,6 +92,88 @@ enum {
 #define LPC_HC_REGS_OPB_SIZE    0x00001000
 
 
+int pnv_lpc_populate(PnvChip *chip, void *fdt, int offset)
+{
+    PnvLpcClass *plc = PNV_LPC_GET_CLASS(chip->lpc);
+
+    if (plc->populate) {
+        return plc->populate(chip, fdt, offset);
+    }
+    return 0;
+}
+
+static int pnv_lpc_power9_populate(PnvChip *chip, void *fdt, int root_offset)
+{
+    const char compat[] = "ibm,power9-lpcm-opb\0simple-bus";
+    const char lpc_compat[] = "ibm,power9-lpc\0ibm,lpc";
+    char *name;
+    int offset, lpcm_offset;
+    /* should depend on a MMIO base address of the chip */
+    uint64_t lpcm_addr = PNV_POWER9_LPCM_BASE;
+    /* TODO: build the ranges properly, array of :
+     *              { offset, addr hi, addr low, size }
+     */
+    uint32_t ranges[4] = { 0,
+                           cpu_to_be32(lpcm_addr >> 32),
+                           cpu_to_be32((uint32_t)lpcm_addr),
+                           cpu_to_be32(PNV_POWER9_LPCM_SIZE / 2),
+    };
+    uint32_t reg[2];
+
+    /*
+     * OPB bus
+     */
+    name = g_strdup_printf("lpcm-opb@%"PRIx64, lpcm_addr);
+    lpcm_offset = fdt_add_subnode(fdt, root_offset, name);
+    _FDT(lpcm_offset);
+    g_free(name);
+    _FDT((fdt_setprop_cell(fdt, lpcm_offset, "#address-cells", 1)));
+    _FDT((fdt_setprop_cell(fdt, lpcm_offset, "#size-cells", 1)));
+    _FDT((fdt_setprop(fdt, lpcm_offset, "compatible", compat, sizeof(compat))));
+    _FDT((fdt_setprop_cell(fdt, lpcm_offset, "ibm,chip-id", chip->chip_id)));
+   _FDT((fdt_setprop(fdt, lpcm_offset, "ranges", ranges, sizeof(ranges))));
+
+    /*
+     * OPB Master registers
+     */
+    name = g_strdup_printf("opb-master@%x", LPC_OPB_REGS_OPB_ADDR);
+    offset = fdt_add_subnode(fdt, lpcm_offset, name);
+    _FDT(offset);
+    g_free(name);
+
+    reg[0] = cpu_to_be32(LPC_OPB_REGS_OPB_ADDR);
+    reg[1] = cpu_to_be32(LPC_OPB_REGS_OPB_SIZE);
+    _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
+    _FDT((fdt_setprop_string(fdt, offset, "compatible",
+                             "ibm,power9-lpcm-opb-master")));
+
+    /*
+     * LPC Host Controller registers
+     */
+    name = g_strdup_printf("lpc-controller@%x", LPC_HC_REGS_OPB_ADDR);
+    offset = fdt_add_subnode(fdt, lpcm_offset, name);
+    _FDT(offset);
+    g_free(name);
+
+    reg[0] = cpu_to_be32(LPC_HC_REGS_OPB_ADDR);
+    reg[1] = cpu_to_be32(LPC_HC_REGS_OPB_SIZE);
+    _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
+    _FDT((fdt_setprop_string(fdt, offset, "compatible",
+                             "ibm,power9-lpc-controller")));
+
+    name = g_strdup_printf("lpc@%x", LPC_FW_OPB_ADDR);
+    offset = fdt_add_subnode(fdt, lpcm_offset, name);
+    _FDT(offset);
+    g_free(name);
+    _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2)));
+    _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1)));
+    _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0)));
+    _FDT((fdt_setprop(fdt, offset, "compatible", lpc_compat,
+                      sizeof(lpc_compat))));
+
+    return 0;
+}
+
 /*
  * TODO: the "primary" cell should only be added on chip 0. This is
  * how skiboot chooses the default LPC controller on multichip
@@ -99,7 +182,8 @@ enum {
  * It would be easly done if we can change the populate() interface to
  * replace the PnvXScomInterface parameter by a PnvChip one
  */
-static int pnv_lpc_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset)
+static int pnv_lpc_power8_populate(PnvXScomInterface *dev, void *fdt,
+                                   int xscom_offset)
 {
     const char compat[] = "ibm,power8-lpc\0ibm,lpc";
     char *name;
@@ -249,6 +333,74 @@ static const MemoryRegionOps pnv_lpc_xscom_ops = {
     .endianness = DEVICE_BIG_ENDIAN,
 };
 
+static uint64_t pnv_lpc_mmio_read(void *opaque, hwaddr addr, unsigned size)
+{
+    PnvLpcController *lpc = PNV_LPC(opaque);
+    uint64_t val = 0;
+    uint32_t opb_addr = addr & ECCB_CTL_ADDR_MASK;
+    MemTxResult result;
+
+    switch (size) {
+    case 4:
+        val = address_space_ldl(&lpc->opb_as, opb_addr, MEMTXATTRS_UNSPECIFIED,
+                                &result);
+        break;
+    case 1:
+        val = address_space_ldub(&lpc->opb_as, opb_addr, MEMTXATTRS_UNSPECIFIED,
+                                 &result);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "OPB read failed at @0x%"
+                      HWADDR_PRIx " invalid size %d\n", addr, size);
+        return 0;
+    }
+
+    if (result != MEMTX_OK) {
+        qemu_log_mask(LOG_GUEST_ERROR, "OPB read failed at @0x%"
+                      HWADDR_PRIx "\n", addr);
+    }
+
+    return val;
+}
+
+static void pnv_lpc_mmio_write(void *opaque, hwaddr addr,
+                                uint64_t val, unsigned size)
+{
+    PnvLpcController *lpc = PNV_LPC(opaque);
+    uint32_t opb_addr = addr & ECCB_CTL_ADDR_MASK;
+    MemTxResult result;
+
+    switch (size) {
+    case 4:
+        address_space_stl(&lpc->opb_as, opb_addr, val, MEMTXATTRS_UNSPECIFIED,
+                          &result);
+         break;
+    case 1:
+        address_space_stb(&lpc->opb_as, opb_addr, val, MEMTXATTRS_UNSPECIFIED,
+                          &result);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "OPB write failed at @0x%"
+                      HWADDR_PRIx " invalid size %d\n", addr, size);
+        return;
+    }
+
+    if (result != MEMTX_OK) {
+        qemu_log_mask(LOG_GUEST_ERROR, "OPB write failed at @0x%"
+                      HWADDR_PRIx "\n", addr);
+    }
+}
+
+static const MemoryRegionOps pnv_lpc_mmio_ops = {
+    .read = pnv_lpc_mmio_read,
+    .write = pnv_lpc_mmio_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_BIG_ENDIAN,
+};
+
 void pnv_lpc_eval_irqs(PnvLpcController *lpc)
 {
     bool lpc_to_opb_irq = false;
@@ -426,9 +578,29 @@ static const MemoryRegionOps opb_master_ops = {
     },
 };
 
+static void pnv_lpc_power8_realize(DeviceState *dev, Error **errp)
+{
+    PnvLpcController *lpc = PNV_LPC(dev);
+
+    /* XScom region for LPC registers */
+    memory_region_init_io(&lpc->xscom_regs, OBJECT(dev),
+                          &pnv_lpc_xscom_ops, lpc, "xscom-lpc",
+                          PNV_XSCOM_LPC_SIZE << 3);
+}
+
+static void pnv_lpc_power9_realize(DeviceState *dev, Error **errp)
+{
+    PnvLpcController *lpc = PNV_LPC(dev);
+
+    /* Initialize MMIO region */
+    memory_region_init_io(&lpc->xscom_regs, OBJECT(dev), &pnv_lpc_mmio_ops, lpc,
+                          "lpcm", PNV_POWER9_LPCM_SIZE);
+}
+
 static void pnv_lpc_realize(DeviceState *dev, Error **errp)
 {
     PnvLpcController *lpc = PNV_LPC(dev);
+    PnvLpcClass *plc = PNV_LPC_GET_CLASS(dev);
     Object *obj;
     Error *error = NULL;
 
@@ -470,11 +642,6 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp)
     memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR,
                                 &lpc->lpc_hc_regs);
 
-    /* XScom region for LPC registers */
-    memory_region_init_io(&lpc->xscom_regs, OBJECT(dev),
-                          &pnv_lpc_xscom_ops, lpc, "xscom-lpc",
-                          PNV_XSCOM_LPC_SIZE << 3);
-
     /* get PSI object from chip */
     obj = object_property_get_link(OBJECT(dev), "psi", &error);
     if (!obj) {
@@ -483,32 +650,96 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp)
         return;
     }
     lpc->psi = PNV_PSI(obj);
+
+    plc->realize(dev, errp);
 }
 
-static void pnv_lpc_class_init(ObjectClass *klass, void *data)
+static void pnv_lpc_power8_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
     PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
+    PnvLpcClass *plc = PNV_LPC_CLASS(klass);
 
-    xdc->populate = pnv_lpc_populate;
+    xdc->populate = pnv_lpc_power8_populate;
 
     dc->realize = pnv_lpc_realize;
+    plc->realize = pnv_lpc_power8_realize;
 }
 
-static const TypeInfo pnv_lpc_info = {
-    .name          = TYPE_PNV_LPC,
-    .parent        = TYPE_DEVICE,
+static const TypeInfo pnv_lpc_power8e_info = {
+    .name          = TYPE_PNV_LPC_POWER8E,
+    .parent        = TYPE_PNV_LPC,
     .instance_size = sizeof(PnvLpcController),
-    .class_init    = pnv_lpc_class_init,
+    .class_init    = pnv_lpc_power8_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_PNV_XSCOM_INTERFACE },
+        { }
+    }
+};
+
+static const TypeInfo pnv_lpc_power8_info = {
+    .name          = TYPE_PNV_LPC_POWER8,
+    .parent        = TYPE_PNV_LPC,
+    .instance_size = sizeof(PnvLpcController),
+    .class_init    = pnv_lpc_power8_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_PNV_XSCOM_INTERFACE },
+        { }
+    }
+};
+
+static const TypeInfo pnv_lpc_power8nvl_info = {
+    .name          = TYPE_PNV_LPC_POWER8NVL,
+    .parent        = TYPE_PNV_LPC,
+    .instance_size = sizeof(PnvLpcController),
+    .class_init    = pnv_lpc_power8_class_init,
     .interfaces = (InterfaceInfo[]) {
         { TYPE_PNV_XSCOM_INTERFACE },
         { }
     }
 };
 
+static void pnv_lpc_power9_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PnvLpcClass *plc = PNV_LPC_CLASS(klass);
+
+    dc->realize = pnv_lpc_realize;
+    plc->realize = pnv_lpc_power9_realize;
+    plc->populate = pnv_lpc_power9_populate;
+}
+
+static const TypeInfo pnv_lpc_power9_info = {
+    .name          = TYPE_PNV_LPC_POWER9,
+    .parent        = TYPE_PNV_LPC,
+    .instance_size = sizeof(PnvLpcController),
+    .class_init    = pnv_lpc_power9_class_init,
+};
+
+static void pnv_lpc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = pnv_lpc_realize;
+    dc->desc = "PowerNV LPC Controller";
+}
+
+static const TypeInfo pnv_lpc_info = {
+    .name          = TYPE_PNV_LPC,
+    .parent        = TYPE_DEVICE,
+    .class_init    = pnv_lpc_class_init,
+    .class_size    = sizeof(PnvLpcClass),
+    .abstract      = true,
+};
+
+
 static void pnv_lpc_register_types(void)
 {
     type_register_static(&pnv_lpc_info);
+    type_register_static(&pnv_lpc_power8e_info);
+    type_register_static(&pnv_lpc_power8_info);
+    type_register_static(&pnv_lpc_power8nvl_info);
+    type_register_static(&pnv_lpc_power9_info);
 }
 
 type_init(pnv_lpc_register_types)
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index ed3316501326..361af311a92e 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -57,7 +57,7 @@ typedef struct PnvChip {
     uint64_t  cores_mask;
     void      *cores;
 
-    PnvLpcController lpc;
+    PnvLpcController *lpc;
     XICSNative   xics;
     PnvPsiController psi;
     PnvOCC           occ;
@@ -154,4 +154,11 @@ typedef struct PnvMachineState {
 #define PNV_PSIHB_BAR_SIZE    0x0000000000100000ull
 
 
+/*
+ * POWER9 MMIO regions
+ */
+#define PNV_POWER9_MMIO_BASE   0x006000000000000ull
+#define PNV_POWER9_LPCM_BASE   (PNV_POWER9_MMIO_BASE + 0x30000000000ull)
+#define PNV_POWER9_LPCM_SIZE   0x100000000ull
+
 #endif /* _PPC_PNV_H */
diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h
index fc348dca50ca..510fab2fd115 100644
--- a/include/hw/ppc/pnv_lpc.h
+++ b/include/hw/ppc/pnv_lpc.h
@@ -22,8 +22,13 @@
 #define TYPE_PNV_LPC "pnv-lpc"
 #define PNV_LPC(obj) \
      OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC)
+#define PNV_LPC_CLASS(klass) \
+     OBJECT_CLASS_CHECK(PnvLpcClass, (klass), TYPE_PNV_LPC)
+#define PNV_LPC_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(PnvLpcClass, (obj), TYPE_PNV_LPC)
 
 typedef struct PnvPsiController PnvPsiController;
+typedef struct PnvChip PnvChip;
 
 typedef struct PnvLpcController {
     DeviceState parent;
@@ -68,7 +73,35 @@ typedef struct PnvLpcController {
     MemoryRegion xscom_regs;
 } PnvLpcController;
 
+typedef struct PnvLpcClass {
+    DeviceClass parent_class;
+
+    int (*populate)(PnvChip *chip, void *fdt, int offset);
+    void (*realize)(DeviceState *dev, Error **errp);
+} PnvLpcClass;
+
+
+#define TYPE_PNV_LPC_POWER8E TYPE_PNV_LPC "-POWER8E"
+#define PNV_LPC_POWER8E(obj) \
+    OBJECT_CHECK(PnvLpc, (obj), TYPE_PNV_LPC_POWER8E)
+
+#define TYPE_PNV_LPC_POWER8 TYPE_PNV_LPC "-POWER8"
+#define PNV_LPC_POWER8(obj) \
+    OBJECT_CHECK(PnvLpc, (obj), TYPE_PNV_LPC_POWER8)
+
+#define TYPE_PNV_LPC_POWER8NVL TYPE_PNV_LPC "-POWER8NVL"
+#define PNV_LPC_POWER8NVL(obj) \
+    OBJECT_CHECK(PnvLpc, (obj), TYPE_PNV_LPC_POWER8NVL)
+
+#define TYPE_PNV_LPC_POWER9 TYPE_PNV_LPC "-POWER9"
+#define PNV_LPC_POWER9(obj) \
+    OBJECT_CHECK(PnvLpc, (obj), TYPE_PNV_LPC_POWER9)
+
+
+
 #define   LPC_HC_IRQ_SERIRQ0            0x80000000 /* all bits down to ... */
 void pnv_lpc_eval_irqs(PnvLpcController *lpc);
 
+int pnv_lpc_populate(PnvChip *chip, void *fdt, int root_offset);
+
 #endif /* _PPC_PNV_LPC_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space
  2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
                   ` (19 preceding siblings ...)
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 20/20] ppc/pnv: add support for POWER9 LPC Controller Cédric Le Goater
@ 2016-10-03  7:59 ` no-reply
  2016-10-03  8:21   ` Cédric Le Goater
  20 siblings, 1 reply; 75+ messages in thread
From: no-reply @ 2016-10-03  7:59 UTC (permalink / raw)
  To: clg; +Cc: famz, qemu-ppc, qemu-devel, david

Hi,

Your series seems to have some coding style problems. See output below for
more information:

Type: series
Message-id: 1475479496-16158-1-git-send-email-clg@kaod.org
Subject: [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space

=== TEST SCRIPT BEGIN ===
#!/bin/bash

BASE=base
n=1
total=$(git log --oneline $BASE.. | wc -l)
failed=0

# Useful git options
git config --local diff.renamelimit 0
git config --local diff.renames True

commits="$(git log --format=%H --reverse $BASE..)"
for c in $commits; do
    echo "Checking PATCH $n/$total: $(git show --no-patch --format=%s $c)..."
    if ! git show $c --format=email | ./scripts/checkpatch.pl --mailback -; then
        failed=1
        echo
    fi
    n=$((n+1))
done

exit $failed
=== TEST SCRIPT END ===

Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
From https://github.com/patchew-project/qemu
 * [new tag]         patchew/1475479496-16158-1-git-send-email-clg@kaod.org -> patchew/1475479496-16158-1-git-send-email-clg@kaod.org
Switched to a new branch 'test'
fcda1d2 ppc/pnv: add support for POWER9 LPC Controller
668c237 ppc/pnv: Add Naples chip support for LPC interrupts
49b9443 ppc/pnv: Add OCC model stub with interrupt support
a02fded ppc/pnv: Add cut down PSI bridge model and hookup external interrupt
9f27258 ppc/pnv: add a XICS native to each PowerNV chip
75067d5 ppc/xics: Add "native" XICS subclass
6a7865a ppc/xics: introduce a helper to insert a new ics
e25d4e5 ppc/xics: introduce helpers to find an ICP from some (CPU) index
75e1e0a ppc/xics: Add xics to the monitor "info pic" command
a1d20d4 ppc/xics: Split ICS into ics-base and ics class
1397a8c ppc/xics: Make the ICSState a list
8a8ae07 ppc/pnv: add a ISA bus
32f029b ppc/pnv: add a LPC controller
7a3791a ppc/pnv: add XSCOM handlers to PnvCore
358a0c8 ppc/pnv: add XSCOM infrastructure
bf2c920 ppc/pnv: add a PnvCore object
281f8e4 ppc/pnv: add a PIR handler to PnvChip
1d6f976 ppc/pnv: add a core mask to PnvChip
e1c6449 ppc/pnv: add a PnvChip object
e40906f ppc/pnv: add skeleton PowerNV platform

=== OUTPUT BEGIN ===
Checking PATCH 1/20: ppc/pnv: add skeleton PowerNV platform...
Checking PATCH 2/20: ppc/pnv: add a PnvChip object...
Checking PATCH 3/20: ppc/pnv: add a core mask to PnvChip...
Checking PATCH 4/20: ppc/pnv: add a PIR handler to PnvChip...
Checking PATCH 5/20: ppc/pnv: add a PnvCore object...
Checking PATCH 6/20: ppc/pnv: add XSCOM infrastructure...
Checking PATCH 7/20: ppc/pnv: add XSCOM handlers to PnvCore...
Checking PATCH 8/20: ppc/pnv: add a LPC controller...
Checking PATCH 9/20: ppc/pnv: add a ISA bus...
Checking PATCH 10/20: ppc/xics: Make the ICSState a list...
Checking PATCH 11/20: ppc/xics: Split ICS into ics-base and ics class...
Checking PATCH 12/20: ppc/xics: Add xics to the monitor "info pic" command...
Checking PATCH 13/20: ppc/xics: introduce helpers to find an ICP from some (CPU) index...
Checking PATCH 14/20: ppc/xics: introduce a helper to insert a new ics...
Checking PATCH 15/20: ppc/xics: Add "native" XICS subclass...
ERROR: spaces required around that '&' (ctx:WxV)
#308: FILE: hw/intc/xics_native.c:246:
+                        (gpointer) &xics->ss[cs->cpu_index]);
                                    ^

total: 1 errors, 0 warnings, 403 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

Checking PATCH 16/20: ppc/pnv: add a XICS native to each PowerNV chip...
Checking PATCH 17/20: ppc/pnv: Add cut down PSI bridge model and hookup external interrupt...
Checking PATCH 18/20: ppc/pnv: Add OCC model stub with interrupt support...
Checking PATCH 19/20: ppc/pnv: Add Naples chip support for LPC interrupts...
Checking PATCH 20/20: ppc/pnv: add support for POWER9 LPC Controller...
=== OUTPUT END ===

Test command exited with code: 1


---
Email generated automatically by Patchew [http://patchew.org/].
Please send your feedback to patchew-devel@freelists.org

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space
  2016-10-03  7:59 ` [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space no-reply
@ 2016-10-03  8:21   ` Cédric Le Goater
  0 siblings, 0 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-03  8:21 UTC (permalink / raw)
  To: qemu-devel; +Cc: famz, qemu-ppc, david

> Checking PATCH 14/20: ppc/xics: introduce a helper to insert a new ics...
> Checking PATCH 15/20: ppc/xics: Add "native" XICS subclass...
> ERROR: spaces required around that '&' (ctx:WxV)
> #308: FILE: hw/intc/xics_native.c:246:
> +                        (gpointer) &xics->ss[cs->cpu_index]);
>                                     ^

So we can either remove the cast or extend the 'typeList' array in checkpatch.

C.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 01/20] ppc/pnv: add skeleton PowerNV platform
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 01/20] ppc/pnv: add skeleton PowerNV platform Cédric Le Goater
@ 2016-10-07  4:14   ` David Gibson
  2016-10-07  4:16     ` David Gibson
                       ` (2 more replies)
  0 siblings, 3 replies; 75+ messages in thread
From: David Gibson @ 2016-10-07  4:14 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 14688 bytes --]

On Mon, Oct 03, 2016 at 09:24:37AM +0200, Cédric Le Goater wrote:
> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> 
> The goal is to emulate a PowerNV system at the level of the skiboot
> firmware, which loads the OS and provides some runtime services. Power
> Systems have a lower firmware (HostBoot) that does low level system
> initialization, like DRAM training. This is beyond the scope of what
> qemu will address in a PowerNV guest.
> 
> No devices yet, not even an interrupt controller. Just to get started,
> some RAM to load the skiboot firmware, the kernel and initrd. The
> device tree is fully created in the machine reset op.
> 
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> [clg: - updated for qemu-2.7
>       - replaced fprintf by error_report
>       - used a common definition of _FDT macro
>       - removed VMStateDescription as migration is not yet supported
>       - added IBM Copyright statements
>       - reworked kernel_filename handling
>       - merged PnvSystem and sPowerNVMachineState
>       - removed PHANDLE_XICP
>       - added ppc_create_page_sizes_prop helper
>       - removed nmi support
>       - removed kvm support
>       - updated powernv machine to version 2.8
>       - removed chips and cpus, They will be provided in another patches
>       - added a machine reset routine to initialize the device tree (also)
>       - french has a squelette and english a skeleton.
>       - improved commit log.
>       - reworked prototypes parameters
>       - added a check on the ram size (thanks to Michael Ellerman)
>       - fixed chip-id cell
>       - changed MAX_CPUS to 2048
>       - simplified memory node creation to one node only
>       - removed machine version
>       - rewrote the device tree creation with the fdt "rw" routines
>       - s/sPowerNVMachineState/PnvMachineState/
>       - etc.]
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>  Changes since v3:
> 
>  - fixed printf format for hwaddr
>  - used fdt_pack() before writing the tree in memory 
>  - removed the requirement on having a kernel loaded as running with
>    just a firmware is fine. We will need to discuss the inclusion of
>    the file skiboot.lid under qemu.

Yes, this isn't terribly useful without it.  The normal procedure for
new roms is this:

   1. Get the upstream git tree for the ROM mirrored to qemu.org
   2. Add a git submodule under roms/ referencing the git mirror on
      qemu.org
   3. Add a pre-built ROM binary to pc-bios/
   4. Add a brief description of the ROM, including upstream git URL
      to pc-bios/README

Steps 2, 3 & 4 can (and usually should) be a single commit.

This code is looking close enough, that having a usable rom image is
probably the last thing stopping merge, at least of these initial
patches.

Probably best to get underway with the rom inclusion ASAP.

>  Changes since v2:
> 
>  - some more copyright header cleanups
>  - remove fdt_addr field from PnvMachineState
> 
>  Changes since v1:
> 
>  - changed MAX_CPUS to 2048
>  - simplified memory node creation to one node only
>  - removed machine version 
>  - rewrote the device tree creation with the fdt "rw" routines
>  - s/sPowerNVMachineState/PnvMachineState/
>  - block_default_type is back to IF_IDE because of the AHCI device
> 
>  default-configs/ppc64-softmmu.mak |   1 +
>  hw/ppc/Makefile.objs              |   2 +
>  hw/ppc/pnv.c                      | 223 ++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h              |  38 +++++++
>  4 files changed, 264 insertions(+)
>  create mode 100644 hw/ppc/pnv.c
>  create mode 100644 include/hw/ppc/pnv.h
> 
> diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak
> index db5a4d6f5eea..67a9bcaa67fa 100644
> --- a/default-configs/ppc64-softmmu.mak
> +++ b/default-configs/ppc64-softmmu.mak
> @@ -39,6 +39,7 @@ CONFIG_I8259=y
>  CONFIG_XILINX=y
>  CONFIG_XILINX_ETHLITE=y
>  CONFIG_PSERIES=y
> +CONFIG_POWERNV=y
>  CONFIG_PREP=y
>  CONFIG_MAC=y
>  CONFIG_E500=y
> diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
> index 99a0d4e581bf..8105db7d5600 100644
> --- a/hw/ppc/Makefile.objs
> +++ b/hw/ppc/Makefile.objs
> @@ -5,6 +5,8 @@ obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o
>  obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
>  obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
>  obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
> +# IBM PowerNV
> +obj-$(CONFIG_POWERNV) += pnv.o
>  ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
>  obj-y += spapr_pci_vfio.o
>  endif
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> new file mode 100644
> index 000000000000..02fc4826baa4
> --- /dev/null
> +++ b/hw/ppc/pnv.c
> @@ -0,0 +1,223 @@
> +/*
> + * QEMU PowerPC PowerNV machine model
> + *
> + * Copyright (c) 2016, IBM Corporation.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "sysemu/sysemu.h"
> +#include "sysemu/numa.h"
> +#include "hw/hw.h"
> +#include "target-ppc/cpu.h"
> +#include "qemu/log.h"
> +#include "hw/ppc/fdt.h"
> +#include "hw/ppc/ppc.h"
> +#include "hw/ppc/pnv.h"
> +#include "hw/loader.h"
> +#include "exec/address-spaces.h"
> +#include "qemu/cutils.h"
> +
> +#include <libfdt.h>
> +
> +#define FDT_MAX_SIZE            0x00100000
> +
> +#define FW_FILE_NAME            "skiboot.lid"
> +#define FW_LOAD_ADDR            0x0
> +#define FW_MAX_SIZE             0x00400000
> +
> +#define KERNEL_LOAD_ADDR        0x20000000
> +#define INITRD_LOAD_ADDR        0x40000000
> +
> +/*
> + * On Power Systems E880, the max cpus (threads) should be :
> + *     4 * 4 sockets * 12 cores * 8 threads = 1536
> + * Let's make it 2^11
> + */
> +#define MAX_CPUS                2048
> +
> +/*
> + * Memory nodes are created by hostboot, one for each range of memory
> + * that has a different "affinity". In practice, it means one range
> + * per chip.
> + */
> +static void powernv_populate_memory_node(void *fdt, int chip_id, hwaddr start,
> +                                         hwaddr size)
> +{
> +    char *mem_name;
> +    uint64_t mem_reg_property[2];
> +    int off;
> +
> +    mem_reg_property[0] = cpu_to_be64(start);
> +    mem_reg_property[1] = cpu_to_be64(size);
> +
> +    mem_name = g_strdup_printf("memory@%"HWADDR_PRIx, start);
> +    off = fdt_add_subnode(fdt, 0, mem_name);
> +    g_free(mem_name);
> +
> +    _FDT((fdt_setprop_string(fdt, off, "device_type", "memory")));
> +    _FDT((fdt_setprop(fdt, off, "reg", mem_reg_property,
> +                       sizeof(mem_reg_property))));
> +    _FDT((fdt_setprop_cell(fdt, off, "ibm,chip-id", chip_id)));
> +}
> +
> +static void *powernv_create_fdt(PnvMachineState *pnv,
> +                                const char *kernel_cmdline)
> +{
> +    void *fdt;
> +    char *buf;
> +    const char plat_compat[] = "qemu,powernv\0ibm,powernv";
> +    int off;
> +
> +    fdt = g_malloc0(FDT_MAX_SIZE);
> +    _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE)));
> +
> +    /* Root node */
> +    _FDT((fdt_setprop_cell(fdt, 0, "#address-cells", 0x2)));
> +    _FDT((fdt_setprop_cell(fdt, 0, "#size-cells", 0x2)));
> +    _FDT((fdt_setprop_string(fdt, 0, "model",
> +                             "IBM PowerNV (emulated by qemu)")));
> +    _FDT((fdt_setprop(fdt, 0, "compatible", plat_compat,
> +                      sizeof(plat_compat))));
> +
> +    buf =  qemu_uuid_unparse_strdup(&qemu_uuid);
> +    _FDT((fdt_setprop_string(fdt, 0, "vm,uuid", buf)));
> +    if (qemu_uuid_set) {
> +        _FDT((fdt_property_string(fdt, "system-id", buf)));
> +    }
> +    g_free(buf);
> +
> +    off = fdt_add_subnode(fdt, 0, "chosen");
> +    if (kernel_cmdline) {
> +        _FDT((fdt_setprop_string(fdt, off, "bootargs", kernel_cmdline)));
> +    }
> +
> +    if (pnv->initrd_size) {
> +        uint32_t start_prop = cpu_to_be32(pnv->initrd_base);
> +        uint32_t end_prop = cpu_to_be32(pnv->initrd_base + pnv->initrd_size);
> +
> +        _FDT((fdt_setprop(fdt, off, "linux,initrd-start",
> +                               &start_prop, sizeof(start_prop))));
> +        _FDT((fdt_setprop(fdt, off, "linux,initrd-end",
> +                               &end_prop, sizeof(end_prop))));
> +    }
> +
> +    /* Put all the memory in one node on chip 0 until we find a way to
> +     * specify different ranges for each chip
> +     */
> +    powernv_populate_memory_node(fdt, 0, 0, ram_size);

machine->ram_size is preferred over the ram_size global these days.
Doesn't matter that much, since this will be reworked for multiple
chips at some point anyway.

> +
> +    return fdt;
> +}
> +
> +static void ppc_powernv_reset(void)
> +{
> +    MachineState *machine = MACHINE(qdev_get_machine());
> +    PnvMachineState *pnv = POWERNV_MACHINE(machine);
> +    void *fdt;
> +
> +    qemu_devices_reset();
> +
> +    fdt = powernv_create_fdt(pnv, machine->kernel_cmdline);
> +
> +    /* Pack resulting tree */
> +    _FDT((fdt_pack(fdt)));
> +
> +    cpu_physical_memory_write(POWERNV_FDT_ADDR, fdt, fdt_totalsize(fdt));
> +}
> +
> +static void ppc_powernv_init(MachineState *machine)
> +{
> +    PnvMachineState *pnv = POWERNV_MACHINE(machine);
> +    ram_addr_t ram_size = machine->ram_size;
> +    MemoryRegion *ram;
> +    char *fw_filename;
> +    long fw_size;
> +    long kernel_size;
> +
> +    /* allocate RAM */
> +    if (ram_size < (1 * G_BYTE)) {
> +        error_report("Warning: skiboot may not work with < 1GB of RAM");
> +    }
> +
> +    ram = g_new(MemoryRegion, 1);
> +    memory_region_allocate_system_memory(ram, NULL, "ppc_powernv.ram",
> +                                         ram_size);
> +    memory_region_add_subregion(get_system_memory(), 0, ram);
> +
> +    /* load skiboot firmware  */
> +    if (bios_name == NULL) {
> +        bios_name = FW_FILE_NAME;
> +    }
> +
> +    fw_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
> +
> +    fw_size = load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE);
> +    if (fw_size < 0) {
> +        hw_error("qemu: could not load OPAL '%s'\n", fw_filename);
> +        exit(1);
> +    }
> +    g_free(fw_filename);
> +
> +    /* load kernel */
> +    kernel_size = load_image_targphys(machine->kernel_filename,
> +                                      KERNEL_LOAD_ADDR, 0x2000000);
> +    if (kernel_size < 0) {
> +        hw_error("qemu: could not load kernel'%s'\n", machine->kernel_filename);
> +        exit(1);
> +    }
> +
> +    /* load initrd */
> +    if (machine->initrd_filename) {
> +        pnv->initrd_base = INITRD_LOAD_ADDR;
> +        pnv->initrd_size = load_image_targphys(machine->initrd_filename,
> +                                  pnv->initrd_base, 0x10000000); /* 128MB max */
> +        if (pnv->initrd_size < 0) {
> +            error_report("qemu: could not load initial ram disk '%s'",
> +                         machine->initrd_filename);
> +            exit(1);
> +        }
> +    }
> +}
> +
> +static void powernv_machine_class_init(ObjectClass *oc, void *data)
> +{
> +    MachineClass *mc = MACHINE_CLASS(oc);
> +
> +    mc->desc = "IBM PowerNV (Non-Virtualized)";
> +    mc->init = ppc_powernv_init;
> +    mc->reset = ppc_powernv_reset;
> +    mc->max_cpus = MAX_CPUS;
> +    mc->block_default_type = IF_IDE; /* Pnv provides a AHCI device for
> +                                      * storage */
> +    mc->no_parallel = 1;
> +    mc->default_boot_order = NULL;
> +    mc->default_ram_size = 1 * G_BYTE;
> +}
> +
> +static const TypeInfo powernv_machine_info = {
> +    .name          = TYPE_POWERNV_MACHINE,
> +    .parent        = TYPE_MACHINE,
> +    .instance_size = sizeof(PnvMachineState),
> +    .class_init    = powernv_machine_class_init,
> +};
> +
> +static void powernv_machine_register_types(void)
> +{
> +    type_register_static(&powernv_machine_info);
> +}
> +
> +type_init(powernv_machine_register_types)
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> new file mode 100644
> index 000000000000..c8a73bc74267
> --- /dev/null
> +++ b/include/hw/ppc/pnv.h
> @@ -0,0 +1,38 @@
> +/*
> + * QEMU PowerPC PowerNV various definitions
> + *
> + * Copyright (c) 2014-2016 BenH, IBM Corporation.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + */
> +#ifndef _PPC_PNV_H
> +#define _PPC_PNV_H
> +
> +#include "hw/boards.h"
> +
> +#define TYPE_POWERNV_MACHINE       MACHINE_TYPE_NAME("powernv")
> +#define POWERNV_MACHINE(obj) \
> +    OBJECT_CHECK(PnvMachineState, (obj), TYPE_POWERNV_MACHINE)
> +
> +typedef struct PnvMachineState {
> +    /*< private >*/
> +    MachineState parent_obj;
> +
> +    uint32_t initrd_base;
> +    long initrd_size;
> +} PnvMachineState;
> +
> +#define POWERNV_FDT_ADDR                0x01000000
> +
> +#endif /* _PPC_PNV_H */

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 01/20] ppc/pnv: add skeleton PowerNV platform
  2016-10-07  4:14   ` David Gibson
@ 2016-10-07  4:16     ` David Gibson
  2016-10-07  7:38     ` Cédric Le Goater
  2016-10-07  8:36     ` Cédric Le Goater
  2 siblings, 0 replies; 75+ messages in thread
From: David Gibson @ 2016-10-07  4:16 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 15697 bytes --]

On Fri, Oct 07, 2016 at 03:14:48PM +1100, David Gibson wrote:
> On Mon, Oct 03, 2016 at 09:24:37AM +0200, Cédric Le Goater wrote:
> > From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> > 
> > The goal is to emulate a PowerNV system at the level of the skiboot
> > firmware, which loads the OS and provides some runtime services. Power
> > Systems have a lower firmware (HostBoot) that does low level system
> > initialization, like DRAM training. This is beyond the scope of what
> > qemu will address in a PowerNV guest.
> > 
> > No devices yet, not even an interrupt controller. Just to get started,
> > some RAM to load the skiboot firmware, the kernel and initrd. The
> > device tree is fully created in the machine reset op.
> > 
> > Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> > [clg: - updated for qemu-2.7
> >       - replaced fprintf by error_report
> >       - used a common definition of _FDT macro
> >       - removed VMStateDescription as migration is not yet supported
> >       - added IBM Copyright statements
> >       - reworked kernel_filename handling
> >       - merged PnvSystem and sPowerNVMachineState
> >       - removed PHANDLE_XICP
> >       - added ppc_create_page_sizes_prop helper
> >       - removed nmi support
> >       - removed kvm support
> >       - updated powernv machine to version 2.8
> >       - removed chips and cpus, They will be provided in another patches
> >       - added a machine reset routine to initialize the device tree (also)
> >       - french has a squelette and english a skeleton.
> >       - improved commit log.
> >       - reworked prototypes parameters
> >       - added a check on the ram size (thanks to Michael Ellerman)
> >       - fixed chip-id cell
> >       - changed MAX_CPUS to 2048
> >       - simplified memory node creation to one node only
> >       - removed machine version
> >       - rewrote the device tree creation with the fdt "rw" routines
> >       - s/sPowerNVMachineState/PnvMachineState/
> >       - etc.]
> > Signed-off-by: Cédric Le Goater <clg@kaod.org>
> > ---
> >  Changes since v3:
> > 
> >  - fixed printf format for hwaddr
> >  - used fdt_pack() before writing the tree in memory 
> >  - removed the requirement on having a kernel loaded as running with
> >    just a firmware is fine. We will need to discuss the inclusion of
> >    the file skiboot.lid under qemu.
> 
> Yes, this isn't terribly useful without it.  The normal procedure for
> new roms is this:
> 
>    1. Get the upstream git tree for the ROM mirrored to qemu.org
>    2. Add a git submodule under roms/ referencing the git mirror on
>       qemu.org
>    3. Add a pre-built ROM binary to pc-bios/
>    4. Add a brief description of the ROM, including upstream git URL
>       to pc-bios/README
> 
> Steps 2, 3 & 4 can (and usually should) be a single commit.
> 
> This code is looking close enough, that having a usable rom image is
> probably the last thing stopping merge, at least of these initial
> patches.
> 
> Probably best to get underway with the rom inclusion ASAP.

Sorry, forgot to add, with the exception of the ROM question, and the
one tiny code nit:

Reviewed-by: David Gibson <david@gibson.dropbear.id.au>

> 
> >  Changes since v2:
> > 
> >  - some more copyright header cleanups
> >  - remove fdt_addr field from PnvMachineState
> > 
> >  Changes since v1:
> > 
> >  - changed MAX_CPUS to 2048
> >  - simplified memory node creation to one node only
> >  - removed machine version 
> >  - rewrote the device tree creation with the fdt "rw" routines
> >  - s/sPowerNVMachineState/PnvMachineState/
> >  - block_default_type is back to IF_IDE because of the AHCI device
> > 
> >  default-configs/ppc64-softmmu.mak |   1 +
> >  hw/ppc/Makefile.objs              |   2 +
> >  hw/ppc/pnv.c                      | 223 ++++++++++++++++++++++++++++++++++++++
> >  include/hw/ppc/pnv.h              |  38 +++++++
> >  4 files changed, 264 insertions(+)
> >  create mode 100644 hw/ppc/pnv.c
> >  create mode 100644 include/hw/ppc/pnv.h
> > 
> > diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak
> > index db5a4d6f5eea..67a9bcaa67fa 100644
> > --- a/default-configs/ppc64-softmmu.mak
> > +++ b/default-configs/ppc64-softmmu.mak
> > @@ -39,6 +39,7 @@ CONFIG_I8259=y
> >  CONFIG_XILINX=y
> >  CONFIG_XILINX_ETHLITE=y
> >  CONFIG_PSERIES=y
> > +CONFIG_POWERNV=y
> >  CONFIG_PREP=y
> >  CONFIG_MAC=y
> >  CONFIG_E500=y
> > diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
> > index 99a0d4e581bf..8105db7d5600 100644
> > --- a/hw/ppc/Makefile.objs
> > +++ b/hw/ppc/Makefile.objs
> > @@ -5,6 +5,8 @@ obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o
> >  obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
> >  obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
> >  obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
> > +# IBM PowerNV
> > +obj-$(CONFIG_POWERNV) += pnv.o
> >  ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
> >  obj-y += spapr_pci_vfio.o
> >  endif
> > diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> > new file mode 100644
> > index 000000000000..02fc4826baa4
> > --- /dev/null
> > +++ b/hw/ppc/pnv.c
> > @@ -0,0 +1,223 @@
> > +/*
> > + * QEMU PowerPC PowerNV machine model
> > + *
> > + * Copyright (c) 2016, IBM Corporation.
> > + *
> > + * This library is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2 of the License, or (at your option) any later version.
> > + *
> > + * This library is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public
> > + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qapi/error.h"
> > +#include "sysemu/sysemu.h"
> > +#include "sysemu/numa.h"
> > +#include "hw/hw.h"
> > +#include "target-ppc/cpu.h"
> > +#include "qemu/log.h"
> > +#include "hw/ppc/fdt.h"
> > +#include "hw/ppc/ppc.h"
> > +#include "hw/ppc/pnv.h"
> > +#include "hw/loader.h"
> > +#include "exec/address-spaces.h"
> > +#include "qemu/cutils.h"
> > +
> > +#include <libfdt.h>
> > +
> > +#define FDT_MAX_SIZE            0x00100000
> > +
> > +#define FW_FILE_NAME            "skiboot.lid"
> > +#define FW_LOAD_ADDR            0x0
> > +#define FW_MAX_SIZE             0x00400000
> > +
> > +#define KERNEL_LOAD_ADDR        0x20000000
> > +#define INITRD_LOAD_ADDR        0x40000000
> > +
> > +/*
> > + * On Power Systems E880, the max cpus (threads) should be :
> > + *     4 * 4 sockets * 12 cores * 8 threads = 1536
> > + * Let's make it 2^11
> > + */
> > +#define MAX_CPUS                2048
> > +
> > +/*
> > + * Memory nodes are created by hostboot, one for each range of memory
> > + * that has a different "affinity". In practice, it means one range
> > + * per chip.
> > + */
> > +static void powernv_populate_memory_node(void *fdt, int chip_id, hwaddr start,
> > +                                         hwaddr size)
> > +{
> > +    char *mem_name;
> > +    uint64_t mem_reg_property[2];
> > +    int off;
> > +
> > +    mem_reg_property[0] = cpu_to_be64(start);
> > +    mem_reg_property[1] = cpu_to_be64(size);
> > +
> > +    mem_name = g_strdup_printf("memory@%"HWADDR_PRIx, start);
> > +    off = fdt_add_subnode(fdt, 0, mem_name);
> > +    g_free(mem_name);
> > +
> > +    _FDT((fdt_setprop_string(fdt, off, "device_type", "memory")));
> > +    _FDT((fdt_setprop(fdt, off, "reg", mem_reg_property,
> > +                       sizeof(mem_reg_property))));
> > +    _FDT((fdt_setprop_cell(fdt, off, "ibm,chip-id", chip_id)));
> > +}
> > +
> > +static void *powernv_create_fdt(PnvMachineState *pnv,
> > +                                const char *kernel_cmdline)
> > +{
> > +    void *fdt;
> > +    char *buf;
> > +    const char plat_compat[] = "qemu,powernv\0ibm,powernv";
> > +    int off;
> > +
> > +    fdt = g_malloc0(FDT_MAX_SIZE);
> > +    _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE)));
> > +
> > +    /* Root node */
> > +    _FDT((fdt_setprop_cell(fdt, 0, "#address-cells", 0x2)));
> > +    _FDT((fdt_setprop_cell(fdt, 0, "#size-cells", 0x2)));
> > +    _FDT((fdt_setprop_string(fdt, 0, "model",
> > +                             "IBM PowerNV (emulated by qemu)")));
> > +    _FDT((fdt_setprop(fdt, 0, "compatible", plat_compat,
> > +                      sizeof(plat_compat))));
> > +
> > +    buf =  qemu_uuid_unparse_strdup(&qemu_uuid);
> > +    _FDT((fdt_setprop_string(fdt, 0, "vm,uuid", buf)));
> > +    if (qemu_uuid_set) {
> > +        _FDT((fdt_property_string(fdt, "system-id", buf)));
> > +    }
> > +    g_free(buf);
> > +
> > +    off = fdt_add_subnode(fdt, 0, "chosen");
> > +    if (kernel_cmdline) {
> > +        _FDT((fdt_setprop_string(fdt, off, "bootargs", kernel_cmdline)));
> > +    }
> > +
> > +    if (pnv->initrd_size) {
> > +        uint32_t start_prop = cpu_to_be32(pnv->initrd_base);
> > +        uint32_t end_prop = cpu_to_be32(pnv->initrd_base + pnv->initrd_size);
> > +
> > +        _FDT((fdt_setprop(fdt, off, "linux,initrd-start",
> > +                               &start_prop, sizeof(start_prop))));
> > +        _FDT((fdt_setprop(fdt, off, "linux,initrd-end",
> > +                               &end_prop, sizeof(end_prop))));
> > +    }
> > +
> > +    /* Put all the memory in one node on chip 0 until we find a way to
> > +     * specify different ranges for each chip
> > +     */
> > +    powernv_populate_memory_node(fdt, 0, 0, ram_size);
> 
> machine->ram_size is preferred over the ram_size global these days.
> Doesn't matter that much, since this will be reworked for multiple
> chips at some point anyway.
> 
> > +
> > +    return fdt;
> > +}
> > +
> > +static void ppc_powernv_reset(void)
> > +{
> > +    MachineState *machine = MACHINE(qdev_get_machine());
> > +    PnvMachineState *pnv = POWERNV_MACHINE(machine);
> > +    void *fdt;
> > +
> > +    qemu_devices_reset();
> > +
> > +    fdt = powernv_create_fdt(pnv, machine->kernel_cmdline);
> > +
> > +    /* Pack resulting tree */
> > +    _FDT((fdt_pack(fdt)));
> > +
> > +    cpu_physical_memory_write(POWERNV_FDT_ADDR, fdt, fdt_totalsize(fdt));
> > +}
> > +
> > +static void ppc_powernv_init(MachineState *machine)
> > +{
> > +    PnvMachineState *pnv = POWERNV_MACHINE(machine);
> > +    ram_addr_t ram_size = machine->ram_size;
> > +    MemoryRegion *ram;
> > +    char *fw_filename;
> > +    long fw_size;
> > +    long kernel_size;
> > +
> > +    /* allocate RAM */
> > +    if (ram_size < (1 * G_BYTE)) {
> > +        error_report("Warning: skiboot may not work with < 1GB of RAM");
> > +    }
> > +
> > +    ram = g_new(MemoryRegion, 1);
> > +    memory_region_allocate_system_memory(ram, NULL, "ppc_powernv.ram",
> > +                                         ram_size);
> > +    memory_region_add_subregion(get_system_memory(), 0, ram);
> > +
> > +    /* load skiboot firmware  */
> > +    if (bios_name == NULL) {
> > +        bios_name = FW_FILE_NAME;
> > +    }
> > +
> > +    fw_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
> > +
> > +    fw_size = load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE);
> > +    if (fw_size < 0) {
> > +        hw_error("qemu: could not load OPAL '%s'\n", fw_filename);
> > +        exit(1);
> > +    }
> > +    g_free(fw_filename);
> > +
> > +    /* load kernel */
> > +    kernel_size = load_image_targphys(machine->kernel_filename,
> > +                                      KERNEL_LOAD_ADDR, 0x2000000);
> > +    if (kernel_size < 0) {
> > +        hw_error("qemu: could not load kernel'%s'\n", machine->kernel_filename);
> > +        exit(1);
> > +    }
> > +
> > +    /* load initrd */
> > +    if (machine->initrd_filename) {
> > +        pnv->initrd_base = INITRD_LOAD_ADDR;
> > +        pnv->initrd_size = load_image_targphys(machine->initrd_filename,
> > +                                  pnv->initrd_base, 0x10000000); /* 128MB max */
> > +        if (pnv->initrd_size < 0) {
> > +            error_report("qemu: could not load initial ram disk '%s'",
> > +                         machine->initrd_filename);
> > +            exit(1);
> > +        }
> > +    }
> > +}
> > +
> > +static void powernv_machine_class_init(ObjectClass *oc, void *data)
> > +{
> > +    MachineClass *mc = MACHINE_CLASS(oc);
> > +
> > +    mc->desc = "IBM PowerNV (Non-Virtualized)";
> > +    mc->init = ppc_powernv_init;
> > +    mc->reset = ppc_powernv_reset;
> > +    mc->max_cpus = MAX_CPUS;
> > +    mc->block_default_type = IF_IDE; /* Pnv provides a AHCI device for
> > +                                      * storage */
> > +    mc->no_parallel = 1;
> > +    mc->default_boot_order = NULL;
> > +    mc->default_ram_size = 1 * G_BYTE;
> > +}
> > +
> > +static const TypeInfo powernv_machine_info = {
> > +    .name          = TYPE_POWERNV_MACHINE,
> > +    .parent        = TYPE_MACHINE,
> > +    .instance_size = sizeof(PnvMachineState),
> > +    .class_init    = powernv_machine_class_init,
> > +};
> > +
> > +static void powernv_machine_register_types(void)
> > +{
> > +    type_register_static(&powernv_machine_info);
> > +}
> > +
> > +type_init(powernv_machine_register_types)
> > diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> > new file mode 100644
> > index 000000000000..c8a73bc74267
> > --- /dev/null
> > +++ b/include/hw/ppc/pnv.h
> > @@ -0,0 +1,38 @@
> > +/*
> > + * QEMU PowerPC PowerNV various definitions
> > + *
> > + * Copyright (c) 2014-2016 BenH, IBM Corporation.
> > + *
> > + * This library is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2 of the License, or (at your option) any later version.
> > + *
> > + * This library is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public
> > + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> > + */
> > +#ifndef _PPC_PNV_H
> > +#define _PPC_PNV_H
> > +
> > +#include "hw/boards.h"
> > +
> > +#define TYPE_POWERNV_MACHINE       MACHINE_TYPE_NAME("powernv")
> > +#define POWERNV_MACHINE(obj) \
> > +    OBJECT_CHECK(PnvMachineState, (obj), TYPE_POWERNV_MACHINE)
> > +
> > +typedef struct PnvMachineState {
> > +    /*< private >*/
> > +    MachineState parent_obj;
> > +
> > +    uint32_t initrd_base;
> > +    long initrd_size;
> > +} PnvMachineState;
> > +
> > +#define POWERNV_FDT_ADDR                0x01000000
> > +
> > +#endif /* _PPC_PNV_H */
> 



-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 02/20] ppc/pnv: add a PnvChip object
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 02/20] ppc/pnv: add a PnvChip object Cédric Le Goater
@ 2016-10-07  4:26   ` David Gibson
  2016-10-07  9:16     ` Cédric Le Goater
  0 siblings, 1 reply; 75+ messages in thread
From: David Gibson @ 2016-10-07  4:26 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 14050 bytes --]

On Mon, Oct 03, 2016 at 09:24:38AM +0200, Cédric Le Goater wrote:
> This is is an abstraction of a POWER8 chip which is a set of cores
> plus other 'units', like the pervasive unit, the interrupt controller,
> the memory controller, the on-chip microcontroller, etc. The whole can
> be seen as a socket. It depends on a cpu model and its characteristics:
> max cores, specific inits are defined in a PnvChipClass.
> 
> We start with an near empty PnvChip with only a few cpu constants
> which we will grow in the subsequent patches with the controllers
> required to run the system.
> 
> The Chip CFAM (Common FRU Access Module) ID gives the model of the
> chip and its version number. It is generally the first thing firmwares
> fetch, available at XSCOM PCB address 0xf000f, to start initialization.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

Reviewed-by: David Gibson <david@gibson.dropbear.id.au>

I do have one small suggested change below, but it's not that important.

> ---
> 
>  chip_type could possibly be removed or calculated from the attribute
>  chip_cfam_id. Let's keep it for now and see how the patchset evolves.
>  This is getting big, maybe should move the code to hw/ppc/pnv_chip.c ?
> 
>  Changes since v3:
> 
>  - removed PnvChipPower* types
>  - removed realize ops of PnvChip
>  - replaced scanf by qemu_strtoul
> 
>  Changes since v2:
> 
>  - forced a POWER8 cpu model if none is specified and check that a
>    PnvChip type exist for it
>  - did some renaming to be consistent with the cpu model names
>  - added POWER9 chip
>  - removed empty realize op
>  - renamed atribute chip_f000f in chip_cfam_id
>  - used error_fatal instead of error_abort when setting the chip
>    properties
>  - introduced a powernv_populate_chip() routine
> 
>  Changes since v1:
>  
>  - introduced a PnvChipClass depending on the cpu model. It also
>    provides some chip constants used by devices, like the cpu model hw
>    id (f000f), a enum type (not sure this is useful yet), a custom
>    realize ops for customization.
>  - the num-chips property can be configured on the command line.
>  
>  hw/ppc/pnv.c         | 194 +++++++++++++++++++++++++++++++++++++++++++++++++--
>  include/hw/ppc/pnv.h |  61 ++++++++++++++++
>  2 files changed, 250 insertions(+), 5 deletions(-)
> 
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 02fc4826baa4..08f72dbdca97 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -74,6 +74,16 @@ static void powernv_populate_memory_node(void *fdt, int chip_id, hwaddr start,
>      _FDT((fdt_setprop_cell(fdt, off, "ibm,chip-id", chip_id)));
>  }
>  
> +static void powernv_populate_chip(PnvChip *chip, void *fdt)
> +{
> +    /* Put all the memory in one node on chip 0 until we find a way to
> +     * specify different ranges for each chip
> +     */
> +    if (chip->chip_id == 0) {
> +        powernv_populate_memory_node(fdt, chip->chip_id, 0, ram_size);
> +    }
> +}
> +
>  static void *powernv_create_fdt(PnvMachineState *pnv,
>                                  const char *kernel_cmdline)
>  {
> @@ -81,6 +91,7 @@ static void *powernv_create_fdt(PnvMachineState *pnv,
>      char *buf;
>      const char plat_compat[] = "qemu,powernv\0ibm,powernv";
>      int off;
> +    int i;
>  
>      fdt = g_malloc0(FDT_MAX_SIZE);
>      _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE)));
> @@ -115,11 +126,10 @@ static void *powernv_create_fdt(PnvMachineState *pnv,
>                                 &end_prop, sizeof(end_prop))));
>      }
>  
> -    /* Put all the memory in one node on chip 0 until we find a way to
> -     * specify different ranges for each chip
> -     */
> -    powernv_populate_memory_node(fdt, 0, 0, ram_size);
> -
> +    /* Populate device tree for each chip */
> +    for (i = 0; i < pnv->num_chips; i++) {
> +        powernv_populate_chip(pnv->chips[i], fdt);
> +    }
>      return fdt;
>  }
>  
> @@ -147,6 +157,8 @@ static void ppc_powernv_init(MachineState *machine)
>      char *fw_filename;
>      long fw_size;
>      long kernel_size;
> +    int i;
> +    char *chip_typename;
>  
>      /* allocate RAM */
>      if (ram_size < (1 * G_BYTE)) {
> @@ -191,6 +203,172 @@ static void ppc_powernv_init(MachineState *machine)
>              exit(1);
>          }
>      }
> +
> +    /* We need some cpu model to instantiate the PnvChip class */
> +    if (machine->cpu_model == NULL) {
> +        machine->cpu_model = "POWER8";
> +    }
> +
> +    /* Create the processor chips */
> +    chip_typename = g_strdup_printf(TYPE_PNV_CHIP "-%s", machine->cpu_model);
> +    if (!object_class_by_name(chip_typename)) {
> +        error_report("qemu: invalid CPU model '%s' for %s machine",
> +                     machine->cpu_model, MACHINE_GET_CLASS(machine)->name);
> +        exit(1);
> +    }
> +
> +    pnv->chips = g_new0(PnvChip *, pnv->num_chips);
> +    for (i = 0; i < pnv->num_chips; i++) {
> +        char chip_name[32];
> +        Object *chip = object_new(chip_typename);
> +
> +        pnv->chips[i] = PNV_CHIP(chip);
> +
> +        snprintf(chip_name, sizeof(chip_name), "chip[%d]", CHIP_HWID(i));
> +        object_property_add_child(OBJECT(pnv), chip_name, chip, &error_fatal);
> +        object_property_set_int(chip, CHIP_HWID(i), "chip-id", &error_fatal);
> +        object_property_set_bool(chip, true, "realized", &error_fatal);
> +    }
> +    g_free(chip_typename);
> +}
> +
> +static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PnvChipClass *k = PNV_CHIP_CLASS(klass);
> +
> +    k->cpu_model = "POWER8E";
> +    k->chip_type = PNV_CHIP_POWER8E;
> +    k->chip_cfam_id = 0x221ef04980000000ull;  /* P8 Murano DD2.1 */
> +    dc->desc = "PowerNV Chip POWER8E";
> +}
> +
> +static const TypeInfo pnv_chip_power8e_info = {
> +    .name          = TYPE_PNV_CHIP_POWER8E,
> +    .parent        = TYPE_PNV_CHIP,
> +    .instance_size = sizeof(PnvChip),
> +    .class_init    = pnv_chip_power8e_class_init,
> +};
> +
> +static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PnvChipClass *k = PNV_CHIP_CLASS(klass);
> +
> +    k->cpu_model = "POWER8";
> +    k->chip_type = PNV_CHIP_POWER8;
> +    k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */
> +    dc->desc = "PowerNV Chip POWER8";
> +}
> +
> +static const TypeInfo pnv_chip_power8_info = {
> +    .name          = TYPE_PNV_CHIP_POWER8,
> +    .parent        = TYPE_PNV_CHIP,
> +    .instance_size = sizeof(PnvChip),
> +    .class_init    = pnv_chip_power8_class_init,
> +};
> +
> +static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PnvChipClass *k = PNV_CHIP_CLASS(klass);
> +
> +    k->cpu_model = "POWER8NVL";
> +    k->chip_type = PNV_CHIP_POWER8NVL;
> +    k->chip_cfam_id = 0x120d304980000000ull;  /* P8 Naples DD1.0 */
> +    dc->desc = "PowerNV Chip POWER8NVL";
> +}
> +
> +static const TypeInfo pnv_chip_power8nvl_info = {
> +    .name          = TYPE_PNV_CHIP_POWER8NVL,
> +    .parent        = TYPE_PNV_CHIP,
> +    .instance_size = sizeof(PnvChip),
> +    .class_init    = pnv_chip_power8nvl_class_init,
> +};
> +
> +static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PnvChipClass *k = PNV_CHIP_CLASS(klass);
> +
> +    k->cpu_model = "POWER9";
> +    k->chip_type = PNV_CHIP_POWER9;
> +    k->chip_cfam_id = 0x100d104980000000ull; /* P9 Nimbus DD1.0 */
> +    dc->desc = "PowerNV Chip POWER9";
> +}
> +
> +static const TypeInfo pnv_chip_power9_info = {
> +    .name          = TYPE_PNV_CHIP_POWER9,
> +    .parent        = TYPE_PNV_CHIP,
> +    .instance_size = sizeof(PnvChip),
> +    .class_init    = pnv_chip_power9_class_init,
> +};
> +
> +static void pnv_chip_realize(DeviceState *dev, Error **errp)
> +{
> +    /* left purposely empty */
> +}
> +
> +static Property pnv_chip_properties[] = {
> +    DEFINE_PROP_UINT32("chip-id", PnvChip, chip_id, 0),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void pnv_chip_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->realize = pnv_chip_realize;
> +    dc->props = pnv_chip_properties;
> +    dc->desc = "PowerNV Chip";
> +}
> +
> +static const TypeInfo pnv_chip_info = {
> +    .name          = TYPE_PNV_CHIP,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .class_init    = pnv_chip_class_init,
> +    .class_size    = sizeof(PnvChipClass),
> +    .abstract      = true,
> +};
> +
> +
> +static char *pnv_get_num_chips(Object *obj, Error **errp)
> +{
> +    return g_strdup_printf("%d", POWERNV_MACHINE(obj)->num_chips);
> +}
> +
> +static void pnv_set_num_chips(Object *obj, const char *value, Error **errp)
> +{
> +    PnvMachineState *pnv = POWERNV_MACHINE(obj);
> +    unsigned long num_chips;
> +
> +    if (qemu_strtoul(value, NULL, 10, &num_chips) < 0) {
> +        error_setg(errp, "invalid num_chips property: '%s'", value);
> +        return;
> +    }
> +
> +    /*
> +     * TODO: should we decide on how many chips we can create based
> +     * on #cores and Venice vs. Murano vs. Naples chip type etc...,
> +     */
> +    if (num_chips < 1 || num_chips > 4) {
> +        error_setg(errp, "invalid number of chips: '%s'", value);
> +        return;
> +    }
> +
> +    pnv->num_chips = num_chips;
> +}
> +
> +static void powernv_machine_initfn(Object *obj)
> +{
> +    PnvMachineState *pnv = POWERNV_MACHINE(obj);
> +    pnv->num_chips = 1;
> +
> +    object_property_add_str(obj, "num-chips", pnv_get_num_chips,
> +                            pnv_set_num_chips, NULL);
> +    object_property_set_description(obj, "num-chips",
> +                                    "Specifies the number of processor chips",
> +                                    NULL);

So I recently discovered that nearly everything done with
object_property_add_*() should usually be done with
object_class_property_add_*() - basically anything where the property
exists for every instance of the class.  Yes, practically everything
in qemu already gets this wrong.  Still, if you get a chance to rework
this, might as well not make this one more.

>  }
>  
>  static void powernv_machine_class_init(ObjectClass *oc, void *data)
> @@ -212,12 +390,18 @@ static const TypeInfo powernv_machine_info = {
>      .name          = TYPE_POWERNV_MACHINE,
>      .parent        = TYPE_MACHINE,
>      .instance_size = sizeof(PnvMachineState),
> +    .instance_init = powernv_machine_initfn,
>      .class_init    = powernv_machine_class_init,
>  };
>  
>  static void powernv_machine_register_types(void)
>  {
>      type_register_static(&powernv_machine_info);
> +    type_register_static(&pnv_chip_info);
> +    type_register_static(&pnv_chip_power8e_info);
> +    type_register_static(&pnv_chip_power8_info);
> +    type_register_static(&pnv_chip_power8nvl_info);
> +    type_register_static(&pnv_chip_power9_info);
>  }
>  
>  type_init(powernv_machine_register_types)
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index c8a73bc74267..da543ed81636 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -20,6 +20,64 @@
>  #define _PPC_PNV_H
>  
>  #include "hw/boards.h"
> +#include "hw/sysbus.h"
> +
> +#define TYPE_PNV_CHIP "powernv-chip"
> +#define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
> +#define PNV_CHIP_CLASS(klass) \
> +     OBJECT_CLASS_CHECK(PnvChipClass, (klass), TYPE_PNV_CHIP)
> +#define PNV_CHIP_GET_CLASS(obj) \
> +     OBJECT_GET_CLASS(PnvChipClass, (obj), TYPE_PNV_CHIP)
> +
> +typedef enum PnvChipType {
> +    PNV_CHIP_POWER8E,     /* AKA Murano (default) */
> +    PNV_CHIP_POWER8,      /* AKA Venice */
> +    PNV_CHIP_POWER8NVL,   /* AKA Naples */
> +    PNV_CHIP_POWER9,      /* AKA Nimbus */
> +} PnvChipType;
> +
> +typedef struct PnvChip {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +    uint32_t     chip_id;
> +} PnvChip;
> +
> +typedef struct PnvChipClass {
> +    /*< private >*/
> +    SysBusDeviceClass parent_class;
> +
> +    /*< public >*/
> +    const char *cpu_model;
> +    PnvChipType  chip_type;
> +    uint64_t     chip_cfam_id;
> +} PnvChipClass;
> +
> +#define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E"
> +#define PNV_CHIP_POWER8E(obj) \
> +    OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8E)
> +
> +#define TYPE_PNV_CHIP_POWER8 TYPE_PNV_CHIP "-POWER8"
> +#define PNV_CHIP_POWER8(obj) \
> +    OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8)
> +
> +#define TYPE_PNV_CHIP_POWER8NVL TYPE_PNV_CHIP "-POWER8NVL"
> +#define PNV_CHIP_POWER8NVL(obj) \
> +    OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8NVL)
> +
> +#define TYPE_PNV_CHIP_POWER9 TYPE_PNV_CHIP "-POWER9"
> +#define PNV_CHIP_POWER9(obj) \
> +    OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER9)
> +
> +/*
> + * This generates a HW chip id depending on an index:
> + *
> + *    0x0, 0x1, 0x10, 0x11
> + *
> + * 4 chips should be the maximum
> + */
> +#define CHIP_HWID(i) ((((i) & 0x3e) << 3) | ((i) & 0x1))
>  
>  #define TYPE_POWERNV_MACHINE       MACHINE_TYPE_NAME("powernv")
>  #define POWERNV_MACHINE(obj) \
> @@ -31,6 +89,9 @@ typedef struct PnvMachineState {
>  
>      uint32_t initrd_base;
>      long initrd_size;
> +
> +    uint32_t  num_chips;
> +    PnvChip   **chips;
>  } PnvMachineState;
>  
>  #define POWERNV_FDT_ADDR                0x01000000

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 03/20] ppc/pnv: add a core mask to PnvChip
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 03/20] ppc/pnv: add a core mask to PnvChip Cédric Le Goater
@ 2016-10-07  4:32   ` David Gibson
  2016-10-07  5:01     ` Benjamin Herrenschmidt
  2016-10-10 12:56     ` Cédric Le Goater
  0 siblings, 2 replies; 75+ messages in thread
From: David Gibson @ 2016-10-07  4:32 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 6850 bytes --]

On Mon, Oct 03, 2016 at 09:24:39AM +0200, Cédric Le Goater wrote:
> This will be used to build real HW ids for the cores and enforce some
> limits on the available cores per chip.

Is there actually a practical reason to allow the user (or machine
type) to override the default core mask?

> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

Apart from the above and one comment below,

Reviewed-by: David Gibson <david@gibson.dropbear.id.au>

> ---
> 
>  Changes since v3 :
> 
>  - reworked pnv_chip_core_sanitize() to return errors and to check the
>    maximum of cores against the instance cores_mask
>   
>  Changes since v2 :
> 
>  - added POWER9 support
>  - removed cores_max 
>  - introduces a pnv_chip_core_sanitize() helper to check the core
>    ids_mask and the maximum number of cores
> 
>  hw/ppc/pnv.c         | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++-
>  include/hw/ppc/pnv.h |  4 +++
>  2 files changed, 81 insertions(+), 1 deletion(-)
> 
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 08f72dbdca97..fc930be94f53 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -227,11 +227,44 @@ static void ppc_powernv_init(MachineState *machine)
>          snprintf(chip_name, sizeof(chip_name), "chip[%d]", CHIP_HWID(i));
>          object_property_add_child(OBJECT(pnv), chip_name, chip, &error_fatal);
>          object_property_set_int(chip, CHIP_HWID(i), "chip-id", &error_fatal);
> +        object_property_set_int(chip, smp_cores, "nr-cores", &error_fatal);
> +        /*
> +         * We could customize cores_mask for the chip here. May be
> +         * using a powernv machine property, like 'num-chips'. Let the
> +         * chip choose the default for now.

I don't think you need any special mechanism for this.  If you just
remove this explicit assignment the chip default will apply, but the
user can alter it using -global.

> +         */
> +        object_property_set_int(chip, 0x0, "cores-mask", &error_fatal);
>          object_property_set_bool(chip, true, "realized", &error_fatal);
>      }
>      g_free(chip_typename);
>  }
>  
> +/* Allowed core identifiers on a POWER8 Processor Chip :
> + *
> + * <EX0 reserved>
> + *  EX1  - Venice only
> + *  EX2  - Venice only
> + *  EX3  - Venice only
> + *  EX4
> + *  EX5
> + *  EX6
> + * <EX7,8 reserved> <reserved>
> + *  EX9  - Venice only
> + *  EX10 - Venice only
> + *  EX11 - Venice only
> + *  EX12
> + *  EX13
> + *  EX14
> + * <EX15 reserved>
> + */
> +#define POWER8E_CORE_MASK  (0x7070ull)
> +#define POWER8_CORE_MASK   (0x7e7eull)
> +
> +/*
> + * POWER9 has 24 cores, ids starting at 0x20
> + */
> +#define POWER9_CORE_MASK   (0xffffff00000000ull)
> +
>  static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
>  {
>      DeviceClass *dc = DEVICE_CLASS(klass);
> @@ -240,6 +273,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
>      k->cpu_model = "POWER8E";
>      k->chip_type = PNV_CHIP_POWER8E;
>      k->chip_cfam_id = 0x221ef04980000000ull;  /* P8 Murano DD2.1 */
> +    k->cores_mask = POWER8E_CORE_MASK;
>      dc->desc = "PowerNV Chip POWER8E";
>  }
>  
> @@ -258,6 +292,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
>      k->cpu_model = "POWER8";
>      k->chip_type = PNV_CHIP_POWER8;
>      k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */
> +    k->cores_mask = POWER8_CORE_MASK;
>      dc->desc = "PowerNV Chip POWER8";
>  }
>  
> @@ -276,6 +311,7 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
>      k->cpu_model = "POWER8NVL";
>      k->chip_type = PNV_CHIP_POWER8NVL;
>      k->chip_cfam_id = 0x120d304980000000ull;  /* P8 Naples DD1.0 */
> +    k->cores_mask = POWER8_CORE_MASK;
>      dc->desc = "PowerNV Chip POWER8NVL";
>  }
>  
> @@ -294,6 +330,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
>      k->cpu_model = "POWER9";
>      k->chip_type = PNV_CHIP_POWER9;
>      k->chip_cfam_id = 0x100d104980000000ull; /* P9 Nimbus DD1.0 */
> +    k->cores_mask = POWER9_CORE_MASK;
>      dc->desc = "PowerNV Chip POWER9";
>  }
>  
> @@ -304,13 +341,52 @@ static const TypeInfo pnv_chip_power9_info = {
>      .class_init    = pnv_chip_power9_class_init,
>  };
>  
> +static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp)
> +{
> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
> +    int cores_max;
> +
> +    /*
> +     * No custom mask for this chip, let's use the default one from *
> +     * the chip class
> +     */
> +    if (!chip->cores_mask) {
> +        chip->cores_mask = pcc->cores_mask;
> +    }
> +
> +    /* filter alien core ids ! some are reserved */
> +    if ((chip->cores_mask & pcc->cores_mask) != chip->cores_mask) {
> +        error_setg(errp, "warning: invalid core mask for chip !");
> +        return;
> +    }
> +    chip->cores_mask &= pcc->cores_mask;
> +
> +    /* now that we have a sane layout, let check the number of cores */
> +    cores_max = hweight_long(chip->cores_mask);
> +    if (chip->nr_cores > cores_max) {
> +        error_setg(errp, "warning: too many cores for chip ! Limit is %d",
> +                   cores_max);
> +        return;
> +    }
> +}
> +
>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
>  {
> -    /* left purposely empty */
> +    PnvChip *chip = PNV_CHIP(dev);
> +    Error *error = NULL;
> +
> +    /* Early checks on the core settings */
> +    pnv_chip_core_sanitize(chip, &error);
> +    if (error) {
> +        error_propagate(errp, error);
> +        return;
> +    }
>  }
>  
>  static Property pnv_chip_properties[] = {
>      DEFINE_PROP_UINT32("chip-id", PnvChip, chip_id, 0),
> +    DEFINE_PROP_UINT32("nr-cores", PnvChip, nr_cores, 1),
> +    DEFINE_PROP_UINT64("cores-mask", PnvChip, cores_mask, 0x0),
>      DEFINE_PROP_END_OF_LIST(),
>  };
>  
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index da543ed81636..2c225c928974 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -42,6 +42,9 @@ typedef struct PnvChip {
>  
>      /*< public >*/
>      uint32_t     chip_id;
> +
> +    uint32_t  nr_cores;
> +    uint64_t  cores_mask;
>  } PnvChip;
>  
>  typedef struct PnvChipClass {
> @@ -52,6 +55,7 @@ typedef struct PnvChipClass {
>      const char *cpu_model;
>      PnvChipType  chip_type;
>      uint64_t     chip_cfam_id;
> +    uint64_t     cores_mask;
>  } PnvChipClass;
>  
>  #define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E"

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 04/20] ppc/pnv: add a PIR handler to PnvChip
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 04/20] ppc/pnv: add a PIR handler " Cédric Le Goater
@ 2016-10-07  4:34   ` David Gibson
  2016-10-10  8:14     ` Cédric Le Goater
  0 siblings, 1 reply; 75+ messages in thread
From: David Gibson @ 2016-10-07  4:34 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 4004 bytes --]

On Mon, Oct 03, 2016 at 09:24:40AM +0200, Cédric Le Goater wrote:
> The Processor Identification Register (PIR) is a register that holds a
> processor identifier which is used for bus transactions (XSCOM) and
> for processor differentiation in multiprocessor systems. It also used
> in the interrupt vector entries (IVE) to identify the thread serving
> the interrupts.
> 
> P9 and P8 have some differences in the CPU PIR encoding.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

Reviewed-by: David Gibson <david@gibson.dropbear.id.au>

Looks fine, although it's a bit hard to be sure since I haven't read
the patches which actually use this yet.

> ---
> 
>  Changes since v3 :
> 
>  - added a couple more comments on the bits definition
>   
>  hw/ppc/pnv.c         | 30 ++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h |  2 ++
>  2 files changed, 32 insertions(+)
> 
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index fc930be94f53..758c849702a0 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -239,6 +239,32 @@ static void ppc_powernv_init(MachineState *machine)
>      g_free(chip_typename);
>  }
>  
> +/*
> + *    0:21  Reserved - Read as zeros
> + *   22:24  Chip ID
> + *   25:28  Core number
> + *   29:31  Thread ID
> + */
> +static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t core_id)
> +{
> +    return (chip->chip_id << 7) | (core_id << 3);
> +}
> +
> +/*
> + *    0:48  Reserved - Read as zeroes
> + *   49:52  Node ID
> + *   53:55  Chip ID
> + *   56     Reserved - Read as zero
> + *   57:61  Core number
> + *   62:63  Thread ID
> + *
> + * We only care about the lower bits. uint32_t is fine for the moment.
> + */
> +static uint32_t pnv_chip_core_pir_p9(PnvChip *chip, uint32_t core_id)
> +{
> +    return (chip->chip_id << 8) | (core_id << 2);
> +}
> +
>  /* Allowed core identifiers on a POWER8 Processor Chip :
>   *
>   * <EX0 reserved>
> @@ -274,6 +300,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
>      k->chip_type = PNV_CHIP_POWER8E;
>      k->chip_cfam_id = 0x221ef04980000000ull;  /* P8 Murano DD2.1 */
>      k->cores_mask = POWER8E_CORE_MASK;
> +    k->core_pir = pnv_chip_core_pir_p8;
>      dc->desc = "PowerNV Chip POWER8E";
>  }
>  
> @@ -293,6 +320,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
>      k->chip_type = PNV_CHIP_POWER8;
>      k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */
>      k->cores_mask = POWER8_CORE_MASK;
> +    k->core_pir = pnv_chip_core_pir_p8;
>      dc->desc = "PowerNV Chip POWER8";
>  }
>  
> @@ -312,6 +340,7 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
>      k->chip_type = PNV_CHIP_POWER8NVL;
>      k->chip_cfam_id = 0x120d304980000000ull;  /* P8 Naples DD1.0 */
>      k->cores_mask = POWER8_CORE_MASK;
> +    k->core_pir = pnv_chip_core_pir_p8;
>      dc->desc = "PowerNV Chip POWER8NVL";
>  }
>  
> @@ -331,6 +360,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
>      k->chip_type = PNV_CHIP_POWER9;
>      k->chip_cfam_id = 0x100d104980000000ull; /* P9 Nimbus DD1.0 */
>      k->cores_mask = POWER9_CORE_MASK;
> +    k->core_pir = pnv_chip_core_pir_p9;
>      dc->desc = "PowerNV Chip POWER9";
>  }
>  
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index 2c225c928974..c676f800e28e 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -56,6 +56,8 @@ typedef struct PnvChipClass {
>      PnvChipType  chip_type;
>      uint64_t     chip_cfam_id;
>      uint64_t     cores_mask;
> +
> +    uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
>  } PnvChipClass;
>  
>  #define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E"

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 05/20] ppc/pnv: add a PnvCore object
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 05/20] ppc/pnv: add a PnvCore object Cédric Le Goater
@ 2016-10-07  4:52   ` David Gibson
  2016-10-10  8:07     ` Cédric Le Goater
  0 siblings, 1 reply; 75+ messages in thread
From: David Gibson @ 2016-10-07  4:52 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 21821 bytes --]

On Mon, Oct 03, 2016 at 09:24:41AM +0200, Cédric Le Goater wrote:
> This is largy inspired by sPAPRCPUCore with some simplification, no
> hotplug for instance. But the differences are small and the objects
> could possibly be merged.
> 
> A set of PnvCore objects is added to the PnvChip and the device
> tree is populated looping on these cores.
> 
> Real HW cpu ids are now generated depending on the chip cpu model, the
> chip id and a core mask.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
> 
>  I did not introduce a single table to construct both the chip types
>  and the corresponding core types yet. Keeping the idea for later as
>  there might be other types to construct with P9 support.
> 
>  Changes since v3:
> 
>  - removed the usage of cpu_index
>  - removed the setting of the msr_mask
>  
>  Changes since v2:
> 
>  - added P9 support
>  - used error_fatal instead of error_abort when setting the chip
>    properties
>  - replaced num_cores by nr_cores
>  - removed gservers properties that were unused on powernv. 
>  - used a 'void *' instead of a 'PnvCore *' to hold core Objects of
>    potentially different size.
>  - qom: linked the core Objects to the chip 
>  - moved device tree creation under powernv_populate_chip()
>  - added a 'pir' property' for ease of use
> 
>  Changes since v1:
> 
>  - changed name to PnvCore
>  - changed PnvChip core array type to a 'PnvCore *cores'
>  - introduced real cpu hw ids using a core mask from the chip
>  - reworked powernv_create_core_node() which populates the device tree
>  - added missing "ibm,pa-features" property 
>  - smp_cpus representing threads, used smp_cores instead to create the
>    cores in the chip.
>  - removed the use of ppc_get_vcpu_dt_id() 
>  - added "POWER8E" and "POWER8NVL" cpu models to exercice the
>    PnvChipClass
> 
>  hw/ppc/Makefile.objs      |   2 +-
>  hw/ppc/pnv.c              | 187 ++++++++++++++++++++++++++++++++++++++++++++++
>  hw/ppc/pnv_core.c         | 186 +++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h      |   3 +
>  include/hw/ppc/pnv_core.h |  48 ++++++++++++
>  5 files changed, 425 insertions(+), 1 deletion(-)
>  create mode 100644 hw/ppc/pnv_core.c
>  create mode 100644 include/hw/ppc/pnv_core.h
> 
> diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
> index 8105db7d5600..f8c7d1db9ade 100644
> --- a/hw/ppc/Makefile.objs
> +++ b/hw/ppc/Makefile.objs
> @@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
>  obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
>  obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
>  # IBM PowerNV
> -obj-$(CONFIG_POWERNV) += pnv.o
> +obj-$(CONFIG_POWERNV) += pnv.o pnv_core.o
>  ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
>  obj-y += spapr_pci_vfio.o
>  endif
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 758c849702a0..2376bb222918 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -27,6 +27,7 @@
>  #include "hw/ppc/fdt.h"
>  #include "hw/ppc/ppc.h"
>  #include "hw/ppc/pnv.h"
> +#include "hw/ppc/pnv_core.h"
>  #include "hw/loader.h"
>  #include "exec/address-spaces.h"
>  #include "qemu/cutils.h"
> @@ -74,14 +75,162 @@ static void powernv_populate_memory_node(void *fdt, int chip_id, hwaddr start,
>      _FDT((fdt_setprop_cell(fdt, off, "ibm,chip-id", chip_id)));
>  }
>  
> +static int get_cpus_node(void *fdt)
> +{
> +    int cpus_offset = fdt_path_offset(fdt, "/cpus");
> +
> +    if (cpus_offset < 0) {
> +        cpus_offset = fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"),
> +                                      "cpus");
> +        if (cpus_offset) {
> +            _FDT((fdt_setprop_cell(fdt, cpus_offset, "#address-cells", 0x1)));
> +            _FDT((fdt_setprop_cell(fdt, cpus_offset, "#size-cells", 0x0)));
> +        }
> +    }
> +    _FDT(cpus_offset);
> +    return cpus_offset;
> +}
> +
> +/*
> + * The PowerNV cores (and threads) need to use real HW ids and not an
> + * incremental index like it has been done on other platforms. This HW
> + * id is stored in the CPU PIR, it is used to create cpu nodes in the
> + * device tree, used in XSCOM to address cores and in interrupt
> + * servers.
> + */
> +static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt)
> +{
> +    CPUState *cs = CPU(DEVICE(pc->threads));
> +    DeviceClass *dc = DEVICE_GET_CLASS(cs);
> +    PowerPCCPU *cpu = POWERPC_CPU(cs);
> +    int smt_threads = ppc_get_compat_smt_threads(cpu);
> +    CPUPPCState *env = &cpu->env;
> +    PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
> +    uint32_t servers_prop[smt_threads];
> +    int i;
> +    uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40),
> +                       0xffffffff, 0xffffffff};
> +    uint32_t tbfreq = PNV_TIMEBASE_FREQ;
> +    uint32_t cpufreq = 1000000000;
> +    uint32_t page_sizes_prop[64];
> +    size_t page_sizes_prop_size;
> +    const uint8_t pa_features[] = { 24, 0,
> +                                    0xf6, 0x3f, 0xc7, 0xc0, 0x80, 0xf0,
> +                                    0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
> +                                    0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
> +                                    0x80, 0x00, 0x80, 0x00, 0x80, 0x00 };
> +    int offset;
> +    char *nodename;
> +    int cpus_offset = get_cpus_node(fdt);
> +
> +    nodename = g_strdup_printf("%s@%x", dc->fw_name, pc->pir);
> +    offset = fdt_add_subnode(fdt, cpus_offset, nodename);
> +    _FDT(offset);
> +    g_free(nodename);
> +
> +    _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", chip->chip_id)));
> +
> +    _FDT((fdt_setprop_cell(fdt, offset, "reg", pc->pir)));
> +    _FDT((fdt_setprop_cell(fdt, offset, "ibm,pir", pc->pir)));
> +    _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu")));
> +
> +    _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", env->spr[SPR_PVR])));
> +    _FDT((fdt_setprop_cell(fdt, offset, "d-cache-block-size",
> +                            env->dcache_line_size)));
> +    _FDT((fdt_setprop_cell(fdt, offset, "d-cache-line-size",
> +                            env->dcache_line_size)));
> +    _FDT((fdt_setprop_cell(fdt, offset, "i-cache-block-size",
> +                            env->icache_line_size)));
> +    _FDT((fdt_setprop_cell(fdt, offset, "i-cache-line-size",
> +                            env->icache_line_size)));
> +
> +    if (pcc->l1_dcache_size) {
> +        _FDT((fdt_setprop_cell(fdt, offset, "d-cache-size",
> +                               pcc->l1_dcache_size)));
> +    } else {
> +        error_report("Warning: Unknown L1 dcache size for cpu");
> +    }
> +    if (pcc->l1_icache_size) {
> +        _FDT((fdt_setprop_cell(fdt, offset, "i-cache-size",
> +                               pcc->l1_icache_size)));
> +    } else {
> +        error_report("Warning: Unknown L1 icache size for cpu");
> +    }
> +
> +    _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq)));
> +    _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq)));
> +    _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", env->slb_nr)));
> +    _FDT((fdt_setprop_string(fdt, offset, "status", "okay")));
> +    _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0)));
> +
> +    if (env->spr_cb[SPR_PURR].oea_read) {
> +        _FDT((fdt_setprop(fdt, offset, "ibm,purr", NULL, 0)));
> +    }
> +
> +    if (env->mmu_model & POWERPC_MMU_1TSEG) {
> +        _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes",
> +                           segs, sizeof(segs))));
> +    }
> +
> +    /* Advertise VMX/VSX (vector extensions) if available
> +     *   0 / no property == no vector extensions
> +     *   1               == VMX / Altivec available
> +     *   2               == VSX available */
> +    if (env->insns_flags & PPC_ALTIVEC) {
> +        uint32_t vmx = (env->insns_flags2 & PPC2_VSX) ? 2 : 1;
> +
> +        _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", vmx)));
> +    }
> +
> +    /* Advertise DFP (Decimal Floating Point) if available
> +     *   0 / no property == no DFP
> +     *   1               == DFP available */
> +    if (env->insns_flags2 & PPC2_DFP) {
> +        _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1)));
> +    }
> +
> +    page_sizes_prop_size = ppc_create_page_sizes_prop(env, page_sizes_prop,
> +                                                  sizeof(page_sizes_prop));
> +    if (page_sizes_prop_size) {
> +        _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes",
> +                           page_sizes_prop, page_sizes_prop_size)));
> +    }
> +
> +    _FDT((fdt_setprop(fdt, offset, "ibm,pa-features",
> +                       pa_features, sizeof(pa_features))));
> +
> +    if (cpu->cpu_version) {
> +        _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", cpu->cpu_version)));
> +    }
> +
> +    /* Build interrupt servers properties */
> +    for (i = 0; i < smt_threads; i++) {
> +        servers_prop[i] = cpu_to_be32(pc->pir + i);
> +    }
> +    _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s",
> +                       servers_prop, sizeof(servers_prop))));
> +}
> +
>  static void powernv_populate_chip(PnvChip *chip, void *fdt)
>  {
> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
> +    char *typename = pnv_core_typename(pcc->cpu_model);
> +    size_t typesize = object_type_get_instance_size(typename);
> +    int i;
> +
> +    for (i = 0; i < chip->nr_cores; i++) {
> +        PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
> +
> +        powernv_create_core_node(chip, pnv_core, fdt);
> +    }
> +
>      /* Put all the memory in one node on chip 0 until we find a way to
>       * specify different ranges for each chip
>       */
>      if (chip->chip_id == 0) {
>          powernv_populate_memory_node(fdt, chip->chip_id, 0, ram_size);
>      }
> +    g_free(typename);
>  }
>  
>  static void *powernv_create_fdt(PnvMachineState *pnv,
> @@ -404,6 +553,15 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>  {
>      PnvChip *chip = PNV_CHIP(dev);
>      Error *error = NULL;
> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
> +    char *typename = pnv_core_typename(pcc->cpu_model);
> +    size_t typesize = object_type_get_instance_size(typename);
> +    int i, core_hwid;
> +
> +    if (!object_class_by_name(typename)) {
> +        error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename);
> +        return;
> +    }
>  
>      /* Early checks on the core settings */
>      pnv_chip_core_sanitize(chip, &error);
> @@ -411,6 +569,35 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>          error_propagate(errp, error);
>          return;
>      }
> +
> +    chip->cores = g_malloc0(typesize * chip->nr_cores);
> +
> +    for (i = 0, core_hwid = 0; (core_hwid < sizeof(chip->cores_mask) * 8)
> +             && (i < chip->nr_cores); core_hwid++) {
> +        char core_name[32];
> +        void *pnv_core = chip->cores + i * typesize;
> +
> +        if (!(chip->cores_mask & (1ull << core_hwid))) {
> +            continue;
> +        }
> +
> +        object_initialize(pnv_core, typesize, typename);
> +        snprintf(core_name, sizeof(core_name), "core[%d]", core_hwid);
> +        object_property_add_child(OBJECT(chip), core_name, OBJECT(pnv_core),
> +                                  &error_fatal);
> +        object_property_set_int(OBJECT(pnv_core), smp_threads, "nr-threads",
> +                                &error_fatal);
> +        object_property_set_int(OBJECT(pnv_core), core_hwid,
> +                                CPU_CORE_PROP_CORE_ID, &error_fatal);
> +        object_property_set_int(OBJECT(pnv_core),
> +                                pcc->core_pir(chip, core_hwid),
> +                                "pir", &error_fatal);
> +        object_property_set_bool(OBJECT(pnv_core), true, "realized",
> +                                 &error_fatal);
> +        object_unref(OBJECT(pnv_core));
> +        i++;
> +    }
> +    g_free(typename);
>  }
>  
>  static Property pnv_chip_properties[] = {
> diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
> new file mode 100644
> index 000000000000..d37788f142f4
> --- /dev/null
> +++ b/hw/ppc/pnv_core.c
> @@ -0,0 +1,186 @@
> +/*
> + * QEMU PowerPC PowerNV CPU Core model
> + *
> + * Copyright (c) 2016, IBM Corporation.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public License
> + * as published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + */
> +#include "qemu/osdep.h"
> +#include "sysemu/sysemu.h"
> +#include "qapi/error.h"
> +#include "target-ppc/cpu.h"
> +#include "hw/ppc/ppc.h"
> +#include "hw/ppc/pnv.h"
> +#include "hw/ppc/pnv_core.h"
> +
> +static void powernv_cpu_reset(void *opaque)
> +{
> +    PowerPCCPU *cpu = opaque;
> +    CPUState *cs = CPU(cpu);
> +    CPUPPCState *env = &cpu->env;
> +    int core_pir;
> +    int thread_index = 0; /* TODO: TCG supports only one thread */
> +
> +    cpu_reset(cs);
> +
> +    core_pir = object_property_get_int(OBJECT(cpu), "core-pir", &error_abort);

Took me a while to figure out how this core-pir alias thing works, but
once I did, it looks ok.  I do wonder if you'll need to change to a
link back to the core object at some point if there's more than just
the pir you need to look up from the core.

> +    /*
> +     * 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 ?
> +     */
> +    env->spr[SPR_PIR] = core_pir + thread_index;

The PIR is read-only, so could it be initialized directly in _init()
rather than in the _reset() handler?

> +    env->spr[SPR_HIOR] = 0;
> +    /*
> +     * the skiboot firmware elects a primary thread to initialize the
> +     * system and it can be any.
> +     */
> +    env->gpr[3] = POWERNV_FDT_ADDR;
> +    env->nip = 0x10;
> +    env->msr |= MSR_HVB; /* Hypervisor mode */
> +}
> +
> +static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
> +{
> +    CPUPPCState *env = &cpu->env;
> +
> +    /* Set time-base frequency to 512 MHz */
> +    cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ);
> +
> +    qemu_register_reset(powernv_cpu_reset, cpu);
> +    powernv_cpu_reset(cpu);

I don't think you need this explicit call to the reset function - all
the registered reset functions should get called after this point, but
before qemu tries to start the guest.

> +}
> +
> +static void pnv_core_realize_child(Object *child, Error **errp)
> +{
> +    Error *local_err = NULL;
> +    CPUState *cs = CPU(child);
> +    PowerPCCPU *cpu = POWERPC_CPU(cs);
> +
> +    object_property_set_bool(child, true, "realized", &local_err);
> +    if (local_err) {
> +        error_propagate(errp, local_err);
> +        return;
> +    }
> +
> +    powernv_cpu_init(cpu, &local_err);
> +    if (local_err) {
> +        error_propagate(errp, local_err);
> +        return;
> +    }
> +}
> +
> +static void pnv_core_realize(DeviceState *dev, Error **errp)
> +{
> +    PnvCore *pc = PNV_CORE(OBJECT(dev));
> +    CPUCore *cc = CPU_CORE(OBJECT(dev));
> +    PnvCoreClass *pcc = PNV_CORE_GET_CLASS(OBJECT(dev));
> +    const char *typename = object_class_get_name(pcc->cpu_oc);
> +    size_t size = object_type_get_instance_size(typename);
> +    Error *local_err = NULL;
> +    void *obj;
> +    int i, j;
> +    char name[32];
> +
> +    pc->threads = g_malloc0(size * cc->nr_threads);
> +    for (i = 0; i < cc->nr_threads; i++) {
> +        obj = pc->threads + i * size;
> +
> +        object_initialize(obj, size, typename);
> +
> +        snprintf(name, sizeof(name), "thread[%d]", i);
> +        object_property_add_child(OBJECT(pc), name, obj, &local_err);
> +        object_property_add_alias(obj, "core-pir", OBJECT(pc),
> +                                  "pir", &local_err);
> +        if (local_err) {
> +            goto err;
> +        }
> +        object_unref(obj);
> +    }
> +
> +    for (j = 0; j < cc->nr_threads; j++) {
> +        obj = pc->threads + j * size;
> +
> +        pnv_core_realize_child(obj, &local_err);
> +        if (local_err) {
> +            goto err;
> +        }
> +    }
> +    return;
> +
> +err:
> +    while (--i >= 0) {
> +        obj = pc->threads + i * size;
> +        object_unparent(obj);
> +    }
> +    g_free(pc->threads);
> +    error_propagate(errp, local_err);
> +}
> +
> +static Property pnv_core_properties[] = {
> +    DEFINE_PROP_UINT32("pir", PnvCore, pir, 0),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void pnv_core_class_init(ObjectClass *oc, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(oc);
> +    PnvCoreClass *pcc = PNV_CORE_CLASS(oc);
> +
> +    dc->realize = pnv_core_realize;
> +    dc->props = pnv_core_properties;
> +    pcc->cpu_oc = cpu_class_by_name(TYPE_POWERPC_CPU, data);
> +}
> +
> +static const TypeInfo pnv_core_info = {
> +    .name           = TYPE_PNV_CORE,
> +    .parent         = TYPE_CPU_CORE,
> +    .instance_size  = sizeof(PnvCore),
> +    .class_size     = sizeof(PnvCoreClass),
> +    .abstract       = true,
> +};
> +
> +/*
> + * Grow this list or merge with SPAPRCoreInfo which is very similar

I don't think combining this with the spapr one makes sense.  If
nothing else I don't think we want to bind what CPU models are
supported for pnv to what models are supported for PAPR.

> + */
> +static const char *pnv_core_models[] = {
> +    "POWER8E", "POWER8", "POWER8NVL", "POWER9"
> +};
> +
> +static void pnv_core_register_types(void)
> +{
> +    int i ;
> +
> +    type_register_static(&pnv_core_info);
> +    for (i = 0; i < ARRAY_SIZE(pnv_core_models); ++i) {
> +        TypeInfo ti = {
> +            .parent = TYPE_PNV_CORE,
> +            .instance_size = sizeof(PnvCore),
> +            .class_init = pnv_core_class_init,
> +            .class_data = (void *) pnv_core_models[i],
> +        };
> +        ti.name = pnv_core_typename(pnv_core_models[i]);
> +        type_register(&ti);
> +        g_free((void *)ti.name);
> +    }
> +}
> +
> +type_init(pnv_core_register_types)
> +
> +char *pnv_core_typename(const char *model)
> +{
> +    return g_strdup_printf(TYPE_PNV_CORE "-%s", model);
> +}
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index c676f800e28e..ed4a360cde3b 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -45,6 +45,7 @@ typedef struct PnvChip {
>  
>      uint32_t  nr_cores;
>      uint64_t  cores_mask;
> +    void      *cores;
>  } PnvChip;
>  
>  typedef struct PnvChipClass {
> @@ -102,4 +103,6 @@ typedef struct PnvMachineState {
>  
>  #define POWERNV_FDT_ADDR                0x01000000
>  
> +#define PNV_TIMEBASE_FREQ           512000000ULL
> +
>  #endif /* _PPC_PNV_H */
> diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h
> new file mode 100644
> index 000000000000..a151e281c017
> --- /dev/null
> +++ b/include/hw/ppc/pnv_core.h
> @@ -0,0 +1,48 @@
> +/*
> + * QEMU PowerPC PowerNV CPU Core model
> + *
> + * Copyright (c) 2016, IBM Corporation.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public License
> + * as published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + */
> +#ifndef _PPC_PNV_CORE_H
> +#define _PPC_PNV_CORE_H
> +
> +#include "hw/cpu/core.h"
> +
> +#define TYPE_PNV_CORE "powernv-cpu-core"
> +#define PNV_CORE(obj) \
> +    OBJECT_CHECK(PnvCore, (obj), TYPE_PNV_CORE)
> +#define PNV_CORE_CLASS(klass) \
> +     OBJECT_CLASS_CHECK(PnvCoreClass, (klass), TYPE_PNV_CORE)
> +#define PNV_CORE_GET_CLASS(obj) \
> +     OBJECT_GET_CLASS(PnvCoreClass, (obj), TYPE_PNV_CORE)
> +
> +typedef struct PnvCore {
> +    /*< private >*/
> +    CPUCore parent_obj;
> +
> +    /*< public >*/
> +    void *threads;
> +    uint32_t pir;
> +} PnvCore;
> +
> +typedef struct PnvCoreClass {
> +    DeviceClass parent_class;
> +    ObjectClass *cpu_oc;
> +} PnvCoreClass;
> +
> +extern char *pnv_core_typename(const char *model);
> +
> +#endif /* _PPC_PNV_CORE_H */

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 03/20] ppc/pnv: add a core mask to PnvChip
  2016-10-07  4:32   ` David Gibson
@ 2016-10-07  5:01     ` Benjamin Herrenschmidt
  2016-10-07  5:11       ` David Gibson
  2016-10-10 12:56     ` Cédric Le Goater
  1 sibling, 1 reply; 75+ messages in thread
From: Benjamin Herrenschmidt @ 2016-10-07  5:01 UTC (permalink / raw)
  To: David Gibson, Cédric Le Goater; +Cc: qemu-ppc, qemu-devel

On Fri, 2016-10-07 at 15:32 +1100, David Gibson wrote:
> On Mon, Oct 03, 2016 at 09:24:39AM +0200, Cédric Le Goater wrote:
> > This will be used to build real HW ids for the cores and enforce
> some
> > limits on the available cores per chip.
> 
> Is there actually a practical reason to allow the user (or machine
> type) to override the default core mask?

None other than mimmicing real HW ... some cores can be disabled
on some chips and we *might* want to mimmic that for some test
scenarios.

Cheers,
Ben.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 03/20] ppc/pnv: add a core mask to PnvChip
  2016-10-07  5:01     ` Benjamin Herrenschmidt
@ 2016-10-07  5:11       ` David Gibson
  2016-10-07  8:24         ` Cédric Le Goater
  0 siblings, 1 reply; 75+ messages in thread
From: David Gibson @ 2016-10-07  5:11 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Cédric Le Goater, qemu-ppc, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 850 bytes --]

On Fri, Oct 07, 2016 at 04:01:52PM +1100, Benjamin Herrenschmidt wrote:
> On Fri, 2016-10-07 at 15:32 +1100, David Gibson wrote:
> > On Mon, Oct 03, 2016 at 09:24:39AM +0200, Cédric Le Goater wrote:
> > > This will be used to build real HW ids for the cores and enforce
> > some
> > > limits on the available cores per chip.
> > 
> > Is there actually a practical reason to allow the user (or machine
> > type) to override the default core mask?
> 
> None other than mimmicing real HW ... some cores can be disabled
> on some chips and we *might* want to mimmic that for some test
> scenarios.

Ok, sounds like a good enough reason to me.

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 01/20] ppc/pnv: add skeleton PowerNV platform
  2016-10-07  4:14   ` David Gibson
  2016-10-07  4:16     ` David Gibson
@ 2016-10-07  7:38     ` Cédric Le Goater
  2016-10-07 16:29       ` Jeff Cody
  2016-10-07  8:36     ` Cédric Le Goater
  2 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-07  7:38 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel, Jeff Cody

Hello,

>>  - removed the requirement on having a kernel loaded as running with
>>    just a firmware is fine. We will need to discuss the inclusion of
>>    the file skiboot.lid under qemu.
> 
> Yes, this isn't terribly useful without it.  The normal procedure for
> new roms is this:
> 
>    1. Get the upstream git tree for the ROM mirrored to qemu.org
>    2. Add a git submodule under roms/ referencing the git mirror on
>       qemu.org
>    3. Add a pre-built ROM binary to pc-bios/
>    4. Add a brief description of the ROM, including upstream git URL
>       to pc-bios/README
> 
> Steps 2, 3 & 4 can (and usually should) be a single commit.
> 
> This code is looking close enough, that having a usable rom image is
> probably the last thing stopping merge, at least of these initial
> patches.

I added one to my repo to let travis run :

	https://github.com/legoater/qemu/commit/d5729f70f2abb0fca2e4167dacb0cf27e0dbaee5

> Probably best to get underway with the rom inclusion ASAP.

I think Jeff is the person to talk to.

Could we start mirroring skiboot, the firmware for PowerPC PowerNV platform, 
on git.qemu-project.org/. This is the official repo :

	https://github.com/open-power/skiboot/

Thanks,

C.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 03/20] ppc/pnv: add a core mask to PnvChip
  2016-10-07  5:11       ` David Gibson
@ 2016-10-07  8:24         ` Cédric Le Goater
  0 siblings, 0 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-07  8:24 UTC (permalink / raw)
  To: David Gibson, Benjamin Herrenschmidt; +Cc: qemu-ppc, qemu-devel

On 10/07/2016 07:11 AM, David Gibson wrote:
> On Fri, Oct 07, 2016 at 04:01:52PM +1100, Benjamin Herrenschmidt wrote:
>> On Fri, 2016-10-07 at 15:32 +1100, David Gibson wrote:
>>> On Mon, Oct 03, 2016 at 09:24:39AM +0200, Cédric Le Goater wrote:
>>>> This will be used to build real HW ids for the cores and enforce
>>> some
>>>> limits on the available cores per chip.
>>>
>>> Is there actually a practical reason to allow the user (or machine
>>> type) to override the default core mask?
>>
>> None other than mimmicing real HW ... some cores can be disabled
>> on some chips and we *might* want to mimmic that for some test
>> scenarios.
> 
> Ok, sounds like a good enough reason to me.

There are really different layouts on the field. Here is a S824,
16 cores, 4 sockets, with these HW ids :

	0x4	0x4	0x5	0x4
	0x5	0x5	0xc	0x5
	0x6	0x6	0xd	0x6
	0xe	0xe	0xe	0xd

Long term, we will want to support unplug I suppose.
	
C.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 01/20] ppc/pnv: add skeleton PowerNV platform
  2016-10-07  4:14   ` David Gibson
  2016-10-07  4:16     ` David Gibson
  2016-10-07  7:38     ` Cédric Le Goater
@ 2016-10-07  8:36     ` Cédric Le Goater
  2 siblings, 0 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-07  8:36 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 10/07/2016 06:14 AM, David Gibson wrote:
> On Mon, Oct 03, 2016 at 09:24:37AM +0200, Cédric Le Goater wrote:
>> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>>
>> The goal is to emulate a PowerNV system at the level of the skiboot
>> firmware, which loads the OS and provides some runtime services. Power
>> Systems have a lower firmware (HostBoot) that does low level system
>> initialization, like DRAM training. This is beyond the scope of what
>> qemu will address in a PowerNV guest.
>>
>> No devices yet, not even an interrupt controller. Just to get started,
>> some RAM to load the skiboot firmware, the kernel and initrd. The
>> device tree is fully created in the machine reset op.
>>
>> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>> [clg: - updated for qemu-2.7
>>       - replaced fprintf by error_report
>>       - used a common definition of _FDT macro
>>       - removed VMStateDescription as migration is not yet supported
>>       - added IBM Copyright statements
>>       - reworked kernel_filename handling
>>       - merged PnvSystem and sPowerNVMachineState
>>       - removed PHANDLE_XICP
>>       - added ppc_create_page_sizes_prop helper
>>       - removed nmi support
>>       - removed kvm support
>>       - updated powernv machine to version 2.8
>>       - removed chips and cpus, They will be provided in another patches
>>       - added a machine reset routine to initialize the device tree (also)
>>       - french has a squelette and english a skeleton.
>>       - improved commit log.
>>       - reworked prototypes parameters
>>       - added a check on the ram size (thanks to Michael Ellerman)
>>       - fixed chip-id cell
>>       - changed MAX_CPUS to 2048
>>       - simplified memory node creation to one node only
>>       - removed machine version
>>       - rewrote the device tree creation with the fdt "rw" routines
>>       - s/sPowerNVMachineState/PnvMachineState/
>>       - etc.]
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>  Changes since v3:
>>
>>  - fixed printf format for hwaddr
>>  - used fdt_pack() before writing the tree in memory 
>>  - removed the requirement on having a kernel loaded as running with
>>    just a firmware is fine. We will need to discuss the inclusion of
>>    the file skiboot.lid under qemu.
> 
> Yes, this isn't terribly useful without it.  The normal procedure for
> new roms is this:
> 
>    1. Get the upstream git tree for the ROM mirrored to qemu.org
>    2. Add a git submodule under roms/ referencing the git mirror on
>       qemu.org
>    3. Add a pre-built ROM binary to pc-bios/
>    4. Add a brief description of the ROM, including upstream git URL
>       to pc-bios/README
> 
> Steps 2, 3 & 4 can (and usually should) be a single commit.
> 
> This code is looking close enough, that having a usable rom image is
> probably the last thing stopping merge, at least of these initial
> patches.
> 
> Probably best to get underway with the rom inclusion ASAP.
> 
>>  Changes since v2:
>>
>>  - some more copyright header cleanups
>>  - remove fdt_addr field from PnvMachineState
>>
>>  Changes since v1:
>>
>>  - changed MAX_CPUS to 2048
>>  - simplified memory node creation to one node only
>>  - removed machine version 
>>  - rewrote the device tree creation with the fdt "rw" routines
>>  - s/sPowerNVMachineState/PnvMachineState/
>>  - block_default_type is back to IF_IDE because of the AHCI device
>>
>>  default-configs/ppc64-softmmu.mak |   1 +
>>  hw/ppc/Makefile.objs              |   2 +
>>  hw/ppc/pnv.c                      | 223 ++++++++++++++++++++++++++++++++++++++
>>  include/hw/ppc/pnv.h              |  38 +++++++
>>  4 files changed, 264 insertions(+)
>>  create mode 100644 hw/ppc/pnv.c
>>  create mode 100644 include/hw/ppc/pnv.h
>>
>> diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak
>> index db5a4d6f5eea..67a9bcaa67fa 100644
>> --- a/default-configs/ppc64-softmmu.mak
>> +++ b/default-configs/ppc64-softmmu.mak
>> @@ -39,6 +39,7 @@ CONFIG_I8259=y
>>  CONFIG_XILINX=y
>>  CONFIG_XILINX_ETHLITE=y
>>  CONFIG_PSERIES=y
>> +CONFIG_POWERNV=y
>>  CONFIG_PREP=y
>>  CONFIG_MAC=y
>>  CONFIG_E500=y
>> diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
>> index 99a0d4e581bf..8105db7d5600 100644
>> --- a/hw/ppc/Makefile.objs
>> +++ b/hw/ppc/Makefile.objs
>> @@ -5,6 +5,8 @@ obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o
>>  obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
>>  obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
>>  obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
>> +# IBM PowerNV
>> +obj-$(CONFIG_POWERNV) += pnv.o
>>  ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
>>  obj-y += spapr_pci_vfio.o
>>  endif
>> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
>> new file mode 100644
>> index 000000000000..02fc4826baa4
>> --- /dev/null
>> +++ b/hw/ppc/pnv.c
>> @@ -0,0 +1,223 @@
>> +/*
>> + * QEMU PowerPC PowerNV machine model
>> + *
>> + * Copyright (c) 2016, IBM Corporation.
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qapi/error.h"
>> +#include "sysemu/sysemu.h"
>> +#include "sysemu/numa.h"
>> +#include "hw/hw.h"
>> +#include "target-ppc/cpu.h"
>> +#include "qemu/log.h"
>> +#include "hw/ppc/fdt.h"
>> +#include "hw/ppc/ppc.h"
>> +#include "hw/ppc/pnv.h"
>> +#include "hw/loader.h"
>> +#include "exec/address-spaces.h"
>> +#include "qemu/cutils.h"
>> +
>> +#include <libfdt.h>
>> +
>> +#define FDT_MAX_SIZE            0x00100000
>> +
>> +#define FW_FILE_NAME            "skiboot.lid"
>> +#define FW_LOAD_ADDR            0x0
>> +#define FW_MAX_SIZE             0x00400000
>> +
>> +#define KERNEL_LOAD_ADDR        0x20000000
>> +#define INITRD_LOAD_ADDR        0x40000000
>> +
>> +/*
>> + * On Power Systems E880, the max cpus (threads) should be :
>> + *     4 * 4 sockets * 12 cores * 8 threads = 1536
>> + * Let's make it 2^11
>> + */
>> +#define MAX_CPUS                2048
>> +
>> +/*
>> + * Memory nodes are created by hostboot, one for each range of memory
>> + * that has a different "affinity". In practice, it means one range
>> + * per chip.
>> + */
>> +static void powernv_populate_memory_node(void *fdt, int chip_id, hwaddr start,
>> +                                         hwaddr size)
>> +{
>> +    char *mem_name;
>> +    uint64_t mem_reg_property[2];
>> +    int off;
>> +
>> +    mem_reg_property[0] = cpu_to_be64(start);
>> +    mem_reg_property[1] = cpu_to_be64(size);
>> +
>> +    mem_name = g_strdup_printf("memory@%"HWADDR_PRIx, start);
>> +    off = fdt_add_subnode(fdt, 0, mem_name);
>> +    g_free(mem_name);
>> +
>> +    _FDT((fdt_setprop_string(fdt, off, "device_type", "memory")));
>> +    _FDT((fdt_setprop(fdt, off, "reg", mem_reg_property,
>> +                       sizeof(mem_reg_property))));
>> +    _FDT((fdt_setprop_cell(fdt, off, "ibm,chip-id", chip_id)));
>> +}
>> +
>> +static void *powernv_create_fdt(PnvMachineState *pnv,
>> +                                const char *kernel_cmdline)
>> +{
>> +    void *fdt;
>> +    char *buf;
>> +    const char plat_compat[] = "qemu,powernv\0ibm,powernv";
>> +    int off;
>> +
>> +    fdt = g_malloc0(FDT_MAX_SIZE);
>> +    _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE)));
>> +
>> +    /* Root node */
>> +    _FDT((fdt_setprop_cell(fdt, 0, "#address-cells", 0x2)));
>> +    _FDT((fdt_setprop_cell(fdt, 0, "#size-cells", 0x2)));
>> +    _FDT((fdt_setprop_string(fdt, 0, "model",
>> +                             "IBM PowerNV (emulated by qemu)")));
>> +    _FDT((fdt_setprop(fdt, 0, "compatible", plat_compat,
>> +                      sizeof(plat_compat))));
>> +
>> +    buf =  qemu_uuid_unparse_strdup(&qemu_uuid);
>> +    _FDT((fdt_setprop_string(fdt, 0, "vm,uuid", buf)));
>> +    if (qemu_uuid_set) {
>> +        _FDT((fdt_property_string(fdt, "system-id", buf)));
>> +    }
>> +    g_free(buf);
>> +
>> +    off = fdt_add_subnode(fdt, 0, "chosen");
>> +    if (kernel_cmdline) {
>> +        _FDT((fdt_setprop_string(fdt, off, "bootargs", kernel_cmdline)));
>> +    }
>> +
>> +    if (pnv->initrd_size) {
>> +        uint32_t start_prop = cpu_to_be32(pnv->initrd_base);
>> +        uint32_t end_prop = cpu_to_be32(pnv->initrd_base + pnv->initrd_size);
>> +
>> +        _FDT((fdt_setprop(fdt, off, "linux,initrd-start",
>> +                               &start_prop, sizeof(start_prop))));
>> +        _FDT((fdt_setprop(fdt, off, "linux,initrd-end",
>> +                               &end_prop, sizeof(end_prop))));
>> +    }
>> +
>> +    /* Put all the memory in one node on chip 0 until we find a way to
>> +     * specify different ranges for each chip
>> +     */
>> +    powernv_populate_memory_node(fdt, 0, 0, ram_size);
> 
> machine->ram_size is preferred over the ram_size global these days.

OK. np. I will change that. 

> Doesn't matter that much, since this will be reworked for multiple
> chips at some point anyway.

We are not very far from multichip I think. XICS will need a little
more work. This mostly because cpu_index will exceed nr_servers. 
We can work around that with a get_icp() handler. LPC and PSIHB will
need some minor tweaks.


Thanks,

C.

>> +
>> +    return fdt;
>> +}
>> +
>> +static void ppc_powernv_reset(void)
>> +{
>> +    MachineState *machine = MACHINE(qdev_get_machine());
>> +    PnvMachineState *pnv = POWERNV_MACHINE(machine);
>> +    void *fdt;
>> +
>> +    qemu_devices_reset();
>> +
>> +    fdt = powernv_create_fdt(pnv, machine->kernel_cmdline);
>> +
>> +    /* Pack resulting tree */
>> +    _FDT((fdt_pack(fdt)));
>> +
>> +    cpu_physical_memory_write(POWERNV_FDT_ADDR, fdt, fdt_totalsize(fdt));
>> +}
>> +
>> +static void ppc_powernv_init(MachineState *machine)
>> +{
>> +    PnvMachineState *pnv = POWERNV_MACHINE(machine);
>> +    ram_addr_t ram_size = machine->ram_size;
>> +    MemoryRegion *ram;
>> +    char *fw_filename;
>> +    long fw_size;
>> +    long kernel_size;
>> +
>> +    /* allocate RAM */
>> +    if (ram_size < (1 * G_BYTE)) {
>> +        error_report("Warning: skiboot may not work with < 1GB of RAM");
>> +    }
>> +
>> +    ram = g_new(MemoryRegion, 1);
>> +    memory_region_allocate_system_memory(ram, NULL, "ppc_powernv.ram",
>> +                                         ram_size);
>> +    memory_region_add_subregion(get_system_memory(), 0, ram);
>> +
>> +    /* load skiboot firmware  */
>> +    if (bios_name == NULL) {
>> +        bios_name = FW_FILE_NAME;
>> +    }
>> +
>> +    fw_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
>> +
>> +    fw_size = load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE);
>> +    if (fw_size < 0) {
>> +        hw_error("qemu: could not load OPAL '%s'\n", fw_filename);
>> +        exit(1);
>> +    }
>> +    g_free(fw_filename);
>> +
>> +    /* load kernel */
>> +    kernel_size = load_image_targphys(machine->kernel_filename,
>> +                                      KERNEL_LOAD_ADDR, 0x2000000);
>> +    if (kernel_size < 0) {
>> +        hw_error("qemu: could not load kernel'%s'\n", machine->kernel_filename);
>> +        exit(1);
>> +    }
>> +
>> +    /* load initrd */
>> +    if (machine->initrd_filename) {
>> +        pnv->initrd_base = INITRD_LOAD_ADDR;
>> +        pnv->initrd_size = load_image_targphys(machine->initrd_filename,
>> +                                  pnv->initrd_base, 0x10000000); /* 128MB max */
>> +        if (pnv->initrd_size < 0) {
>> +            error_report("qemu: could not load initial ram disk '%s'",
>> +                         machine->initrd_filename);
>> +            exit(1);
>> +        }
>> +    }
>> +}
>> +
>> +static void powernv_machine_class_init(ObjectClass *oc, void *data)
>> +{
>> +    MachineClass *mc = MACHINE_CLASS(oc);
>> +
>> +    mc->desc = "IBM PowerNV (Non-Virtualized)";
>> +    mc->init = ppc_powernv_init;
>> +    mc->reset = ppc_powernv_reset;
>> +    mc->max_cpus = MAX_CPUS;
>> +    mc->block_default_type = IF_IDE; /* Pnv provides a AHCI device for
>> +                                      * storage */
>> +    mc->no_parallel = 1;
>> +    mc->default_boot_order = NULL;
>> +    mc->default_ram_size = 1 * G_BYTE;
>> +}
>> +
>> +static const TypeInfo powernv_machine_info = {
>> +    .name          = TYPE_POWERNV_MACHINE,
>> +    .parent        = TYPE_MACHINE,
>> +    .instance_size = sizeof(PnvMachineState),
>> +    .class_init    = powernv_machine_class_init,
>> +};
>> +
>> +static void powernv_machine_register_types(void)
>> +{
>> +    type_register_static(&powernv_machine_info);
>> +}
>> +
>> +type_init(powernv_machine_register_types)
>> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
>> new file mode 100644
>> index 000000000000..c8a73bc74267
>> --- /dev/null
>> +++ b/include/hw/ppc/pnv.h
>> @@ -0,0 +1,38 @@
>> +/*
>> + * QEMU PowerPC PowerNV various definitions
>> + *
>> + * Copyright (c) 2014-2016 BenH, IBM Corporation.
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
>> + */
>> +#ifndef _PPC_PNV_H
>> +#define _PPC_PNV_H
>> +
>> +#include "hw/boards.h"
>> +
>> +#define TYPE_POWERNV_MACHINE       MACHINE_TYPE_NAME("powernv")
>> +#define POWERNV_MACHINE(obj) \
>> +    OBJECT_CHECK(PnvMachineState, (obj), TYPE_POWERNV_MACHINE)
>> +
>> +typedef struct PnvMachineState {
>> +    /*< private >*/
>> +    MachineState parent_obj;
>> +
>> +    uint32_t initrd_base;
>> +    long initrd_size;
>> +} PnvMachineState;
>> +
>> +#define POWERNV_FDT_ADDR                0x01000000
>> +
>> +#endif /* _PPC_PNV_H */
> 

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 02/20] ppc/pnv: add a PnvChip object
  2016-10-07  4:26   ` David Gibson
@ 2016-10-07  9:16     ` Cédric Le Goater
  0 siblings, 0 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-07  9:16 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 10/07/2016 06:26 AM, David Gibson wrote:
> On Mon, Oct 03, 2016 at 09:24:38AM +0200, Cédric Le Goater wrote:
>> This is is an abstraction of a POWER8 chip which is a set of cores
>> plus other 'units', like the pervasive unit, the interrupt controller,
>> the memory controller, the on-chip microcontroller, etc. The whole can
>> be seen as a socket. It depends on a cpu model and its characteristics:
>> max cores, specific inits are defined in a PnvChipClass.
>>
>> We start with an near empty PnvChip with only a few cpu constants
>> which we will grow in the subsequent patches with the controllers
>> required to run the system.
>>
>> The Chip CFAM (Common FRU Access Module) ID gives the model of the
>> chip and its version number. It is generally the first thing firmwares
>> fetch, available at XSCOM PCB address 0xf000f, to start initialization.
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> 
> Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
> 
> I do have one small suggested change below, but it's not that important.
> 
>> ---
>>
>>  chip_type could possibly be removed or calculated from the attribute
>>  chip_cfam_id. Let's keep it for now and see how the patchset evolves.
>>  This is getting big, maybe should move the code to hw/ppc/pnv_chip.c ?
>>
>>  Changes since v3:
>>
>>  - removed PnvChipPower* types
>>  - removed realize ops of PnvChip
>>  - replaced scanf by qemu_strtoul
>>
>>  Changes since v2:
>>
>>  - forced a POWER8 cpu model if none is specified and check that a
>>    PnvChip type exist for it
>>  - did some renaming to be consistent with the cpu model names
>>  - added POWER9 chip
>>  - removed empty realize op
>>  - renamed atribute chip_f000f in chip_cfam_id
>>  - used error_fatal instead of error_abort when setting the chip
>>    properties
>>  - introduced a powernv_populate_chip() routine
>>
>>  Changes since v1:
>>  
>>  - introduced a PnvChipClass depending on the cpu model. It also
>>    provides some chip constants used by devices, like the cpu model hw
>>    id (f000f), a enum type (not sure this is useful yet), a custom
>>    realize ops for customization.
>>  - the num-chips property can be configured on the command line.
>>  
>>  hw/ppc/pnv.c         | 194 +++++++++++++++++++++++++++++++++++++++++++++++++--
>>  include/hw/ppc/pnv.h |  61 ++++++++++++++++
>>  2 files changed, 250 insertions(+), 5 deletions(-)
>>
>> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
>> index 02fc4826baa4..08f72dbdca97 100644
>> --- a/hw/ppc/pnv.c
>> +++ b/hw/ppc/pnv.c
>> @@ -74,6 +74,16 @@ static void powernv_populate_memory_node(void *fdt, int chip_id, hwaddr start,
>>      _FDT((fdt_setprop_cell(fdt, off, "ibm,chip-id", chip_id)));
>>  }
>>  
>> +static void powernv_populate_chip(PnvChip *chip, void *fdt)
>> +{
>> +    /* Put all the memory in one node on chip 0 until we find a way to
>> +     * specify different ranges for each chip
>> +     */
>> +    if (chip->chip_id == 0) {
>> +        powernv_populate_memory_node(fdt, chip->chip_id, 0, ram_size);
>> +    }
>> +}
>> +
>>  static void *powernv_create_fdt(PnvMachineState *pnv,
>>                                  const char *kernel_cmdline)
>>  {
>> @@ -81,6 +91,7 @@ static void *powernv_create_fdt(PnvMachineState *pnv,
>>      char *buf;
>>      const char plat_compat[] = "qemu,powernv\0ibm,powernv";
>>      int off;
>> +    int i;
>>  
>>      fdt = g_malloc0(FDT_MAX_SIZE);
>>      _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE)));
>> @@ -115,11 +126,10 @@ static void *powernv_create_fdt(PnvMachineState *pnv,
>>                                 &end_prop, sizeof(end_prop))));
>>      }
>>  
>> -    /* Put all the memory in one node on chip 0 until we find a way to
>> -     * specify different ranges for each chip
>> -     */
>> -    powernv_populate_memory_node(fdt, 0, 0, ram_size);
>> -
>> +    /* Populate device tree for each chip */
>> +    for (i = 0; i < pnv->num_chips; i++) {
>> +        powernv_populate_chip(pnv->chips[i], fdt);
>> +    }
>>      return fdt;
>>  }
>>  
>> @@ -147,6 +157,8 @@ static void ppc_powernv_init(MachineState *machine)
>>      char *fw_filename;
>>      long fw_size;
>>      long kernel_size;
>> +    int i;
>> +    char *chip_typename;
>>  
>>      /* allocate RAM */
>>      if (ram_size < (1 * G_BYTE)) {
>> @@ -191,6 +203,172 @@ static void ppc_powernv_init(MachineState *machine)
>>              exit(1);
>>          }
>>      }
>> +
>> +    /* We need some cpu model to instantiate the PnvChip class */
>> +    if (machine->cpu_model == NULL) {
>> +        machine->cpu_model = "POWER8";
>> +    }
>> +
>> +    /* Create the processor chips */
>> +    chip_typename = g_strdup_printf(TYPE_PNV_CHIP "-%s", machine->cpu_model);
>> +    if (!object_class_by_name(chip_typename)) {
>> +        error_report("qemu: invalid CPU model '%s' for %s machine",
>> +                     machine->cpu_model, MACHINE_GET_CLASS(machine)->name);
>> +        exit(1);
>> +    }
>> +
>> +    pnv->chips = g_new0(PnvChip *, pnv->num_chips);
>> +    for (i = 0; i < pnv->num_chips; i++) {
>> +        char chip_name[32];
>> +        Object *chip = object_new(chip_typename);
>> +
>> +        pnv->chips[i] = PNV_CHIP(chip);
>> +
>> +        snprintf(chip_name, sizeof(chip_name), "chip[%d]", CHIP_HWID(i));
>> +        object_property_add_child(OBJECT(pnv), chip_name, chip, &error_fatal);
>> +        object_property_set_int(chip, CHIP_HWID(i), "chip-id", &error_fatal);
>> +        object_property_set_bool(chip, true, "realized", &error_fatal);
>> +    }
>> +    g_free(chip_typename);
>> +}
>> +
>> +static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    PnvChipClass *k = PNV_CHIP_CLASS(klass);
>> +
>> +    k->cpu_model = "POWER8E";
>> +    k->chip_type = PNV_CHIP_POWER8E;
>> +    k->chip_cfam_id = 0x221ef04980000000ull;  /* P8 Murano DD2.1 */
>> +    dc->desc = "PowerNV Chip POWER8E";
>> +}
>> +
>> +static const TypeInfo pnv_chip_power8e_info = {
>> +    .name          = TYPE_PNV_CHIP_POWER8E,
>> +    .parent        = TYPE_PNV_CHIP,
>> +    .instance_size = sizeof(PnvChip),
>> +    .class_init    = pnv_chip_power8e_class_init,
>> +};
>> +
>> +static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    PnvChipClass *k = PNV_CHIP_CLASS(klass);
>> +
>> +    k->cpu_model = "POWER8";
>> +    k->chip_type = PNV_CHIP_POWER8;
>> +    k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */
>> +    dc->desc = "PowerNV Chip POWER8";
>> +}
>> +
>> +static const TypeInfo pnv_chip_power8_info = {
>> +    .name          = TYPE_PNV_CHIP_POWER8,
>> +    .parent        = TYPE_PNV_CHIP,
>> +    .instance_size = sizeof(PnvChip),
>> +    .class_init    = pnv_chip_power8_class_init,
>> +};
>> +
>> +static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    PnvChipClass *k = PNV_CHIP_CLASS(klass);
>> +
>> +    k->cpu_model = "POWER8NVL";
>> +    k->chip_type = PNV_CHIP_POWER8NVL;
>> +    k->chip_cfam_id = 0x120d304980000000ull;  /* P8 Naples DD1.0 */
>> +    dc->desc = "PowerNV Chip POWER8NVL";
>> +}
>> +
>> +static const TypeInfo pnv_chip_power8nvl_info = {
>> +    .name          = TYPE_PNV_CHIP_POWER8NVL,
>> +    .parent        = TYPE_PNV_CHIP,
>> +    .instance_size = sizeof(PnvChip),
>> +    .class_init    = pnv_chip_power8nvl_class_init,
>> +};
>> +
>> +static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    PnvChipClass *k = PNV_CHIP_CLASS(klass);
>> +
>> +    k->cpu_model = "POWER9";
>> +    k->chip_type = PNV_CHIP_POWER9;
>> +    k->chip_cfam_id = 0x100d104980000000ull; /* P9 Nimbus DD1.0 */
>> +    dc->desc = "PowerNV Chip POWER9";
>> +}
>> +
>> +static const TypeInfo pnv_chip_power9_info = {
>> +    .name          = TYPE_PNV_CHIP_POWER9,
>> +    .parent        = TYPE_PNV_CHIP,
>> +    .instance_size = sizeof(PnvChip),
>> +    .class_init    = pnv_chip_power9_class_init,
>> +};
>> +
>> +static void pnv_chip_realize(DeviceState *dev, Error **errp)
>> +{
>> +    /* left purposely empty */
>> +}
>> +
>> +static Property pnv_chip_properties[] = {
>> +    DEFINE_PROP_UINT32("chip-id", PnvChip, chip_id, 0),
>> +    DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static void pnv_chip_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    dc->realize = pnv_chip_realize;
>> +    dc->props = pnv_chip_properties;
>> +    dc->desc = "PowerNV Chip";
>> +}
>> +
>> +static const TypeInfo pnv_chip_info = {
>> +    .name          = TYPE_PNV_CHIP,
>> +    .parent        = TYPE_SYS_BUS_DEVICE,
>> +    .class_init    = pnv_chip_class_init,
>> +    .class_size    = sizeof(PnvChipClass),
>> +    .abstract      = true,
>> +};
>> +
>> +
>> +static char *pnv_get_num_chips(Object *obj, Error **errp)
>> +{
>> +    return g_strdup_printf("%d", POWERNV_MACHINE(obj)->num_chips);
>> +}
>> +
>> +static void pnv_set_num_chips(Object *obj, const char *value, Error **errp)
>> +{
>> +    PnvMachineState *pnv = POWERNV_MACHINE(obj);
>> +    unsigned long num_chips;
>> +
>> +    if (qemu_strtoul(value, NULL, 10, &num_chips) < 0) {
>> +        error_setg(errp, "invalid num_chips property: '%s'", value);
>> +        return;
>> +    }
>> +
>> +    /*
>> +     * TODO: should we decide on how many chips we can create based
>> +     * on #cores and Venice vs. Murano vs. Naples chip type etc...,
>> +     */
>> +    if (num_chips < 1 || num_chips > 4) {
>> +        error_setg(errp, "invalid number of chips: '%s'", value);
>> +        return;
>> +    }
>> +
>> +    pnv->num_chips = num_chips;
>> +}
>> +
>> +static void powernv_machine_initfn(Object *obj)
>> +{
>> +    PnvMachineState *pnv = POWERNV_MACHINE(obj);
>> +    pnv->num_chips = 1;
>> +
>> +    object_property_add_str(obj, "num-chips", pnv_get_num_chips,
>> +                            pnv_set_num_chips, NULL);
>> +    object_property_set_description(obj, "num-chips",
>> +                                    "Specifies the number of processor chips",
>> +                                    NULL);
> 
> So I recently discovered that nearly everything done with
> object_property_add_*() should usually be done with
> object_class_property_add_*() - basically anything where the property
> exists for every instance of the class.  Yes, practically everything
> in qemu already gets this wrong.  Still, if you get a chance to rework
> this, might as well not make this one more.

Sure I will in v5.

Thanks,

C. 

>>  }
>>  
>>  static void powernv_machine_class_init(ObjectClass *oc, void *data)
>> @@ -212,12 +390,18 @@ static const TypeInfo powernv_machine_info = {
>>      .name          = TYPE_POWERNV_MACHINE,
>>      .parent        = TYPE_MACHINE,
>>      .instance_size = sizeof(PnvMachineState),
>> +    .instance_init = powernv_machine_initfn,
>>      .class_init    = powernv_machine_class_init,
>>  };
>>  
>>  static void powernv_machine_register_types(void)
>>  {
>>      type_register_static(&powernv_machine_info);
>> +    type_register_static(&pnv_chip_info);
>> +    type_register_static(&pnv_chip_power8e_info);
>> +    type_register_static(&pnv_chip_power8_info);
>> +    type_register_static(&pnv_chip_power8nvl_info);
>> +    type_register_static(&pnv_chip_power9_info);
>>  }
>>  
>>  type_init(powernv_machine_register_types)
>> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
>> index c8a73bc74267..da543ed81636 100644
>> --- a/include/hw/ppc/pnv.h
>> +++ b/include/hw/ppc/pnv.h
>> @@ -20,6 +20,64 @@
>>  #define _PPC_PNV_H
>>  
>>  #include "hw/boards.h"
>> +#include "hw/sysbus.h"
>> +
>> +#define TYPE_PNV_CHIP "powernv-chip"
>> +#define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
>> +#define PNV_CHIP_CLASS(klass) \
>> +     OBJECT_CLASS_CHECK(PnvChipClass, (klass), TYPE_PNV_CHIP)
>> +#define PNV_CHIP_GET_CLASS(obj) \
>> +     OBJECT_GET_CLASS(PnvChipClass, (obj), TYPE_PNV_CHIP)
>> +
>> +typedef enum PnvChipType {
>> +    PNV_CHIP_POWER8E,     /* AKA Murano (default) */
>> +    PNV_CHIP_POWER8,      /* AKA Venice */
>> +    PNV_CHIP_POWER8NVL,   /* AKA Naples */
>> +    PNV_CHIP_POWER9,      /* AKA Nimbus */
>> +} PnvChipType;
>> +
>> +typedef struct PnvChip {
>> +    /*< private >*/
>> +    SysBusDevice parent_obj;
>> +
>> +    /*< public >*/
>> +    uint32_t     chip_id;
>> +} PnvChip;
>> +
>> +typedef struct PnvChipClass {
>> +    /*< private >*/
>> +    SysBusDeviceClass parent_class;
>> +
>> +    /*< public >*/
>> +    const char *cpu_model;
>> +    PnvChipType  chip_type;
>> +    uint64_t     chip_cfam_id;
>> +} PnvChipClass;
>> +
>> +#define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E"
>> +#define PNV_CHIP_POWER8E(obj) \
>> +    OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8E)
>> +
>> +#define TYPE_PNV_CHIP_POWER8 TYPE_PNV_CHIP "-POWER8"
>> +#define PNV_CHIP_POWER8(obj) \
>> +    OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8)
>> +
>> +#define TYPE_PNV_CHIP_POWER8NVL TYPE_PNV_CHIP "-POWER8NVL"
>> +#define PNV_CHIP_POWER8NVL(obj) \
>> +    OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8NVL)
>> +
>> +#define TYPE_PNV_CHIP_POWER9 TYPE_PNV_CHIP "-POWER9"
>> +#define PNV_CHIP_POWER9(obj) \
>> +    OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER9)
>> +
>> +/*
>> + * This generates a HW chip id depending on an index:
>> + *
>> + *    0x0, 0x1, 0x10, 0x11
>> + *
>> + * 4 chips should be the maximum
>> + */
>> +#define CHIP_HWID(i) ((((i) & 0x3e) << 3) | ((i) & 0x1))
>>  
>>  #define TYPE_POWERNV_MACHINE       MACHINE_TYPE_NAME("powernv")
>>  #define POWERNV_MACHINE(obj) \
>> @@ -31,6 +89,9 @@ typedef struct PnvMachineState {
>>  
>>      uint32_t initrd_base;
>>      long initrd_size;
>> +
>> +    uint32_t  num_chips;
>> +    PnvChip   **chips;
>>  } PnvMachineState;
>>  
>>  #define POWERNV_FDT_ADDR                0x01000000
> 

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 01/20] ppc/pnv: add skeleton PowerNV platform
  2016-10-07  7:38     ` Cédric Le Goater
@ 2016-10-07 16:29       ` Jeff Cody
  0 siblings, 0 replies; 75+ messages in thread
From: Jeff Cody @ 2016-10-07 16:29 UTC (permalink / raw)
  To: Cédric Le Goater
  Cc: David Gibson, qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On Fri, Oct 07, 2016 at 09:38:07AM +0200, Cédric Le Goater wrote:
> Hello,
> 
> >>  - removed the requirement on having a kernel loaded as running with
> >>    just a firmware is fine. We will need to discuss the inclusion of
> >>    the file skiboot.lid under qemu.
> > 
> > Yes, this isn't terribly useful without it.  The normal procedure for
> > new roms is this:
> > 
> >    1. Get the upstream git tree for the ROM mirrored to qemu.org
> >    2. Add a git submodule under roms/ referencing the git mirror on
> >       qemu.org
> >    3. Add a pre-built ROM binary to pc-bios/
> >    4. Add a brief description of the ROM, including upstream git URL
> >       to pc-bios/README
> > 
> > Steps 2, 3 & 4 can (and usually should) be a single commit.
> > 
> > This code is looking close enough, that having a usable rom image is
> > probably the last thing stopping merge, at least of these initial
> > patches.
> 
> I added one to my repo to let travis run :
> 
> 	https://github.com/legoater/qemu/commit/d5729f70f2abb0fca2e4167dacb0cf27e0dbaee5
> 
> > Probably best to get underway with the rom inclusion ASAP.
> 
> I think Jeff is the person to talk to.
> 
> Could we start mirroring skiboot, the firmware for PowerPC PowerNV platform, 
> on git.qemu-project.org/. This is the official repo :
> 
> 	https://github.com/open-power/skiboot/
>

Thanks Cédric,

I added the skiboot mirror.  It is available at:

git://git.qemu.org/skiboot.git
http://git.qemu.org/git/skiboot.git

If you need anything else, just let me know.


Thanks,
Jeff

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 05/20] ppc/pnv: add a PnvCore object
  2016-10-07  4:52   ` David Gibson
@ 2016-10-10  8:07     ` Cédric Le Goater
  0 siblings, 0 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-10  8:07 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 10/07/2016 06:52 AM, David Gibson wrote:
> On Mon, Oct 03, 2016 at 09:24:41AM +0200, Cédric Le Goater wrote:
>> This is largy inspired by sPAPRCPUCore with some simplification, no
>> hotplug for instance. But the differences are small and the objects
>> could possibly be merged.
>>
>> A set of PnvCore objects is added to the PnvChip and the device
>> tree is populated looping on these cores.
>>
>> Real HW cpu ids are now generated depending on the chip cpu model, the
>> chip id and a core mask.
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>
>>  I did not introduce a single table to construct both the chip types
>>  and the corresponding core types yet. Keeping the idea for later as
>>  there might be other types to construct with P9 support.
>>
>>  Changes since v3:
>>
>>  - removed the usage of cpu_index
>>  - removed the setting of the msr_mask
>>  
>>  Changes since v2:
>>
>>  - added P9 support
>>  - used error_fatal instead of error_abort when setting the chip
>>    properties
>>  - replaced num_cores by nr_cores
>>  - removed gservers properties that were unused on powernv. 
>>  - used a 'void *' instead of a 'PnvCore *' to hold core Objects of
>>    potentially different size.
>>  - qom: linked the core Objects to the chip 
>>  - moved device tree creation under powernv_populate_chip()
>>  - added a 'pir' property' for ease of use
>>
>>  Changes since v1:
>>
>>  - changed name to PnvCore
>>  - changed PnvChip core array type to a 'PnvCore *cores'
>>  - introduced real cpu hw ids using a core mask from the chip
>>  - reworked powernv_create_core_node() which populates the device tree
>>  - added missing "ibm,pa-features" property 
>>  - smp_cpus representing threads, used smp_cores instead to create the
>>    cores in the chip.
>>  - removed the use of ppc_get_vcpu_dt_id() 
>>  - added "POWER8E" and "POWER8NVL" cpu models to exercice the
>>    PnvChipClass
>>
>>  hw/ppc/Makefile.objs      |   2 +-
>>  hw/ppc/pnv.c              | 187 ++++++++++++++++++++++++++++++++++++++++++++++
>>  hw/ppc/pnv_core.c         | 186 +++++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/ppc/pnv.h      |   3 +
>>  include/hw/ppc/pnv_core.h |  48 ++++++++++++
>>  5 files changed, 425 insertions(+), 1 deletion(-)
>>  create mode 100644 hw/ppc/pnv_core.c
>>  create mode 100644 include/hw/ppc/pnv_core.h
>>
>> diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
>> index 8105db7d5600..f8c7d1db9ade 100644
>> --- a/hw/ppc/Makefile.objs
>> +++ b/hw/ppc/Makefile.objs
>> @@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
>>  obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
>>  obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
>>  # IBM PowerNV
>> -obj-$(CONFIG_POWERNV) += pnv.o
>> +obj-$(CONFIG_POWERNV) += pnv.o pnv_core.o
>>  ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
>>  obj-y += spapr_pci_vfio.o
>>  endif
>> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
>> index 758c849702a0..2376bb222918 100644
>> --- a/hw/ppc/pnv.c
>> +++ b/hw/ppc/pnv.c
>> @@ -27,6 +27,7 @@
>>  #include "hw/ppc/fdt.h"
>>  #include "hw/ppc/ppc.h"
>>  #include "hw/ppc/pnv.h"
>> +#include "hw/ppc/pnv_core.h"
>>  #include "hw/loader.h"
>>  #include "exec/address-spaces.h"
>>  #include "qemu/cutils.h"
>> @@ -74,14 +75,162 @@ static void powernv_populate_memory_node(void *fdt, int chip_id, hwaddr start,
>>      _FDT((fdt_setprop_cell(fdt, off, "ibm,chip-id", chip_id)));
>>  }
>>  
>> +static int get_cpus_node(void *fdt)
>> +{
>> +    int cpus_offset = fdt_path_offset(fdt, "/cpus");
>> +
>> +    if (cpus_offset < 0) {
>> +        cpus_offset = fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"),
>> +                                      "cpus");
>> +        if (cpus_offset) {
>> +            _FDT((fdt_setprop_cell(fdt, cpus_offset, "#address-cells", 0x1)));
>> +            _FDT((fdt_setprop_cell(fdt, cpus_offset, "#size-cells", 0x0)));
>> +        }
>> +    }
>> +    _FDT(cpus_offset);
>> +    return cpus_offset;
>> +}
>> +
>> +/*
>> + * The PowerNV cores (and threads) need to use real HW ids and not an
>> + * incremental index like it has been done on other platforms. This HW
>> + * id is stored in the CPU PIR, it is used to create cpu nodes in the
>> + * device tree, used in XSCOM to address cores and in interrupt
>> + * servers.
>> + */
>> +static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt)
>> +{
>> +    CPUState *cs = CPU(DEVICE(pc->threads));
>> +    DeviceClass *dc = DEVICE_GET_CLASS(cs);
>> +    PowerPCCPU *cpu = POWERPC_CPU(cs);
>> +    int smt_threads = ppc_get_compat_smt_threads(cpu);
>> +    CPUPPCState *env = &cpu->env;
>> +    PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
>> +    uint32_t servers_prop[smt_threads];
>> +    int i;
>> +    uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40),
>> +                       0xffffffff, 0xffffffff};
>> +    uint32_t tbfreq = PNV_TIMEBASE_FREQ;
>> +    uint32_t cpufreq = 1000000000;
>> +    uint32_t page_sizes_prop[64];
>> +    size_t page_sizes_prop_size;
>> +    const uint8_t pa_features[] = { 24, 0,
>> +                                    0xf6, 0x3f, 0xc7, 0xc0, 0x80, 0xf0,
>> +                                    0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
>> +                                    0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
>> +                                    0x80, 0x00, 0x80, 0x00, 0x80, 0x00 };
>> +    int offset;
>> +    char *nodename;
>> +    int cpus_offset = get_cpus_node(fdt);
>> +
>> +    nodename = g_strdup_printf("%s@%x", dc->fw_name, pc->pir);
>> +    offset = fdt_add_subnode(fdt, cpus_offset, nodename);
>> +    _FDT(offset);
>> +    g_free(nodename);
>> +
>> +    _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", chip->chip_id)));
>> +
>> +    _FDT((fdt_setprop_cell(fdt, offset, "reg", pc->pir)));
>> +    _FDT((fdt_setprop_cell(fdt, offset, "ibm,pir", pc->pir)));
>> +    _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu")));
>> +
>> +    _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", env->spr[SPR_PVR])));
>> +    _FDT((fdt_setprop_cell(fdt, offset, "d-cache-block-size",
>> +                            env->dcache_line_size)));
>> +    _FDT((fdt_setprop_cell(fdt, offset, "d-cache-line-size",
>> +                            env->dcache_line_size)));
>> +    _FDT((fdt_setprop_cell(fdt, offset, "i-cache-block-size",
>> +                            env->icache_line_size)));
>> +    _FDT((fdt_setprop_cell(fdt, offset, "i-cache-line-size",
>> +                            env->icache_line_size)));
>> +
>> +    if (pcc->l1_dcache_size) {
>> +        _FDT((fdt_setprop_cell(fdt, offset, "d-cache-size",
>> +                               pcc->l1_dcache_size)));
>> +    } else {
>> +        error_report("Warning: Unknown L1 dcache size for cpu");
>> +    }
>> +    if (pcc->l1_icache_size) {
>> +        _FDT((fdt_setprop_cell(fdt, offset, "i-cache-size",
>> +                               pcc->l1_icache_size)));
>> +    } else {
>> +        error_report("Warning: Unknown L1 icache size for cpu");
>> +    }
>> +
>> +    _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq)));
>> +    _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq)));
>> +    _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", env->slb_nr)));
>> +    _FDT((fdt_setprop_string(fdt, offset, "status", "okay")));
>> +    _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0)));
>> +
>> +    if (env->spr_cb[SPR_PURR].oea_read) {
>> +        _FDT((fdt_setprop(fdt, offset, "ibm,purr", NULL, 0)));
>> +    }
>> +
>> +    if (env->mmu_model & POWERPC_MMU_1TSEG) {
>> +        _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes",
>> +                           segs, sizeof(segs))));
>> +    }
>> +
>> +    /* Advertise VMX/VSX (vector extensions) if available
>> +     *   0 / no property == no vector extensions
>> +     *   1               == VMX / Altivec available
>> +     *   2               == VSX available */
>> +    if (env->insns_flags & PPC_ALTIVEC) {
>> +        uint32_t vmx = (env->insns_flags2 & PPC2_VSX) ? 2 : 1;
>> +
>> +        _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", vmx)));
>> +    }
>> +
>> +    /* Advertise DFP (Decimal Floating Point) if available
>> +     *   0 / no property == no DFP
>> +     *   1               == DFP available */
>> +    if (env->insns_flags2 & PPC2_DFP) {
>> +        _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1)));
>> +    }
>> +
>> +    page_sizes_prop_size = ppc_create_page_sizes_prop(env, page_sizes_prop,
>> +                                                  sizeof(page_sizes_prop));
>> +    if (page_sizes_prop_size) {
>> +        _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes",
>> +                           page_sizes_prop, page_sizes_prop_size)));
>> +    }
>> +
>> +    _FDT((fdt_setprop(fdt, offset, "ibm,pa-features",
>> +                       pa_features, sizeof(pa_features))));
>> +
>> +    if (cpu->cpu_version) {
>> +        _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", cpu->cpu_version)));
>> +    }
>> +
>> +    /* Build interrupt servers properties */
>> +    for (i = 0; i < smt_threads; i++) {
>> +        servers_prop[i] = cpu_to_be32(pc->pir + i);
>> +    }
>> +    _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s",
>> +                       servers_prop, sizeof(servers_prop))));
>> +}
>> +
>>  static void powernv_populate_chip(PnvChip *chip, void *fdt)
>>  {
>> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
>> +    char *typename = pnv_core_typename(pcc->cpu_model);
>> +    size_t typesize = object_type_get_instance_size(typename);
>> +    int i;
>> +
>> +    for (i = 0; i < chip->nr_cores; i++) {
>> +        PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
>> +
>> +        powernv_create_core_node(chip, pnv_core, fdt);
>> +    }
>> +
>>      /* Put all the memory in one node on chip 0 until we find a way to
>>       * specify different ranges for each chip
>>       */
>>      if (chip->chip_id == 0) {
>>          powernv_populate_memory_node(fdt, chip->chip_id, 0, ram_size);
>>      }
>> +    g_free(typename);
>>  }
>>  
>>  static void *powernv_create_fdt(PnvMachineState *pnv,
>> @@ -404,6 +553,15 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>  {
>>      PnvChip *chip = PNV_CHIP(dev);
>>      Error *error = NULL;
>> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
>> +    char *typename = pnv_core_typename(pcc->cpu_model);
>> +    size_t typesize = object_type_get_instance_size(typename);
>> +    int i, core_hwid;
>> +
>> +    if (!object_class_by_name(typename)) {
>> +        error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename);
>> +        return;
>> +    }
>>  
>>      /* Early checks on the core settings */
>>      pnv_chip_core_sanitize(chip, &error);
>> @@ -411,6 +569,35 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>          error_propagate(errp, error);
>>          return;
>>      }
>> +
>> +    chip->cores = g_malloc0(typesize * chip->nr_cores);
>> +
>> +    for (i = 0, core_hwid = 0; (core_hwid < sizeof(chip->cores_mask) * 8)
>> +             && (i < chip->nr_cores); core_hwid++) {
>> +        char core_name[32];
>> +        void *pnv_core = chip->cores + i * typesize;
>> +
>> +        if (!(chip->cores_mask & (1ull << core_hwid))) {
>> +            continue;
>> +        }
>> +
>> +        object_initialize(pnv_core, typesize, typename);
>> +        snprintf(core_name, sizeof(core_name), "core[%d]", core_hwid);
>> +        object_property_add_child(OBJECT(chip), core_name, OBJECT(pnv_core),
>> +                                  &error_fatal);
>> +        object_property_set_int(OBJECT(pnv_core), smp_threads, "nr-threads",
>> +                                &error_fatal);
>> +        object_property_set_int(OBJECT(pnv_core), core_hwid,
>> +                                CPU_CORE_PROP_CORE_ID, &error_fatal);
>> +        object_property_set_int(OBJECT(pnv_core),
>> +                                pcc->core_pir(chip, core_hwid),
>> +                                "pir", &error_fatal);
>> +        object_property_set_bool(OBJECT(pnv_core), true, "realized",
>> +                                 &error_fatal);
>> +        object_unref(OBJECT(pnv_core));
>> +        i++;
>> +    }
>> +    g_free(typename);
>>  }
>>  
>>  static Property pnv_chip_properties[] = {
>> diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
>> new file mode 100644
>> index 000000000000..d37788f142f4
>> --- /dev/null
>> +++ b/hw/ppc/pnv_core.c
>> @@ -0,0 +1,186 @@
>> +/*
>> + * QEMU PowerPC PowerNV CPU Core model
>> + *
>> + * Copyright (c) 2016, IBM Corporation.
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public License
>> + * as published by the Free Software Foundation; either version 2 of
>> + * the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
>> + */
>> +#include "qemu/osdep.h"
>> +#include "sysemu/sysemu.h"
>> +#include "qapi/error.h"
>> +#include "target-ppc/cpu.h"
>> +#include "hw/ppc/ppc.h"
>> +#include "hw/ppc/pnv.h"
>> +#include "hw/ppc/pnv_core.h"
>> +
>> +static void powernv_cpu_reset(void *opaque)
>> +{
>> +    PowerPCCPU *cpu = opaque;
>> +    CPUState *cs = CPU(cpu);
>> +    CPUPPCState *env = &cpu->env;
>> +    int core_pir;
>> +    int thread_index = 0; /* TODO: TCG supports only one thread */
>> +
>> +    cpu_reset(cs);
>> +
>> +    core_pir = object_property_get_int(OBJECT(cpu), "core-pir", &error_abort);
> 
> Took me a while to figure out how this core-pir alias thing works, but
> once I did, it looks ok.  I do wonder if you'll need to change to a
> link back to the core object at some point if there's more than just
> the pir you need to look up from the core.

yes. That is how I handled it in a first version, with a back link to the
core, but the current patchset does not need it at runtime. so I just used 
a property.

>> +    /*
>> +     * 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 ?
>> +     */
>> +    env->spr[SPR_PIR] = core_pir + thread_index;
> 
> The PIR is read-only, so could it be initialized directly in _init()
> rather than in the _reset() handler?

yes. I found now how it should be set in init :

	env->spr_cb[SPR_PIR]->default_value


> 
>> +    env->spr[SPR_HIOR] = 0;

HIOR is for 970. I will remove that also from reset.


>> +    /*
>> +     * the skiboot firmware elects a primary thread to initialize the
>> +     * system and it can be any.
>> +     */
>> +    env->gpr[3] = POWERNV_FDT_ADDR;
>> +    env->nip = 0x10;
>> +    env->msr |= MSR_HVB; /* Hypervisor mode */
>> +}
>> +
>> +static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
>> +{
>> +    CPUPPCState *env = &cpu->env;
>> +
>> +    /* Set time-base frequency to 512 MHz */
>> +    cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ);
>> +
>> +    qemu_register_reset(powernv_cpu_reset, cpu);
>> +    powernv_cpu_reset(cpu);
> 
> I don't think you need this explicit call to the reset function - all
> the registered reset functions should get called after this point, but
> before qemu tries to start the guest.

I think you are right. This is just adding an extra superfluous call. 
 
> 
>> +}
>> +
>> +static void pnv_core_realize_child(Object *child, Error **errp)
>> +{
>> +    Error *local_err = NULL;
>> +    CPUState *cs = CPU(child);
>> +    PowerPCCPU *cpu = POWERPC_CPU(cs);
>> +
>> +    object_property_set_bool(child, true, "realized", &local_err);
>> +    if (local_err) {
>> +        error_propagate(errp, local_err);
>> +        return;
>> +    }
>> +
>> +    powernv_cpu_init(cpu, &local_err);
>> +    if (local_err) {
>> +        error_propagate(errp, local_err);
>> +        return;
>> +    }
>> +}
>> +
>> +static void pnv_core_realize(DeviceState *dev, Error **errp)
>> +{
>> +    PnvCore *pc = PNV_CORE(OBJECT(dev));
>> +    CPUCore *cc = CPU_CORE(OBJECT(dev));
>> +    PnvCoreClass *pcc = PNV_CORE_GET_CLASS(OBJECT(dev));
>> +    const char *typename = object_class_get_name(pcc->cpu_oc);
>> +    size_t size = object_type_get_instance_size(typename);
>> +    Error *local_err = NULL;
>> +    void *obj;
>> +    int i, j;
>> +    char name[32];
>> +
>> +    pc->threads = g_malloc0(size * cc->nr_threads);
>> +    for (i = 0; i < cc->nr_threads; i++) {
>> +        obj = pc->threads + i * size;
>> +
>> +        object_initialize(obj, size, typename);
>> +
>> +        snprintf(name, sizeof(name), "thread[%d]", i);
>> +        object_property_add_child(OBJECT(pc), name, obj, &local_err);
>> +        object_property_add_alias(obj, "core-pir", OBJECT(pc),
>> +                                  "pir", &local_err);
>> +        if (local_err) {
>> +            goto err;
>> +        }
>> +        object_unref(obj);
>> +    }
>> +
>> +    for (j = 0; j < cc->nr_threads; j++) {
>> +        obj = pc->threads + j * size;
>> +
>> +        pnv_core_realize_child(obj, &local_err);
>> +        if (local_err) {
>> +            goto err;
>> +        }
>> +    }
>> +    return;
>> +
>> +err:
>> +    while (--i >= 0) {
>> +        obj = pc->threads + i * size;
>> +        object_unparent(obj);
>> +    }
>> +    g_free(pc->threads);
>> +    error_propagate(errp, local_err);
>> +}
>> +
>> +static Property pnv_core_properties[] = {
>> +    DEFINE_PROP_UINT32("pir", PnvCore, pir, 0),
>> +    DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static void pnv_core_class_init(ObjectClass *oc, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(oc);
>> +    PnvCoreClass *pcc = PNV_CORE_CLASS(oc);
>> +
>> +    dc->realize = pnv_core_realize;
>> +    dc->props = pnv_core_properties;
>> +    pcc->cpu_oc = cpu_class_by_name(TYPE_POWERPC_CPU, data);
>> +}
>> +
>> +static const TypeInfo pnv_core_info = {
>> +    .name           = TYPE_PNV_CORE,
>> +    .parent         = TYPE_CPU_CORE,
>> +    .instance_size  = sizeof(PnvCore),
>> +    .class_size     = sizeof(PnvCoreClass),
>> +    .abstract       = true,
>> +};
>> +
>> +/*
>> + * Grow this list or merge with SPAPRCoreInfo which is very similar
> 
> I don't think combining this with the spapr one makes sense.  If
> nothing else I don't think we want to bind what CPU models are
> supported for pnv to what models are supported for PAPR.

ok.

Thanks,

C. 

> 
>> + */
>> +static const char *pnv_core_models[] = {
>> +    "POWER8E", "POWER8", "POWER8NVL", "POWER9"
>> +};
>> +
>> +static void pnv_core_register_types(void)
>> +{
>> +    int i ;
>> +
>> +    type_register_static(&pnv_core_info);
>> +    for (i = 0; i < ARRAY_SIZE(pnv_core_models); ++i) {
>> +        TypeInfo ti = {
>> +            .parent = TYPE_PNV_CORE,
>> +            .instance_size = sizeof(PnvCore),
>> +            .class_init = pnv_core_class_init,
>> +            .class_data = (void *) pnv_core_models[i],
>> +        };
>> +        ti.name = pnv_core_typename(pnv_core_models[i]);
>> +        type_register(&ti);
>> +        g_free((void *)ti.name);
>> +    }
>> +}
>> +
>> +type_init(pnv_core_register_types)
>> +
>> +char *pnv_core_typename(const char *model)
>> +{
>> +    return g_strdup_printf(TYPE_PNV_CORE "-%s", model);
>> +}
>> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
>> index c676f800e28e..ed4a360cde3b 100644
>> --- a/include/hw/ppc/pnv.h
>> +++ b/include/hw/ppc/pnv.h
>> @@ -45,6 +45,7 @@ typedef struct PnvChip {
>>  
>>      uint32_t  nr_cores;
>>      uint64_t  cores_mask;
>> +    void      *cores;
>>  } PnvChip;
>>  
>>  typedef struct PnvChipClass {
>> @@ -102,4 +103,6 @@ typedef struct PnvMachineState {
>>  
>>  #define POWERNV_FDT_ADDR                0x01000000
>>  
>> +#define PNV_TIMEBASE_FREQ           512000000ULL
>> +
>>  #endif /* _PPC_PNV_H */
>> diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h
>> new file mode 100644
>> index 000000000000..a151e281c017
>> --- /dev/null
>> +++ b/include/hw/ppc/pnv_core.h
>> @@ -0,0 +1,48 @@
>> +/*
>> + * QEMU PowerPC PowerNV CPU Core model
>> + *
>> + * Copyright (c) 2016, IBM Corporation.
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public License
>> + * as published by the Free Software Foundation; either version 2 of
>> + * the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
>> + */
>> +#ifndef _PPC_PNV_CORE_H
>> +#define _PPC_PNV_CORE_H
>> +
>> +#include "hw/cpu/core.h"
>> +
>> +#define TYPE_PNV_CORE "powernv-cpu-core"
>> +#define PNV_CORE(obj) \
>> +    OBJECT_CHECK(PnvCore, (obj), TYPE_PNV_CORE)
>> +#define PNV_CORE_CLASS(klass) \
>> +     OBJECT_CLASS_CHECK(PnvCoreClass, (klass), TYPE_PNV_CORE)
>> +#define PNV_CORE_GET_CLASS(obj) \
>> +     OBJECT_GET_CLASS(PnvCoreClass, (obj), TYPE_PNV_CORE)
>> +
>> +typedef struct PnvCore {
>> +    /*< private >*/
>> +    CPUCore parent_obj;
>> +
>> +    /*< public >*/
>> +    void *threads;
>> +    uint32_t pir;
>> +} PnvCore;
>> +
>> +typedef struct PnvCoreClass {
>> +    DeviceClass parent_class;
>> +    ObjectClass *cpu_oc;
>> +} PnvCoreClass;
>> +
>> +extern char *pnv_core_typename(const char *model);
>> +
>> +#endif /* _PPC_PNV_CORE_H */
> 

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 04/20] ppc/pnv: add a PIR handler to PnvChip
  2016-10-07  4:34   ` David Gibson
@ 2016-10-10  8:14     ` Cédric Le Goater
  0 siblings, 0 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-10  8:14 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 10/07/2016 06:34 AM, David Gibson wrote:
> On Mon, Oct 03, 2016 at 09:24:40AM +0200, Cédric Le Goater wrote:
>> The Processor Identification Register (PIR) is a register that holds a
>> processor identifier which is used for bus transactions (XSCOM) and
>> for processor differentiation in multiprocessor systems. It also used
>> in the interrupt vector entries (IVE) to identify the thread serving
>> the interrupts.
>>
>> P9 and P8 have some differences in the CPU PIR encoding.
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> 
> Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
> 
> Looks fine, although it's a bit hard to be sure since I haven't read
> the patches which actually use this yet.

This is used in the next patch when the pir of the PnvCore is initialized :

+        object_property_set_int(OBJECT(pnv_core),
+                                pcc->core_pir(chip, core_hwid),
+                                "pir", &error_fatal);

Then this "pir" property of the core is added as a property alias to 
PowerPCCPU, which uses it to initialize SPR_PIR.

Thanks,

C. 

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 03/20] ppc/pnv: add a core mask to PnvChip
  2016-10-07  4:32   ` David Gibson
  2016-10-07  5:01     ` Benjamin Herrenschmidt
@ 2016-10-10 12:56     ` Cédric Le Goater
  2016-10-11 10:24       ` David Gibson
  1 sibling, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-10 12:56 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel


>> @@ -227,11 +227,44 @@ static void ppc_powernv_init(MachineState *machine)
>>          snprintf(chip_name, sizeof(chip_name), "chip[%d]", CHIP_HWID(i));
>>          object_property_add_child(OBJECT(pnv), chip_name, chip, &error_fatal);
>>          object_property_set_int(chip, CHIP_HWID(i), "chip-id", &error_fatal);
>> +        object_property_set_int(chip, smp_cores, "nr-cores", &error_fatal);
>> +        /*
>> +         * We could customize cores_mask for the chip here. May be
>> +         * using a powernv machine property, like 'num-chips'. Let the
>> +         * chip choose the default for now.
> 
> I don't think you need any special mechanism for this.  If you just
> remove this explicit assignment the chip default will apply, but the
> user can alter it using -global.

Using a command line with :

	-global powernv-chip-POWER8.cores-mask=0x7070

would work for one chip but not for more. Let's start with that, I will 
remove the comment for now. multiple chip is for later.

Thanks,

C.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 03/20] ppc/pnv: add a core mask to PnvChip
  2016-10-10 12:56     ` Cédric Le Goater
@ 2016-10-11 10:24       ` David Gibson
  2016-10-12  8:53         ` Cédric Le Goater
  0 siblings, 1 reply; 75+ messages in thread
From: David Gibson @ 2016-10-11 10:24 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 1529 bytes --]

On Mon, Oct 10, 2016 at 02:56:25PM +0200, Cédric Le Goater wrote:
> 
> >> @@ -227,11 +227,44 @@ static void ppc_powernv_init(MachineState *machine)
> >>          snprintf(chip_name, sizeof(chip_name), "chip[%d]", CHIP_HWID(i));
> >>          object_property_add_child(OBJECT(pnv), chip_name, chip, &error_fatal);
> >>          object_property_set_int(chip, CHIP_HWID(i), "chip-id", &error_fatal);
> >> +        object_property_set_int(chip, smp_cores, "nr-cores", &error_fatal);
> >> +        /*
> >> +         * We could customize cores_mask for the chip here. May be
> >> +         * using a powernv machine property, like 'num-chips'. Let the
> >> +         * chip choose the default for now.
> > 
> > I don't think you need any special mechanism for this.  If you just
> > remove this explicit assignment the chip default will apply, but the
> > user can alter it using -global.
> 
> Using a command line with :
> 
> 	-global powernv-chip-POWER8.cores-mask=0x7070
> 
> would work for one chip but not for more. Let's start with that, I will 
> remove the comment for now. multiple chip is for later.

Well, it works for more than one chip if you want the same mask for
each of them.  If you want different masks, I think you can still do
it with -set, but working out the right arguments can be a PITA.

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 03/20] ppc/pnv: add a core mask to PnvChip
  2016-10-11 10:24       ` David Gibson
@ 2016-10-12  8:53         ` Cédric Le Goater
  2016-10-13  0:24           ` David Gibson
  0 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-12  8:53 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 10/11/2016 12:24 PM, David Gibson wrote:
> On Mon, Oct 10, 2016 at 02:56:25PM +0200, Cédric Le Goater wrote:
>>
>>>> @@ -227,11 +227,44 @@ static void ppc_powernv_init(MachineState *machine)
>>>>          snprintf(chip_name, sizeof(chip_name), "chip[%d]", CHIP_HWID(i));
>>>>          object_property_add_child(OBJECT(pnv), chip_name, chip, &error_fatal);
>>>>          object_property_set_int(chip, CHIP_HWID(i), "chip-id", &error_fatal);
>>>> +        object_property_set_int(chip, smp_cores, "nr-cores", &error_fatal);
>>>> +        /*
>>>> +         * We could customize cores_mask for the chip here. May be
>>>> +         * using a powernv machine property, like 'num-chips'. Let the
>>>> +         * chip choose the default for now.
>>>
>>> I don't think you need any special mechanism for this.  If you just
>>> remove this explicit assignment the chip default will apply, but the
>>> user can alter it using -global.
>>
>> Using a command line with :
>>
>> 	-global powernv-chip-POWER8.cores-mask=0x7070
>>
>> would work for one chip but not for more. Let's start with that, I will 
>> remove the comment for now. multiple chip is for later.
> 
> Well, it works for more than one chip if you want the same mask for
> each of them.  If you want different masks, I think you can still do
> it with -set, but working out the right arguments can be a PITA.

That would be the best solution but I did not find a way to address one
chip object to do a "qom set".

(qemu) info qom-tree 
/machine (powernv-machine)
  /unattached (container)
    /system[0] (qemu:memory-region)
    /sysbus (System)
    /ppc_powernv.ram[0] (qemu:memory-region)
    /io[0] (qemu:memory-region)
  /peripheral-anon (container)
  /peripheral (container)
  /chip[1] (powernv-chip-POWER8)
    ...
  /chip[0] (powernv-chip-POWER8)
    ...
 
We will have a similar need with the ram to spread the contents on
the chips. 

C.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 03/20] ppc/pnv: add a core mask to PnvChip
  2016-10-12  8:53         ` Cédric Le Goater
@ 2016-10-13  0:24           ` David Gibson
  0 siblings, 0 replies; 75+ messages in thread
From: David Gibson @ 2016-10-13  0:24 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 2431 bytes --]

On Wed, Oct 12, 2016 at 10:53:12AM +0200, Cédric Le Goater wrote:
> On 10/11/2016 12:24 PM, David Gibson wrote:
> > On Mon, Oct 10, 2016 at 02:56:25PM +0200, Cédric Le Goater wrote:
> >>
> >>>> @@ -227,11 +227,44 @@ static void ppc_powernv_init(MachineState *machine)
> >>>>          snprintf(chip_name, sizeof(chip_name), "chip[%d]", CHIP_HWID(i));
> >>>>          object_property_add_child(OBJECT(pnv), chip_name, chip, &error_fatal);
> >>>>          object_property_set_int(chip, CHIP_HWID(i), "chip-id", &error_fatal);
> >>>> +        object_property_set_int(chip, smp_cores, "nr-cores", &error_fatal);
> >>>> +        /*
> >>>> +         * We could customize cores_mask for the chip here. May be
> >>>> +         * using a powernv machine property, like 'num-chips'. Let the
> >>>> +         * chip choose the default for now.
> >>>
> >>> I don't think you need any special mechanism for this.  If you just
> >>> remove this explicit assignment the chip default will apply, but the
> >>> user can alter it using -global.
> >>
> >> Using a command line with :
> >>
> >> 	-global powernv-chip-POWER8.cores-mask=0x7070
> >>
> >> would work for one chip but not for more. Let's start with that, I will 
> >> remove the comment for now. multiple chip is for later.
> > 
> > Well, it works for more than one chip if you want the same mask for
> > each of them.  If you want different masks, I think you can still do
> > it with -set, but working out the right arguments can be a PITA.
> 
> That would be the best solution but I did not find a way to address one
> chip object to do a "qom set".

Yeah, that's what I mean working out the arguments can be tricky.

> 
> (qemu) info qom-tree 
> /machine (powernv-machine)
>   /unattached (container)
>     /system[0] (qemu:memory-region)
>     /sysbus (System)
>     /ppc_powernv.ram[0] (qemu:memory-region)
>     /io[0] (qemu:memory-region)
>   /peripheral-anon (container)
>   /peripheral (container)
>   /chip[1] (powernv-chip-POWER8)
>     ...
>   /chip[0] (powernv-chip-POWER8)
>     ...
>  
> We will have a similar need with the ram to spread the contents on
> the chips. 

Sure.  Let's cross these bridges when we come to them.

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/20] ppc/pnv: add XSCOM infrastructure
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 06/20] ppc/pnv: add XSCOM infrastructure Cédric Le Goater
@ 2016-10-13  0:41   ` David Gibson
  2016-10-13  6:26     ` Cédric Le Goater
  2016-11-07  8:26   ` Olaf Hering
  1 sibling, 1 reply; 75+ messages in thread
From: David Gibson @ 2016-10-13  0:41 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 20742 bytes --]

On Mon, Oct 03, 2016 at 09:24:42AM +0200, Cédric Le Goater wrote:
> On a real POWER8 system, the Pervasive Interconnect Bus (PIB) serves
> as a backbone to connect different units of the system. The host
> firmware connects to the PIB through a bridge unit, the
> Alter-Display-Unit (ADU), which gives him access to all the chiplets
> on the PCB network (Pervasive Connect Bus), the PIB acting as the root
> of this network.
> 
> XSCOM (serial communication) is the interface to the sideband bus
> provided by the POWER8 pervasive unit to read and write to chiplets
> resources. This is needed by the host firmware, OPAL and to a lesser
> extent, Linux. This is among others how the PCI Host bridges get
> configured at boot or how the LPC bus is accessed.
> 
> To represent the ADU of a real system, we introduce a specific
> AddressSpace to dispatch XSCOM accesses to the targeted chiplets. The
> translation of an XSCOM address into a PCB register address is
> slightly different between the P9 and the P8. This is handled before
> the dispatch using a 8byte alignment for all.
> 
> To customize the device tree, a QOM InterfaceClass, PnvXScomInterface,
> is provided with a populate() handler. The chip populates the device
> tree by simply looping on its children. Therefore, each model needing
> custom nodes should not forget to declare itself as a child at
> instantiation time.
> 
> Based on previous work done by :
>       Benjamin Herrenschmidt <benh@kernel.crashing.org>
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
> 
>  Changes since v3:
> 
>  - reworked the model to dispatch addresses to the memory regions
>    using pcb_addr << 3, which is a no-op for the P9. The benefit is
>    that all the address translation work can be done before dispatch
>    and the conversion handlers in the chip and in the xscom interface
>    are gone.
>    
>  - removed the proxy PnnXscom object and extended the PnvChip object
>    with an address space for XSCOM and its associated memory region.
>    
>  - changed the read/write handlers in the address space to use
>    address_space_stq() and address_space_ldq()
>    
>  - introduced 'fake' default read/write handlers to handle 'core'
>    registers. We can add a real device model when more work needs to
>    be done under these.
>    
>  - fixed an issue with the monitor doing read/write in the XSCOM
>    address space. When under the monitor, we don't have a cpu to
>    update the HMER SPR. That might need more work in the long term.
>    
>  - introduced a xscom base field to hold the xscom base address as
>    it is different on P9
> 
>  - renamed the devnode() handler to populate()
> 
>  Changes since v2:
> 
>  - QOMified the model.
>  
>  - all mappings in main memory space are now gathered in
>    pnv_chip_realize() as done on other architectures.
>    
>  - removed XScomBus. The parenthood is established through the QOM
>    model
>    
>  - replaced the XScomDevice with an InterfaceClass : PnvXScomInterface. 
>  - introduced an XSCOM address space to dispatch accesses to the
>    chiplets
> 
>  hw/ppc/Makefile.objs       |   2 +-
>  hw/ppc/pnv.c               |  25 +++++
>  hw/ppc/pnv_xscom.c         | 262 +++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h       |  15 +++
>  include/hw/ppc/pnv_xscom.h |  47 ++++++++
>  5 files changed, 350 insertions(+), 1 deletion(-)
>  create mode 100644 hw/ppc/pnv_xscom.c
>  create mode 100644 include/hw/ppc/pnv_xscom.h
> 
> diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
> index f8c7d1db9ade..08c213c40684 100644
> --- a/hw/ppc/Makefile.objs
> +++ b/hw/ppc/Makefile.objs
> @@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
>  obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
>  obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
>  # IBM PowerNV
> -obj-$(CONFIG_POWERNV) += pnv.o pnv_core.o
> +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o
>  ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
>  obj-y += spapr_pci_vfio.o
>  endif
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 2376bb222918..5e19b6880387 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -32,6 +32,8 @@
>  #include "exec/address-spaces.h"
>  #include "qemu/cutils.h"
>  
> +#include "hw/ppc/pnv_xscom.h"
> +
>  #include <libfdt.h>
>  
>  #define FDT_MAX_SIZE            0x00100000
> @@ -218,6 +220,8 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
>      size_t typesize = object_type_get_instance_size(typename);
>      int i;
>  
> +    pnv_xscom_populate(chip, fdt, 0);
> +
>      for (i = 0; i < chip->nr_cores; i++) {
>          PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
>  
> @@ -450,6 +454,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
>      k->chip_cfam_id = 0x221ef04980000000ull;  /* P8 Murano DD2.1 */
>      k->cores_mask = POWER8E_CORE_MASK;
>      k->core_pir = pnv_chip_core_pir_p8;
> +    k->xscom_base = 0x003fc0000000000ull;
>      dc->desc = "PowerNV Chip POWER8E";
>  }
>  
> @@ -470,6 +475,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
>      k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */
>      k->cores_mask = POWER8_CORE_MASK;
>      k->core_pir = pnv_chip_core_pir_p8;
> +    k->xscom_base = 0x003fc0000000000ull;
>      dc->desc = "PowerNV Chip POWER8";
>  }
>  
> @@ -490,6 +496,7 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
>      k->chip_cfam_id = 0x120d304980000000ull;  /* P8 Naples DD1.0 */
>      k->cores_mask = POWER8_CORE_MASK;
>      k->core_pir = pnv_chip_core_pir_p8;
> +    k->xscom_base = 0x003fc0000000000ull;
>      dc->desc = "PowerNV Chip POWER8NVL";
>  }
>  
> @@ -510,6 +517,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
>      k->chip_cfam_id = 0x100d104980000000ull; /* P9 Nimbus DD1.0 */
>      k->cores_mask = POWER9_CORE_MASK;
>      k->core_pir = pnv_chip_core_pir_p9;
> +    k->xscom_base = 0x00603fc00000000ull;
>      dc->desc = "PowerNV Chip POWER9";
>  }
>  
> @@ -549,6 +557,14 @@ static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp)
>      }
>  }
>  
> +static void pnv_chip_init(Object *obj)
> +{
> +    PnvChip *chip = PNV_CHIP(obj);
> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
> +
> +    chip->xscom_base = pcc->xscom_base;

Is there any reason you really need the per-isntance xscom_base as
well as the per-class xscom_base?

If you do have a per-instance value, it seems like you should fold in
the calculation from PNV_XSCOM_BASE() here.

> +}
> +
>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
>  {
>      PnvChip *chip = PNV_CHIP(dev);
> @@ -563,6 +579,14 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>          return;
>      }
>  
> +    /* XSCOM bridge */
> +    pnv_xscom_realize(chip, &error);
> +    if (error) {
> +        error_propagate(errp, error);
> +        return;
> +    }
> +    sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip));
> +
>      /* Early checks on the core settings */
>      pnv_chip_core_sanitize(chip, &error);
>      if (error) {
> @@ -620,6 +644,7 @@ static const TypeInfo pnv_chip_info = {
>      .name          = TYPE_PNV_CHIP,
>      .parent        = TYPE_SYS_BUS_DEVICE,
>      .class_init    = pnv_chip_class_init,
> +    .instance_init = pnv_chip_init,
>      .class_size    = sizeof(PnvChipClass),
>      .abstract      = true,
>  };
> diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c
> new file mode 100644
> index 000000000000..ce1182d9a13e
> --- /dev/null
> +++ b/hw/ppc/pnv_xscom.c
> @@ -0,0 +1,262 @@
> +/*
> + * QEMU PowerPC PowerNV XSCOM bus
> + *
> + * Copyright (c) 2016, IBM Corporation.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + */
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "hw/hw.h"
> +#include "qemu/log.h"
> +#include "sysemu/kvm.h"
> +#include "target-ppc/cpu.h"
> +#include "hw/sysbus.h"
> +
> +#include "hw/ppc/fdt.h"
> +#include "hw/ppc/pnv_xscom.h"
> +#include "hw/ppc/pnv.h"
> +
> +#include <libfdt.h>
> +
> +static void xscom_complete(uint64_t hmer_bits)

I think this should take a cpu parameter instead of assuming
current_cpu.  Of course, the callers will probably just use
current_cpu, but in the callers it's a bit more obvious that
current_cpu is actually the right thing.

> +{
> +    CPUState *cs = current_cpu;
> +
> +    /*
> +     * TODO: When the read/write comes from the monitor, we do not
> +     * have a cpu.
> +     */
> +    if (cs) {
> +        PowerPCCPU *cpu = POWERPC_CPU(cs);
> +        CPUPPCState *env = &cpu->env;
> +
> +        /*
> +         * TODO: Need a CPU helper to set HMER, also handle generation
> +         * of HMIs
> +         */
> +        cpu_synchronize_state(cs);
> +        env->spr[SPR_HMER] |= hmer_bits;
> +    }
> +}
> +
> +static uint32_t pnv_xscom_pcba(PnvChip *chip, uint64_t addr)
> +{
> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
> +
> +    addr &= (PNV_XSCOM_SIZE - 1);
> +    if (pcc->chip_type == PNV_CHIP_POWER9) {
> +        return addr >> 3;
> +    } else {
> +        return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf);
> +    }
> +}
> +
> +static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba)
> +{
> +    switch (pcba) {
> +    case 0xf000f:
> +        return PNV_CHIP_GET_CLASS(chip)->chip_cfam_id;
> +    case 0x1010c00:     /* PIBAM FIR */
> +    case 0x1010c03:     /* PIBAM FIR MASK */
> +    case 0x2020007:     /* ADU stuff */
> +    case 0x2020009:     /* ADU stuff */
> +    case 0x202000f:     /* ADU stuff */
> +        return 0;
> +    case 0x2013f00:     /* PBA stuff */
> +    case 0x2013f01:     /* PBA stuff */
> +    case 0x2013f02:     /* PBA stuff */
> +    case 0x2013f03:     /* PBA stuff */
> +    case 0x2013f04:     /* PBA stuff */
> +    case 0x2013f05:     /* PBA stuff */
> +    case 0x2013f06:     /* PBA stuff */
> +    case 0x2013f07:     /* PBA stuff */
> +        return 0;
> +    case 0x2013028:     /* CAPP stuff */
> +    case 0x201302a:     /* CAPP stuff */
> +    case 0x2013801:     /* CAPP stuff */
> +    case 0x2013802:     /* CAPP stuff */
> +        return 0;
> +    default:
> +        return -1;
> +    }
> +}
> +
> +static bool xscom_write_default(PnvChip *chip, uint32_t pcba, uint64_t val)
> +{
> +    /* We ignore writes to these */
> +    switch (pcba) {
> +    case 0xf000f:       /* chip id is RO */
> +    case 0x1010c00:     /* PIBAM FIR */
> +    case 0x1010c01:     /* PIBAM FIR */
> +    case 0x1010c02:     /* PIBAM FIR */
> +    case 0x1010c03:     /* PIBAM FIR MASK */
> +    case 0x1010c04:     /* PIBAM FIR MASK */
> +    case 0x1010c05:     /* PIBAM FIR MASK */
> +    case 0x2020007:     /* ADU stuff */
> +    case 0x2020009:     /* ADU stuff */
> +    case 0x202000f:     /* ADU stuff */
> +        return true;
> +    default:
> +        return false;
> +    }
> +}
> +
> +static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)
> +{
> +    PnvChip *chip = opaque;
> +    uint32_t pcba = pnv_xscom_pcba(chip, addr);
> +    uint64_t val = 0;
> +    MemTxResult result;
> +
> +    /* Handle some SCOMs here before dispatch */
> +    val = xscom_read_default(chip, pcba);
> +    if (val != -1) {
> +        goto complete;
> +    }
> +
> +    val = address_space_ldq(&chip->xscom_as, pcba << 3, MEMTXATTRS_UNSPECIFIED,
> +                            &result);
> +    if (result != MEMTX_OK) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "XSCOM read failed at @0x%"
> +                      HWADDR_PRIx " pcba=0x%08x\n", addr, pcba);
> +        xscom_complete(HMER_XSCOM_FAIL | HMER_XSCOM_DONE);
> +        return 0;
> +    }
> +
> +complete:
> +    xscom_complete(HMER_XSCOM_DONE);
> +    return val;
> +}
> +
> +static void xscom_write(void *opaque, hwaddr addr, uint64_t val,
> +                        unsigned width)
> +{
> +    PnvChip *chip = opaque;
> +    uint32_t pcba = pnv_xscom_pcba(chip, addr);
> +    MemTxResult result;
> +
> +    /* Handle some SCOMs here before dispatch */
> +    if (xscom_write_default(chip, pcba, val)) {
> +        goto complete;
> +    }
> +
> +    address_space_stq(&chip->xscom_as, pcba << 3, val, MEMTXATTRS_UNSPECIFIED,
> +                      &result);
> +    if (result != MEMTX_OK) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "XSCOM write failed at @0x%"
> +                      HWADDR_PRIx " pcba=0x%08x data=0x%" PRIx64 "\n",
> +                      addr, pcba, val);
> +        xscom_complete(HMER_XSCOM_FAIL | HMER_XSCOM_DONE);
> +        return;
> +    }
> +
> +complete:
> +    xscom_complete(HMER_XSCOM_DONE);
> +}
> +
> +const MemoryRegionOps pnv_xscom_ops = {
> +    .read = xscom_read,
> +    .write = 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,
> +};
> +
> +void pnv_xscom_realize(PnvChip *chip, Error **errp)
> +{
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(chip);
> +    char *name;
> +
> +    name = g_strdup_printf("xscom-%x", chip->chip_id);
> +    memory_region_init_io(&chip->xscom_mmio, OBJECT(chip), &pnv_xscom_ops,
> +                          chip, name, PNV_XSCOM_SIZE);
> +    sysbus_init_mmio(sbd, &chip->xscom_mmio);
> +
> +    memory_region_init(&chip->xscom, OBJECT(chip), name, PNV_XSCOM_SIZE);
> +    address_space_init(&chip->xscom_as, &chip->xscom, name);
> +    g_free(name);
> +}
> +
> +static const TypeInfo pnv_xscom_interface_info = {
> +    .name = TYPE_PNV_XSCOM_INTERFACE,
> +    .parent = TYPE_INTERFACE,
> +    .class_size = sizeof(PnvXScomInterfaceClass),
> +};
> +
> +static void pnv_xscom_register_types(void)
> +{
> +    type_register_static(&pnv_xscom_interface_info);
> +}
> +
> +type_init(pnv_xscom_register_types)
> +
> +typedef struct ForeachPopulateArgs {
> +    void *fdt;
> +    int xscom_offset;
> +} ForeachPopulateArgs;
> +
> +static int xscom_populate_child(Object *child, void *opaque)
> +{
> +    if (object_dynamic_cast(child, TYPE_PNV_XSCOM_INTERFACE)) {
> +        ForeachPopulateArgs *args = opaque;
> +        PnvXScomInterface *xd = PNV_XSCOM_INTERFACE(child);
> +        PnvXScomInterfaceClass *xc = PNV_XSCOM_INTERFACE_GET_CLASS(xd);
> +
> +        if (xc->populate) {
> +            _FDT((xc->populate(xd, args->fdt, args->xscom_offset)));
> +        }
> +    }
> +    return 0;
> +}
> +
> +static const char compat_p8[] = "ibm,power8-xscom\0ibm,xscom";
> +static const char compat_p9[] = "ibm,power9-xscom\0ibm,xscom";
> +
> +int pnv_xscom_populate(PnvChip *chip, void *fdt, int root_offset)
> +{
> +    uint64_t reg[] = { cpu_to_be64(PNV_XSCOM_BASE(chip)),
> +                       cpu_to_be64(PNV_XSCOM_SIZE) };
> +    int xscom_offset;
> +    ForeachPopulateArgs args;
> +    char *name;
> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
> +
> +    name = g_strdup_printf("xscom@%" PRIx64, be64_to_cpu(reg[0]));
> +    xscom_offset = fdt_add_subnode(fdt, root_offset, name);
> +    _FDT(xscom_offset);
> +    g_free(name);
> +    _FDT((fdt_setprop_cell(fdt, xscom_offset, "ibm,chip-id", chip->chip_id)));
> +    _FDT((fdt_setprop_cell(fdt, xscom_offset, "#address-cells", 1)));
> +    _FDT((fdt_setprop_cell(fdt, xscom_offset, "#size-cells", 1)));
> +    _FDT((fdt_setprop(fdt, xscom_offset, "reg", reg, sizeof(reg))));
> +
> +    if (pcc->chip_type == PNV_CHIP_POWER9) {
> +        _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat_p9,
> +                          sizeof(compat_p9))));
> +    } else {
> +        _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat_p8,
> +                          sizeof(compat_p8))));
> +    }
> +
> +    _FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0)));
> +
> +    args.fdt = fdt;
> +    args.xscom_offset = xscom_offset;
> +
> +    object_child_foreach(OBJECT(chip), xscom_populate_child, &args);
> +    return 0;
> +}
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index ed4a360cde3b..f89eddb6e5e0 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -21,6 +21,7 @@
>  
>  #include "hw/boards.h"
>  #include "hw/sysbus.h"
> +#include "hw/ppc/pnv_xscom.h"
>  
>  #define TYPE_PNV_CHIP "powernv-chip"
>  #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
> @@ -43,6 +44,11 @@ typedef struct PnvChip {
>      /*< public >*/
>      uint32_t     chip_id;
>  
> +    hwaddr       xscom_base;
> +    MemoryRegion xscom_mmio;
> +    MemoryRegion xscom;
> +    AddressSpace xscom_as;
> +
>      uint32_t  nr_cores;
>      uint64_t  cores_mask;
>      void      *cores;
> @@ -58,6 +64,8 @@ typedef struct PnvChipClass {
>      uint64_t     chip_cfam_id;
>      uint64_t     cores_mask;
>  
> +    hwaddr       xscom_base;
> +
>      uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
>  } PnvChipClass;
>  
> @@ -105,4 +113,11 @@ typedef struct PnvMachineState {
>  
>  #define PNV_TIMEBASE_FREQ           512000000ULL
>  
> +/*
> + * POWER8 MMIO base addresses
> + */
> +#define PNV_XSCOM_SIZE        0x800000000ull
> +#define PNV_XSCOM_BASE(chip)                                            \
> +    (chip->xscom_base + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE)
> +
>  #endif /* _PPC_PNV_H */
> diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
> new file mode 100644
> index 000000000000..f50eb0bc4099
> --- /dev/null
> +++ b/include/hw/ppc/pnv_xscom.h
> @@ -0,0 +1,47 @@
> +/*
> + * QEMU PowerPC PowerNV XSCOM bus definitions
> + *
> + * Copyright (c) 2016, IBM Corporation.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + */
> +#ifndef _PPC_PNV_XSCOM_H
> +#define _PPC_PNV_XSCOM_H
> +
> +#include "qom/object.h"
> +
> +typedef struct PnvChip PnvChip;
> +
> +typedef struct PnvXScomInterface {
> +    Object parent;
> +} PnvXScomInterface;
> +
> +#define TYPE_PNV_XSCOM_INTERFACE "pnv-xscom-interface"
> +#define PNV_XSCOM_INTERFACE(obj) \
> +     OBJECT_CHECK(PnvXScomInterface, (obj), TYPE_PNV_XSCOM_INTERFACE)
> +#define PNV_XSCOM_INTERFACE_CLASS(klass)                \
> +    OBJECT_CLASS_CHECK(PnvXScomInterfaceClass, (klass), \
> +                       TYPE_PNV_XSCOM_INTERFACE)
> +#define PNV_XSCOM_INTERFACE_GET_CLASS(obj) \
> +     OBJECT_GET_CLASS(PnvXScomInterfaceClass, (obj), TYPE_PNV_XSCOM_INTERFACE)
> +
> +typedef struct PnvXScomInterfaceClass {
> +    InterfaceClass parent;
> +    int (*populate)(PnvXScomInterface *dev, void *fdt, int offset);
> +} PnvXScomInterfaceClass;
> +
> +extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
> +extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
> +
> +#endif /* _PPC_PNV_XSCOM_H */

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 07/20] ppc/pnv: add XSCOM handlers to PnvCore
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 07/20] ppc/pnv: add XSCOM handlers to PnvCore Cédric Le Goater
@ 2016-10-13  0:51   ` David Gibson
  2016-10-13  6:50     ` Cédric Le Goater
  0 siblings, 1 reply; 75+ messages in thread
From: David Gibson @ 2016-10-13  0:51 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 5899 bytes --]

On Mon, Oct 03, 2016 at 09:24:43AM +0200, Cédric Le Goater wrote:
> Now that we are using real HW ids for the cores in PowerNV chips, we
> can route the XSCOM accesses to them. We just need to attach a
> specific XSCOM memory region to each core in the appropriate window
> for the core number.
> 
> To start with, let's install the DTS (Digital Thermal Sensor) handlers
> which should return 38°C for each core.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
> 
>  Changes since v3:
> 
>  - moved to new XSCOM model
>  - kept the write op on the XSCOM memory region for later use
> 
>  Changes since v2:
> 
>  - added a XSCOM memory region to handle access to the EX core
>    registers   
>  - extended the PnvCore object with a XSCOM_INTERFACE so that we can
>    use pnv_xscom_pcba() and pnv_xscom_addr() to handle XSCOM address
>    translation.
> 
>  hw/ppc/pnv.c               |  4 ++++
>  hw/ppc/pnv_core.c          | 50 ++++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv_core.h  |  2 ++
>  include/hw/ppc/pnv_xscom.h | 19 ++++++++++++++++++
>  4 files changed, 75 insertions(+)
> 
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 5e19b6880387..ffe245fe59d2 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -620,6 +620,10 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>                                   &error_fatal);
>          object_unref(OBJECT(pnv_core));
>          i++;
> +
> +        memory_region_add_subregion(&chip->xscom,
> +                         PNV_XSCOM_EX_CORE_BASE(core_hwid) << 3,
> +                         &PNV_CORE(pnv_core)->xscom_regs);

Might be worth adding some convenience functions for doing the various
bits of xscom MR juggling, otherwise this looks fine.

>      }
>      g_free(typename);
>  }
> diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
> index d37788f142f4..a1c8a14f06b6 100644
> --- a/hw/ppc/pnv_core.c
> +++ b/hw/ppc/pnv_core.c
> @@ -19,6 +19,7 @@
>  #include "qemu/osdep.h"
>  #include "sysemu/sysemu.h"
>  #include "qapi/error.h"
> +#include "qemu/log.h"
>  #include "target-ppc/cpu.h"
>  #include "hw/ppc/ppc.h"
>  #include "hw/ppc/pnv.h"
> @@ -64,6 +65,51 @@ static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
>      powernv_cpu_reset(cpu);
>  }
>  
> +/*
> + * These values are read by the PowerNV HW monitors under Linux
> + */
> +#define PNV_XSCOM_EX_DTS_RESULT0     0x50000
> +#define PNV_XSCOM_EX_DTS_RESULT1     0x50001
> +
> +static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr,
> +                                    unsigned int width)
> +{
> +    uint32_t offset = addr >> 3;
> +    uint64_t val = 0;
> +
> +    /* The result should be 38 C */
> +    switch (offset) {
> +    case PNV_XSCOM_EX_DTS_RESULT0:
> +        val = 0x26f024f023f0000ull;
> +        break;
> +    case PNV_XSCOM_EX_DTS_RESULT1:
> +        val = 0x24f000000000000ull;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx,
> +                  addr);
> +    }
> +
> +    return val;
> +}
> +
> +static void pnv_core_xscom_write(void *opaque, hwaddr addr, uint64_t val,
> +                                 unsigned int width)
> +{
> +    qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx,
> +                  addr);
> +}
> +
> +static const MemoryRegionOps pnv_core_xscom_ops = {
> +    .read = pnv_core_xscom_read,
> +    .write = pnv_core_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_realize_child(Object *child, Error **errp)
>  {
>      Error *local_err = NULL;
> @@ -119,6 +165,10 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
>              goto err;
>          }
>      }
> +
> +    snprintf(name, sizeof(name), "xscom-core.%d", cc->core_id);
> +    memory_region_init_io(&pc->xscom_regs, OBJECT(dev), &pnv_core_xscom_ops,
> +                          pc, name,  PNV_XSCOM_EX_CORE_SIZE << 3);
>      return;
>  
>  err:
> diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h
> index a151e281c017..2955a41c901f 100644
> --- a/include/hw/ppc/pnv_core.h
> +++ b/include/hw/ppc/pnv_core.h
> @@ -36,6 +36,8 @@ typedef struct PnvCore {
>      /*< public >*/
>      void *threads;
>      uint32_t pir;
> +
> +    MemoryRegion xscom_regs;
>  } PnvCore;
>  
>  typedef struct PnvCoreClass {
> diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
> index f50eb0bc4099..79975a6cbe46 100644
> --- a/include/hw/ppc/pnv_xscom.h
> +++ b/include/hw/ppc/pnv_xscom.h
> @@ -41,6 +41,25 @@ typedef struct PnvXScomInterfaceClass {
>      int (*populate)(PnvXScomInterface *dev, void *fdt, int offset);
>  } PnvXScomInterfaceClass;
>  
> +/*
> + * Layout of the XSCOM PCB addresses of EX core 1
> + *
> + *   GPIO        0x1100xxxx
> + *   SCOM        0x1101xxxx
> + *   OHA         0x1102xxxx
> + *   CLOCK CTL   0x1103xxxx
> + *   FIR         0x1104xxxx
> + *   THERM       0x1105xxxx
> + *   <reserved>  0x1106xxxx
> + *               ..
> + *               0x110Exxxx
> + *   PCB SLAVE   0x110Fxxxx
> + */
> +
> +#define PNV_XSCOM_EX_BASE         0x10000000
> +#define PNV_XSCOM_EX_CORE_BASE(i) (PNV_XSCOM_EX_BASE | (((uint64_t)i) << 24))
> +#define PNV_XSCOM_EX_CORE_SIZE    0x100000
> +
>  extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
>  extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
>  

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 08/20] ppc/pnv: add a LPC controller
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 08/20] ppc/pnv: add a LPC controller Cédric Le Goater
@ 2016-10-13  2:52   ` David Gibson
  2016-10-13  2:53     ` David Gibson
  2016-10-13  6:31     ` Cédric Le Goater
  0 siblings, 2 replies; 75+ messages in thread
From: David Gibson @ 2016-10-13  2:52 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 25187 bytes --]

On Mon, Oct 03, 2016 at 09:24:44AM +0200, Cédric Le Goater wrote:
> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> 
> The LPC (Low Pin Count) interface on a POWER8 is made accessible to
> the system through the ADU (XSCOM interface). This interface is part
> of set of units connected together via a local OPB (On-Chip Peripheral
> Bus) which act as a bridge between the ADU and the off chip LPC
> endpoints, like external flash modules.
> 
> The most important units of this OPB are :
>  - OPB Master: contains the ADU slave logic, a set of internal
>    registers and the logic to control the OPB.
>  - LPCHC (LPC HOST Controller): which implements a OPB Slave, a set of
>    internal registers and the LPC HOST Controller to control the LPC
>    interface.
> 
> Four address spaces are provided to the ADU :
>  - LPC Bus Firmware Memory
>  - LPC Bus Memory
>  - LPC Bus I/O (ISA bus)
>  - and the registers for the OPB Master and the LPC Host Controller
> 
> On POWER8, an intermediate hop is necessary to reach the OPB, through
> a unit called the ECCB. OPB commands are simply mangled in ECCB write
> commands.
> 
> On POWER9, the OPB master address space can be accessed via MMIO. The
> logic is same but the code will be simpler as the XSCOM and ECCB hops
> are not necessary anymore.
> 
> This version of the LPC controller model doesn't yet implement support
> for the SerIRQ deserializer present in the Naples version of the chip
> though some preliminary work is there.
> 
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> [clg: - updated for qemu-2.7
>       - ported on latest PowerNV patchset
>       - changed the XSCOM interface to fit new model
>       - QOMified the model
>       - moved the ISA hunks in another patch
>       - removed printf logging
>       - added a couple of UNIMP logging
>       - rewrote commit log ]
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

It looks reasonable as far as it goes.

I don't see anything wiring this up to qemu's common ISA
infrastructure, which seems a bit odd.

> ---
> 
>  Todo:
> 
>  - rework the address_space read/write ops as they should be shared
>    with the P9 support.
> 
>  Changes since v3:
> 
>  - rewrote commit log
>  - fixed appendprop
>  
>  hw/ppc/Makefile.objs       |   2 +-
>  hw/ppc/pnv.c               |   9 +
>  hw/ppc/pnv_lpc.c           | 471 +++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h       |   3 +
>  include/hw/ppc/pnv_lpc.h   |  67 +++++++
>  include/hw/ppc/pnv_xscom.h |   3 +
>  6 files changed, 554 insertions(+), 1 deletion(-)
>  create mode 100644 hw/ppc/pnv_lpc.c
>  create mode 100644 include/hw/ppc/pnv_lpc.h
> 
> diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
> index 08c213c40684..ebc72af0a7c6 100644
> --- a/hw/ppc/Makefile.objs
> +++ b/hw/ppc/Makefile.objs
> @@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
>  obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
>  obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
>  # IBM PowerNV
> -obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o
> +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o
>  ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
>  obj-y += spapr_pci_vfio.o
>  endif
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index ffe245fe59d2..e41244294435 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -563,6 +563,9 @@ static void pnv_chip_init(Object *obj)
>      PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
>  
>      chip->xscom_base = pcc->xscom_base;
> +
> +    object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
> +    object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
>  }
>  
>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
> @@ -626,6 +629,12 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>                           &PNV_CORE(pnv_core)->xscom_regs);
>      }
>      g_free(typename);
> +
> +    /* Create LPC controller */
> +    object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
> +                             &error_fatal);
> +    memory_region_add_subregion(&chip->xscom, PNV_XSCOM_LPC_BASE << 3,
> +                                &chip->lpc.xscom_regs);
>  }
>  
>  static Property pnv_chip_properties[] = {
> diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
> new file mode 100644
> index 000000000000..210cc1cff167
> --- /dev/null
> +++ b/hw/ppc/pnv_lpc.c
> @@ -0,0 +1,471 @@
> +/*
> + * QEMU PowerPC PowerNV LPC controller
> + *
> + * Copyright (c) 2016, IBM Corporation.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "sysemu/sysemu.h"
> +#include "target-ppc/cpu.h"
> +#include "qapi/error.h"
> +#include "qemu/log.h"
> +
> +#include "hw/ppc/pnv_lpc.h"
> +#include "hw/ppc/pnv.h"
> +#include "hw/ppc/fdt.h"
> +
> +#include <libfdt.h>
> +
> +enum {
> +    ECCB_CTL    = 0,
> +    ECCB_RESET  = 1,
> +    ECCB_STAT   = 2,
> +    ECCB_DATA   = 3,
> +};
> +
> +/* OPB Master LS registers */
> +#define OPB_MASTER_LS_IRQ_STAT  0x50
> +#define   OPB_MASTER_IRQ_LPC            0x00000800
> +#define OPB_MASTER_LS_IRQ_MASK  0x54
> +#define OPB_MASTER_LS_IRQ_POL   0x58
> +#define OPB_MASTER_LS_IRQ_INPUT 0x5c
> +
> +/* LPC HC registers */
> +#define LPC_HC_FW_SEG_IDSEL     0x24
> +#define LPC_HC_FW_RD_ACC_SIZE   0x28
> +#define   LPC_HC_FW_RD_1B               0x00000000
> +#define   LPC_HC_FW_RD_2B               0x01000000
> +#define   LPC_HC_FW_RD_4B               0x02000000
> +#define   LPC_HC_FW_RD_16B              0x04000000
> +#define   LPC_HC_FW_RD_128B             0x07000000
> +#define LPC_HC_IRQSER_CTRL      0x30
> +#define   LPC_HC_IRQSER_EN              0x80000000
> +#define   LPC_HC_IRQSER_QMODE           0x40000000
> +#define   LPC_HC_IRQSER_START_MASK      0x03000000
> +#define   LPC_HC_IRQSER_START_4CLK      0x00000000
> +#define   LPC_HC_IRQSER_START_6CLK      0x01000000
> +#define   LPC_HC_IRQSER_START_8CLK      0x02000000
> +#define LPC_HC_IRQMASK          0x34    /* same bit defs as LPC_HC_IRQSTAT */
> +#define LPC_HC_IRQSTAT          0x38
> +#define   LPC_HC_IRQ_SERIRQ0            0x80000000 /* all bits down to ... */
> +#define   LPC_HC_IRQ_SERIRQ16           0x00008000 /* IRQ16=IOCHK#, IRQ2=SMI# */
> +#define   LPC_HC_IRQ_SERIRQ_ALL         0xffff8000
> +#define   LPC_HC_IRQ_LRESET             0x00000400
> +#define   LPC_HC_IRQ_SYNC_ABNORM_ERR    0x00000080
> +#define   LPC_HC_IRQ_SYNC_NORESP_ERR    0x00000040
> +#define   LPC_HC_IRQ_SYNC_NORM_ERR      0x00000020
> +#define   LPC_HC_IRQ_SYNC_TIMEOUT_ERR   0x00000010
> +#define   LPC_HC_IRQ_SYNC_TARG_TAR_ERR  0x00000008
> +#define   LPC_HC_IRQ_SYNC_BM_TAR_ERR    0x00000004
> +#define   LPC_HC_IRQ_SYNC_BM0_REQ       0x00000002
> +#define   LPC_HC_IRQ_SYNC_BM1_REQ       0x00000001
> +#define LPC_HC_ERROR_ADDRESS    0x40
> +
> +#define LPC_OPB_SIZE            0x100000000ull
> +
> +#define ISA_IO_SIZE             0x00010000
> +#define ISA_MEM_SIZE            0x10000000
> +#define LPC_IO_OPB_ADDR         0xd0010000
> +#define LPC_IO_OPB_SIZE         0x00010000
> +#define LPC_MEM_OPB_ADDR        0xe0010000
> +#define LPC_MEM_OPB_SIZE        0x10000000
> +#define LPC_FW_OPB_ADDR         0xf0000000
> +#define LPC_FW_OPB_SIZE         0x10000000
> +
> +#define LPC_OPB_REGS_OPB_ADDR   0xc0010000
> +#define LPC_OPB_REGS_OPB_SIZE   0x00002000
> +#define LPC_HC_REGS_OPB_ADDR    0xc0012000
> +#define LPC_HC_REGS_OPB_SIZE    0x00001000
> +
> +
> +/*
> + * TODO: the "primary" cell should only be added on chip 0. This is
> + * how skiboot chooses the default LPC controller on multichip
> + * systems.
> + *
> + * It would be easly done if we can change the populate() interface to
> + * replace the PnvXScomInterface parameter by a PnvChip one
> + */
> +static int pnv_lpc_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset)
> +{
> +    const char compat[] = "ibm,power8-lpc\0ibm,lpc";
> +    char *name;
> +    int offset;
> +    uint32_t lpc_pcba = PNV_XSCOM_LPC_BASE;
> +    uint32_t reg[] = {
> +        cpu_to_be32(lpc_pcba),
> +        cpu_to_be32(PNV_XSCOM_LPC_SIZE)
> +    };
> +
> +    name = g_strdup_printf("isa@%x", lpc_pcba);
> +    offset = fdt_add_subnode(fdt, xscom_offset, name);
> +    _FDT(offset);
> +    g_free(name);
> +
> +    _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
> +    _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2)));
> +    _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1)));
> +    _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0)));
> +    _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat))));
> +    return 0;
> +}
> +
> +/*
> + * These read/write handlers of the OPB address space should be common
> + * with the P9 LPC Controller which uses direct MMIOs.
> + *
> + * TODO: rework to use address_space_stq() and address_space_ldq()
> + * instead.
> + */
> +static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data,
> +                     int sz)
> +{
> +    bool success;
> +
> +    /* XXX Handle access size limits and FW read caching here */
> +    success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED,
> +                                data, sz, false);
> +
> +    return success;
> +}
> +
> +static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data,
> +                      int sz)
> +{
> +    bool success;
> +
> +    /* XXX Handle access size limits here */
> +    success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED,
> +                                data, sz, true);
> +
> +    return success;
> +}
> +
> +#define ECCB_CTL_READ           (1ull << (63 - 15))
> +#define ECCB_CTL_SZ_LSH         (63 - 7)
> +#define ECCB_CTL_SZ_MASK        (0xfull << ECCB_CTL_SZ_LSH)
> +#define ECCB_CTL_ADDR_MASK      0xffffffffu;
> +
> +#define ECCB_STAT_OP_DONE       (1ull << (63 - 52))
> +#define ECCB_STAT_OP_ERR        (1ull << (63 - 52))
> +#define ECCB_STAT_RD_DATA_LSH   (63 - 37)
> +#define ECCB_STAT_RD_DATA_MASK  (0xffffffff << ECCB_STAT_RD_DATA_LSH)
> +
> +static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd)
> +{
> +    /* XXX Check for magic bits at the top, addr size etc... */
> +    unsigned int sz = (cmd & ECCB_CTL_SZ_MASK) >> ECCB_CTL_SZ_LSH;
> +    uint32_t opb_addr = cmd & ECCB_CTL_ADDR_MASK;
> +    uint8_t data[4];
> +    bool success;
> +
> +    if (cmd & ECCB_CTL_READ) {
> +        success = opb_read(lpc, opb_addr, data, sz);
> +        if (success) {
> +            lpc->eccb_stat_reg = ECCB_STAT_OP_DONE |
> +                    (((uint64_t)data[0]) << 24 |
> +                     ((uint64_t)data[1]) << 16 |
> +                     ((uint64_t)data[2]) <<  8 |
> +                     ((uint64_t)data[3])) << ECCB_STAT_RD_DATA_LSH;
> +        } else {
> +            lpc->eccb_stat_reg = ECCB_STAT_OP_DONE |
> +                    (0xffffffffull << ECCB_STAT_RD_DATA_LSH);
> +        }
> +    } else {
> +        data[0] = lpc->eccb_data_reg >> 24;
> +        data[1] = lpc->eccb_data_reg >> 16;
> +        data[2] = lpc->eccb_data_reg >>  8;
> +        data[3] = lpc->eccb_data_reg;
> +
> +        success = opb_write(lpc, opb_addr, data, sz);
> +        lpc->eccb_stat_reg = ECCB_STAT_OP_DONE;
> +    }
> +    /* XXX Which error bit (if any) to signal OPB error ? */
> +}
> +
> +static uint64_t pnv_lpc_xscom_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    PnvLpcController *lpc = PNV_LPC(opaque);
> +    uint32_t offset = addr >> 3;
> +    uint64_t val = 0;
> +
> +    switch (offset & 3) {
> +    case ECCB_CTL:
> +    case ECCB_RESET:
> +        val = 0;
> +        break;
> +    case ECCB_STAT:
> +        val = lpc->eccb_stat_reg;
> +        lpc->eccb_stat_reg = 0;
> +        break;
> +    case ECCB_DATA:
> +        val = ((uint64_t)lpc->eccb_data_reg) << 32;
> +        break;
> +    }
> +    return val;
> +}
> +
> +static void pnv_lpc_xscom_write(void *opaque, hwaddr addr,
> +                                uint64_t val, unsigned size)
> +{
> +    PnvLpcController *lpc = PNV_LPC(opaque);
> +    uint32_t offset = addr >> 3;
> +
> +    switch (offset & 3) {
> +    case ECCB_CTL:
> +        pnv_lpc_do_eccb(lpc, val);
> +        break;
> +    case ECCB_RESET:
> +        /*  XXXX  */
> +        break;
> +    case ECCB_STAT:
> +        break;
> +    case ECCB_DATA:
> +        lpc->eccb_data_reg = val >> 32;
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps pnv_lpc_xscom_ops = {
> +    .read = pnv_lpc_xscom_read,
> +    .write = pnv_lpc_xscom_write,
> +    .valid.min_access_size = 8,
> +    .valid.max_access_size = 8,
> +    .impl.min_access_size = 8,
> +    .impl.max_access_size = 8,
> +    .endianness = DEVICE_BIG_ENDIAN,
> +};
> +
> +static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    PnvLpcController *lpc = opaque;
> +    uint64_t val = 0xfffffffffffffffful;
> +
> +    switch (addr) {
> +    case LPC_HC_FW_SEG_IDSEL:
> +        val =  lpc->lpc_hc_fw_seg_idsel;
> +        break;
> +    case LPC_HC_FW_RD_ACC_SIZE:
> +        val =  lpc->lpc_hc_fw_rd_acc_size;
> +        break;
> +    case LPC_HC_IRQSER_CTRL:
> +        val =  lpc->lpc_hc_irqser_ctrl;
> +        break;
> +    case LPC_HC_IRQMASK:
> +        val =  lpc->lpc_hc_irqmask;
> +        break;
> +    case LPC_HC_IRQSTAT:
> +        val =  lpc->lpc_hc_irqstat;
> +        break;
> +    case LPC_HC_ERROR_ADDRESS:
> +        val =  lpc->lpc_hc_error_addr;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%"
> +                      HWADDR_PRIx "\n", addr);
> +    }
> +    return val;
> +}
> +
> +static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val,
> +                         unsigned size)
> +{
> +    PnvLpcController *lpc = opaque;
> +
> +    /* XXX Filter out reserved bits */
> +
> +    switch (addr) {
> +    case LPC_HC_FW_SEG_IDSEL:
> +        /* XXX Actually figure out how that works as this impact
> +         * memory regions/aliases
> +         */
> +        lpc->lpc_hc_fw_seg_idsel = val;
> +        break;
> +    case LPC_HC_FW_RD_ACC_SIZE:
> +        lpc->lpc_hc_fw_rd_acc_size = val;
> +        break;
> +    case LPC_HC_IRQSER_CTRL:
> +        lpc->lpc_hc_irqser_ctrl = val;
> +        break;
> +    case LPC_HC_IRQMASK:
> +        lpc->lpc_hc_irqmask = val;
> +        break;
> +    case LPC_HC_IRQSTAT:
> +        lpc->lpc_hc_irqstat &= ~val;
> +        break;
> +    case LPC_HC_ERROR_ADDRESS:
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%"
> +                      HWADDR_PRIx "\n", addr);
> +    }
> +}
> +
> +static const MemoryRegionOps lpc_hc_ops = {
> +    .read = lpc_hc_read,
> +    .write = lpc_hc_write,
> +    .endianness = DEVICE_BIG_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +    .impl = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    PnvLpcController *lpc = opaque;
> +    uint64_t val = 0xfffffffffffffffful;
> +
> +    switch (addr) {
> +    case OPB_MASTER_LS_IRQ_STAT:
> +        val = lpc->opb_irq_stat;
> +        break;
> +    case OPB_MASTER_LS_IRQ_MASK:
> +        val = lpc->opb_irq_mask;
> +        break;
> +    case OPB_MASTER_LS_IRQ_POL:
> +        val = lpc->opb_irq_pol;
> +        break;
> +    case OPB_MASTER_LS_IRQ_INPUT:
> +        val = lpc->opb_irq_input;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%"
> +                      HWADDR_PRIx "\n", addr);
> +    }
> +
> +    return val;
> +}
> +
> +static void opb_master_write(void *opaque, hwaddr addr,
> +                             uint64_t val, unsigned size)
> +{
> +    PnvLpcController *lpc = opaque;
> +
> +    switch (addr) {
> +    case OPB_MASTER_LS_IRQ_STAT:
> +        lpc->opb_irq_stat &= ~val;
> +        break;
> +    case OPB_MASTER_LS_IRQ_MASK:
> +        /* XXX Filter out reserved bits */
> +        lpc->opb_irq_mask = val;
> +        break;
> +    case OPB_MASTER_LS_IRQ_POL:
> +        /* XXX Filter out reserved bits */
> +        lpc->opb_irq_pol = val;
> +        break;
> +    case OPB_MASTER_LS_IRQ_INPUT:
> +        /* Read only */
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%"
> +                      HWADDR_PRIx "\n", addr);
> +    }
> +}
> +
> +static const MemoryRegionOps opb_master_ops = {
> +    .read = opb_master_read,
> +    .write = opb_master_write,
> +    .endianness = DEVICE_BIG_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +    .impl = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static void pnv_lpc_realize(DeviceState *dev, Error **errp)
> +{
> +    PnvLpcController *lpc = PNV_LPC(dev);
> +
> +    /* Reg inits */
> +    lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B;
> +
> +    /* Create address space and backing MR for the OPB bus */
> +    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.
> +     */
> +    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);
> +
> +    /* Create windows from the OPB space to the ISA space */
> +    memory_region_init_alias(&lpc->opb_isa_io, OBJECT(dev), "lpc-isa-io",
> +                             &lpc->isa_io, 0, LPC_IO_OPB_SIZE);
> +    memory_region_add_subregion(&lpc->opb_mr, LPC_IO_OPB_ADDR,
> +                                &lpc->opb_isa_io);
> +    memory_region_init_alias(&lpc->opb_isa_mem, OBJECT(dev), "lpc-isa-mem",
> +                             &lpc->isa_mem, 0, LPC_MEM_OPB_SIZE);
> +    memory_region_add_subregion(&lpc->opb_mr, LPC_MEM_OPB_ADDR,
> +                                &lpc->opb_isa_mem);
> +    memory_region_init_alias(&lpc->opb_isa_fw, OBJECT(dev), "lpc-isa-fw",
> +                             &lpc->isa_mem, 0, LPC_FW_OPB_SIZE);
> +    memory_region_add_subregion(&lpc->opb_mr, LPC_FW_OPB_ADDR,
> +                                &lpc->opb_isa_fw);
> +
> +    /* Create MMIO regions for LPC HC and OPB registers */
> +    memory_region_init_io(&lpc->opb_master_regs, OBJECT(dev), &opb_master_ops,
> +                          lpc, "lpc-opb-master", LPC_OPB_REGS_OPB_SIZE);
> +    memory_region_add_subregion(&lpc->opb_mr, LPC_OPB_REGS_OPB_ADDR,
> +                                &lpc->opb_master_regs);
> +    memory_region_init_io(&lpc->lpc_hc_regs, OBJECT(dev), &lpc_hc_ops, lpc,
> +                          "lpc-hc", LPC_HC_REGS_OPB_SIZE);
> +    memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR,
> +                                &lpc->lpc_hc_regs);
> +
> +    /* XScom region for LPC registers */
> +    memory_region_init_io(&lpc->xscom_regs, OBJECT(dev),
> +                          &pnv_lpc_xscom_ops, lpc, "xscom-lpc",
> +                          PNV_XSCOM_LPC_SIZE << 3);
> +}
> +
> +static void pnv_lpc_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
> +
> +    xdc->populate = pnv_lpc_populate;
> +
> +    dc->realize = pnv_lpc_realize;
> +}
> +
> +static const TypeInfo pnv_lpc_info = {
> +    .name          = TYPE_PNV_LPC,
> +    .parent        = TYPE_DEVICE,
> +    .instance_size = sizeof(PnvLpcController),
> +    .class_init    = pnv_lpc_class_init,
> +    .interfaces = (InterfaceInfo[]) {
> +        { TYPE_PNV_XSCOM_INTERFACE },
> +        { }
> +    }
> +};
> +
> +static void pnv_lpc_register_types(void)
> +{
> +    type_register_static(&pnv_lpc_info);
> +}
> +
> +type_init(pnv_lpc_register_types)
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index f89eddb6e5e0..e586ff4e735e 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -22,6 +22,7 @@
>  #include "hw/boards.h"
>  #include "hw/sysbus.h"
>  #include "hw/ppc/pnv_xscom.h"
> +#include "hw/ppc/pnv_lpc.h"
>  
>  #define TYPE_PNV_CHIP "powernv-chip"
>  #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
> @@ -52,6 +53,8 @@ typedef struct PnvChip {
>      uint32_t  nr_cores;
>      uint64_t  cores_mask;
>      void      *cores;
> +
> +    PnvLpcController lpc;
>  } PnvChip;
>  
>  typedef struct PnvChipClass {
> diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h
> new file mode 100644
> index 000000000000..38e5506975aa
> --- /dev/null
> +++ b/include/hw/ppc/pnv_lpc.h
> @@ -0,0 +1,67 @@
> +/*
> + * QEMU PowerPC PowerNV LPC controller
> + *
> + * Copyright (c) 2016, IBM Corporation.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + */
> +#ifndef _PPC_PNV_LPC_H
> +#define _PPC_PNV_LPC_H
> +
> +#define TYPE_PNV_LPC "pnv-lpc"
> +#define PNV_LPC(obj) \
> +     OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC)
> +
> +typedef struct PnvLpcController {
> +    DeviceState parent;
> +
> +    uint64_t eccb_stat_reg;
> +    uint32_t eccb_data_reg;
> +
> +    /* OPB bus */
> +    MemoryRegion opb_mr;
> +    AddressSpace opb_as;
> +
> +    /* ISA IO and Memory space */
> +    MemoryRegion isa_io;
> +    MemoryRegion isa_mem;
> +
> +    /* Windows from OPB to ISA (aliases) */
> +    MemoryRegion opb_isa_io;
> +    MemoryRegion opb_isa_mem;
> +    MemoryRegion opb_isa_fw;
> +
> +    /* Registers */
> +    MemoryRegion lpc_hc_regs;
> +    MemoryRegion opb_master_regs;
> +
> +    /* OPB Master LS registers */
> +    uint32_t opb_irq_stat;
> +    uint32_t opb_irq_mask;
> +    uint32_t opb_irq_pol;
> +    uint32_t opb_irq_input;
> +
> +    /* LPC HC registers */
> +    uint32_t lpc_hc_fw_seg_idsel;
> +    uint32_t lpc_hc_fw_rd_acc_size;
> +    uint32_t lpc_hc_irqser_ctrl;
> +    uint32_t lpc_hc_irqmask;
> +    uint32_t lpc_hc_irqstat;
> +    uint32_t lpc_hc_error_addr;
> +
> +    /* XSCOM registers */
> +    MemoryRegion xscom_regs;
> +} PnvLpcController;
> +
> +#endif /* _PPC_PNV_LPC_H */
> diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
> index 79975a6cbe46..3b8a8b4fc6ff 100644
> --- a/include/hw/ppc/pnv_xscom.h
> +++ b/include/hw/ppc/pnv_xscom.h
> @@ -60,6 +60,9 @@ typedef struct PnvXScomInterfaceClass {
>  #define PNV_XSCOM_EX_CORE_BASE(i) (PNV_XSCOM_EX_BASE | (((uint64_t)i) << 24))
>  #define PNV_XSCOM_EX_CORE_SIZE    0x100000
>  
> +#define PNV_XSCOM_LPC_BASE        0xb0020
> +#define PNV_XSCOM_LPC_SIZE        0x4
> +
>  extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
>  extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
>  

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 08/20] ppc/pnv: add a LPC controller
  2016-10-13  2:52   ` David Gibson
@ 2016-10-13  2:53     ` David Gibson
  2016-10-13  6:31     ` Cédric Le Goater
  1 sibling, 0 replies; 75+ messages in thread
From: David Gibson @ 2016-10-13  2:53 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 26756 bytes --]

On Thu, Oct 13, 2016 at 01:52:55PM +1100, David Gibson wrote:
> On Mon, Oct 03, 2016 at 09:24:44AM +0200, Cédric Le Goater wrote:
> > From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> > 
> > The LPC (Low Pin Count) interface on a POWER8 is made accessible to
> > the system through the ADU (XSCOM interface). This interface is part
> > of set of units connected together via a local OPB (On-Chip Peripheral
> > Bus) which act as a bridge between the ADU and the off chip LPC
> > endpoints, like external flash modules.
> > 
> > The most important units of this OPB are :
> >  - OPB Master: contains the ADU slave logic, a set of internal
> >    registers and the logic to control the OPB.
> >  - LPCHC (LPC HOST Controller): which implements a OPB Slave, a set of
> >    internal registers and the LPC HOST Controller to control the LPC
> >    interface.
> > 
> > Four address spaces are provided to the ADU :
> >  - LPC Bus Firmware Memory
> >  - LPC Bus Memory
> >  - LPC Bus I/O (ISA bus)
> >  - and the registers for the OPB Master and the LPC Host Controller
> > 
> > On POWER8, an intermediate hop is necessary to reach the OPB, through
> > a unit called the ECCB. OPB commands are simply mangled in ECCB write
> > commands.
> > 
> > On POWER9, the OPB master address space can be accessed via MMIO. The
> > logic is same but the code will be simpler as the XSCOM and ECCB hops
> > are not necessary anymore.
> > 
> > This version of the LPC controller model doesn't yet implement support
> > for the SerIRQ deserializer present in the Naples version of the chip
> > though some preliminary work is there.
> > 
> > Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> > [clg: - updated for qemu-2.7
> >       - ported on latest PowerNV patchset
> >       - changed the XSCOM interface to fit new model
> >       - QOMified the model
> >       - moved the ISA hunks in another patch
> >       - removed printf logging
> >       - added a couple of UNIMP logging
> >       - rewrote commit log ]
> > Signed-off-by: Cédric Le Goater <clg@kaod.org>
> 
> It looks reasonable as far as it goes.
> 
> I don't see anything wiring this up to qemu's common ISA
> infrastructure, which seems a bit odd.

Sorry.. hadn't read the next patch yet.

Reviewed-by: David Gibson <david@gibson.dropbear.id.au>

> 
> > ---
> > 
> >  Todo:
> > 
> >  - rework the address_space read/write ops as they should be shared
> >    with the P9 support.
> > 
> >  Changes since v3:
> > 
> >  - rewrote commit log
> >  - fixed appendprop
> >  
> >  hw/ppc/Makefile.objs       |   2 +-
> >  hw/ppc/pnv.c               |   9 +
> >  hw/ppc/pnv_lpc.c           | 471 +++++++++++++++++++++++++++++++++++++++++++++
> >  include/hw/ppc/pnv.h       |   3 +
> >  include/hw/ppc/pnv_lpc.h   |  67 +++++++
> >  include/hw/ppc/pnv_xscom.h |   3 +
> >  6 files changed, 554 insertions(+), 1 deletion(-)
> >  create mode 100644 hw/ppc/pnv_lpc.c
> >  create mode 100644 include/hw/ppc/pnv_lpc.h
> > 
> > diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
> > index 08c213c40684..ebc72af0a7c6 100644
> > --- a/hw/ppc/Makefile.objs
> > +++ b/hw/ppc/Makefile.objs
> > @@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
> >  obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
> >  obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
> >  # IBM PowerNV
> > -obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o
> > +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o
> >  ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
> >  obj-y += spapr_pci_vfio.o
> >  endif
> > diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> > index ffe245fe59d2..e41244294435 100644
> > --- a/hw/ppc/pnv.c
> > +++ b/hw/ppc/pnv.c
> > @@ -563,6 +563,9 @@ static void pnv_chip_init(Object *obj)
> >      PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
> >  
> >      chip->xscom_base = pcc->xscom_base;
> > +
> > +    object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
> > +    object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
> >  }
> >  
> >  static void pnv_chip_realize(DeviceState *dev, Error **errp)
> > @@ -626,6 +629,12 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
> >                           &PNV_CORE(pnv_core)->xscom_regs);
> >      }
> >      g_free(typename);
> > +
> > +    /* Create LPC controller */
> > +    object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
> > +                             &error_fatal);
> > +    memory_region_add_subregion(&chip->xscom, PNV_XSCOM_LPC_BASE << 3,
> > +                                &chip->lpc.xscom_regs);
> >  }
> >  
> >  static Property pnv_chip_properties[] = {
> > diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
> > new file mode 100644
> > index 000000000000..210cc1cff167
> > --- /dev/null
> > +++ b/hw/ppc/pnv_lpc.c
> > @@ -0,0 +1,471 @@
> > +/*
> > + * QEMU PowerPC PowerNV LPC controller
> > + *
> > + * Copyright (c) 2016, IBM Corporation.
> > + *
> > + * This library is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2 of the License, or (at your option) any later version.
> > + *
> > + * This library is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public
> > + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "sysemu/sysemu.h"
> > +#include "target-ppc/cpu.h"
> > +#include "qapi/error.h"
> > +#include "qemu/log.h"
> > +
> > +#include "hw/ppc/pnv_lpc.h"
> > +#include "hw/ppc/pnv.h"
> > +#include "hw/ppc/fdt.h"
> > +
> > +#include <libfdt.h>
> > +
> > +enum {
> > +    ECCB_CTL    = 0,
> > +    ECCB_RESET  = 1,
> > +    ECCB_STAT   = 2,
> > +    ECCB_DATA   = 3,
> > +};
> > +
> > +/* OPB Master LS registers */
> > +#define OPB_MASTER_LS_IRQ_STAT  0x50
> > +#define   OPB_MASTER_IRQ_LPC            0x00000800
> > +#define OPB_MASTER_LS_IRQ_MASK  0x54
> > +#define OPB_MASTER_LS_IRQ_POL   0x58
> > +#define OPB_MASTER_LS_IRQ_INPUT 0x5c
> > +
> > +/* LPC HC registers */
> > +#define LPC_HC_FW_SEG_IDSEL     0x24
> > +#define LPC_HC_FW_RD_ACC_SIZE   0x28
> > +#define   LPC_HC_FW_RD_1B               0x00000000
> > +#define   LPC_HC_FW_RD_2B               0x01000000
> > +#define   LPC_HC_FW_RD_4B               0x02000000
> > +#define   LPC_HC_FW_RD_16B              0x04000000
> > +#define   LPC_HC_FW_RD_128B             0x07000000
> > +#define LPC_HC_IRQSER_CTRL      0x30
> > +#define   LPC_HC_IRQSER_EN              0x80000000
> > +#define   LPC_HC_IRQSER_QMODE           0x40000000
> > +#define   LPC_HC_IRQSER_START_MASK      0x03000000
> > +#define   LPC_HC_IRQSER_START_4CLK      0x00000000
> > +#define   LPC_HC_IRQSER_START_6CLK      0x01000000
> > +#define   LPC_HC_IRQSER_START_8CLK      0x02000000
> > +#define LPC_HC_IRQMASK          0x34    /* same bit defs as LPC_HC_IRQSTAT */
> > +#define LPC_HC_IRQSTAT          0x38
> > +#define   LPC_HC_IRQ_SERIRQ0            0x80000000 /* all bits down to ... */
> > +#define   LPC_HC_IRQ_SERIRQ16           0x00008000 /* IRQ16=IOCHK#, IRQ2=SMI# */
> > +#define   LPC_HC_IRQ_SERIRQ_ALL         0xffff8000
> > +#define   LPC_HC_IRQ_LRESET             0x00000400
> > +#define   LPC_HC_IRQ_SYNC_ABNORM_ERR    0x00000080
> > +#define   LPC_HC_IRQ_SYNC_NORESP_ERR    0x00000040
> > +#define   LPC_HC_IRQ_SYNC_NORM_ERR      0x00000020
> > +#define   LPC_HC_IRQ_SYNC_TIMEOUT_ERR   0x00000010
> > +#define   LPC_HC_IRQ_SYNC_TARG_TAR_ERR  0x00000008
> > +#define   LPC_HC_IRQ_SYNC_BM_TAR_ERR    0x00000004
> > +#define   LPC_HC_IRQ_SYNC_BM0_REQ       0x00000002
> > +#define   LPC_HC_IRQ_SYNC_BM1_REQ       0x00000001
> > +#define LPC_HC_ERROR_ADDRESS    0x40
> > +
> > +#define LPC_OPB_SIZE            0x100000000ull
> > +
> > +#define ISA_IO_SIZE             0x00010000
> > +#define ISA_MEM_SIZE            0x10000000
> > +#define LPC_IO_OPB_ADDR         0xd0010000
> > +#define LPC_IO_OPB_SIZE         0x00010000
> > +#define LPC_MEM_OPB_ADDR        0xe0010000
> > +#define LPC_MEM_OPB_SIZE        0x10000000
> > +#define LPC_FW_OPB_ADDR         0xf0000000
> > +#define LPC_FW_OPB_SIZE         0x10000000
> > +
> > +#define LPC_OPB_REGS_OPB_ADDR   0xc0010000
> > +#define LPC_OPB_REGS_OPB_SIZE   0x00002000
> > +#define LPC_HC_REGS_OPB_ADDR    0xc0012000
> > +#define LPC_HC_REGS_OPB_SIZE    0x00001000
> > +
> > +
> > +/*
> > + * TODO: the "primary" cell should only be added on chip 0. This is
> > + * how skiboot chooses the default LPC controller on multichip
> > + * systems.
> > + *
> > + * It would be easly done if we can change the populate() interface to
> > + * replace the PnvXScomInterface parameter by a PnvChip one
> > + */
> > +static int pnv_lpc_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset)
> > +{
> > +    const char compat[] = "ibm,power8-lpc\0ibm,lpc";
> > +    char *name;
> > +    int offset;
> > +    uint32_t lpc_pcba = PNV_XSCOM_LPC_BASE;
> > +    uint32_t reg[] = {
> > +        cpu_to_be32(lpc_pcba),
> > +        cpu_to_be32(PNV_XSCOM_LPC_SIZE)
> > +    };
> > +
> > +    name = g_strdup_printf("isa@%x", lpc_pcba);
> > +    offset = fdt_add_subnode(fdt, xscom_offset, name);
> > +    _FDT(offset);
> > +    g_free(name);
> > +
> > +    _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
> > +    _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2)));
> > +    _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1)));
> > +    _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0)));
> > +    _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat))));
> > +    return 0;
> > +}
> > +
> > +/*
> > + * These read/write handlers of the OPB address space should be common
> > + * with the P9 LPC Controller which uses direct MMIOs.
> > + *
> > + * TODO: rework to use address_space_stq() and address_space_ldq()
> > + * instead.
> > + */
> > +static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data,
> > +                     int sz)
> > +{
> > +    bool success;
> > +
> > +    /* XXX Handle access size limits and FW read caching here */
> > +    success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED,
> > +                                data, sz, false);
> > +
> > +    return success;
> > +}
> > +
> > +static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data,
> > +                      int sz)
> > +{
> > +    bool success;
> > +
> > +    /* XXX Handle access size limits here */
> > +    success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED,
> > +                                data, sz, true);
> > +
> > +    return success;
> > +}
> > +
> > +#define ECCB_CTL_READ           (1ull << (63 - 15))
> > +#define ECCB_CTL_SZ_LSH         (63 - 7)
> > +#define ECCB_CTL_SZ_MASK        (0xfull << ECCB_CTL_SZ_LSH)
> > +#define ECCB_CTL_ADDR_MASK      0xffffffffu;
> > +
> > +#define ECCB_STAT_OP_DONE       (1ull << (63 - 52))
> > +#define ECCB_STAT_OP_ERR        (1ull << (63 - 52))
> > +#define ECCB_STAT_RD_DATA_LSH   (63 - 37)
> > +#define ECCB_STAT_RD_DATA_MASK  (0xffffffff << ECCB_STAT_RD_DATA_LSH)
> > +
> > +static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd)
> > +{
> > +    /* XXX Check for magic bits at the top, addr size etc... */
> > +    unsigned int sz = (cmd & ECCB_CTL_SZ_MASK) >> ECCB_CTL_SZ_LSH;
> > +    uint32_t opb_addr = cmd & ECCB_CTL_ADDR_MASK;
> > +    uint8_t data[4];
> > +    bool success;
> > +
> > +    if (cmd & ECCB_CTL_READ) {
> > +        success = opb_read(lpc, opb_addr, data, sz);
> > +        if (success) {
> > +            lpc->eccb_stat_reg = ECCB_STAT_OP_DONE |
> > +                    (((uint64_t)data[0]) << 24 |
> > +                     ((uint64_t)data[1]) << 16 |
> > +                     ((uint64_t)data[2]) <<  8 |
> > +                     ((uint64_t)data[3])) << ECCB_STAT_RD_DATA_LSH;
> > +        } else {
> > +            lpc->eccb_stat_reg = ECCB_STAT_OP_DONE |
> > +                    (0xffffffffull << ECCB_STAT_RD_DATA_LSH);
> > +        }
> > +    } else {
> > +        data[0] = lpc->eccb_data_reg >> 24;
> > +        data[1] = lpc->eccb_data_reg >> 16;
> > +        data[2] = lpc->eccb_data_reg >>  8;
> > +        data[3] = lpc->eccb_data_reg;
> > +
> > +        success = opb_write(lpc, opb_addr, data, sz);
> > +        lpc->eccb_stat_reg = ECCB_STAT_OP_DONE;
> > +    }
> > +    /* XXX Which error bit (if any) to signal OPB error ? */
> > +}
> > +
> > +static uint64_t pnv_lpc_xscom_read(void *opaque, hwaddr addr, unsigned size)
> > +{
> > +    PnvLpcController *lpc = PNV_LPC(opaque);
> > +    uint32_t offset = addr >> 3;
> > +    uint64_t val = 0;
> > +
> > +    switch (offset & 3) {
> > +    case ECCB_CTL:
> > +    case ECCB_RESET:
> > +        val = 0;
> > +        break;
> > +    case ECCB_STAT:
> > +        val = lpc->eccb_stat_reg;
> > +        lpc->eccb_stat_reg = 0;
> > +        break;
> > +    case ECCB_DATA:
> > +        val = ((uint64_t)lpc->eccb_data_reg) << 32;
> > +        break;
> > +    }
> > +    return val;
> > +}
> > +
> > +static void pnv_lpc_xscom_write(void *opaque, hwaddr addr,
> > +                                uint64_t val, unsigned size)
> > +{
> > +    PnvLpcController *lpc = PNV_LPC(opaque);
> > +    uint32_t offset = addr >> 3;
> > +
> > +    switch (offset & 3) {
> > +    case ECCB_CTL:
> > +        pnv_lpc_do_eccb(lpc, val);
> > +        break;
> > +    case ECCB_RESET:
> > +        /*  XXXX  */
> > +        break;
> > +    case ECCB_STAT:
> > +        break;
> > +    case ECCB_DATA:
> > +        lpc->eccb_data_reg = val >> 32;
> > +        break;
> > +    }
> > +}
> > +
> > +static const MemoryRegionOps pnv_lpc_xscom_ops = {
> > +    .read = pnv_lpc_xscom_read,
> > +    .write = pnv_lpc_xscom_write,
> > +    .valid.min_access_size = 8,
> > +    .valid.max_access_size = 8,
> > +    .impl.min_access_size = 8,
> > +    .impl.max_access_size = 8,
> > +    .endianness = DEVICE_BIG_ENDIAN,
> > +};
> > +
> > +static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size)
> > +{
> > +    PnvLpcController *lpc = opaque;
> > +    uint64_t val = 0xfffffffffffffffful;
> > +
> > +    switch (addr) {
> > +    case LPC_HC_FW_SEG_IDSEL:
> > +        val =  lpc->lpc_hc_fw_seg_idsel;
> > +        break;
> > +    case LPC_HC_FW_RD_ACC_SIZE:
> > +        val =  lpc->lpc_hc_fw_rd_acc_size;
> > +        break;
> > +    case LPC_HC_IRQSER_CTRL:
> > +        val =  lpc->lpc_hc_irqser_ctrl;
> > +        break;
> > +    case LPC_HC_IRQMASK:
> > +        val =  lpc->lpc_hc_irqmask;
> > +        break;
> > +    case LPC_HC_IRQSTAT:
> > +        val =  lpc->lpc_hc_irqstat;
> > +        break;
> > +    case LPC_HC_ERROR_ADDRESS:
> > +        val =  lpc->lpc_hc_error_addr;
> > +        break;
> > +    default:
> > +        qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%"
> > +                      HWADDR_PRIx "\n", addr);
> > +    }
> > +    return val;
> > +}
> > +
> > +static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val,
> > +                         unsigned size)
> > +{
> > +    PnvLpcController *lpc = opaque;
> > +
> > +    /* XXX Filter out reserved bits */
> > +
> > +    switch (addr) {
> > +    case LPC_HC_FW_SEG_IDSEL:
> > +        /* XXX Actually figure out how that works as this impact
> > +         * memory regions/aliases
> > +         */
> > +        lpc->lpc_hc_fw_seg_idsel = val;
> > +        break;
> > +    case LPC_HC_FW_RD_ACC_SIZE:
> > +        lpc->lpc_hc_fw_rd_acc_size = val;
> > +        break;
> > +    case LPC_HC_IRQSER_CTRL:
> > +        lpc->lpc_hc_irqser_ctrl = val;
> > +        break;
> > +    case LPC_HC_IRQMASK:
> > +        lpc->lpc_hc_irqmask = val;
> > +        break;
> > +    case LPC_HC_IRQSTAT:
> > +        lpc->lpc_hc_irqstat &= ~val;
> > +        break;
> > +    case LPC_HC_ERROR_ADDRESS:
> > +        break;
> > +    default:
> > +        qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%"
> > +                      HWADDR_PRIx "\n", addr);
> > +    }
> > +}
> > +
> > +static const MemoryRegionOps lpc_hc_ops = {
> > +    .read = lpc_hc_read,
> > +    .write = lpc_hc_write,
> > +    .endianness = DEVICE_BIG_ENDIAN,
> > +    .valid = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4,
> > +    },
> > +    .impl = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4,
> > +    },
> > +};
> > +
> > +static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size)
> > +{
> > +    PnvLpcController *lpc = opaque;
> > +    uint64_t val = 0xfffffffffffffffful;
> > +
> > +    switch (addr) {
> > +    case OPB_MASTER_LS_IRQ_STAT:
> > +        val = lpc->opb_irq_stat;
> > +        break;
> > +    case OPB_MASTER_LS_IRQ_MASK:
> > +        val = lpc->opb_irq_mask;
> > +        break;
> > +    case OPB_MASTER_LS_IRQ_POL:
> > +        val = lpc->opb_irq_pol;
> > +        break;
> > +    case OPB_MASTER_LS_IRQ_INPUT:
> > +        val = lpc->opb_irq_input;
> > +        break;
> > +    default:
> > +        qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%"
> > +                      HWADDR_PRIx "\n", addr);
> > +    }
> > +
> > +    return val;
> > +}
> > +
> > +static void opb_master_write(void *opaque, hwaddr addr,
> > +                             uint64_t val, unsigned size)
> > +{
> > +    PnvLpcController *lpc = opaque;
> > +
> > +    switch (addr) {
> > +    case OPB_MASTER_LS_IRQ_STAT:
> > +        lpc->opb_irq_stat &= ~val;
> > +        break;
> > +    case OPB_MASTER_LS_IRQ_MASK:
> > +        /* XXX Filter out reserved bits */
> > +        lpc->opb_irq_mask = val;
> > +        break;
> > +    case OPB_MASTER_LS_IRQ_POL:
> > +        /* XXX Filter out reserved bits */
> > +        lpc->opb_irq_pol = val;
> > +        break;
> > +    case OPB_MASTER_LS_IRQ_INPUT:
> > +        /* Read only */
> > +        break;
> > +    default:
> > +        qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%"
> > +                      HWADDR_PRIx "\n", addr);
> > +    }
> > +}
> > +
> > +static const MemoryRegionOps opb_master_ops = {
> > +    .read = opb_master_read,
> > +    .write = opb_master_write,
> > +    .endianness = DEVICE_BIG_ENDIAN,
> > +    .valid = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4,
> > +    },
> > +    .impl = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4,
> > +    },
> > +};
> > +
> > +static void pnv_lpc_realize(DeviceState *dev, Error **errp)
> > +{
> > +    PnvLpcController *lpc = PNV_LPC(dev);
> > +
> > +    /* Reg inits */
> > +    lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B;
> > +
> > +    /* Create address space and backing MR for the OPB bus */
> > +    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.
> > +     */
> > +    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);
> > +
> > +    /* Create windows from the OPB space to the ISA space */
> > +    memory_region_init_alias(&lpc->opb_isa_io, OBJECT(dev), "lpc-isa-io",
> > +                             &lpc->isa_io, 0, LPC_IO_OPB_SIZE);
> > +    memory_region_add_subregion(&lpc->opb_mr, LPC_IO_OPB_ADDR,
> > +                                &lpc->opb_isa_io);
> > +    memory_region_init_alias(&lpc->opb_isa_mem, OBJECT(dev), "lpc-isa-mem",
> > +                             &lpc->isa_mem, 0, LPC_MEM_OPB_SIZE);
> > +    memory_region_add_subregion(&lpc->opb_mr, LPC_MEM_OPB_ADDR,
> > +                                &lpc->opb_isa_mem);
> > +    memory_region_init_alias(&lpc->opb_isa_fw, OBJECT(dev), "lpc-isa-fw",
> > +                             &lpc->isa_mem, 0, LPC_FW_OPB_SIZE);
> > +    memory_region_add_subregion(&lpc->opb_mr, LPC_FW_OPB_ADDR,
> > +                                &lpc->opb_isa_fw);
> > +
> > +    /* Create MMIO regions for LPC HC and OPB registers */
> > +    memory_region_init_io(&lpc->opb_master_regs, OBJECT(dev), &opb_master_ops,
> > +                          lpc, "lpc-opb-master", LPC_OPB_REGS_OPB_SIZE);
> > +    memory_region_add_subregion(&lpc->opb_mr, LPC_OPB_REGS_OPB_ADDR,
> > +                                &lpc->opb_master_regs);
> > +    memory_region_init_io(&lpc->lpc_hc_regs, OBJECT(dev), &lpc_hc_ops, lpc,
> > +                          "lpc-hc", LPC_HC_REGS_OPB_SIZE);
> > +    memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR,
> > +                                &lpc->lpc_hc_regs);
> > +
> > +    /* XScom region for LPC registers */
> > +    memory_region_init_io(&lpc->xscom_regs, OBJECT(dev),
> > +                          &pnv_lpc_xscom_ops, lpc, "xscom-lpc",
> > +                          PNV_XSCOM_LPC_SIZE << 3);
> > +}
> > +
> > +static void pnv_lpc_class_init(ObjectClass *klass, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(klass);
> > +    PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
> > +
> > +    xdc->populate = pnv_lpc_populate;
> > +
> > +    dc->realize = pnv_lpc_realize;
> > +}
> > +
> > +static const TypeInfo pnv_lpc_info = {
> > +    .name          = TYPE_PNV_LPC,
> > +    .parent        = TYPE_DEVICE,
> > +    .instance_size = sizeof(PnvLpcController),
> > +    .class_init    = pnv_lpc_class_init,
> > +    .interfaces = (InterfaceInfo[]) {
> > +        { TYPE_PNV_XSCOM_INTERFACE },
> > +        { }
> > +    }
> > +};
> > +
> > +static void pnv_lpc_register_types(void)
> > +{
> > +    type_register_static(&pnv_lpc_info);
> > +}
> > +
> > +type_init(pnv_lpc_register_types)
> > diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> > index f89eddb6e5e0..e586ff4e735e 100644
> > --- a/include/hw/ppc/pnv.h
> > +++ b/include/hw/ppc/pnv.h
> > @@ -22,6 +22,7 @@
> >  #include "hw/boards.h"
> >  #include "hw/sysbus.h"
> >  #include "hw/ppc/pnv_xscom.h"
> > +#include "hw/ppc/pnv_lpc.h"
> >  
> >  #define TYPE_PNV_CHIP "powernv-chip"
> >  #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
> > @@ -52,6 +53,8 @@ typedef struct PnvChip {
> >      uint32_t  nr_cores;
> >      uint64_t  cores_mask;
> >      void      *cores;
> > +
> > +    PnvLpcController lpc;
> >  } PnvChip;
> >  
> >  typedef struct PnvChipClass {
> > diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h
> > new file mode 100644
> > index 000000000000..38e5506975aa
> > --- /dev/null
> > +++ b/include/hw/ppc/pnv_lpc.h
> > @@ -0,0 +1,67 @@
> > +/*
> > + * QEMU PowerPC PowerNV LPC controller
> > + *
> > + * Copyright (c) 2016, IBM Corporation.
> > + *
> > + * This library is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2 of the License, or (at your option) any later version.
> > + *
> > + * This library is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public
> > + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> > + */
> > +#ifndef _PPC_PNV_LPC_H
> > +#define _PPC_PNV_LPC_H
> > +
> > +#define TYPE_PNV_LPC "pnv-lpc"
> > +#define PNV_LPC(obj) \
> > +     OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC)
> > +
> > +typedef struct PnvLpcController {
> > +    DeviceState parent;
> > +
> > +    uint64_t eccb_stat_reg;
> > +    uint32_t eccb_data_reg;
> > +
> > +    /* OPB bus */
> > +    MemoryRegion opb_mr;
> > +    AddressSpace opb_as;
> > +
> > +    /* ISA IO and Memory space */
> > +    MemoryRegion isa_io;
> > +    MemoryRegion isa_mem;
> > +
> > +    /* Windows from OPB to ISA (aliases) */
> > +    MemoryRegion opb_isa_io;
> > +    MemoryRegion opb_isa_mem;
> > +    MemoryRegion opb_isa_fw;
> > +
> > +    /* Registers */
> > +    MemoryRegion lpc_hc_regs;
> > +    MemoryRegion opb_master_regs;
> > +
> > +    /* OPB Master LS registers */
> > +    uint32_t opb_irq_stat;
> > +    uint32_t opb_irq_mask;
> > +    uint32_t opb_irq_pol;
> > +    uint32_t opb_irq_input;
> > +
> > +    /* LPC HC registers */
> > +    uint32_t lpc_hc_fw_seg_idsel;
> > +    uint32_t lpc_hc_fw_rd_acc_size;
> > +    uint32_t lpc_hc_irqser_ctrl;
> > +    uint32_t lpc_hc_irqmask;
> > +    uint32_t lpc_hc_irqstat;
> > +    uint32_t lpc_hc_error_addr;
> > +
> > +    /* XSCOM registers */
> > +    MemoryRegion xscom_regs;
> > +} PnvLpcController;
> > +
> > +#endif /* _PPC_PNV_LPC_H */
> > diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
> > index 79975a6cbe46..3b8a8b4fc6ff 100644
> > --- a/include/hw/ppc/pnv_xscom.h
> > +++ b/include/hw/ppc/pnv_xscom.h
> > @@ -60,6 +60,9 @@ typedef struct PnvXScomInterfaceClass {
> >  #define PNV_XSCOM_EX_CORE_BASE(i) (PNV_XSCOM_EX_BASE | (((uint64_t)i) << 24))
> >  #define PNV_XSCOM_EX_CORE_SIZE    0x100000
> >  
> > +#define PNV_XSCOM_LPC_BASE        0xb0020
> > +#define PNV_XSCOM_LPC_SIZE        0x4
> > +
> >  extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
> >  extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
> >  
> 



-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 09/20] ppc/pnv: add a ISA bus
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 09/20] ppc/pnv: add a ISA bus Cédric Le Goater
@ 2016-10-13  2:58   ` David Gibson
  0 siblings, 0 replies; 75+ messages in thread
From: David Gibson @ 2016-10-13  2:58 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 5019 bytes --]

On Mon, Oct 03, 2016 at 09:24:45AM +0200, Cédric Le Goater wrote:
> As Qemu only supports a single instance of the ISA bus, we use the LPC
> controller of chip 0 to create one and plug in a couple of useful
> devices, like an UART and RTC. An IPMI BT device, which is also an ISA
> device, can be defined on the command line to connect an external BMC.
> That is for later.
> 
> The PowerNV machine now has a console. Skiboot should load a kernel
> and jump into it but execution will stop quite early because we lack a
> model for the native XICS controller for the moment :
> 
>     [    0.000000] NR_IRQS:512 nr_irqs:512 16
>     [    0.000000] XICS: Cannot find a Presentation Controller !
>     [    0.000000] ------------[ cut here ]------------
>     [    0.000000] WARNING: at arch/powerpc/platforms/powernv/setup.c:81
>     ...
>     [    0.000000] NIP [c00000000079d65c] pnv_init_IRQ+0x30/0x44
> 
> You can still do a few things under xmon.
> 
> Based on previous work from :
>       Benjamin Herrenschmidt <benh@kernel.crashing.org>
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

Reviewed-by: David Gibson <david@gibson.dropbear.id.au>

> ---
>  hw/ppc/pnv.c         | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h |  2 ++
>  2 files changed, 67 insertions(+)
> 
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index e41244294435..4a71b18bf38b 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -34,6 +34,10 @@
>  
>  #include "hw/ppc/pnv_xscom.h"
>  
> +#include "hw/isa/isa.h"
> +#include "hw/char/serial.h"
> +#include "hw/timer/mc146818rtc.h"
> +
>  #include <libfdt.h>
>  
>  #define FDT_MAX_SIZE            0x00100000
> @@ -302,6 +306,58 @@ static void ppc_powernv_reset(void)
>      cpu_physical_memory_write(POWERNV_FDT_ADDR, fdt, fdt_totalsize(fdt));
>  }
>  
> +/* If we don't use the built-in LPC interrupt deserializer, we need
> + * to provide a set of qirqs for the ISA bus or things will go bad.
> + *
> + * Most machines using pre-Naples chips (without said deserializer)
> + * have a CPLD that will collect the SerIRQ and shoot them as a
> + * single level interrupt to the P8 chip. So let's setup a hook
> + * for doing just that.
> + *
> + * Note: The actual interrupt input isn't emulated yet, this will
> + * come with the PSI bridge model.
> + */
> +static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
> +{
> +    /* We don't yet emulate the PSI bridge which provides the external
> +     * interrupt, so just drop interrupts on the floor
> +     */
> +}
> +
> +static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
> +{
> +     /* XXX TODO */
> +}
> +
> +static ISABus *pnv_isa_create(PnvChip *chip)
> +{
> +    PnvLpcController *lpc = &chip->lpc;
> +    ISABus *isa_bus;
> +    qemu_irq *irqs;
> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
> +
> +    /* let isa_bus_new() create its own bridge on SysBus otherwise
> +     * devices speficied on the command line won't find the bus and
> +     * will fail to create.
> +     */
> +    isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io,
> +                          &error_fatal);
> +
> +    /* Not all variants have a working serial irq decoder. If not,
> +     * handling of LPC interrupts becomes a platform issue (some
> +     * platforms have a CPLD to do it).
> +     */
> +    if (pcc->chip_type == PNV_CHIP_POWER8NVL) {
> +        irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler, lpc, ISA_NUM_IRQS);
> +    } else {
> +        irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, NULL,
> +                                  ISA_NUM_IRQS);
> +    }
> +
> +    isa_bus_irqs(isa_bus, irqs);
> +    return isa_bus;
> +}
> +
>  static void ppc_powernv_init(MachineState *machine)
>  {
>      PnvMachineState *pnv = POWERNV_MACHINE(machine);
> @@ -390,6 +446,15 @@ static void ppc_powernv_init(MachineState *machine)
>          object_property_set_bool(chip, true, "realized", &error_fatal);
>      }
>      g_free(chip_typename);
> +
> +    /* Instantiate ISA bus on chip 0 */
> +    pnv->isa_bus = pnv_isa_create(pnv->chips[0]);
> +
> +    /* Create serial port */
> +    serial_hds_isa_init(pnv->isa_bus, MAX_SERIAL_PORTS);
> +
> +    /* Create an RTC ISA device too */
> +    rtc_init(pnv->isa_bus, 2000, NULL);
>  }
>  
>  /*
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index e586ff4e735e..617c3fdd4f06 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -110,6 +110,8 @@ typedef struct PnvMachineState {
>  
>      uint32_t  num_chips;
>      PnvChip   **chips;
> +
> +    ISABus *isa_bus;
>  } PnvMachineState;
>  
>  #define POWERNV_FDT_ADDR                0x01000000

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/20] ppc/pnv: add XSCOM infrastructure
  2016-10-13  0:41   ` David Gibson
@ 2016-10-13  6:26     ` Cédric Le Goater
  0 siblings, 0 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-13  6:26 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 10/13/2016 02:41 AM, David Gibson wrote:
> On Mon, Oct 03, 2016 at 09:24:42AM +0200, Cédric Le Goater wrote:
>> On a real POWER8 system, the Pervasive Interconnect Bus (PIB) serves
>> as a backbone to connect different units of the system. The host
>> firmware connects to the PIB through a bridge unit, the
>> Alter-Display-Unit (ADU), which gives him access to all the chiplets
>> on the PCB network (Pervasive Connect Bus), the PIB acting as the root
>> of this network.
>>
>> XSCOM (serial communication) is the interface to the sideband bus
>> provided by the POWER8 pervasive unit to read and write to chiplets
>> resources. This is needed by the host firmware, OPAL and to a lesser
>> extent, Linux. This is among others how the PCI Host bridges get
>> configured at boot or how the LPC bus is accessed.
>>
>> To represent the ADU of a real system, we introduce a specific
>> AddressSpace to dispatch XSCOM accesses to the targeted chiplets. The
>> translation of an XSCOM address into a PCB register address is
>> slightly different between the P9 and the P8. This is handled before
>> the dispatch using a 8byte alignment for all.
>>
>> To customize the device tree, a QOM InterfaceClass, PnvXScomInterface,
>> is provided with a populate() handler. The chip populates the device
>> tree by simply looping on its children. Therefore, each model needing
>> custom nodes should not forget to declare itself as a child at
>> instantiation time.
>>
>> Based on previous work done by :
>>       Benjamin Herrenschmidt <benh@kernel.crashing.org>
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>
>>  Changes since v3:
>>
>>  - reworked the model to dispatch addresses to the memory regions
>>    using pcb_addr << 3, which is a no-op for the P9. The benefit is
>>    that all the address translation work can be done before dispatch
>>    and the conversion handlers in the chip and in the xscom interface
>>    are gone.
>>    
>>  - removed the proxy PnnXscom object and extended the PnvChip object
>>    with an address space for XSCOM and its associated memory region.
>>    
>>  - changed the read/write handlers in the address space to use
>>    address_space_stq() and address_space_ldq()
>>    
>>  - introduced 'fake' default read/write handlers to handle 'core'
>>    registers. We can add a real device model when more work needs to
>>    be done under these.
>>    
>>  - fixed an issue with the monitor doing read/write in the XSCOM
>>    address space. When under the monitor, we don't have a cpu to
>>    update the HMER SPR. That might need more work in the long term.
>>    
>>  - introduced a xscom base field to hold the xscom base address as
>>    it is different on P9
>>
>>  - renamed the devnode() handler to populate()
>>
>>  Changes since v2:
>>
>>  - QOMified the model.
>>  
>>  - all mappings in main memory space are now gathered in
>>    pnv_chip_realize() as done on other architectures.
>>    
>>  - removed XScomBus. The parenthood is established through the QOM
>>    model
>>    
>>  - replaced the XScomDevice with an InterfaceClass : PnvXScomInterface. 
>>  - introduced an XSCOM address space to dispatch accesses to the
>>    chiplets
>>
>>  hw/ppc/Makefile.objs       |   2 +-
>>  hw/ppc/pnv.c               |  25 +++++
>>  hw/ppc/pnv_xscom.c         | 262 +++++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/ppc/pnv.h       |  15 +++
>>  include/hw/ppc/pnv_xscom.h |  47 ++++++++
>>  5 files changed, 350 insertions(+), 1 deletion(-)
>>  create mode 100644 hw/ppc/pnv_xscom.c
>>  create mode 100644 include/hw/ppc/pnv_xscom.h
>>
>> diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
>> index f8c7d1db9ade..08c213c40684 100644
>> --- a/hw/ppc/Makefile.objs
>> +++ b/hw/ppc/Makefile.objs
>> @@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
>>  obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
>>  obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
>>  # IBM PowerNV
>> -obj-$(CONFIG_POWERNV) += pnv.o pnv_core.o
>> +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o
>>  ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
>>  obj-y += spapr_pci_vfio.o
>>  endif
>> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
>> index 2376bb222918..5e19b6880387 100644
>> --- a/hw/ppc/pnv.c
>> +++ b/hw/ppc/pnv.c
>> @@ -32,6 +32,8 @@
>>  #include "exec/address-spaces.h"
>>  #include "qemu/cutils.h"
>>  
>> +#include "hw/ppc/pnv_xscom.h"
>> +
>>  #include <libfdt.h>
>>  
>>  #define FDT_MAX_SIZE            0x00100000
>> @@ -218,6 +220,8 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
>>      size_t typesize = object_type_get_instance_size(typename);
>>      int i;
>>  
>> +    pnv_xscom_populate(chip, fdt, 0);
>> +
>>      for (i = 0; i < chip->nr_cores; i++) {
>>          PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
>>  
>> @@ -450,6 +454,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
>>      k->chip_cfam_id = 0x221ef04980000000ull;  /* P8 Murano DD2.1 */
>>      k->cores_mask = POWER8E_CORE_MASK;
>>      k->core_pir = pnv_chip_core_pir_p8;
>> +    k->xscom_base = 0x003fc0000000000ull;
>>      dc->desc = "PowerNV Chip POWER8E";
>>  }
>>  
>> @@ -470,6 +475,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
>>      k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */
>>      k->cores_mask = POWER8_CORE_MASK;
>>      k->core_pir = pnv_chip_core_pir_p8;
>> +    k->xscom_base = 0x003fc0000000000ull;
>>      dc->desc = "PowerNV Chip POWER8";
>>  }
>>  
>> @@ -490,6 +496,7 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
>>      k->chip_cfam_id = 0x120d304980000000ull;  /* P8 Naples DD1.0 */
>>      k->cores_mask = POWER8_CORE_MASK;
>>      k->core_pir = pnv_chip_core_pir_p8;
>> +    k->xscom_base = 0x003fc0000000000ull;
>>      dc->desc = "PowerNV Chip POWER8NVL";
>>  }
>>  
>> @@ -510,6 +517,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
>>      k->chip_cfam_id = 0x100d104980000000ull; /* P9 Nimbus DD1.0 */
>>      k->cores_mask = POWER9_CORE_MASK;
>>      k->core_pir = pnv_chip_core_pir_p9;
>> +    k->xscom_base = 0x00603fc00000000ull;
>>      dc->desc = "PowerNV Chip POWER9";
>>  }
>>  
>> @@ -549,6 +557,14 @@ static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp)
>>      }
>>  }
>>  
>> +static void pnv_chip_init(Object *obj)
>> +{
>> +    PnvChip *chip = PNV_CHIP(obj);
>> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
>> +
>> +    chip->xscom_base = pcc->xscom_base;
> 
> Is there any reason you really need the per-isntance xscom_base as
> well as the per-class xscom_base?

The per-instance xscom_base is not strictly necessary. It is a chip 
constant relative to the chip id. So I need the chip to calculate it.

> If you do have a per-instance value, it seems like you should fold in
> the calculation from PNV_XSCOM_BASE() here.

But I can make it a little cleaner. We need the value in pnv_chip_realize()
and pnv_xscom_populate() to populate the device tree. something like 
that would do :

 ( PNV_CHIP_GET_CLASS(chip)->xscom_base + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE)

I want to add a mmio_base because the ICP BAR and the PSIHB BAR depend 
on it. PHB will also and the Centaur if we model this chip one day.

>> +}
>> +
>>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>  {
>>      PnvChip *chip = PNV_CHIP(dev);
>> @@ -563,6 +579,14 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>          return;
>>      }
>>  
>> +    /* XSCOM bridge */
>> +    pnv_xscom_realize(chip, &error);
>> +    if (error) {
>> +        error_propagate(errp, error);
>> +        return;
>> +    }
>> +    sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip));
>> +
>>      /* Early checks on the core settings */
>>      pnv_chip_core_sanitize(chip, &error);
>>      if (error) {
>> @@ -620,6 +644,7 @@ static const TypeInfo pnv_chip_info = {
>>      .name          = TYPE_PNV_CHIP,
>>      .parent        = TYPE_SYS_BUS_DEVICE,
>>      .class_init    = pnv_chip_class_init,
>> +    .instance_init = pnv_chip_init,
>>      .class_size    = sizeof(PnvChipClass),
>>      .abstract      = true,
>>  };
>> diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c
>> new file mode 100644
>> index 000000000000..ce1182d9a13e
>> --- /dev/null
>> +++ b/hw/ppc/pnv_xscom.c
>> @@ -0,0 +1,262 @@
>> +/*
>> + * QEMU PowerPC PowerNV XSCOM bus
>> + *
>> + * Copyright (c) 2016, IBM Corporation.
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
>> + */
>> +#include "qemu/osdep.h"
>> +#include "qapi/error.h"
>> +#include "hw/hw.h"
>> +#include "qemu/log.h"
>> +#include "sysemu/kvm.h"
>> +#include "target-ppc/cpu.h"
>> +#include "hw/sysbus.h"
>> +
>> +#include "hw/ppc/fdt.h"
>> +#include "hw/ppc/pnv_xscom.h"
>> +#include "hw/ppc/pnv.h"
>> +
>> +#include <libfdt.h>
>> +
>> +static void xscom_complete(uint64_t hmer_bits)
> 
> I think this should take a cpu parameter instead of assuming
> current_cpu.  Of course, the callers will probably just use
> current_cpu, but in the callers it's a bit more obvious that
> current_cpu is actually the right thing.

Yes. the 'if (cs)' statement below is good argument for what you
say.

Thanks,

C.


>> +{
>> +    CPUState *cs = current_cpu;
>> +
>> +    /*
>> +     * TODO: When the read/write comes from the monitor, we do not
>> +     * have a cpu.
>> +     */
>> +    if (cs) {
>> +        PowerPCCPU *cpu = POWERPC_CPU(cs);
>> +        CPUPPCState *env = &cpu->env;
>> +
>> +        /*
>> +         * TODO: Need a CPU helper to set HMER, also handle generation
>> +         * of HMIs
>> +         */
>> +        cpu_synchronize_state(cs);
>> +        env->spr[SPR_HMER] |= hmer_bits;
>> +    }
>> +}
>> +
>> +static uint32_t pnv_xscom_pcba(PnvChip *chip, uint64_t addr)
>> +{
>> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
>> +
>> +    addr &= (PNV_XSCOM_SIZE - 1);
>> +    if (pcc->chip_type == PNV_CHIP_POWER9) {
>> +        return addr >> 3;
>> +    } else {
>> +        return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf);
>> +    }
>> +}
>> +
>> +static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba)
>> +{
>> +    switch (pcba) {
>> +    case 0xf000f:
>> +        return PNV_CHIP_GET_CLASS(chip)->chip_cfam_id;
>> +    case 0x1010c00:     /* PIBAM FIR */
>> +    case 0x1010c03:     /* PIBAM FIR MASK */
>> +    case 0x2020007:     /* ADU stuff */
>> +    case 0x2020009:     /* ADU stuff */
>> +    case 0x202000f:     /* ADU stuff */
>> +        return 0;
>> +    case 0x2013f00:     /* PBA stuff */
>> +    case 0x2013f01:     /* PBA stuff */
>> +    case 0x2013f02:     /* PBA stuff */
>> +    case 0x2013f03:     /* PBA stuff */
>> +    case 0x2013f04:     /* PBA stuff */
>> +    case 0x2013f05:     /* PBA stuff */
>> +    case 0x2013f06:     /* PBA stuff */
>> +    case 0x2013f07:     /* PBA stuff */
>> +        return 0;
>> +    case 0x2013028:     /* CAPP stuff */
>> +    case 0x201302a:     /* CAPP stuff */
>> +    case 0x2013801:     /* CAPP stuff */
>> +    case 0x2013802:     /* CAPP stuff */
>> +        return 0;
>> +    default:
>> +        return -1;
>> +    }
>> +}
>> +
>> +static bool xscom_write_default(PnvChip *chip, uint32_t pcba, uint64_t val)
>> +{
>> +    /* We ignore writes to these */
>> +    switch (pcba) {
>> +    case 0xf000f:       /* chip id is RO */
>> +    case 0x1010c00:     /* PIBAM FIR */
>> +    case 0x1010c01:     /* PIBAM FIR */
>> +    case 0x1010c02:     /* PIBAM FIR */
>> +    case 0x1010c03:     /* PIBAM FIR MASK */
>> +    case 0x1010c04:     /* PIBAM FIR MASK */
>> +    case 0x1010c05:     /* PIBAM FIR MASK */
>> +    case 0x2020007:     /* ADU stuff */
>> +    case 0x2020009:     /* ADU stuff */
>> +    case 0x202000f:     /* ADU stuff */
>> +        return true;
>> +    default:
>> +        return false;
>> +    }
>> +}
>> +
>> +static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)
>> +{
>> +    PnvChip *chip = opaque;
>> +    uint32_t pcba = pnv_xscom_pcba(chip, addr);
>> +    uint64_t val = 0;
>> +    MemTxResult result;
>> +
>> +    /* Handle some SCOMs here before dispatch */
>> +    val = xscom_read_default(chip, pcba);
>> +    if (val != -1) {
>> +        goto complete;
>> +    }
>> +
>> +    val = address_space_ldq(&chip->xscom_as, pcba << 3, MEMTXATTRS_UNSPECIFIED,
>> +                            &result);
>> +    if (result != MEMTX_OK) {
>> +        qemu_log_mask(LOG_GUEST_ERROR, "XSCOM read failed at @0x%"
>> +                      HWADDR_PRIx " pcba=0x%08x\n", addr, pcba);
>> +        xscom_complete(HMER_XSCOM_FAIL | HMER_XSCOM_DONE);
>> +        return 0;
>> +    }
>> +
>> +complete:
>> +    xscom_complete(HMER_XSCOM_DONE);
>> +    return val;
>> +}
>> +
>> +static void xscom_write(void *opaque, hwaddr addr, uint64_t val,
>> +                        unsigned width)
>> +{
>> +    PnvChip *chip = opaque;
>> +    uint32_t pcba = pnv_xscom_pcba(chip, addr);
>> +    MemTxResult result;
>> +
>> +    /* Handle some SCOMs here before dispatch */
>> +    if (xscom_write_default(chip, pcba, val)) {
>> +        goto complete;
>> +    }
>> +
>> +    address_space_stq(&chip->xscom_as, pcba << 3, val, MEMTXATTRS_UNSPECIFIED,
>> +                      &result);
>> +    if (result != MEMTX_OK) {
>> +        qemu_log_mask(LOG_GUEST_ERROR, "XSCOM write failed at @0x%"
>> +                      HWADDR_PRIx " pcba=0x%08x data=0x%" PRIx64 "\n",
>> +                      addr, pcba, val);
>> +        xscom_complete(HMER_XSCOM_FAIL | HMER_XSCOM_DONE);
>> +        return;
>> +    }
>> +
>> +complete:
>> +    xscom_complete(HMER_XSCOM_DONE);
>> +}
>> +
>> +const MemoryRegionOps pnv_xscom_ops = {
>> +    .read = xscom_read,
>> +    .write = 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,
>> +};
>> +
>> +void pnv_xscom_realize(PnvChip *chip, Error **errp)
>> +{
>> +    SysBusDevice *sbd = SYS_BUS_DEVICE(chip);
>> +    char *name;
>> +
>> +    name = g_strdup_printf("xscom-%x", chip->chip_id);
>> +    memory_region_init_io(&chip->xscom_mmio, OBJECT(chip), &pnv_xscom_ops,
>> +                          chip, name, PNV_XSCOM_SIZE);
>> +    sysbus_init_mmio(sbd, &chip->xscom_mmio);
>> +
>> +    memory_region_init(&chip->xscom, OBJECT(chip), name, PNV_XSCOM_SIZE);
>> +    address_space_init(&chip->xscom_as, &chip->xscom, name);
>> +    g_free(name);
>> +}
>> +
>> +static const TypeInfo pnv_xscom_interface_info = {
>> +    .name = TYPE_PNV_XSCOM_INTERFACE,
>> +    .parent = TYPE_INTERFACE,
>> +    .class_size = sizeof(PnvXScomInterfaceClass),
>> +};
>> +
>> +static void pnv_xscom_register_types(void)
>> +{
>> +    type_register_static(&pnv_xscom_interface_info);
>> +}
>> +
>> +type_init(pnv_xscom_register_types)
>> +
>> +typedef struct ForeachPopulateArgs {
>> +    void *fdt;
>> +    int xscom_offset;
>> +} ForeachPopulateArgs;
>> +
>> +static int xscom_populate_child(Object *child, void *opaque)
>> +{
>> +    if (object_dynamic_cast(child, TYPE_PNV_XSCOM_INTERFACE)) {
>> +        ForeachPopulateArgs *args = opaque;
>> +        PnvXScomInterface *xd = PNV_XSCOM_INTERFACE(child);
>> +        PnvXScomInterfaceClass *xc = PNV_XSCOM_INTERFACE_GET_CLASS(xd);
>> +
>> +        if (xc->populate) {
>> +            _FDT((xc->populate(xd, args->fdt, args->xscom_offset)));
>> +        }
>> +    }
>> +    return 0;
>> +}
>> +
>> +static const char compat_p8[] = "ibm,power8-xscom\0ibm,xscom";
>> +static const char compat_p9[] = "ibm,power9-xscom\0ibm,xscom";
>> +
>> +int pnv_xscom_populate(PnvChip *chip, void *fdt, int root_offset)
>> +{
>> +    uint64_t reg[] = { cpu_to_be64(PNV_XSCOM_BASE(chip)),
>> +                       cpu_to_be64(PNV_XSCOM_SIZE) };
>> +    int xscom_offset;
>> +    ForeachPopulateArgs args;
>> +    char *name;
>> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
>> +
>> +    name = g_strdup_printf("xscom@%" PRIx64, be64_to_cpu(reg[0]));
>> +    xscom_offset = fdt_add_subnode(fdt, root_offset, name);
>> +    _FDT(xscom_offset);
>> +    g_free(name);
>> +    _FDT((fdt_setprop_cell(fdt, xscom_offset, "ibm,chip-id", chip->chip_id)));
>> +    _FDT((fdt_setprop_cell(fdt, xscom_offset, "#address-cells", 1)));
>> +    _FDT((fdt_setprop_cell(fdt, xscom_offset, "#size-cells", 1)));
>> +    _FDT((fdt_setprop(fdt, xscom_offset, "reg", reg, sizeof(reg))));
>> +
>> +    if (pcc->chip_type == PNV_CHIP_POWER9) {
>> +        _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat_p9,
>> +                          sizeof(compat_p9))));
>> +    } else {
>> +        _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat_p8,
>> +                          sizeof(compat_p8))));
>> +    }
>> +
>> +    _FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0)));
>> +
>> +    args.fdt = fdt;
>> +    args.xscom_offset = xscom_offset;
>> +
>> +    object_child_foreach(OBJECT(chip), xscom_populate_child, &args);
>> +    return 0;
>> +}
>> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
>> index ed4a360cde3b..f89eddb6e5e0 100644
>> --- a/include/hw/ppc/pnv.h
>> +++ b/include/hw/ppc/pnv.h
>> @@ -21,6 +21,7 @@
>>  
>>  #include "hw/boards.h"
>>  #include "hw/sysbus.h"
>> +#include "hw/ppc/pnv_xscom.h"
>>  
>>  #define TYPE_PNV_CHIP "powernv-chip"
>>  #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
>> @@ -43,6 +44,11 @@ typedef struct PnvChip {
>>      /*< public >*/
>>      uint32_t     chip_id;
>>  
>> +    hwaddr       xscom_base;
>> +    MemoryRegion xscom_mmio;
>> +    MemoryRegion xscom;
>> +    AddressSpace xscom_as;
>> +
>>      uint32_t  nr_cores;
>>      uint64_t  cores_mask;
>>      void      *cores;
>> @@ -58,6 +64,8 @@ typedef struct PnvChipClass {
>>      uint64_t     chip_cfam_id;
>>      uint64_t     cores_mask;
>>  
>> +    hwaddr       xscom_base;
>> +
>>      uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
>>  } PnvChipClass;
>>  
>> @@ -105,4 +113,11 @@ typedef struct PnvMachineState {
>>  
>>  #define PNV_TIMEBASE_FREQ           512000000ULL
>>  
>> +/*
>> + * POWER8 MMIO base addresses
>> + */
>> +#define PNV_XSCOM_SIZE        0x800000000ull
>> +#define PNV_XSCOM_BASE(chip)                                            \
>> +    (chip->xscom_base + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE)
>> +
>>  #endif /* _PPC_PNV_H */
>> diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
>> new file mode 100644
>> index 000000000000..f50eb0bc4099
>> --- /dev/null
>> +++ b/include/hw/ppc/pnv_xscom.h
>> @@ -0,0 +1,47 @@
>> +/*
>> + * QEMU PowerPC PowerNV XSCOM bus definitions
>> + *
>> + * Copyright (c) 2016, IBM Corporation.
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
>> + */
>> +#ifndef _PPC_PNV_XSCOM_H
>> +#define _PPC_PNV_XSCOM_H
>> +
>> +#include "qom/object.h"
>> +
>> +typedef struct PnvChip PnvChip;
>> +
>> +typedef struct PnvXScomInterface {
>> +    Object parent;
>> +} PnvXScomInterface;
>> +
>> +#define TYPE_PNV_XSCOM_INTERFACE "pnv-xscom-interface"
>> +#define PNV_XSCOM_INTERFACE(obj) \
>> +     OBJECT_CHECK(PnvXScomInterface, (obj), TYPE_PNV_XSCOM_INTERFACE)
>> +#define PNV_XSCOM_INTERFACE_CLASS(klass)                \
>> +    OBJECT_CLASS_CHECK(PnvXScomInterfaceClass, (klass), \
>> +                       TYPE_PNV_XSCOM_INTERFACE)
>> +#define PNV_XSCOM_INTERFACE_GET_CLASS(obj) \
>> +     OBJECT_GET_CLASS(PnvXScomInterfaceClass, (obj), TYPE_PNV_XSCOM_INTERFACE)
>> +
>> +typedef struct PnvXScomInterfaceClass {
>> +    InterfaceClass parent;
>> +    int (*populate)(PnvXScomInterface *dev, void *fdt, int offset);
>> +} PnvXScomInterfaceClass;
>> +
>> +extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
>> +extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
>> +
>> +#endif /* _PPC_PNV_XSCOM_H */
> 

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 08/20] ppc/pnv: add a LPC controller
  2016-10-13  2:52   ` David Gibson
  2016-10-13  2:53     ` David Gibson
@ 2016-10-13  6:31     ` Cédric Le Goater
  1 sibling, 0 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-13  6:31 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 10/13/2016 04:52 AM, David Gibson wrote:
> On Mon, Oct 03, 2016 at 09:24:44AM +0200, Cédric Le Goater wrote:
>> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>>
>> The LPC (Low Pin Count) interface on a POWER8 is made accessible to
>> the system through the ADU (XSCOM interface). This interface is part
>> of set of units connected together via a local OPB (On-Chip Peripheral
>> Bus) which act as a bridge between the ADU and the off chip LPC
>> endpoints, like external flash modules.
>>
>> The most important units of this OPB are :
>>  - OPB Master: contains the ADU slave logic, a set of internal
>>    registers and the logic to control the OPB.
>>  - LPCHC (LPC HOST Controller): which implements a OPB Slave, a set of
>>    internal registers and the LPC HOST Controller to control the LPC
>>    interface.
>>
>> Four address spaces are provided to the ADU :
>>  - LPC Bus Firmware Memory
>>  - LPC Bus Memory
>>  - LPC Bus I/O (ISA bus)
>>  - and the registers for the OPB Master and the LPC Host Controller
>>
>> On POWER8, an intermediate hop is necessary to reach the OPB, through
>> a unit called the ECCB. OPB commands are simply mangled in ECCB write
>> commands.
>>
>> On POWER9, the OPB master address space can be accessed via MMIO. The
>> logic is same but the code will be simpler as the XSCOM and ECCB hops
>> are not necessary anymore.
>>
>> This version of the LPC controller model doesn't yet implement support
>> for the SerIRQ deserializer present in the Naples version of the chip
>> though some preliminary work is there.
>>
>> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>> [clg: - updated for qemu-2.7
>>       - ported on latest PowerNV patchset
>>       - changed the XSCOM interface to fit new model
>>       - QOMified the model
>>       - moved the ISA hunks in another patch
>>       - removed printf logging
>>       - added a couple of UNIMP logging
>>       - rewrote commit log ]
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> 
> It looks reasonable as far as it goes.
> 
> I don't see anything wiring this up to qemu's common ISA
> infrastructure, which seems a bit odd.

Yes. I wanted to distinguish the wiring from the model. It is because
there is two different ways to handle the interrupt depending on the
chip type and also because we pick chip[0]->lpc to be the primary bus.

Thanks,

C. 

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 07/20] ppc/pnv: add XSCOM handlers to PnvCore
  2016-10-13  0:51   ` David Gibson
@ 2016-10-13  6:50     ` Cédric Le Goater
  2016-10-13 22:24       ` David Gibson
  0 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-13  6:50 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 10/13/2016 02:51 AM, David Gibson wrote:
> On Mon, Oct 03, 2016 at 09:24:43AM +0200, Cédric Le Goater wrote:
>> Now that we are using real HW ids for the cores in PowerNV chips, we
>> can route the XSCOM accesses to them. We just need to attach a
>> specific XSCOM memory region to each core in the appropriate window
>> for the core number.
>>
>> To start with, let's install the DTS (Digital Thermal Sensor) handlers
>> which should return 38°C for each core.
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>
>>  Changes since v3:
>>
>>  - moved to new XSCOM model
>>  - kept the write op on the XSCOM memory region for later use
>>
>>  Changes since v2:
>>
>>  - added a XSCOM memory region to handle access to the EX core
>>    registers   
>>  - extended the PnvCore object with a XSCOM_INTERFACE so that we can
>>    use pnv_xscom_pcba() and pnv_xscom_addr() to handle XSCOM address
>>    translation.
>>
>>  hw/ppc/pnv.c               |  4 ++++
>>  hw/ppc/pnv_core.c          | 50 ++++++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/ppc/pnv_core.h  |  2 ++
>>  include/hw/ppc/pnv_xscom.h | 19 ++++++++++++++++++
>>  4 files changed, 75 insertions(+)
>>
>> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
>> index 5e19b6880387..ffe245fe59d2 100644
>> --- a/hw/ppc/pnv.c
>> +++ b/hw/ppc/pnv.c
>> @@ -620,6 +620,10 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>                                   &error_fatal);
>>          object_unref(OBJECT(pnv_core));
>>          i++;
>> +
>> +        memory_region_add_subregion(&chip->xscom,
>> +                         PNV_XSCOM_EX_CORE_BASE(core_hwid) << 3,
>> +                         &PNV_CORE(pnv_core)->xscom_regs);
> 
> Might be worth adding some convenience functions for doing the various
> bits of xscom MR juggling, otherwise this looks fine.

To do the 8 byte shifting ? or something like this :
 
	void pnv_xscom_add_subregion(PnvChip *chip, uint32_t pcba,
			     	     MemoryRegion *subregion);

or even :

	void pnv_xscom_add_subregion(PnvChip *chip, PnvXScomInterface *obj)

but that would require some more handlers under  PnvXScomInterface.

Thanks,

C.


>>      }
>>      g_free(typename);
>>  }
>> diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
>> index d37788f142f4..a1c8a14f06b6 100644
>> --- a/hw/ppc/pnv_core.c
>> +++ b/hw/ppc/pnv_core.c
>> @@ -19,6 +19,7 @@
>>  #include "qemu/osdep.h"
>>  #include "sysemu/sysemu.h"
>>  #include "qapi/error.h"
>> +#include "qemu/log.h"
>>  #include "target-ppc/cpu.h"
>>  #include "hw/ppc/ppc.h"
>>  #include "hw/ppc/pnv.h"
>> @@ -64,6 +65,51 @@ static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
>>      powernv_cpu_reset(cpu);
>>  }
>>  
>> +/*
>> + * These values are read by the PowerNV HW monitors under Linux
>> + */
>> +#define PNV_XSCOM_EX_DTS_RESULT0     0x50000
>> +#define PNV_XSCOM_EX_DTS_RESULT1     0x50001
>> +
>> +static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr,
>> +                                    unsigned int width)
>> +{
>> +    uint32_t offset = addr >> 3;
>> +    uint64_t val = 0;
>> +
>> +    /* The result should be 38 C */
>> +    switch (offset) {
>> +    case PNV_XSCOM_EX_DTS_RESULT0:
>> +        val = 0x26f024f023f0000ull;
>> +        break;
>> +    case PNV_XSCOM_EX_DTS_RESULT1:
>> +        val = 0x24f000000000000ull;
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx,
>> +                  addr);
>> +    }
>> +
>> +    return val;
>> +}
>> +
>> +static void pnv_core_xscom_write(void *opaque, hwaddr addr, uint64_t val,
>> +                                 unsigned int width)
>> +{
>> +    qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx,
>> +                  addr);
>> +}
>> +
>> +static const MemoryRegionOps pnv_core_xscom_ops = {
>> +    .read = pnv_core_xscom_read,
>> +    .write = pnv_core_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_realize_child(Object *child, Error **errp)
>>  {
>>      Error *local_err = NULL;
>> @@ -119,6 +165,10 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
>>              goto err;
>>          }
>>      }
>> +
>> +    snprintf(name, sizeof(name), "xscom-core.%d", cc->core_id);
>> +    memory_region_init_io(&pc->xscom_regs, OBJECT(dev), &pnv_core_xscom_ops,
>> +                          pc, name,  PNV_XSCOM_EX_CORE_SIZE << 3);
>>      return;
>>  
>>  err:
>> diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h
>> index a151e281c017..2955a41c901f 100644
>> --- a/include/hw/ppc/pnv_core.h
>> +++ b/include/hw/ppc/pnv_core.h
>> @@ -36,6 +36,8 @@ typedef struct PnvCore {
>>      /*< public >*/
>>      void *threads;
>>      uint32_t pir;
>> +
>> +    MemoryRegion xscom_regs;
>>  } PnvCore;
>>  
>>  typedef struct PnvCoreClass {
>> diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
>> index f50eb0bc4099..79975a6cbe46 100644
>> --- a/include/hw/ppc/pnv_xscom.h
>> +++ b/include/hw/ppc/pnv_xscom.h
>> @@ -41,6 +41,25 @@ typedef struct PnvXScomInterfaceClass {
>>      int (*populate)(PnvXScomInterface *dev, void *fdt, int offset);
>>  } PnvXScomInterfaceClass;
>>  
>> +/*
>> + * Layout of the XSCOM PCB addresses of EX core 1
>> + *
>> + *   GPIO        0x1100xxxx
>> + *   SCOM        0x1101xxxx
>> + *   OHA         0x1102xxxx
>> + *   CLOCK CTL   0x1103xxxx
>> + *   FIR         0x1104xxxx
>> + *   THERM       0x1105xxxx
>> + *   <reserved>  0x1106xxxx
>> + *               ..
>> + *               0x110Exxxx
>> + *   PCB SLAVE   0x110Fxxxx
>> + */
>> +
>> +#define PNV_XSCOM_EX_BASE         0x10000000
>> +#define PNV_XSCOM_EX_CORE_BASE(i) (PNV_XSCOM_EX_BASE | (((uint64_t)i) << 24))
>> +#define PNV_XSCOM_EX_CORE_SIZE    0x100000
>> +
>>  extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
>>  extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
>>  
> 

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 07/20] ppc/pnv: add XSCOM handlers to PnvCore
  2016-10-13  6:50     ` Cédric Le Goater
@ 2016-10-13 22:24       ` David Gibson
  0 siblings, 0 replies; 75+ messages in thread
From: David Gibson @ 2016-10-13 22:24 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 7023 bytes --]

On Thu, Oct 13, 2016 at 08:50:41AM +0200, Cédric Le Goater wrote:
> On 10/13/2016 02:51 AM, David Gibson wrote:
> > On Mon, Oct 03, 2016 at 09:24:43AM +0200, Cédric Le Goater wrote:
> >> Now that we are using real HW ids for the cores in PowerNV chips, we
> >> can route the XSCOM accesses to them. We just need to attach a
> >> specific XSCOM memory region to each core in the appropriate window
> >> for the core number.
> >>
> >> To start with, let's install the DTS (Digital Thermal Sensor) handlers
> >> which should return 38°C for each core.
> >>
> >> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> >> ---
> >>
> >>  Changes since v3:
> >>
> >>  - moved to new XSCOM model
> >>  - kept the write op on the XSCOM memory region for later use
> >>
> >>  Changes since v2:
> >>
> >>  - added a XSCOM memory region to handle access to the EX core
> >>    registers   
> >>  - extended the PnvCore object with a XSCOM_INTERFACE so that we can
> >>    use pnv_xscom_pcba() and pnv_xscom_addr() to handle XSCOM address
> >>    translation.
> >>
> >>  hw/ppc/pnv.c               |  4 ++++
> >>  hw/ppc/pnv_core.c          | 50 ++++++++++++++++++++++++++++++++++++++++++++++
> >>  include/hw/ppc/pnv_core.h  |  2 ++
> >>  include/hw/ppc/pnv_xscom.h | 19 ++++++++++++++++++
> >>  4 files changed, 75 insertions(+)
> >>
> >> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> >> index 5e19b6880387..ffe245fe59d2 100644
> >> --- a/hw/ppc/pnv.c
> >> +++ b/hw/ppc/pnv.c
> >> @@ -620,6 +620,10 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
> >>                                   &error_fatal);
> >>          object_unref(OBJECT(pnv_core));
> >>          i++;
> >> +
> >> +        memory_region_add_subregion(&chip->xscom,
> >> +                         PNV_XSCOM_EX_CORE_BASE(core_hwid) << 3,
> >> +                         &PNV_CORE(pnv_core)->xscom_regs);
> > 
> > Might be worth adding some convenience functions for doing the various
> > bits of xscom MR juggling, otherwise this looks fine.
> 
> To do the 8 byte shifting ? or something like this :
>  
> 	void pnv_xscom_add_subregion(PnvChip *chip, uint32_t pcba,
> 			     	     MemoryRegion *subregion);

Yes, something like that, which incorporates the << 3.  And maybe
another one to construct the MR for use on the scom device side.

> or even :
> 
> 	void pnv_xscom_add_subregion(PnvChip *chip, PnvXScomInterface *obj)
> 
> but that would require some more handlers under  PnvXScomInterface.
> 
> Thanks,
> 
> C.
> 
> 
> >>      }
> >>      g_free(typename);
> >>  }
> >> diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
> >> index d37788f142f4..a1c8a14f06b6 100644
> >> --- a/hw/ppc/pnv_core.c
> >> +++ b/hw/ppc/pnv_core.c
> >> @@ -19,6 +19,7 @@
> >>  #include "qemu/osdep.h"
> >>  #include "sysemu/sysemu.h"
> >>  #include "qapi/error.h"
> >> +#include "qemu/log.h"
> >>  #include "target-ppc/cpu.h"
> >>  #include "hw/ppc/ppc.h"
> >>  #include "hw/ppc/pnv.h"
> >> @@ -64,6 +65,51 @@ static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
> >>      powernv_cpu_reset(cpu);
> >>  }
> >>  
> >> +/*
> >> + * These values are read by the PowerNV HW monitors under Linux
> >> + */
> >> +#define PNV_XSCOM_EX_DTS_RESULT0     0x50000
> >> +#define PNV_XSCOM_EX_DTS_RESULT1     0x50001
> >> +
> >> +static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr,
> >> +                                    unsigned int width)
> >> +{
> >> +    uint32_t offset = addr >> 3;
> >> +    uint64_t val = 0;
> >> +
> >> +    /* The result should be 38 C */
> >> +    switch (offset) {
> >> +    case PNV_XSCOM_EX_DTS_RESULT0:
> >> +        val = 0x26f024f023f0000ull;
> >> +        break;
> >> +    case PNV_XSCOM_EX_DTS_RESULT1:
> >> +        val = 0x24f000000000000ull;
> >> +        break;
> >> +    default:
> >> +        qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx,
> >> +                  addr);
> >> +    }
> >> +
> >> +    return val;
> >> +}
> >> +
> >> +static void pnv_core_xscom_write(void *opaque, hwaddr addr, uint64_t val,
> >> +                                 unsigned int width)
> >> +{
> >> +    qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx,
> >> +                  addr);
> >> +}
> >> +
> >> +static const MemoryRegionOps pnv_core_xscom_ops = {
> >> +    .read = pnv_core_xscom_read,
> >> +    .write = pnv_core_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_realize_child(Object *child, Error **errp)
> >>  {
> >>      Error *local_err = NULL;
> >> @@ -119,6 +165,10 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
> >>              goto err;
> >>          }
> >>      }
> >> +
> >> +    snprintf(name, sizeof(name), "xscom-core.%d", cc->core_id);
> >> +    memory_region_init_io(&pc->xscom_regs, OBJECT(dev), &pnv_core_xscom_ops,
> >> +                          pc, name,  PNV_XSCOM_EX_CORE_SIZE << 3);
> >>      return;
> >>  
> >>  err:
> >> diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h
> >> index a151e281c017..2955a41c901f 100644
> >> --- a/include/hw/ppc/pnv_core.h
> >> +++ b/include/hw/ppc/pnv_core.h
> >> @@ -36,6 +36,8 @@ typedef struct PnvCore {
> >>      /*< public >*/
> >>      void *threads;
> >>      uint32_t pir;
> >> +
> >> +    MemoryRegion xscom_regs;
> >>  } PnvCore;
> >>  
> >>  typedef struct PnvCoreClass {
> >> diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
> >> index f50eb0bc4099..79975a6cbe46 100644
> >> --- a/include/hw/ppc/pnv_xscom.h
> >> +++ b/include/hw/ppc/pnv_xscom.h
> >> @@ -41,6 +41,25 @@ typedef struct PnvXScomInterfaceClass {
> >>      int (*populate)(PnvXScomInterface *dev, void *fdt, int offset);
> >>  } PnvXScomInterfaceClass;
> >>  
> >> +/*
> >> + * Layout of the XSCOM PCB addresses of EX core 1
> >> + *
> >> + *   GPIO        0x1100xxxx
> >> + *   SCOM        0x1101xxxx
> >> + *   OHA         0x1102xxxx
> >> + *   CLOCK CTL   0x1103xxxx
> >> + *   FIR         0x1104xxxx
> >> + *   THERM       0x1105xxxx
> >> + *   <reserved>  0x1106xxxx
> >> + *               ..
> >> + *               0x110Exxxx
> >> + *   PCB SLAVE   0x110Fxxxx
> >> + */
> >> +
> >> +#define PNV_XSCOM_EX_BASE         0x10000000
> >> +#define PNV_XSCOM_EX_CORE_BASE(i) (PNV_XSCOM_EX_BASE | (((uint64_t)i) << 24))
> >> +#define PNV_XSCOM_EX_CORE_SIZE    0x100000
> >> +
> >>  extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
> >>  extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
> >>  
> > 
> 

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 12/20] ppc/xics: Add xics to the monitor "info pic" command
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 12/20] ppc/xics: Add xics to the monitor "info pic" command Cédric Le Goater
@ 2016-10-14  5:30   ` David Gibson
  2016-10-14  7:39     ` Cédric Le Goater
  0 siblings, 1 reply; 75+ messages in thread
From: David Gibson @ 2016-10-14  5:30 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 6246 bytes --]

On Mon, Oct 03, 2016 at 09:24:48AM +0200, Cédric Le Goater wrote:
> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> 
> Useful to debug interrupt problems.
> 
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> [clg: - updated for qemu-2.7
>       - added a test on ->irqs as it is not necessarily allocated
>         (PHB3_MSI)
>       - removed static variable g_xics and replace with a loop on all
>         children to find the xics objects. ]
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

So, this is fine in principle.  However info pic has recently been
reworked to better allow different PICs to add stuff in there.  Your
patches will need to be reworked on top of that.

See 61b97833 and surrounding commits.

> ---
>  hmp-commands-info.hx  |  2 ++
>  hw/intc/xics.c        | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/ppc/ppc.c          | 14 ++++++++++++++
>  include/hw/ppc/ppc.h  |  2 ++
>  include/hw/ppc/xics.h |  2 ++
>  monitor.c             |  4 ++++
>  6 files changed, 76 insertions(+)
> 
> diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
> index 19729e55aea2..ab11eaf54348 100644
> --- a/hmp-commands-info.hx
> +++ b/hmp-commands-info.hx
> @@ -203,6 +203,8 @@ ETEXI
>          .cmd        = sun4m_hmp_info_pic,
>  #elif defined(TARGET_LM32)
>          .cmd        = lm32_hmp_info_pic,
> +#elif defined(TARGET_PPC)
> +        .cmd        = ppc_hmp_info_pic,
>  #else
>          .cmd        = hmp_info_pic,
>  #endif
> diff --git a/hw/intc/xics.c b/hw/intc/xics.c
> index f40b00003a45..3bbbcc847791 100644
> --- a/hw/intc/xics.c
> +++ b/hw/intc/xics.c
> @@ -35,6 +35,7 @@
>  #include "hw/ppc/xics.h"
>  #include "qemu/error-report.h"
>  #include "qapi/visitor.h"
> +#include "monitor/monitor.h"
>  
>  int xics_get_cpu_index_by_dt_id(int cpu_dt_id)
>  {
> @@ -633,6 +634,57 @@ static int ics_simple_dispatch_post_load(void *opaque, int version_id)
>      return 0;
>  }
>  
> +static int xics_hmp_info_pic_child(Object *child, void *opaque)
> +{
> +    Monitor *mon = opaque;
> +
> +    if (object_dynamic_cast(child, TYPE_XICS_COMMON)) {
> +        XICSState *xics = XICS_COMMON(child);
> +        ICSState *ics;
> +        uint32_t i;
> +
> +        for (i = 0; i < xics->nr_servers; i++) {
> +            ICPState *icp = &xics->ss[i];
> +
> +            if (!icp->output) {
> +                continue;
> +            }
> +            monitor_printf(mon, "CPU %d XIRR=%08x (%p) PP=%02x MFRR=%02x\n",
> +                           i, icp->xirr, icp->xirr_owner,
> +                           icp->pending_priority, icp->mfrr);
> +        }
> +
> +        QLIST_FOREACH(ics, &xics->ics, list) {
> +            monitor_printf(mon, "ICS %4x..%4x %p\n",
> +                           ics->offset, ics->offset + ics->nr_irqs - 1, ics);
> +
> +            if (!ics->irqs) {
> +                continue;
> +            }
> +
> +            for (i = 0; i < ics->nr_irqs; i++) {
> +                ICSIRQState *irq = ics->irqs + i;
> +
> +                if (!(irq->flags & XICS_FLAGS_IRQ_MASK)) {
> +                    continue;
> +                }
> +                monitor_printf(mon, "  %4x %s %02x %02x\n",
> +                               ics->offset + i,
> +                               (irq->flags & XICS_FLAGS_IRQ_LSI) ?
> +                               "LSI" : "MSI",
> +                               irq->priority, irq->status);
> +            }
> +        }
> +    }
> +    return 0;
> +}
> +
> +void xics_hmp_info_pic(Monitor *mon, const QDict *qdict)
> +{
> +    object_child_foreach_recursive(OBJECT(qdev_get_machine()),
> +                                   xics_hmp_info_pic_child, mon);
> +}
> +
>  static const VMStateDescription vmstate_ics_simple_irq = {
>      .name = "ics/irq",
>      .version_id = 2,
> diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
> index 89458690097f..bc734281f509 100644
> --- a/hw/ppc/ppc.c
> +++ b/hw/ppc/ppc.c
> @@ -27,6 +27,7 @@
>  #include "hw/hw.h"
>  #include "hw/ppc/ppc.h"
>  #include "hw/ppc/ppc_e500.h"
> +#include "hw/i386/pc.h"
>  #include "qemu/timer.h"
>  #include "sysemu/sysemu.h"
>  #include "sysemu/cpus.h"
> @@ -39,6 +40,10 @@
>  #include "kvm_ppc.h"
>  #include "trace.h"
>  
> +#if defined(TARGET_PPC64)
> +#include "hw/ppc/xics.h"
> +#endif
> +
>  //#define PPC_DEBUG_IRQ
>  //#define PPC_DEBUG_TB
>  
> @@ -1376,3 +1381,12 @@ void ppc_cpu_parse_features(const char *cpu_model)
>      cc->parse_features(typename, model_pieces[1], &error_fatal);
>      g_strfreev(model_pieces);
>  }
> +
> +void ppc_hmp_info_pic(Monitor *mon, const QDict *qdict)
> +{
> +    /* Call in turn every PIC around. OpenPIC doesn't have one yet */
> +#ifdef TARGET_PPC64
> +    xics_hmp_info_pic(mon, qdict);
> +#endif
> +    hmp_info_pic(mon, qdict);
> +}
> diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h
> index 00c1fb1e720a..b36024a1213c 100644
> --- a/include/hw/ppc/ppc.h
> +++ b/include/hw/ppc/ppc.h
> @@ -3,6 +3,8 @@
>  
>  #include "target-ppc/cpu-qom.h"
>  
> +void ppc_hmp_info_pic(Monitor *mon, const QDict *qdict);
> +
>  void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level);
>  
>  /* PowerPC hardware exceptions management helpers */
> diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
> index 66ae55ded387..ca9f8da542e0 100644
> --- a/include/hw/ppc/xics.h
> +++ b/include/hw/ppc/xics.h
> @@ -205,4 +205,6 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi);
>  
>  ICSState *xics_find_source(XICSState *icp, int irq);
>  
> +void xics_hmp_info_pic(Monitor *mon, const QDict *qdict);
> +
>  #endif /* XICS_H */
> diff --git a/monitor.c b/monitor.c
> index 83c4edfce08e..70e17fa9f1fd 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -91,6 +91,10 @@
>  #include "hw/s390x/storage-keys.h"
>  #endif
>  
> +#if defined(TARGET_PPC)
> +#include "hw/ppc/ppc.h"
> +#endif
> +
>  /*
>   * Supported types:
>   *

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 10/20] ppc/xics: Make the ICSState a list
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 10/20] ppc/xics: Make the ICSState a list Cédric Le Goater
@ 2016-10-14  5:32   ` David Gibson
  2016-10-14  7:35     ` Cédric Le Goater
  0 siblings, 1 reply; 75+ messages in thread
From: David Gibson @ 2016-10-14  5:32 UTC (permalink / raw)
  To: Cédric Le Goater
  Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel, Nikunj A Dadhania

[-- Attachment #1: Type: text/plain, Size: 25084 bytes --]

On Mon, Oct 03, 2016 at 09:24:46AM +0200, Cédric Le Goater wrote:
> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> 
> Instead of an array of fixed sized blocks, use a list, as we will need
> to have sources with variable number of interrupts. SPAPR only uses
> a single entry. Native will create more. If performance becomes an
> issue we can add some hashed lookup but for now this will do fine.
> 
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> [ move the initialization of list to xics_common_initfn,
>   restore xirr_owner after migration and move restoring to
>   icp_post_load]
> Signed-off-by: Nikunj A Dadhania <nikunj@linux.vnet.ibm.com>
> [ clg: removed the icp_post_load() changes from nikunj patchset v3:
>        http://patchwork.ozlabs.org/patch/646008/ ]
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

I think this and 11/20 are good enough and sufficiently standalone
that I've merged them into ppc-for-2.8.

> ---
>  hw/intc/trace-events  |  5 +--
>  hw/intc/xics.c        | 83 ++++++++++++++++++++++++++++--------------------
>  hw/intc/xics_kvm.c    | 27 +++++++++++-----
>  hw/intc/xics_spapr.c  | 88 +++++++++++++++++++++++++++++++++------------------
>  hw/ppc/spapr_events.c |  2 +-
>  hw/ppc/spapr_pci.c    |  5 ++-
>  hw/ppc/spapr_vio.c    |  2 +-
>  include/hw/ppc/xics.h | 13 ++++----
>  8 files changed, 139 insertions(+), 86 deletions(-)
> 
> diff --git a/hw/intc/trace-events b/hw/intc/trace-events
> index f12192c082b5..aa342a86fa6c 100644
> --- a/hw/intc/trace-events
> +++ b/hw/intc/trace-events
> @@ -56,10 +56,11 @@ xics_set_irq_lsi(int srcno, int nr) "set_irq_lsi: srcno %d [irq %#x]"
>  xics_ics_write_xive(int nr, int srcno, int server, uint8_t priority) "ics_write_xive: irq %#x [src %d] server %#x prio %#x"
>  xics_ics_reject(int nr, int srcno) "reject irq %#x [src %d]"
>  xics_ics_eoi(int nr) "ics_eoi: irq %#x"
> -xics_alloc(int src, int irq) "source#%d, irq %d"
> -xics_alloc_block(int src, int first, int num, bool lsi, int align) "source#%d, first irq %d, %d irqs, lsi=%d, alignnum %d"
> +xics_alloc(int irq) "irq %d"
> +xics_alloc_block(int first, int num, bool lsi, int align) "first irq %d, %d irqs, lsi=%d, alignnum %d"
>  xics_ics_free(int src, int irq, int num) "Source#%d, first irq %d, %d irqs"
>  xics_ics_free_warn(int src, int irq) "Source#%d, irq %d is already free"
> +xics_icp_post_load(uint32_t server_no, uint32_t xirr, uint64_t addr, uint8_t pend) "server_no %d, xirr %#x, xirr_owner 0x%" PRIx64 ", pending %d"
>  
>  # hw/intc/s390_flic_kvm.c
>  flic_create_device(int err) "flic: create device failed %d"
> diff --git a/hw/intc/xics.c b/hw/intc/xics.c
> index 69162f0328fa..433af93254fb 100644
> --- a/hw/intc/xics.c
> +++ b/hw/intc/xics.c
> @@ -96,13 +96,16 @@ void xics_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
>  static void xics_common_reset(DeviceState *d)
>  {
>      XICSState *xics = XICS_COMMON(d);
> +    ICSState *ics;
>      int i;
>  
>      for (i = 0; i < xics->nr_servers; i++) {
>          device_reset(DEVICE(&xics->ss[i]));
>      }
>  
> -    device_reset(DEVICE(xics->ics));
> +    QLIST_FOREACH(ics, &xics->ics, list) {
> +        device_reset(DEVICE(ics));
> +    }
>  }
>  
>  static void xics_prop_get_nr_irqs(Object *obj, Visitor *v, const char *name,
> @@ -134,7 +137,6 @@ static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, const char *name,
>      }
>  
>      assert(info->set_nr_irqs);
> -    assert(xics->ics);
>      info->set_nr_irqs(xics, value, errp);
>  }
>  
> @@ -174,6 +176,9 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
>  
>  static void xics_common_initfn(Object *obj)
>  {
> +    XICSState *xics = XICS_COMMON(obj);
> +
> +    QLIST_INIT(&xics->ics);
>      object_property_add(obj, "nr_irqs", "int",
>                          xics_prop_get_nr_irqs, xics_prop_set_nr_irqs,
>                          NULL, NULL, NULL);
> @@ -212,33 +217,35 @@ static void ics_reject(ICSState *ics, int nr);
>  static void ics_resend(ICSState *ics);
>  static void ics_eoi(ICSState *ics, int nr);
>  
> -static void icp_check_ipi(XICSState *xics, int server)
> +static void icp_check_ipi(ICPState *ss)
>  {
> -    ICPState *ss = xics->ss + server;
> -
>      if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) {
>          return;
>      }
>  
> -    trace_xics_icp_check_ipi(server, ss->mfrr);
> +    trace_xics_icp_check_ipi(ss->cs->cpu_index, ss->mfrr);
>  
> -    if (XISR(ss)) {
> -        ics_reject(xics->ics, XISR(ss));
> +    if (XISR(ss) && ss->xirr_owner) {
> +        ics_reject(ss->xirr_owner, XISR(ss));
>      }
>  
>      ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI;
>      ss->pending_priority = ss->mfrr;
> +    ss->xirr_owner = NULL;
>      qemu_irq_raise(ss->output);
>  }
>  
>  static void icp_resend(XICSState *xics, int server)
>  {
>      ICPState *ss = xics->ss + server;
> +    ICSState *ics;
>  
>      if (ss->mfrr < CPPR(ss)) {
> -        icp_check_ipi(xics, server);
> +        icp_check_ipi(ss);
> +    }
> +    QLIST_FOREACH(ics, &xics->ics, list) {
> +        ics_resend(ics);
>      }
> -    ics_resend(xics->ics);
>  }
>  
>  void icp_set_cppr(XICSState *xics, int server, uint8_t cppr)
> @@ -256,7 +263,10 @@ void icp_set_cppr(XICSState *xics, int server, uint8_t cppr)
>              ss->xirr &= ~XISR_MASK; /* Clear XISR */
>              ss->pending_priority = 0xff;
>              qemu_irq_lower(ss->output);
> -            ics_reject(xics->ics, old_xisr);
> +            if (ss->xirr_owner) {
> +                ics_reject(ss->xirr_owner, old_xisr);
> +                ss->xirr_owner = NULL;
> +            }
>          }
>      } else {
>          if (!XISR(ss)) {
> @@ -271,7 +281,7 @@ void icp_set_mfrr(XICSState *xics, int server, uint8_t mfrr)
>  
>      ss->mfrr = mfrr;
>      if (mfrr < CPPR(ss)) {
> -        icp_check_ipi(xics, server);
> +        icp_check_ipi(ss);
>      }
>  }
>  
> @@ -282,6 +292,7 @@ uint32_t icp_accept(ICPState *ss)
>      qemu_irq_lower(ss->output);
>      ss->xirr = ss->pending_priority << 24;
>      ss->pending_priority = 0xff;
> +    ss->xirr_owner = NULL;
>  
>      trace_xics_icp_accept(xirr, ss->xirr);
>  
> @@ -299,30 +310,40 @@ uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr)
>  void icp_eoi(XICSState *xics, int server, uint32_t xirr)
>  {
>      ICPState *ss = xics->ss + server;
> +    ICSState *ics;
> +    uint32_t irq;
>  
>      /* Send EOI -> ICS */
>      ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK);
>      trace_xics_icp_eoi(server, xirr, ss->xirr);
> -    ics_eoi(xics->ics, xirr & XISR_MASK);
> +    irq = xirr & XISR_MASK;
> +    QLIST_FOREACH(ics, &xics->ics, list) {
> +        if (ics_valid_irq(ics, irq)) {
> +            ics_eoi(ics, irq);
> +        }
> +    }
>      if (!XISR(ss)) {
>          icp_resend(xics, server);
>      }
>  }
>  
> -static void icp_irq(XICSState *xics, int server, int nr, uint8_t priority)
> +static void icp_irq(ICSState *ics, int server, int nr, uint8_t priority)
>  {
> +    XICSState *xics = ics->xics;
>      ICPState *ss = xics->ss + server;
>  
>      trace_xics_icp_irq(server, nr, priority);
>  
>      if ((priority >= CPPR(ss))
>          || (XISR(ss) && (ss->pending_priority <= priority))) {
> -        ics_reject(xics->ics, nr);
> +        ics_reject(ics, nr);
>      } else {
> -        if (XISR(ss)) {
> -            ics_reject(xics->ics, XISR(ss));
> +        if (XISR(ss) && ss->xirr_owner) {
> +            ics_reject(ss->xirr_owner, XISR(ss));
> +            ss->xirr_owner = NULL;
>          }
>          ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK);
> +        ss->xirr_owner = ics;
>          ss->pending_priority = priority;
>          trace_xics_icp_raise(ss->xirr, ss->pending_priority);
>          qemu_irq_raise(ss->output);
> @@ -405,8 +426,7 @@ static void resend_msi(ICSState *ics, int srcno)
>      if (irq->status & XICS_STATUS_REJECTED) {
>          irq->status &= ~XICS_STATUS_REJECTED;
>          if (irq->priority != 0xff) {
> -            icp_irq(ics->xics, irq->server, srcno + ics->offset,
> -                    irq->priority);
> +            icp_irq(ics, irq->server, srcno + ics->offset, irq->priority);
>          }
>      }
>  }
> @@ -419,7 +439,7 @@ static void resend_lsi(ICSState *ics, int srcno)
>          && (irq->status & XICS_STATUS_ASSERTED)
>          && !(irq->status & XICS_STATUS_SENT)) {
>          irq->status |= XICS_STATUS_SENT;
> -        icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
> +        icp_irq(ics, irq->server, srcno + ics->offset, irq->priority);
>      }
>  }
>  
> @@ -434,7 +454,7 @@ static void set_irq_msi(ICSState *ics, int srcno, int val)
>              irq->status |= XICS_STATUS_MASKED_PENDING;
>              trace_xics_masked_pending();
>          } else  {
> -            icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
> +            icp_irq(ics, irq->server, srcno + ics->offset, irq->priority);
>          }
>      }
>  }
> @@ -473,7 +493,7 @@ static void write_xive_msi(ICSState *ics, int srcno)
>      }
>  
>      irq->status &= ~XICS_STATUS_MASKED_PENDING;
> -    icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
> +    icp_irq(ics, irq->server, srcno + ics->offset, irq->priority);
>  }
>  
>  static void write_xive_lsi(ICSState *ics, int srcno)
> @@ -662,28 +682,23 @@ static const TypeInfo ics_info = {
>  /*
>   * Exported functions
>   */
> -int xics_find_source(XICSState *xics, int irq)
> +ICSState *xics_find_source(XICSState *xics, int irq)
>  {
> -    int sources = 1;
> -    int src;
> +    ICSState *ics;
>  
> -    /* FIXME: implement multiple sources */
> -    for (src = 0; src < sources; ++src) {
> -        ICSState *ics = &xics->ics[src];
> +    QLIST_FOREACH(ics, &xics->ics, list) {
>          if (ics_valid_irq(ics, irq)) {
> -            return src;
> +            return ics;
>          }
>      }
> -
> -    return -1;
> +    return NULL;
>  }
>  
>  qemu_irq xics_get_qirq(XICSState *xics, int irq)
>  {
> -    int src = xics_find_source(xics, irq);
> +    ICSState *ics = xics_find_source(xics, irq);
>  
> -    if (src >= 0) {
> -        ICSState *ics = &xics->ics[src];
> +    if (ics) {
>          return ics->qirqs[irq - ics->offset];
>      }
>  
> diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c
> index c9caefcf2b0b..843905cfbd37 100644
> --- a/hw/intc/xics_kvm.c
> +++ b/hw/intc/xics_kvm.c
> @@ -361,7 +361,13 @@ static void xics_kvm_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
>  static void xics_kvm_set_nr_irqs(XICSState *xics, uint32_t nr_irqs,
>                                   Error **errp)
>  {
> -    xics->nr_irqs = xics->ics->nr_irqs = nr_irqs;
> +    ICSState *ics = QLIST_FIRST(&xics->ics);
> +
> +    /* This needs to be deprecated ... */
> +    xics->nr_irqs = nr_irqs;
> +    if (ics) {
> +        ics->nr_irqs = nr_irqs;
> +    }
>  }
>  
>  static void xics_kvm_set_nr_servers(XICSState *xics, uint32_t nr_servers,
> @@ -394,6 +400,7 @@ static void xics_kvm_realize(DeviceState *dev, Error **errp)
>  {
>      KVMXICSState *xicskvm = XICS_SPAPR_KVM(dev);
>      XICSState *xics = XICS_COMMON(dev);
> +    ICSState *ics;
>      int i, rc;
>      Error *error = NULL;
>      struct kvm_create_device xics_create_device = {
> @@ -445,10 +452,12 @@ static void xics_kvm_realize(DeviceState *dev, Error **errp)
>  
>      xicskvm->kernel_xics_fd = xics_create_device.fd;
>  
> -    object_property_set_bool(OBJECT(xics->ics), true, "realized", &error);
> -    if (error) {
> -        error_propagate(errp, error);
> -        goto fail;
> +    QLIST_FOREACH(ics, &xics->ics, list) {
> +        object_property_set_bool(OBJECT(ics), true, "realized", &error);
> +        if (error) {
> +            error_propagate(errp, error);
> +            goto fail;
> +        }
>      }
>  
>      assert(xics->nr_servers);
> @@ -477,10 +486,12 @@ fail:
>  static void xics_kvm_initfn(Object *obj)
>  {
>      XICSState *xics = XICS_COMMON(obj);
> +    ICSState *ics;
>  
> -    xics->ics = ICS(object_new(TYPE_KVM_ICS));
> -    object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
> -    xics->ics->xics = xics;
> +    ics = ICS(object_new(TYPE_KVM_ICS));
> +    object_property_add_child(obj, "ics", OBJECT(ics), NULL);
> +    ics->xics = xics;
> +    QLIST_INSERT_HEAD(&xics->ics, ics, list);
>  }
>  
>  static void xics_kvm_class_init(ObjectClass *oc, void *data)
> diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c
> index 618826dacf0b..0b0845d6280f 100644
> --- a/hw/intc/xics_spapr.c
> +++ b/hw/intc/xics_spapr.c
> @@ -113,13 +113,17 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
>                            uint32_t nargs, target_ulong args,
>                            uint32_t nret, target_ulong rets)
>  {
> -    ICSState *ics = spapr->xics->ics;
> +    ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
>      uint32_t nr, server, priority;
>  
>      if ((nargs != 3) || (nret != 1)) {
>          rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
>          return;
>      }
> +    if (!ics) {
> +        rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
> +        return;
> +    }
>  
>      nr = rtas_ld(args, 0);
>      server = xics_get_cpu_index_by_dt_id(rtas_ld(args, 1));
> @@ -141,13 +145,17 @@ static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
>                            uint32_t nargs, target_ulong args,
>                            uint32_t nret, target_ulong rets)
>  {
> -    ICSState *ics = spapr->xics->ics;
> +    ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
>      uint32_t nr;
>  
>      if ((nargs != 1) || (nret != 3)) {
>          rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
>          return;
>      }
> +    if (!ics) {
> +        rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
> +        return;
> +    }
>  
>      nr = rtas_ld(args, 0);
>  
> @@ -166,13 +174,17 @@ static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr,
>                           uint32_t nargs, target_ulong args,
>                           uint32_t nret, target_ulong rets)
>  {
> -    ICSState *ics = spapr->xics->ics;
> +    ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
>      uint32_t nr;
>  
>      if ((nargs != 1) || (nret != 1)) {
>          rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
>          return;
>      }
> +    if (!ics) {
> +        rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
> +        return;
> +    }
>  
>      nr = rtas_ld(args, 0);
>  
> @@ -192,13 +204,17 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr,
>                          uint32_t nargs, target_ulong args,
>                          uint32_t nret, target_ulong rets)
>  {
> -    ICSState *ics = spapr->xics->ics;
> +    ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
>      uint32_t nr;
>  
>      if ((nargs != 1) || (nret != 1)) {
>          rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
>          return;
>      }
> +    if (!ics) {
> +        rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
> +        return;
> +    }
>  
>      nr = rtas_ld(args, 0);
>  
> @@ -217,7 +233,13 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr,
>  static void xics_spapr_set_nr_irqs(XICSState *xics, uint32_t nr_irqs,
>                                     Error **errp)
>  {
> -    xics->nr_irqs = xics->ics->nr_irqs = nr_irqs;
> +    ICSState *ics = QLIST_FIRST(&xics->ics);
> +
> +    /* This needs to be deprecated ... */
> +    xics->nr_irqs = nr_irqs;
> +    if (ics) {
> +        ics->nr_irqs = nr_irqs;
> +    }
>  }
>  
>  static void xics_spapr_set_nr_servers(XICSState *xics, uint32_t nr_servers,
> @@ -240,6 +262,7 @@ static void xics_spapr_set_nr_servers(XICSState *xics, uint32_t nr_servers,
>  static void xics_spapr_realize(DeviceState *dev, Error **errp)
>  {
>      XICSState *xics = XICS_SPAPR(dev);
> +    ICSState *ics;
>      Error *error = NULL;
>      int i;
>  
> @@ -261,10 +284,12 @@ static void xics_spapr_realize(DeviceState *dev, Error **errp)
>      spapr_register_hypercall(H_EOI, h_eoi);
>      spapr_register_hypercall(H_IPOLL, h_ipoll);
>  
> -    object_property_set_bool(OBJECT(xics->ics), true, "realized", &error);
> -    if (error) {
> -        error_propagate(errp, error);
> -        return;
> +    QLIST_FOREACH(ics, &xics->ics, list) {
> +        object_property_set_bool(OBJECT(ics), true, "realized", &error);
> +        if (error) {
> +            error_propagate(errp, error);
> +            return;
> +        }
>      }
>  
>      for (i = 0; i < xics->nr_servers; i++) {
> @@ -280,10 +305,12 @@ static void xics_spapr_realize(DeviceState *dev, Error **errp)
>  static void xics_spapr_initfn(Object *obj)
>  {
>      XICSState *xics = XICS_SPAPR(obj);
> +    ICSState *ics;
>  
> -    xics->ics = ICS(object_new(TYPE_ICS));
> -    object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
> -    xics->ics->xics = xics;
> +    ics = ICS(object_new(TYPE_ICS));
> +    object_property_add_child(obj, "ics", OBJECT(ics), NULL);
> +    ics->xics = xics;
> +    QLIST_INSERT_HEAD(&xics->ics, ics, list);
>  }
>  
>  static void xics_spapr_class_init(ObjectClass *oc, void *data)
> @@ -329,14 +356,15 @@ static int ics_find_free_block(ICSState *ics, int num, int alignnum)
>      return -1;
>  }
>  
> -int xics_spapr_alloc(XICSState *xics, int src, int irq_hint, bool lsi,
> -                     Error **errp)
> +int xics_spapr_alloc(XICSState *xics, int irq_hint, bool lsi, Error **errp)
>  {
> -    ICSState *ics = &xics->ics[src];
> +    ICSState *ics = QLIST_FIRST(&xics->ics);
>      int irq;
>  
> +    if (!ics) {
> +        return -1;
> +    }
>      if (irq_hint) {
> -        assert(src == xics_find_source(xics, irq_hint));
>          if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) {
>              error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint);
>              return -1;
> @@ -352,7 +380,7 @@ int xics_spapr_alloc(XICSState *xics, int src, int irq_hint, bool lsi,
>      }
>  
>      ics_set_irq_type(ics, irq - ics->offset, lsi);
> -    trace_xics_alloc(src, irq);
> +    trace_xics_alloc(irq);
>  
>      return irq;
>  }
> @@ -361,13 +389,16 @@ int xics_spapr_alloc(XICSState *xics, int src, int irq_hint, bool lsi,
>   * Allocate block of consecutive IRQs, and return the number of the first IRQ in
>   * the block. If align==true, aligns the first IRQ number to num.
>   */
> -int xics_spapr_alloc_block(XICSState *xics, int src, int num, bool lsi,
> -                           bool align, Error **errp)
> +int xics_spapr_alloc_block(XICSState *xics, int num, bool lsi, bool align,
> +                           Error **errp)
>  {
> +    ICSState *ics = QLIST_FIRST(&xics->ics);
>      int i, first = -1;
> -    ICSState *ics = &xics->ics[src];
>  
> -    assert(src == 0);
> +    if (!ics) {
> +        return -1;
> +    }
> +
>      /*
>       * MSIMesage::data is used for storing VIRQ so
>       * it has to be aligned to num to support multiple
> @@ -394,7 +425,7 @@ int xics_spapr_alloc_block(XICSState *xics, int src, int num, bool lsi,
>      }
>      first += ics->offset;
>  
> -    trace_xics_alloc_block(src, first, num, lsi, align);
> +    trace_xics_alloc_block(first, num, lsi, align);
>  
>      return first;
>  }
> @@ -405,7 +436,7 @@ static void ics_free(ICSState *ics, int srcno, int num)
>  
>      for (i = srcno; i < srcno + num; ++i) {
>          if (ICS_IRQ_FREE(ics, i)) {
> -            trace_xics_ics_free_warn(ics - ics->xics->ics, i + ics->offset);
> +            trace_xics_ics_free_warn(0, i + ics->offset);
>          }
>          memset(&ics->irqs[i], 0, sizeof(ICSIRQState));
>      }
> @@ -413,15 +444,10 @@ static void ics_free(ICSState *ics, int srcno, int num)
>  
>  void xics_spapr_free(XICSState *xics, int irq, int num)
>  {
> -    int src = xics_find_source(xics, irq);
> -
> -    if (src >= 0) {
> -        ICSState *ics = &xics->ics[src];
> -
> -        /* FIXME: implement multiple sources */
> -        assert(src == 0);
> +    ICSState *ics = xics_find_source(xics, irq);
>  
> -        trace_xics_ics_free(ics - xics->ics, irq, num);
> +    if (ics) {
> +        trace_xics_ics_free(0, irq, num);
>          ics_free(ics, irq - ics->offset, num);
>      }
>  }
> diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c
> index 4c7b6aeab630..6d3534541c85 100644
> --- a/hw/ppc/spapr_events.c
> +++ b/hw/ppc/spapr_events.c
> @@ -594,7 +594,7 @@ out_no_events:
>  void spapr_events_init(sPAPRMachineState *spapr)
>  {
>      QTAILQ_INIT(&spapr->pending_events);
> -    spapr->check_exception_irq = xics_spapr_alloc(spapr->xics, 0, 0, false,
> +    spapr->check_exception_irq = xics_spapr_alloc(spapr->xics, 0, false,
>                                              &error_fatal);
>      spapr->epow_notifier.notify = spapr_powerdown_req;
>      qemu_register_powerdown_notifier(&spapr->epow_notifier);
> diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
> index 4f008654d673..a7ca98838799 100644
> --- a/hw/ppc/spapr_pci.c
> +++ b/hw/ppc/spapr_pci.c
> @@ -363,7 +363,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
>      }
>  
>      /* Allocate MSIs */
> -    irq = xics_spapr_alloc_block(spapr->xics, 0, req_num, false,
> +    irq = xics_spapr_alloc_block(spapr->xics, req_num, false,
>                             ret_intr_type == RTAS_TYPE_MSI, &err);
>      if (err) {
>          error_reportf_err(err, "Can't allocate MSIs for device %x: ",
> @@ -1445,8 +1445,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
>          uint32_t irq;
>          Error *local_err = NULL;
>  
> -        irq = xics_spapr_alloc_block(spapr->xics, 0, 1, true, false,
> -                                     &local_err);
> +        irq = xics_spapr_alloc_block(spapr->xics, 1, true, false, &local_err);
>          if (local_err) {
>              error_propagate(errp, local_err);
>              error_prepend(errp, "can't allocate LSIs: ");
> diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c
> index d68dd35679bd..3648aa596044 100644
> --- a/hw/ppc/spapr_vio.c
> +++ b/hw/ppc/spapr_vio.c
> @@ -453,7 +453,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
>          dev->qdev.id = id;
>      }
>  
> -    dev->irq = xics_spapr_alloc(spapr->xics, 0, dev->irq, false, &local_err);
> +    dev->irq = xics_spapr_alloc(spapr->xics, dev->irq, false, &local_err);
>      if (local_err) {
>          error_propagate(errp, local_err);
>          return;
> diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
> index 5aac67ad89b8..e49a2dab934a 100644
> --- a/include/hw/ppc/xics.h
> +++ b/include/hw/ppc/xics.h
> @@ -85,7 +85,7 @@ struct XICSState {
>      uint32_t nr_servers;
>      uint32_t nr_irqs;
>      ICPState *ss;
> -    ICSState *ics;
> +    QLIST_HEAD(, ICSState) ics;
>  };
>  
>  #define TYPE_ICP "icp"
> @@ -111,6 +111,7 @@ struct ICPState {
>      DeviceState parent_obj;
>      /*< public >*/
>      CPUState *cs;
> +    ICSState *xirr_owner;
>      uint32_t xirr;
>      uint8_t pending_priority;
>      uint8_t mfrr;
> @@ -145,6 +146,7 @@ struct ICSState {
>      qemu_irq *qirqs;
>      ICSIRQState *irqs;
>      XICSState *xics;
> +    QLIST_ENTRY(ICSState) list;
>  };
>  
>  static inline bool ics_valid_irq(ICSState *ics, uint32_t nr)
> @@ -172,10 +174,9 @@ struct ICSIRQState {
>  #define XICS_IRQS_SPAPR               1024
>  
>  qemu_irq xics_get_qirq(XICSState *icp, int irq);
> -int xics_spapr_alloc(XICSState *icp, int src, int irq_hint, bool lsi,
> -                     Error **errp);
> -int xics_spapr_alloc_block(XICSState *icp, int src, int num, bool lsi,
> -                           bool align, Error **errp);
> +int xics_spapr_alloc(XICSState *icp, int irq_hint, bool lsi, Error **errp);
> +int xics_spapr_alloc_block(XICSState *icp, int num, bool lsi, bool align,
> +                           Error **errp);
>  void xics_spapr_free(XICSState *icp, int irq, int num);
>  
>  void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu);
> @@ -195,6 +196,6 @@ void ics_write_xive(ICSState *ics, int nr, int server,
>  
>  void ics_set_irq_type(ICSState *ics, int srcno, bool lsi);
>  
> -int xics_find_source(XICSState *icp, int irq);
> +ICSState *xics_find_source(XICSState *icp, int irq);
>  
>  #endif /* XICS_H */

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 13/20] ppc/xics: introduce helpers to find an ICP from some (CPU) index
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 13/20] ppc/xics: introduce helpers to find an ICP from some (CPU) index Cédric Le Goater
@ 2016-10-14  5:34   ` David Gibson
  2016-10-14  7:44     ` Cédric Le Goater
  0 siblings, 1 reply; 75+ messages in thread
From: David Gibson @ 2016-10-14  5:34 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 8828 bytes --]

On Mon, Oct 03, 2016 at 09:24:49AM +0200, Cédric Le Goater wrote:
> Today, the Interrupt Presentation Controllers (ICP) are stored in an
> array under the base class XICSState. They are simply indexed using
> the CPU index field of CPUState. This made sense for the current
> derived classes, spapr and kvm, as the CPU index are contiguous.
> Nevertheless some problems have started to occur with CPU hotplug.
> 
> With the PowerNV platform CPUs, this is not true anymore. Real HW ids
> are being used and they are not contiguous. So we need a way to
> customize the lookups in the array. Below is a proposal for this
> purpose. A couple of helpers are introduced to hide the nature of the
> underlying ICP array and also a new XICSStateClass method 'find_icp'
> to let the derived classes customize the ICP lookups.
> 
> A followup patch would be to let the derived class decide on the ICP
> storage. They could use a hash table for instance. We would need to
> introduce a new class method 'get_icp' for that. Or simply, change the
> ICP array for a hash table and let the derived class decide on the key
> to use.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

Uuuh.. IIRC this series has now been reworked (as suggested) to keep
cpu_index contiguous and have a separate hardware id.  Doesn't that
mean this patch can go away (though the callers might need to use a
hwid->cpu_index helper).

> ---
>  hw/intc/xics.c        | 48 ++++++++++++++++++++++++++++++++++++------------
>  hw/intc/xics_kvm.c    |  7 ++-----
>  hw/intc/xics_spapr.c  | 12 ++++++------
>  include/hw/ppc/xics.h |  2 ++
>  4 files changed, 46 insertions(+), 23 deletions(-)
> 
> diff --git a/hw/intc/xics.c b/hw/intc/xics.c
> index 3bbbcc847791..876c472aaa69 100644
> --- a/hw/intc/xics.c
> +++ b/hw/intc/xics.c
> @@ -48,12 +48,32 @@ int xics_get_cpu_index_by_dt_id(int cpu_dt_id)
>      return -1;
>  }
>  
> +ICPState *xics_find_icp(XICSState *xics, int cpu_index)
> +{
> +    XICSStateClass *xsc = XICS_COMMON_GET_CLASS(xics);
> +    ICPState *icp = xsc->find_icp(xics, cpu_index);
> +
> +    assert(icp);
> +
> +    return icp;
> +}
> +
> +static ICPState *xics_get_icp(XICSState *xics, CPUState *cs)
> +{
> +    ICPState *ss;
> +
> +    assert(cs->cpu_index < xics->nr_servers);
> +
> +    ss = &xics->ss[cs->cpu_index];
> +    ss->cs = cs;
> +    return ss;
> +}
> +
>  void xics_cpu_destroy(XICSState *xics, PowerPCCPU *cpu)
>  {
>      CPUState *cs = CPU(cpu);
> -    ICPState *ss = &xics->ss[cs->cpu_index];
> +    ICPState *ss = xics_find_icp(xics, cs->cpu_index);
>  
> -    assert(cs->cpu_index < xics->nr_servers);
>      assert(cs == ss->cs);
>  
>      ss->output = NULL;
> @@ -64,13 +84,9 @@ void xics_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
>  {
>      CPUState *cs = CPU(cpu);
>      CPUPPCState *env = &cpu->env;
> -    ICPState *ss = &xics->ss[cs->cpu_index];
> +    ICPState *ss = xics_get_icp(xics, cs);
>      XICSStateClass *info = XICS_COMMON_GET_CLASS(xics);
>  
> -    assert(cs->cpu_index < xics->nr_servers);
> -
> -    ss->cs = cs;
> -
>      if (info->cpu_setup) {
>          info->cpu_setup(xics, cpu);
>      }
> @@ -94,6 +110,12 @@ void xics_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
>  /*
>   * XICS Common class - parent for emulated XICS and KVM-XICS
>   */
> +
> +static ICPState *xics_common_find_icp(XICSState *xics, int cpu_index)
> +{
> +    return &xics->ss[cpu_index];
> +}
> +
>  static void xics_common_reset(DeviceState *d)
>  {
>      XICSState *xics = XICS_COMMON(d);
> @@ -191,8 +213,10 @@ static void xics_common_initfn(Object *obj)
>  static void xics_common_class_init(ObjectClass *oc, void *data)
>  {
>      DeviceClass *dc = DEVICE_CLASS(oc);
> +    XICSStateClass *xsc = XICS_COMMON_CLASS(oc);
>  
>      dc->reset = xics_common_reset;
> +    xsc->find_icp = xics_common_find_icp;
>  }
>  
>  static const TypeInfo xics_common_info = {
> @@ -261,7 +285,7 @@ static void icp_check_ipi(ICPState *ss)
>  
>  static void icp_resend(XICSState *xics, int server)
>  {
> -    ICPState *ss = xics->ss + server;
> +    ICPState *ss = xics_find_icp(xics, server);
>      ICSState *ics;
>  
>      if (ss->mfrr < CPPR(ss)) {
> @@ -274,7 +298,7 @@ static void icp_resend(XICSState *xics, int server)
>  
>  void icp_set_cppr(XICSState *xics, int server, uint8_t cppr)
>  {
> -    ICPState *ss = xics->ss + server;
> +    ICPState *ss = xics_find_icp(xics, server);
>      uint8_t old_cppr;
>      uint32_t old_xisr;
>  
> @@ -301,7 +325,7 @@ void icp_set_cppr(XICSState *xics, int server, uint8_t cppr)
>  
>  void icp_set_mfrr(XICSState *xics, int server, uint8_t mfrr)
>  {
> -    ICPState *ss = xics->ss + server;
> +    ICPState *ss = xics_find_icp(xics, server);
>  
>      ss->mfrr = mfrr;
>      if (mfrr < CPPR(ss)) {
> @@ -333,7 +357,7 @@ uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr)
>  
>  void icp_eoi(XICSState *xics, int server, uint32_t xirr)
>  {
> -    ICPState *ss = xics->ss + server;
> +    ICPState *ss = xics_find_icp(xics, server);
>      ICSState *ics;
>      uint32_t irq;
>  
> @@ -354,7 +378,7 @@ void icp_eoi(XICSState *xics, int server, uint32_t xirr)
>  static void icp_irq(ICSState *ics, int server, int nr, uint8_t priority)
>  {
>      XICSState *xics = ics->xics;
> -    ICPState *ss = xics->ss + server;
> +    ICPState *ss = xics_find_icp(xics, server);
>  
>      trace_xics_icp_irq(server, nr, priority);
>  
> diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c
> index 9c2f198fd142..b666bb59fc24 100644
> --- a/hw/intc/xics_kvm.c
> +++ b/hw/intc/xics_kvm.c
> @@ -326,14 +326,11 @@ static const TypeInfo ics_kvm_info = {
>   */
>  static void xics_kvm_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
>  {
> -    CPUState *cs;
> -    ICPState *ss;
> +    CPUState *cs = CPU(cpu);
> +    ICPState *ss = xics_find_icp(xics, cs->cpu_index);
>      KVMXICSState *xicskvm = XICS_SPAPR_KVM(xics);
>      int ret;
>  
> -    cs = CPU(cpu);
> -    ss = &xics->ss[cs->cpu_index];
> -
>      assert(cs->cpu_index < xics->nr_servers);
>      if (xicskvm->kernel_xics_fd == -1) {
>          abort();
> diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c
> index e8d0623c2cb5..af29998b1255 100644
> --- a/hw/intc/xics_spapr.c
> +++ b/hw/intc/xics_spapr.c
> @@ -67,9 +67,9 @@ static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
>                             target_ulong opcode, target_ulong *args)
>  {
>      CPUState *cs = CPU(cpu);
> -    uint32_t xirr = icp_accept(spapr->xics->ss + cs->cpu_index);
> +    ICPState *ss = xics_find_icp(spapr->xics, cs->cpu_index);
>  
> -    args[0] = xirr;
> +    args[0] = icp_accept(ss);
>      return H_SUCCESS;
>  }
>  
> @@ -77,10 +77,9 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
>                               target_ulong opcode, target_ulong *args)
>  {
>      CPUState *cs = CPU(cpu);
> -    ICPState *ss = &spapr->xics->ss[cs->cpu_index];
> -    uint32_t xirr = icp_accept(ss);
> +    ICPState *ss = xics_find_icp(spapr->xics, cs->cpu_index);
>  
> -    args[0] = xirr;
> +    args[0] = icp_accept(ss);
>      args[1] = cpu_get_host_ticks();
>      return H_SUCCESS;
>  }
> @@ -99,8 +98,9 @@ static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr,
>                              target_ulong opcode, target_ulong *args)
>  {
>      CPUState *cs = CPU(cpu);
> +    ICPState *ss = xics_find_icp(spapr->xics, cs->cpu_index);
>      uint32_t mfrr;
> -    uint32_t xirr = icp_ipoll(spapr->xics->ss + cs->cpu_index, &mfrr);
> +    uint32_t xirr = icp_ipoll(ss, &mfrr);
>  
>      args[0] = xirr;
>      args[1] = mfrr;
> diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
> index ca9f8da542e0..52c426d409c9 100644
> --- a/include/hw/ppc/xics.h
> +++ b/include/hw/ppc/xics.h
> @@ -76,6 +76,7 @@ struct XICSStateClass {
>      void (*cpu_setup)(XICSState *icp, PowerPCCPU *cpu);
>      void (*set_nr_irqs)(XICSState *icp, uint32_t nr_irqs, Error **errp);
>      void (*set_nr_servers)(XICSState *icp, uint32_t nr_servers, Error **errp);
> +    ICPState *(*find_icp)(XICSState *xics, int cpu_index);
>  };
>  
>  struct XICSState {
> @@ -206,5 +207,6 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi);
>  ICSState *xics_find_source(XICSState *icp, int irq);
>  
>  void xics_hmp_info_pic(Monitor *mon, const QDict *qdict);
> +ICPState *xics_find_icp(XICSState *xics, int cpu_index);
>  
>  #endif /* XICS_H */

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 15/20] ppc/xics: Add "native" XICS subclass
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 15/20] ppc/xics: Add "native" XICS subclass Cédric Le Goater
@ 2016-10-14  6:10   ` David Gibson
  2016-10-14  9:40     ` Cédric Le Goater
  0 siblings, 1 reply; 75+ messages in thread
From: David Gibson @ 2016-10-14  6:10 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 17269 bytes --]

On Mon, Oct 03, 2016 at 09:24:51AM +0200, Cédric Le Goater wrote:
> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> 
> This provides access to the MMIO based Interrupt Presentation
> Controllers (ICP) as found on a POWER8 system.
> 
> A new XICSNative class is introduced to hold the MMIO region of the
> ICPs. It also makes use of a hash table to associate the ICPState of a
> CPU with a HW processor id, as this is the server number presented in
> the XIVEs.
> 
> The class routine 'find_icp' provide the way to do the lookups when
> needed in the XICS base class.
> 
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> [clg: - naming cleanups
>       - replaced the use of xics_get_cpu_index_by_dt_id() by xics_find_icp()
>       - added some qemu logging in case of error      
>       - introduced a xics_native_find_icp routine to map icp index to
>         cpu index      
>       - moved sysbus mapping to chip ]
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
> 
>  checkpatch complains on this one, but it seems to be a false positive :
>  
>  ERROR: spaces required around that '&' (ctx:WxV)
>  #314: FILE: hw/intc/xics_native.c:246:
>  +                        (gpointer) &xics->ss[cs->cpu_index]);
> 
>  default-configs/ppc64-softmmu.mak |   3 +-
>  hw/intc/Makefile.objs             |   1 +
>  hw/intc/xics_native.c             | 327 ++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h              |  19 +++
>  include/hw/ppc/xics.h             |  24 +++
>  5 files changed, 373 insertions(+), 1 deletion(-)
>  create mode 100644 hw/intc/xics_native.c
> 
> diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak
> index 67a9bcaa67fa..a22c93a48686 100644
> --- a/default-configs/ppc64-softmmu.mak
> +++ b/default-configs/ppc64-softmmu.mak
> @@ -48,8 +48,9 @@ CONFIG_PLATFORM_BUS=y
>  CONFIG_ETSEC=y
>  CONFIG_LIBDECNUMBER=y
>  # For pSeries
> -CONFIG_XICS=$(CONFIG_PSERIES)
> +CONFIG_XICS=$(or $(CONFIG_PSERIES),$(CONFIG_POWERNV))
>  CONFIG_XICS_SPAPR=$(CONFIG_PSERIES)
> +CONFIG_XICS_NATIVE=$(CONFIG_POWERNV)
>  CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM))
>  # For PReP
>  CONFIG_MC146818RTC=y
> diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
> index 05ec21b21e0e..7be5dfc8347b 100644
> --- a/hw/intc/Makefile.objs
> +++ b/hw/intc/Makefile.objs
> @@ -31,6 +31,7 @@ obj-$(CONFIG_RASPI) += bcm2835_ic.o bcm2836_control.o
>  obj-$(CONFIG_SH4) += sh_intc.o
>  obj-$(CONFIG_XICS) += xics.o
>  obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o
> +obj-$(CONFIG_XICS_NATIVE) += xics_native.o
>  obj-$(CONFIG_XICS_KVM) += xics_kvm.o
>  obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
>  obj-$(CONFIG_S390_FLIC) += s390_flic.o
> diff --git a/hw/intc/xics_native.c b/hw/intc/xics_native.c
> new file mode 100644
> index 000000000000..16413d807f65
> --- /dev/null
> +++ b/hw/intc/xics_native.c
> @@ -0,0 +1,327 @@
> +/*
> + * QEMU PowerPC PowerNV machine model
> + *
> + * Native version of ICS/ICP
> + *
> + * Copyright (c) 2016, IBM Corporation.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "qemu-common.h"
> +#include "cpu.h"
> +#include "hw/hw.h"
> +#include "qemu/log.h"
> +#include "qapi/error.h"
> +
> +#include "hw/ppc/fdt.h"
> +#include "hw/ppc/xics.h"
> +#include "hw/ppc/pnv.h"
> +
> +#include <libfdt.h>
> +
> +static void xics_native_reset(void *opaque)
> +{
> +    device_reset(DEVICE(opaque));
> +}
> +
> +static void xics_native_initfn(Object *obj)
> +{
> +    XICSState *xics = XICS_COMMON(obj);
> +
> +    QLIST_INIT(&xics->ics);

This shouldn't be necessary, because it's done in the base class
initfn (which is called before the subclass initfns).

> +    /*
> +     * Let's not forget to register a reset handler else the ICPs
> +     * won't be initialized with the correct values. Trouble ahead !
> +     */
> +    qemu_register_reset(xics_native_reset, xics);

And. this shouldn't be necessary.  If you set dc->reset to the right
thing in class_init (which the xics base class should already do) then
the device model will automatically reset the device, you shouldn't
need an extra reset handler.

> +}
> +
> +static uint64_t xics_native_read(void *opaque, hwaddr addr, unsigned width)
> +{
> +    XICSState *s = opaque;
> +    uint32_t cpu_id = (addr & (PNV_XICS_SIZE - 1)) >> 12;
> +    bool byte0 = (width == 1 && (addr & 0x3) == 0);
> +    uint64_t val = 0xffffffff;
> +    ICPState *ss;
> +
> +    ss = xics_find_icp(s, cpu_id);

IIUC, cpu_id is the hardware id here... so why aren't you using the
hash table you added to do this mapping?  (although see comments
elswhere, that I'm not sure that mapping belongs within xics).

Another option to avoid the lookup would be to register each icp page
as a separate MR, then you could set the opaque pointer directly to ss
instead of to the global xics.

> +    if (!ss) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP server %d\n", cpu_id);
> +        return val;
> +    }
> +
> +    switch (addr & 0xffc) {
> +    case 0: /* poll */
> +        val = icp_ipoll(ss, NULL);
> +        if (byte0) {
> +            val >>= 24;
> +        } else if (width != 4) {
> +            goto bad_access;
> +        }
> +        break;
> +    case 4: /* xirr */
> +        if (byte0) {
> +            val = icp_ipoll(ss, NULL) >> 24;
> +        } else if (width == 4) {
> +            val = icp_accept(ss);
> +        } else {
> +            goto bad_access;
> +        }
> +        break;
> +    case 12:
> +        if (byte0) {
> +            val = ss->mfrr;
> +        } else {
> +            goto bad_access;
> +        }
> +        break;
> +    case 16:
> +        if (width == 4) {
> +            val = ss->links[0];
> +        } else {
> +            goto bad_access;
> +        }
> +        break;
> +    case 20:
> +        if (width == 4) {
> +            val = ss->links[1];
> +        } else {
> +            goto bad_access;
> +        }
> +        break;
> +    case 24:
> +        if (width == 4) {
> +            val = ss->links[2];
> +        } else {
> +            goto bad_access;
> +        }
> +        break;
> +    default:
> +bad_access:
> +        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
> +                      HWADDR_PRIx"/%d\n", addr, width);
> +    }
> +
> +    return val;
> +}
> +
> +static void xics_native_write(void *opaque, hwaddr addr, uint64_t val,
> +                        unsigned width)
> +{
> +    XICSState *s = opaque;
> +    uint32_t cpu_id = (addr & (PNV_XICS_SIZE - 1)) >> 12;
> +    bool byte0 = (width == 1 && (addr & 0x3) == 0);
> +    ICPState *ss;
> +
> +    ss = xics_find_icp(s, cpu_id);
> +    if (!ss) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP server %d\n", cpu_id);
> +        return;
> +    }
> +
> +    switch (addr & 0xffc) {
> +    case 4: /* xirr */
> +        if (byte0) {
> +            icp_set_cppr(s, cpu_id, val);
> +        } else if (width == 4) {
> +            icp_eoi(s, cpu_id, val);
> +        } else {
> +            goto bad_access;
> +        }
> +        break;
> +    case 12:
> +        if (byte0) {
> +            icp_set_mfrr(s, cpu_id, val);
> +        } else {
> +            goto bad_access;
> +        }
> +        break;
> +    case 16:
> +        if (width == 4) {
> +            ss->links[0] = val;
> +        } else {
> +            goto bad_access;
> +        }
> +        break;
> +    case 20:
> +        if (width == 4) {
> +            ss->links[1] = val;
> +        } else {
> +            goto bad_access;
> +        }
> +        break;
> +    case 24:
> +        if (width == 4) {
> +            ss->links[2] = val;
> +        } else {
> +            goto bad_access;
> +        }
> +        break;
> +    default:
> +bad_access:
> +        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
> +                      HWADDR_PRIx"/%d\n", addr, width);
> +    }
> +}
> +
> +static const MemoryRegionOps xics_native_ops = {
> +    .read = xics_native_read,
> +    .write = xics_native_write,
> +    .valid.min_access_size = 1,
> +    .valid.max_access_size = 4,
> +    .impl.min_access_size = 1,
> +    .impl.max_access_size = 4,
> +    .endianness = DEVICE_BIG_ENDIAN,
> +};
> +
> +static void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers,
> +                                Error **errp)
> +{
> +    int i;
> +
> +    icp->nr_servers = nr_servers;
> +
> +    icp->ss = g_malloc0(icp->nr_servers * sizeof(ICPState));
> +    for (i = 0; i < icp->nr_servers; i++) {
> +        char name[32];
> +        object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP);
> +        snprintf(name, sizeof(name), "icp[%d]", i);
> +        object_property_add_child(OBJECT(icp), name, OBJECT(&icp->ss[i]),
> +                                  errp);

AFAICT this is identical to xics-spapr version and only difference to
xics-kvm is it uses TYPE_KVM_ICP.  We should look at fusing these -
maybe just having an icp typename in the xics class.

> +    }
> +}
> +
> +static void xics_native_realize(DeviceState *dev, Error **errp)
> +{
> +    XICSState *xics = XICS_COMMON(dev);
> +    XICSNative *xicsn = XICS_NATIVE(dev);
> +    Error *error = NULL;
> +    int i;
> +
> +    if (!xics->nr_servers) {
> +        error_setg(errp, "Number of servers needs to be greater than 0");
> +        return;
> +    }
> +
> +    for (i = 0; i < xics->nr_servers; i++) {
> +        object_property_set_bool(OBJECT(&xics->ss[i]), true, "realized",
> +                                 &error);
> +        if (error) {
> +            error_propagate(errp, error);
> +            return;
> +        }
> +    }
> +
> +    xicsn->pir_table = g_hash_table_new(g_direct_hash, g_direct_equal);

I'm not sure having this map inside xics makes sense.  Wouldn't it be
more widely useful to have a fast PIR -> cpu_index map at the machine
level?  That could also use the structure of the hwids to do a fast
lookup neatly (collapse core id with a small table, recombine with
chip and thread id).  A hash table seems a big hammer to throw at it.

> +    /* Register MMIO regions */
> +    memory_region_init_io(&xicsn->icp_mmio, OBJECT(dev), &xics_native_ops,
> +                          xicsn, "xics", PNV_XICS_SIZE);
> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xicsn->icp_mmio);
> +}
> +
> +static void xics_native_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
> +{
> +    CPUState *cs = CPU(cpu);
> +    CPUPPCState *env = &cpu->env;
> +    XICSNative *xicsn = XICS_NATIVE(xics);
> +
> +    assert(cs->cpu_index < xics->nr_servers);
> +    g_hash_table_insert(xicsn->pir_table, GINT_TO_POINTER(env->spr[SPR_PIR]),
> +                        (gpointer) &xics->ss[cs->cpu_index]);
> +}
> +
> +static ICPState *xics_native_find_icp(XICSState *xics, int pir)
> +{
> +    XICSNative *xicsn = XICS_NATIVE(xics);
> +    gpointer key, value;
> +    gboolean found = g_hash_table_lookup_extended(xicsn->pir_table,
> +                                GINT_TO_POINTER(pir), &key, &value);
> +
> +    assert(found);
> +
> +    return (ICPState *) value;
> +}
> +
> +static void xics_native_class_init(ObjectClass *oc, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(oc);
> +    XICSStateClass *xsc = XICS_NATIVE_CLASS(oc);
> +
> +    dc->realize = xics_native_realize;
> +    xsc->set_nr_servers = xics_set_nr_servers;
> +    xsc->cpu_setup = xics_native_cpu_setup;
> +    xsc->find_icp = xics_native_find_icp;
> +}
> +
> +static const TypeInfo xics_native_info = {
> +    .name          = TYPE_XICS_NATIVE,
> +    .parent        = TYPE_XICS_COMMON,
> +    .instance_size = sizeof(XICSNative),
> +    .class_size = sizeof(XICSStateClass),
> +    .class_init    = xics_native_class_init,
> +    .instance_init = xics_native_initfn,
> +};
> +
> +static void xics_native_register_types(void)
> +{
> +    type_register_static(&xics_native_info);
> +}
> +
> +type_init(xics_native_register_types)
> +
> +void xics_native_populate_icp(PnvChip *chip, void *fdt, int offset,
> +                              uint32_t pir, uint32_t count)
> +{
> +    uint64_t addr;
> +    char *name;
> +    const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp";
> +    uint32_t irange[2], i, rsize;
> +    uint64_t *reg;
> +
> +    /*
> +     * TODO: add multichip ICP BAR
> +     */
> +    addr = PNV_XICS_BASE | (pir << 12);
> +
> +    irange[0] = cpu_to_be32(pir);
> +    irange[1] = cpu_to_be32(count);
> +
> +    rsize = sizeof(uint64_t) * 2 * count;
> +    reg = g_malloc(rsize);
> +    for (i = 0; i < count; i++) {
> +        reg[i * 2] = cpu_to_be64(addr | ((pir + i) * 0x1000));
> +        reg[i * 2 + 1] = cpu_to_be64(0x1000);
> +    }
> +
> +    name = g_strdup_printf("interrupt-controller@%"PRIX64, addr);
> +    offset = fdt_add_subnode(fdt, offset, name);
> +    _FDT(offset);
> +    g_free(name);
> +
> +    _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat))));
> +    _FDT((fdt_setprop(fdt, offset, "reg", reg, rsize)));
> +    _FDT((fdt_setprop_string(fdt, offset, "device_type",
> +                              "PowerPC-External-Interrupt-Presentation")));
> +    _FDT((fdt_setprop(fdt, offset, "interrupt-controller", NULL, 0)));
> +    _FDT((fdt_setprop(fdt, offset, "ibm,interrupt-server-ranges",
> +                       irange, sizeof(irange))));
> +    _FDT((fdt_setprop_cell(fdt, offset, "#interrupt-cells", 1)));
> +    _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0)));
> +    g_free(reg);
> +}
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index 617c3fdd4f06..3f24b87d199b 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -125,4 +125,23 @@ typedef struct PnvMachineState {
>  #define PNV_XSCOM_BASE(chip)                                            \
>      (chip->xscom_base + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE)
>  
> +/*
> + * XSCOM 0x20109CA defines the ICP BAR:
> + *
> + * 0:29   : bits 14 to 43 of address to define 1 MB region.
> + * 30     : 1 to enable ICP to receive loads/stores against its BAR region
> + * 31:63  : Constant 0
> + *
> + * Usually defined as :
> + *
> + *      0xffffe00200000000 -> 0x0003ffff80000000
> + *      0xffffe00600000000 -> 0x0003ffff80100000
> + *      0xffffe02200000000 -> 0x0003ffff80800000
> + *      0xffffe02600000000 -> 0x0003ffff80900000
> + *
> + * TODO: make a macro using the chip hw id ?
> + */
> +#define PNV_XICS_BASE         0x0003ffff80000000ull
> +#define PNV_XICS_SIZE         0x0000000000100000ull
> +
>  #endif /* _PPC_PNV_H */
> diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
> index dea9b92d4726..77ce786f000e 100644
> --- a/include/hw/ppc/xics.h
> +++ b/include/hw/ppc/xics.h
> @@ -118,8 +118,27 @@ struct ICPState {
>      uint8_t mfrr;
>      qemu_irq output;
>      bool cap_irq_xics_enabled;
> +
> +    /*
> +     * for XICSNative (not used by Linux).
> +     */
> +    uint32_t links[3];
>  };
>  
> +#define TYPE_XICS_NATIVE "xics-native"
> +#define XICS_NATIVE(obj) OBJECT_CHECK(XICSNative, (obj), TYPE_XICS_NATIVE)
> +#define XICS_NATIVE_CLASS(klass) \
> +     OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS_NATIVE)
> +#define XICS_NATIVE_GET_CLASS(obj) \
> +     OBJECT_CLASS_CHECK(XICSStateClass, (obj), TYPE_XICS_NATIVE)
> +
> +typedef struct XICSNative {
> +    XICSState parent_obj;
> +
> +    GHashTable *pir_table;
> +    MemoryRegion icp_mmio;
> +} XICSNative;
> +
>  #define TYPE_ICS_BASE "ics-base"
>  #define ICS_BASE(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS_BASE)
>  
> @@ -210,4 +229,9 @@ void xics_hmp_info_pic(Monitor *mon, const QDict *qdict);
>  ICPState *xics_find_icp(XICSState *xics, int cpu_index);
>  void xics_insert_ics(XICSState *xics, ICSState *ics);
>  
> +typedef struct PnvChip PnvChip;
> +
> +void xics_native_populate_icp(PnvChip *chip, void *fdt, int offset,
> +                              uint32_t base, uint32_t count);
> +
>  #endif /* XICS_H */

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 16/20] ppc/pnv: add a XICS native to each PowerNV chip
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 16/20] ppc/pnv: add a XICS native to each PowerNV chip Cédric Le Goater
@ 2016-10-14  6:18   ` David Gibson
  2016-10-18 14:47     ` Cédric Le Goater
  0 siblings, 1 reply; 75+ messages in thread
From: David Gibson @ 2016-10-14  6:18 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 7368 bytes --]

On Mon, Oct 03, 2016 at 09:24:52AM +0200, Cédric Le Goater wrote:
> and also link the XICS object to each core as it is needed to do the
> CPU setup.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>  hw/ppc/pnv.c         | 18 ++++++++++++++++++
>  hw/ppc/pnv_core.c    | 25 +++++++++++++++++++++----
>  include/hw/ppc/pnv.h |  2 ++
>  3 files changed, 41 insertions(+), 4 deletions(-)
> 
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 4a71b18bf38b..6335ca11efe7 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -32,6 +32,7 @@
>  #include "exec/address-spaces.h"
>  #include "qemu/cutils.h"
>  
> +#include "hw/ppc/xics.h"
>  #include "hw/ppc/pnv_xscom.h"
>  
>  #include "hw/isa/isa.h"
> @@ -223,6 +224,7 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
>      char *typename = pnv_core_typename(pcc->cpu_model);
>      size_t typesize = object_type_get_instance_size(typename);
>      int i;
> +    int smt = 1; /* TCG does not support more for the moment */
>  
>      pnv_xscom_populate(chip, fdt, 0);
>  
> @@ -230,6 +232,9 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
>          PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
>  
>          powernv_create_core_node(chip, pnv_core, fdt);
> +
> +        /* Interrupt presentation controllers (ICP). One per core. */
> +        xics_native_populate_icp(chip, fdt, 0, pnv_core->pir, smt);
>      }
>  
>      /* Put all the memory in one node on chip 0 until we find a way to
> @@ -631,6 +636,9 @@ static void pnv_chip_init(Object *obj)
>  
>      object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
>      object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
> +
> +    object_initialize(&chip->xics, sizeof(chip->xics), TYPE_XICS_NATIVE);
> +    object_property_add_child(obj, "xics", OBJECT(&chip->xics), NULL);
>  }
>  
>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
> @@ -641,6 +649,7 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>      char *typename = pnv_core_typename(pcc->cpu_model);
>      size_t typesize = object_type_get_instance_size(typename);
>      int i, core_hwid;
> +    int smt = 1; /* TCG does not support more for the moment */
>  
>      if (!object_class_by_name(typename)) {
>          error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename);
> @@ -662,6 +671,13 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>          return;
>      }
>  
> +    /* Set up Interrupt Controller before we create the VCPUs */
> +    object_property_set_int(OBJECT(&chip->xics), smp_cpus * smt / smp_threads,
> +                            "nr_servers",  &error_fatal);

/ smp_threads doesn't look right (more actual threads means less
servers).  I think you just want smp_cpus * smp_threads.  Or actually
cores_per_chip * smp_threads.

> +    object_property_set_bool(OBJECT(&chip->xics), true, "realized",
> +                             &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&chip->xics), 0, PNV_XICS_BASE);
> +
>      chip->cores = g_malloc0(typesize * chip->nr_cores);
>  
>      for (i = 0, core_hwid = 0; (core_hwid < sizeof(chip->cores_mask) * 8)
> @@ -684,6 +700,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>          object_property_set_int(OBJECT(pnv_core),
>                                  pcc->core_pir(chip, core_hwid),
>                                  "pir", &error_fatal);
> +        object_property_add_const_link(OBJECT(pnv_core), "xics",
> +                                       OBJECT(&chip->xics), &error_fatal);
>          object_property_set_bool(OBJECT(pnv_core), true, "realized",
>                                   &error_fatal);
>          object_unref(OBJECT(pnv_core));
> diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
> index a1c8a14f06b6..fe18e3150f78 100644
> --- a/hw/ppc/pnv_core.c
> +++ b/hw/ppc/pnv_core.c
> @@ -24,6 +24,7 @@
>  #include "hw/ppc/ppc.h"
>  #include "hw/ppc/pnv.h"
>  #include "hw/ppc/pnv_core.h"
> +#include "hw/ppc/xics.h"
>  
>  static void powernv_cpu_reset(void *opaque)
>  {
> @@ -54,7 +55,7 @@ static void powernv_cpu_reset(void *opaque)
>      env->msr |= MSR_HVB; /* Hypervisor mode */
>  }
>  
> -static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
> +static void powernv_cpu_init(PowerPCCPU *cpu, XICSState *xics, Error **errp)
>  {
>      CPUPPCState *env = &cpu->env;
>  
> @@ -63,6 +64,12 @@ static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
>  
>      qemu_register_reset(powernv_cpu_reset, cpu);
>      powernv_cpu_reset(cpu);
> +
> +    /*
> +     * XICS native cpu_setup() expects SPR_PIR to be set. So it needs
> +     * to run after powernv_cpu_reset()
> +     */
> +    xics_cpu_setup(xics, cpu);
>  }
>  
>  /*
> @@ -110,7 +117,7 @@ static const MemoryRegionOps pnv_core_xscom_ops = {
>      .endianness = DEVICE_BIG_ENDIAN,
>  };
>  
> -static void pnv_core_realize_child(Object *child, Error **errp)
> +static void pnv_core_realize_child(Object *child, XICSState *xics, Error **errp)
>  {
>      Error *local_err = NULL;
>      CPUState *cs = CPU(child);
> @@ -122,7 +129,7 @@ static void pnv_core_realize_child(Object *child, Error **errp)
>          return;
>      }
>  
> -    powernv_cpu_init(cpu, &local_err);
> +    powernv_cpu_init(cpu, xics, &local_err);
>      if (local_err) {
>          error_propagate(errp, local_err);
>          return;
> @@ -140,6 +147,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
>      void *obj;
>      int i, j;
>      char name[32];
> +    XICSState *xics;
>  
>      pc->threads = g_malloc0(size * cc->nr_threads);
>      for (i = 0; i < cc->nr_threads; i++) {
> @@ -157,10 +165,19 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
>          object_unref(obj);
>      }
>  
> +    /* get XICS object from chip */
> +    obj = object_property_get_link(OBJECT(dev), "xics", &local_err);
> +    if (!obj) {
> +        error_setg(errp, "%s: required link 'xics' not found: %s",
> +                   __func__, error_get_pretty(local_err));
> +        return;
> +    }
> +    xics = XICS_COMMON(obj);
> +
>      for (j = 0; j < cc->nr_threads; j++) {
>          obj = pc->threads + j * size;
>  
> -        pnv_core_realize_child(obj, &local_err);
> +        pnv_core_realize_child(obj, xics, &local_err);
>          if (local_err) {
>              goto err;
>          }
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index 3f24b87d199b..73d26c55d993 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -23,6 +23,7 @@
>  #include "hw/sysbus.h"
>  #include "hw/ppc/pnv_xscom.h"
>  #include "hw/ppc/pnv_lpc.h"
> +#include "hw/ppc/xics.h"
>  
>  #define TYPE_PNV_CHIP "powernv-chip"
>  #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
> @@ -55,6 +56,7 @@ typedef struct PnvChip {
>      void      *cores;
>  
>      PnvLpcController lpc;
> +    XICSNative   xics;
>  } PnvChip;
>  
>  typedef struct PnvChipClass {

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 17/20] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 17/20] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt Cédric Le Goater
@ 2016-10-14  6:32   ` David Gibson
  2016-10-14  7:13     ` Benjamin Herrenschmidt
  2016-10-14  8:07     ` Cédric Le Goater
  0 siblings, 2 replies; 75+ messages in thread
From: David Gibson @ 2016-10-14  6:32 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 30930 bytes --]

On Mon, Oct 03, 2016 at 09:24:53AM +0200, Cédric Le Goater wrote:
> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> 
> The PSI (Processor Service Interface) is one of the engines of the
> "Bridge" unit which connects the different interfaces to the Power
> Processor.
> 
> This adds just enough of the PSI bridge to handle various on-chip and
> the one external interrupt. The rest of PSI has to do with the link to
> the IBM FSP service processor which we don't plan to emulate (not used
> on OpenPower machines).
> 
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> [clg: - updated for qemu-2.7
>       - changed the XSCOM interface to fit new model
>       - QOMified the model ]
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>  hw/ppc/Makefile.objs       |   2 +-
>  hw/ppc/pnv.c               |  38 ++-
>  hw/ppc/pnv_psi.c           | 598 +++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h       |   6 +
>  include/hw/ppc/pnv_psi.h   |  64 +++++
>  include/hw/ppc/pnv_xscom.h |   3 +
>  6 files changed, 702 insertions(+), 9 deletions(-)
>  create mode 100644 hw/ppc/pnv_psi.c
>  create mode 100644 include/hw/ppc/pnv_psi.h
> 
> diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
> index ebc72af0a7c6..4feb15b360c8 100644
> --- a/hw/ppc/Makefile.objs
> +++ b/hw/ppc/Makefile.objs
> @@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
>  obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
>  obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
>  # IBM PowerNV
> -obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o
> +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o
>  ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
>  obj-y += spapr_pci_vfio.o
>  endif
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 6335ca11efe7..b17e205c74db 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -318,15 +318,24 @@ static void ppc_powernv_reset(void)
>   * have a CPLD that will collect the SerIRQ and shoot them as a
>   * single level interrupt to the P8 chip. So let's setup a hook
>   * for doing just that.
> - *
> - * Note: The actual interrupt input isn't emulated yet, this will
> - * come with the PSI bridge model.
>   */
>  static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
>  {
> -    /* We don't yet emulate the PSI bridge which provides the external
> -     * interrupt, so just drop interrupts on the floor
> -     */
> +    static uint32_t irqstate;

Hmm.. static local with important state?  That it's not clear whether
it should be per-chip or not?

I'm not averse to hacks for early bringup, but it should at least have
a FIXME comment on it.

> +    uint32_t old_state = irqstate;
> +    PnvPsiController *psi = opaque;
> +
> +    if (n >= ISA_NUM_IRQS) {
> +        return;
> +    }
> +    if (level) {
> +        irqstate |= 1u << n;
> +    } else {
> +        irqstate &= ~(1u << n);
> +    }
> +    if (irqstate != old_state) {
> +        pnv_psi_irq_set(psi, PSIHB_IRQ_EXTERNAL, irqstate != 0);
> +    }
>  }
>  
>  static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
> @@ -355,8 +364,8 @@ static ISABus *pnv_isa_create(PnvChip *chip)
>      if (pcc->chip_type == PNV_CHIP_POWER8NVL) {
>          irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler, lpc, ISA_NUM_IRQS);
>      } else {
> -        irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, NULL,
> -                                  ISA_NUM_IRQS);
> +        irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, &chip->psi,
> +                                 ISA_NUM_IRQS);
>      }
>  
>      isa_bus_irqs(isa_bus, irqs);
> @@ -639,6 +648,9 @@ static void pnv_chip_init(Object *obj)
>  
>      object_initialize(&chip->xics, sizeof(chip->xics), TYPE_XICS_NATIVE);
>      object_property_add_child(obj, "xics", OBJECT(&chip->xics), NULL);
> +
> +    object_initialize(&chip->psi, sizeof(chip->psi), TYPE_PNV_PSI);
> +    object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL);
>  }
>  
>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
> @@ -713,6 +725,16 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>      }
>      g_free(typename);
>  
> +
> +    /* Create PSI */
> +    object_property_set_bool(OBJECT(&chip->psi), true, "realized",
> +                             &error_fatal);
> +    memory_region_add_subregion(&chip->xscom, PNV_XSCOM_PSI_BASE << 3,
> +                                &chip->psi.xscom_regs);
> +
> +    /* link in the PSI ICS */
> +    xics_insert_ics(XICS_COMMON(&chip->xics), &chip->psi.ics);
> +
>      /* Create LPC controller */
>      object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
>                               &error_fatal);
> diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c
> new file mode 100644
> index 000000000000..4f903b9985e9
> --- /dev/null
> +++ b/hw/ppc/pnv_psi.c
> @@ -0,0 +1,598 @@
> +/*
> + * QEMU PowerNV PowerPC PSI interface
> + *
> + * Copyright (c) 2016, IBM Corporation
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/hw.h"
> +#include "target-ppc/cpu.h"
> +#include "qemu/log.h"
> +#include "qapi/error.h"
> +
> +#include "exec/address-spaces.h"
> +
> +#include "hw/ppc/fdt.h"
> +#include "hw/ppc/pnv.h"
> +#include "hw/ppc/pnv_psi.h"
> +
> +#include <libfdt.h>
> +
> +#define PSIHB_XSCOM_FIR_RW      0x00
> +#define PSIHB_XSCOM_FIR_AND     0x01
> +#define PSIHB_XSCOM_FIR_OR      0x02
> +#define PSIHB_XSCOM_FIRMASK_RW  0x03
> +#define PSIHB_XSCOM_FIRMASK_AND 0x04
> +#define PSIHB_XSCOM_FIRMASK_OR  0x05
> +#define PSIHB_XSCOM_FIRACT0     0x06
> +#define PSIHB_XSCOM_FIRACT1     0x07
> +#define PSIHB_XSCOM_BAR         0x0a
> +#define   PSIHB_BAR_EN                  0x0000000000000001ull
> +#define PSIHB_XSCOM_FSPBAR      0x0b
> +#define PSIHB_XSCOM_CR          0x0e
> +#define   PSIHB_CR_FSP_CMD_ENABLE       0x8000000000000000ull
> +#define   PSIHB_CR_FSP_MMIO_ENABLE      0x4000000000000000ull
> +#define   PSIHB_CR_FSP_IRQ_ENABLE       0x1000000000000000ull
> +#define   PSIHB_CR_FSP_ERR_RSP_ENABLE   0x0800000000000000ull
> +#define   PSIHB_CR_PSI_LINK_ENABLE      0x0400000000000000ull
> +#define   PSIHB_CR_FSP_RESET            0x0200000000000000ull
> +#define   PSIHB_CR_PSIHB_RESET          0x0100000000000000ull
> +#define   PSIHB_CR_PSI_IRQ              0x0000800000000000ull
> +#define   PSIHB_CR_FSP_IRQ              0x0000400000000000ull
> +#define   PSIHB_CR_FSP_LINK_ACTIVE      0x0000200000000000ull
> +          /* and more ... */
> +#define PSIHB_XSCOM_SEMR        0x0f
> +#define PSIHB_XSCOM_XIVR_PSI    0x10
> +#define   PSIHB_XIVR_SERVER_SH  40
> +#define   PSIHB_XIVR_SERVER_MSK (0xffffull << PSIHB_XIVR_SERVER_SH)
> +#define   PSIHB_XIVR_PRIO_SH    32
> +#define   PSIHB_XIVR_PRIO_MSK   (0xffull << PSIHB_XIVR_PRIO_SH)
> +#define   PSIHB_XIVR_SRC_SH             29
> +#define   PSIHB_XIVR_SRC_MSK    (0x7ull << PSIHB_XIVR_SRC_SH)
> +#define   PSIHB_XIVR_PENDING    0x01000000ull
> +#define PSIHB_XSCOM_SCR         0x12
> +#define PSIHB_XSCOM_CCR         0x13
> +#define PSIHB_XSCOM_DMA_UPADD   0x14
> +#define PSIHB_XSCOM_IRQ_STAT    0x15
> +#define  PSIHB_IRQ_STAT_OCC             0x0000001000000000ull
> +#define  PSIHB_IRQ_STAT_FSI             0x0000000800000000ull
> +#define  PSIHB_IRQ_STAT_LPCI2C          0x0000000400000000ull
> +#define  PSIHB_IRQ_STAT_LOCERR          0x0000000200000000ull
> +#define  PSIHB_IRQ_STAT_EXT             0x0000000100000000ull
> +#define PSIHB_XSCOM_XIVR_OCC    0x16
> +#define PSIHB_XSCOM_XIVR_FSI    0x17
> +#define PSIHB_XSCOM_XIVR_LPCI2C 0x18
> +#define PSIHB_XSCOM_XIVR_LOCERR 0x19
> +#define PSIHB_XSCOM_XIVR_EXT    0x1a
> +#define PSIHB_XSCOM_IRSN        0x1b
> +#define   PSIHB_IRSN_COMP_SH            45
> +#define   PSIHB_IRSN_COMP_MSK           (0x7ffffull << PSIHB_IRSN_COMP_SH)
> +#define   PSIHB_IRSN_IRQ_MUX            0x0000000800000000ull
> +#define   PSIHB_IRSN_IRQ_RESET          0x0000000400000000ull
> +#define   PSIHB_IRSN_DOWNSTREAM_EN      0x0000000200000000ull
> +#define   PSIHB_IRSN_UPSTREAM_EN        0x0000000100000000ull
> +#define   PSIHB_IRSN_COMPMASK_SH        13
> +#define   PSIHB_IRSN_COMPMASK_MSK       (0x7ffffull << PSIHB_IRSN_COMPMASK_SH)
> +
> +#define PSIHB_MMIO_BAR          0x00
> +#define PSIHB_MMIO_FSPBAR       0x08
> +#define PSIHB_MMIO_CR           0x20
> +#define PSIHB_MMIO_SEMR         0x28
> +#define PSIHB_MMIO_XIVR_PSI     0x30
> +#define PSIHB_MMIO_SCR          0x40
> +#define PSIHB_MMIO_CCR          0x48
> +#define PSIHB_MMIO_DMA_UPADD    0x50
> +#define PSIHB_MMIO_IRQ_STAT     0x58
> +#define PSIHB_MMIO_XIVR_OCC     0x60
> +#define PSIHB_MMIO_XIVR_FSI     0x68
> +#define PSIHB_MMIO_XIVR_LPCI2C  0x70
> +#define PSIHB_MMIO_XIVR_LOCERR  0x78
> +#define PSIHB_MMIO_XIVR_EXT     0x80
> +#define PSIHB_MMIO_IRSN         0x88
> +#define PSIHB_MMIO_MAX          0x100
> +
> +static const uint32_t psi_mmio_to_xscom[PSIHB_MMIO_MAX / 8] = {
> +        [PSIHB_MMIO_BAR / 8]         = PSIHB_XSCOM_BAR,
> +        [PSIHB_MMIO_FSPBAR / 8]      = PSIHB_XSCOM_FSPBAR,
> +        [PSIHB_MMIO_CR / 8]          = PSIHB_XSCOM_CR,
> +        [PSIHB_MMIO_SCR / 8]         = PSIHB_XSCOM_SCR,
> +        [PSIHB_MMIO_CCR / 8]         = PSIHB_XSCOM_CCR,
> +        [PSIHB_MMIO_SEMR / 8]        = PSIHB_XSCOM_SEMR,
> +        [PSIHB_MMIO_XIVR_PSI / 8]    = PSIHB_XSCOM_XIVR_PSI,
> +        [PSIHB_MMIO_XIVR_OCC / 8]    = PSIHB_XSCOM_XIVR_OCC,
> +        [PSIHB_MMIO_XIVR_FSI / 8]    = PSIHB_XSCOM_XIVR_FSI,
> +        [PSIHB_MMIO_XIVR_LPCI2C / 8] = PSIHB_XSCOM_XIVR_LPCI2C,
> +        [PSIHB_MMIO_XIVR_LOCERR / 8] = PSIHB_XSCOM_XIVR_LOCERR,
> +        [PSIHB_MMIO_XIVR_EXT / 8]    = PSIHB_XSCOM_XIVR_EXT,
> +        [PSIHB_MMIO_IRQ_STAT / 8]    = PSIHB_XSCOM_IRQ_STAT,
> +        [PSIHB_MMIO_DMA_UPADD / 8]   = PSIHB_XSCOM_DMA_UPADD,
> +        [PSIHB_MMIO_IRSN / 8]        = PSIHB_XSCOM_IRSN,
> +};
> +
> +static void pnv_psi_set_bar(PnvPsiController *psi, uint64_t bar)
> +{
> +    MemoryRegion *sysmem = get_system_memory();
> +    uint64_t old = psi->regs[PSIHB_XSCOM_BAR];
> +
> +    psi->regs[PSIHB_XSCOM_BAR] = bar & 0x0003fffffff00001;
> +
> +    /* Update MR, always remove it first */
> +    if (old & PSIHB_BAR_EN) {
> +        memory_region_del_subregion(sysmem, &psi->regs_mr);
> +    }
> +    /* Then add it back if needed */
> +    if (bar & PSIHB_BAR_EN) {
> +        uint64_t addr = bar & 0x0003fffffff00000;
> +        memory_region_add_subregion(sysmem, addr, &psi->regs_mr);
> +    }
> +}
> +
> +static void pnv_psi_update_fsp_mr(PnvPsiController *psi)
> +{
> +    /* XXX Update FSP MR if/when we support FSP BAR */
> +}
> +
> +static void pnv_psi_set_cr(PnvPsiController *psi, uint64_t cr)
> +{
> +    uint64_t old = psi->regs[PSIHB_XSCOM_CR];
> +
> +    psi->regs[PSIHB_XSCOM_CR] = cr & 0x0003ffff00000000;
> +
> +    /* Check some bit changes */
> +    if ((old ^ psi->regs[PSIHB_XSCOM_CR]) & PSIHB_CR_FSP_MMIO_ENABLE) {
> +        pnv_psi_update_fsp_mr(psi);
> +    }
> +}
> +
> +static void pnv_psi_set_irsn(PnvPsiController *psi, uint64_t val)
> +{
> +    uint32_t offset;
> +    ICSState *ics = &psi->ics;
> +
> +    /* In this model we ignore the up/down enable bits for now
> +     * as SW doesn't use them (other than setting them at boot).
> +     * We ignore IRQ_MUX, its meaning isn't clear and we don't use
> +     * it and finally we ignore reset (XXX fix that ?)
> +     */
> +    psi->regs[PSIHB_XSCOM_IRSN] = val & (PSIHB_IRSN_COMP_MSK |
> +                                         PSIHB_IRSN_IRQ_MUX |
> +                                         PSIHB_IRSN_DOWNSTREAM_EN |
> +                                         PSIHB_IRSN_DOWNSTREAM_EN |
> +                                         PSIHB_IRSN_DOWNSTREAM_EN);
> +
> +    /* We ignore the compare mask as well, our ICS emulation is too
> +     * simplistic to make any use if it, and we extract the offset
> +     * from the compare value
> +     */
> +    offset = (val & PSIHB_IRSN_COMP_MSK) >> PSIHB_IRSN_COMP_SH;
> +    ics->offset = offset;
> +}
> +
> +static bool pnv_psi_irq_bits(PnvPsiController *psi, PnvPsiIrq irq,
> +                             uint32_t *out_xivr_reg,
> +                             uint32_t *out_stat_reg,
> +                             uint64_t *out_stat_bit)
> +{
> +    switch (irq) {
> +    case PSIHB_IRQ_PSI:
> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI;
> +        *out_stat_reg = PSIHB_XSCOM_CR;
> +        *out_stat_bit = PSIHB_CR_PSI_IRQ;
> +        break;
> +    case PSIHB_IRQ_FSP:
> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI;
> +        *out_stat_reg = PSIHB_XSCOM_CR;
> +        *out_stat_bit = PSIHB_CR_FSP_IRQ;
> +        break;
> +    case PSIHB_IRQ_OCC:
> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_OCC;
> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
> +        *out_stat_bit = PSIHB_IRQ_STAT_OCC;
> +        break;
> +    case PSIHB_IRQ_FSI:
> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_FSI;
> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
> +        *out_stat_bit = PSIHB_IRQ_STAT_FSI;
> +        break;
> +    case PSIHB_IRQ_LPC_I2C:
> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_LPCI2C;
> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
> +        *out_stat_bit = PSIHB_IRQ_STAT_LPCI2C;
> +        break;
> +    case PSIHB_IRQ_LOCAL_ERR:
> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_LOCERR;
> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
> +        *out_stat_bit = PSIHB_IRQ_STAT_LOCERR;
> +        break;
> +    case PSIHB_IRQ_EXTERNAL:
> +        *out_xivr_reg = PSIHB_XSCOM_XIVR_EXT;
> +        *out_stat_reg = PSIHB_XSCOM_IRQ_STAT;
> +        *out_stat_bit = PSIHB_IRQ_STAT_EXT;
> +        break;
> +    default:
> +        return false;
> +    }
> +    return true;
> +}
> +
> +void pnv_psi_irq_set(PnvPsiController *psi, PnvPsiIrq irq, bool state)
> +{
> +    ICSState *ics = &psi->ics;
> +    uint32_t xivr_reg;
> +    uint32_t stat_reg;
> +    uint64_t stat_bit;
> +    uint32_t src;
> +    bool masked;
> +
> +    if (!pnv_psi_irq_bits(psi, irq, &xivr_reg, &stat_reg, &stat_bit)) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", irq);
> +        return;
> +    }
> +
> +    src = (psi->regs[xivr_reg] & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH;
> +    masked = (psi->regs[xivr_reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK;
> +    if (state) {
> +        psi->regs[stat_reg] |= stat_bit;
> +        /* XXX optimization: check mask here. That means re-evaluating
> +         * when unmasking, thus TODO
> +         */
> +        qemu_irq_raise(ics->qirqs[src]);
> +    } else {
> +        psi->regs[stat_reg] &= ~stat_bit;
> +
> +        /* FSP and PSI are muxed so don't lower if either still set */
> +        if (stat_reg != PSIHB_XSCOM_CR ||
> +            !(psi->regs[stat_reg] & (PSIHB_CR_PSI_IRQ | PSIHB_CR_FSP_IRQ))) {
> +            qemu_irq_lower(ics->qirqs[src]);
> +        } else {
> +            state = true;
> +        }
> +    }
> +
> +    /* XXX Note about the emulation of the pending bit: This isn't
> +     * entirely correct. The pending bit should be cleared when the
> +     * EOI has been received. However, we don't have callbacks on
> +     * EOI (especially not under KVM) so no way to emulate that
> +     * properly, so instead we just set that bit as the logical
> +     * "output" of the XIVR (ie pending & !masked)
> +     * XXX TODO: Also update it on set_xivr
> +     */
> +    if (state && !masked) {
> +        psi->regs[xivr_reg] |= PSIHB_XIVR_PENDING;
> +    } else {
> +        psi->regs[xivr_reg] &= ~PSIHB_XIVR_PENDING;
> +    }
> +}
> +
> +static void pnv_psi_set_xivr(PnvPsiController *psi, uint32_t reg, uint64_t val)
> +{
> +    ICSState *ics = &psi->ics;
> +    uint16_t server;
> +    uint8_t prio;
> +    uint8_t src;
> +
> +    psi->regs[reg] = (psi->regs[reg] & PSIHB_XIVR_PENDING) |
> +            (val & (PSIHB_XIVR_SERVER_MSK |
> +                    PSIHB_XIVR_PRIO_MSK |
> +                    PSIHB_XIVR_SRC_MSK));
> +    val = psi->regs[reg];
> +    server = (val & PSIHB_XIVR_SERVER_MSK) >> PSIHB_XIVR_SERVER_SH;
> +    prio = (val & PSIHB_XIVR_PRIO_MSK) >> PSIHB_XIVR_PRIO_SH;
> +    src = (val & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH;
> +    if (src > PSIHB_IRQ_EXTERNAL) {
> +        /* XXX Generate error ? */
> +        return;
> +    }
> +
> +    /*
> +     * Linux fills the irq xivr with the hw processor id plus the link
> +     * bits. shift back to get something valid.
> +     */
> +    server >>= 2;
> +
> +    /* Now because of source remapping, weird things can happen
> +     * if you change the source number dynamically, our simple ICS
> +     * doesn't deal with remapping. So we just poke a different
> +     * ICS entry based on what source number was written. This will
> +     * do for now but a more accurate implementation would instead
> +     * use a fixed server/prio and a remapper of the generated irq.
> +     */
> +    ics_simple_write_xive(ics, src, server, prio, prio);
> +}
> +
> +static bool pnv_psi_reg_read(PnvPsiController *psi, uint32_t offset,
> +                             uint64_t *out_val, bool mmio)
> +{
> +    switch (offset) {
> +    case PSIHB_XSCOM_FIR_RW:
> +    case PSIHB_XSCOM_FIRACT0:
> +    case PSIHB_XSCOM_FIRACT1:
> +    case PSIHB_XSCOM_BAR:
> +    case PSIHB_XSCOM_FSPBAR:
> +    case PSIHB_XSCOM_CR:
> +    case PSIHB_XSCOM_XIVR_PSI:
> +    case PSIHB_XSCOM_XIVR_OCC:
> +    case PSIHB_XSCOM_XIVR_FSI:
> +    case PSIHB_XSCOM_XIVR_LPCI2C:
> +    case PSIHB_XSCOM_XIVR_LOCERR:
> +    case PSIHB_XSCOM_XIVR_EXT:
> +    case PSIHB_XSCOM_IRQ_STAT:
> +    case PSIHB_XSCOM_SEMR:
> +    case PSIHB_XSCOM_DMA_UPADD:
> +    case PSIHB_XSCOM_IRSN:
> +        *out_val = psi->regs[offset];
> +        return true;
> +    }
> +    return false;
> +}
> +
> +static bool pnv_psi_reg_write(PnvPsiController *psi, uint32_t offset,
> +                              uint64_t val, bool mmio)
> +{
> +    switch (offset) {
> +    case PSIHB_XSCOM_FIR_RW:
> +    case PSIHB_XSCOM_FIRACT0:
> +    case PSIHB_XSCOM_FIRACT1:
> +    case PSIHB_XSCOM_SEMR:
> +    case PSIHB_XSCOM_DMA_UPADD:
> +        psi->regs[offset] = val;
> +        return true;
> +    case PSIHB_XSCOM_FIR_OR:
> +        psi->regs[PSIHB_XSCOM_FIR_RW] |= val;
> +        return true;
> +    case PSIHB_XSCOM_FIR_AND:
> +        psi->regs[PSIHB_XSCOM_FIR_RW] &= val;
> +        return true;
> +    case PSIHB_XSCOM_BAR:
> +        /* Only XSCOM can write this one */
> +        if (!mmio) {
> +            pnv_psi_set_bar(psi, val);
> +        }
> +        return true;
> +    case PSIHB_XSCOM_FSPBAR:
> +        psi->regs[PSIHB_XSCOM_BAR] = val & 0x0003ffff00000000;
> +        pnv_psi_update_fsp_mr(psi);
> +        return true;
> +    case PSIHB_XSCOM_CR:
> +        pnv_psi_set_cr(psi, val);
> +        return true;
> +    case PSIHB_XSCOM_SCR:
> +        pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] | val);
> +        return true;
> +    case PSIHB_XSCOM_CCR:
> +        pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] & ~val);
> +        return true;
> +    case PSIHB_XSCOM_XIVR_PSI:
> +    case PSIHB_XSCOM_XIVR_OCC:
> +    case PSIHB_XSCOM_XIVR_FSI:
> +    case PSIHB_XSCOM_XIVR_LPCI2C:
> +    case PSIHB_XSCOM_XIVR_LOCERR:
> +    case PSIHB_XSCOM_XIVR_EXT:
> +        pnv_psi_set_xivr(psi, offset, val);
> +        return true;
> +    case PSIHB_XSCOM_IRQ_STAT:
> +        /* Read only, should we generate an error ? */
> +        return true;
> +    case PSIHB_XSCOM_IRSN:
> +        pnv_psi_set_irsn(psi, val);
> +        return true;
> +    }
> +    return false;
> +}
> +
> +static uint64_t pnv_psi_mmio_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    PnvPsiController *psi = opaque;
> +    uint32_t xscom_off;
> +    uint64_t val;
> +
> +    if (size != 8) {
> +        goto fail;
> +    }
> +
> +    addr &= (PNV_PSIHB_BAR_SIZE - 1);
> +
> +    if (addr >= PSIHB_MMIO_MAX) {
> +        goto fail;
> +    }
> +    xscom_off = psi_mmio_to_xscom[addr / 8];
> +    if (xscom_off == 0) {
> +        goto fail;
> +    }
> +    if (pnv_psi_reg_read(psi, xscom_off, &val, true)) {
> +        return val;
> +    }
> + fail:
> +    return 0xffffffffffffffffull;
> +}
> +
> +static void pnv_psi_mmio_write(void *opaque, hwaddr addr,
> +                              uint64_t val, unsigned size)
> +{
> +    PnvPsiController *psi = opaque;
> +    uint32_t xscom_off;
> +
> +    if (size != 8) {
> +        return;
> +    }
> +
> +    addr &= (PNV_PSIHB_BAR_SIZE - 1);
> +
> +    if (addr >= PSIHB_MMIO_MAX) {
> +        return;
> +    }
> +    xscom_off = psi_mmio_to_xscom[addr / 8];
> +    if (xscom_off == 0) {
> +        return;
> +    }
> +    pnv_psi_reg_write(psi, xscom_off, val, true);
> +}
> +
> +static const MemoryRegionOps psi_mmio_ops = {
> +    .read = pnv_psi_mmio_read,
> +    .write = pnv_psi_mmio_write,
> +    .endianness = DEVICE_BIG_ENDIAN,
> +    .valid = {
> +        .min_access_size = 8,
> +        .max_access_size = 8,
> +    },
> +    .impl = {
> +        .min_access_size = 8,
> +        .max_access_size = 8,
> +    },
> +};
> +
> +static uint64_t pnv_psi_xscom_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    PnvPsiController *psi = PNV_PSI(opaque);
> +    uint32_t offset = addr >> 3;
> +    uint64_t val = 0;
> +
> +    pnv_psi_reg_read(psi, offset, &val, false);
> +
> +    return val;
> +}
> +
> +static void pnv_psi_xscom_write(void *opaque, hwaddr addr,
> +                                uint64_t val, unsigned size)
> +{
> +    PnvPsiController *psi = PNV_PSI(opaque);
> +    uint32_t offset = addr >> 3;
> +
> +    pnv_psi_reg_write(psi, offset, val, false);
> +}
> +
> +static const MemoryRegionOps pnv_psi_xscom_ops = {
> +    .read = pnv_psi_xscom_read,
> +    .write = pnv_psi_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_psi_init(Object *obj)
> +{
> +    PnvPsiController *psi = PNV_PSI(obj);
> +
> +    object_initialize(&psi->ics, sizeof(psi->ics), TYPE_ICS_SIMPLE);
> +    object_property_add_child(obj, "ics-psi", OBJECT(&psi->ics), NULL);
> +}
> +
> +static void pnv_psi_realize(DeviceState *dev, Error **errp)
> +{
> +    PnvPsiController *psi = PNV_PSI(dev);
> +    ICSState *ics = &psi->ics;
> +    Error *error = NULL;
> +    unsigned int i;
> +
> +    /* Initialize MMIO region */
> +    memory_region_init_io(&psi->regs_mr, OBJECT(dev), &psi_mmio_ops, psi,
> +                          "psihb", PNV_PSIHB_BAR_SIZE);
> +
> +    /* Default BAR. Use object properties ? */
> +    pnv_psi_set_bar(psi, PNV_PSIHB_BAR | PSIHB_BAR_EN);
> +
> +    /* Default sources in XIVR */
> +    psi->regs[PSIHB_XSCOM_XIVR_PSI] = PSIHB_XIVR_PRIO_MSK |
> +            (0ull << PSIHB_XIVR_SRC_SH);
> +    psi->regs[PSIHB_XSCOM_XIVR_OCC] = PSIHB_XIVR_PRIO_MSK |
> +            (1ull << PSIHB_XIVR_SRC_SH);
> +    psi->regs[PSIHB_XSCOM_XIVR_FSI] = PSIHB_XIVR_PRIO_MSK |
> +            (2ull << PSIHB_XIVR_SRC_SH);
> +    psi->regs[PSIHB_XSCOM_XIVR_LPCI2C] = PSIHB_XIVR_PRIO_MSK |
> +            (3ull << PSIHB_XIVR_SRC_SH);
> +    psi->regs[PSIHB_XSCOM_XIVR_LOCERR] = PSIHB_XIVR_PRIO_MSK |
> +            (4ull << PSIHB_XIVR_SRC_SH);
> +    psi->regs[PSIHB_XSCOM_XIVR_EXT] = PSIHB_XIVR_PRIO_MSK |
> +            (5ull << PSIHB_XIVR_SRC_SH);
> +
> +    /*
> +     * PSI interrupt control source
> +     * FIXME: nr_irqs should a property
> +     */
> +    ics->nr_irqs = PSI_NUM_INTERRUPTS;
> +    object_property_set_bool(OBJECT(ics), true, "realized", &error);
> +    if (error) {
> +        error_propagate(errp, error);
> +        return;
> +    }
> +
> +    for (i = 0; i < ics->nr_irqs; i++) {
> +        ics_set_irq_type(ics, i, true);
> +    }
> +
> +    /* XScom region for PSI registers */
> +    memory_region_init_io(&psi->xscom_regs, OBJECT(dev), &pnv_psi_xscom_ops,
> +                psi, "xscom-psi", PNV_XSCOM_PSI_SIZE << 3);
> +}
> +
> +static int pnv_psi_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset)
> +{
> +    const char compat[] = "ibm,power8-psihb-x\0ibm,psihb-x";
> +    char *name;
> +    int offset;
> +    uint32_t lpc_pcba = PNV_XSCOM_PSI_BASE;
> +    uint32_t reg[] = {
> +        cpu_to_be32(lpc_pcba),
> +        cpu_to_be32(PNV_XSCOM_PSI_SIZE)
> +    };
> +
> +    name = g_strdup_printf("psihb@%x", lpc_pcba);
> +    offset = fdt_add_subnode(fdt, xscom_offset, name);
> +    _FDT(offset);
> +    g_free(name);
> +
> +    _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
> +
> +    _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2)));
> +    _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1)));
> +    _FDT((fdt_setprop(fdt, offset, "compatible", compat,
> +                      sizeof(compat))));
> +    return 0;
> +}
> +
> +
> +static void pnv_psi_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
> +
> +    xdc->populate = pnv_psi_populate;
> +
> +    dc->realize = pnv_psi_realize;
> +}
> +
> +static const TypeInfo pnv_psi_info = {
> +    .name          = TYPE_PNV_PSI,
> +    .parent        = TYPE_DEVICE,
> +    .instance_size = sizeof(PnvPsiController),
> +    .instance_init = pnv_psi_init,
> +    .class_init    = pnv_psi_class_init,
> +    .interfaces    = (InterfaceInfo[]) {
> +        { TYPE_PNV_XSCOM_INTERFACE },
> +        { }
> +    }
> +};
> +
> +static void pnv_psi_register_types(void)
> +{
> +    type_register_static(&pnv_psi_info);
> +}
> +
> +type_init(pnv_psi_register_types)
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index 73d26c55d993..473fd2318d87 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -24,6 +24,7 @@
>  #include "hw/ppc/pnv_xscom.h"
>  #include "hw/ppc/pnv_lpc.h"
>  #include "hw/ppc/xics.h"
> +#include "hw/ppc/pnv_psi.h"
>  
>  #define TYPE_PNV_CHIP "powernv-chip"
>  #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
> @@ -57,6 +58,7 @@ typedef struct PnvChip {
>  
>      PnvLpcController lpc;
>      XICSNative   xics;
> +    PnvPsiController psi;
>  } PnvChip;
>  
>  typedef struct PnvChipClass {
> @@ -146,4 +148,8 @@ typedef struct PnvMachineState {
>  #define PNV_XICS_BASE         0x0003ffff80000000ull
>  #define PNV_XICS_SIZE         0x0000000000100000ull
>  
> +#define PNV_PSIHB_BAR         0x0003fffe80000000ull
> +#define PNV_PSIHB_BAR_SIZE    0x0000000000100000ull
> +
> +
>  #endif /* _PPC_PNV_H */
> diff --git a/include/hw/ppc/pnv_psi.h b/include/hw/ppc/pnv_psi.h
> new file mode 100644
> index 000000000000..30bb9bc71839
> --- /dev/null
> +++ b/include/hw/ppc/pnv_psi.h
> @@ -0,0 +1,64 @@
> +/*
> + * QEMU PowerPC PowerNV PSI controller
> + *
> + * Copyright (c) 2016, IBM Corporation.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + */
> +#ifndef _PPC_PNV_PSI_H
> +#define _PPC_PNV_PSI_H
> +
> +#include "hw/ppc/pnv_xscom.h"
> +#include "hw/ppc/pnv_psi.h"
> +
> +#define TYPE_PNV_PSI "pnv-psi"
> +#define PNV_PSI(obj) \
> +     OBJECT_CHECK(PnvPsiController, (obj), TYPE_PNV_PSI)
> +
> +#define PSIHB_XSCOM_MAX         0x20
> +
> +typedef struct XICSState XICSState;
> +
> +typedef struct PnvPsiController {
> +    DeviceState parent;
> +
> +    MemoryRegion regs_mr;
> +
> +    /* FSP region not supported */
> +    /* MemoryRegion fsp_mr; */
> +
> +    /* Interrupt generation */
> +    ICSState ics;
> +
> +    /* Registers */
> +    uint64_t regs[PSIHB_XSCOM_MAX];
> +
> +    MemoryRegion xscom_regs;
> +} PnvPsiController;
> +
> +typedef enum PnvPsiIrq {
> +    PSIHB_IRQ_PSI, /* internal use only */
> +    PSIHB_IRQ_FSP, /* internal use only */
> +    PSIHB_IRQ_OCC,
> +    PSIHB_IRQ_FSI,
> +    PSIHB_IRQ_LPC_I2C,
> +    PSIHB_IRQ_LOCAL_ERR,
> +    PSIHB_IRQ_EXTERNAL,
> +} PnvPsiIrq;
> +
> +#define PSI_NUM_INTERRUPTS 6
> +
> +extern void pnv_psi_irq_set(PnvPsiController *psi, PnvPsiIrq irq, bool state);
> +
> +#endif /* _PPC_PNV_PSI_H */
> diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
> index 3b8a8b4fc6ff..28fbec8ee8e0 100644
> --- a/include/hw/ppc/pnv_xscom.h
> +++ b/include/hw/ppc/pnv_xscom.h
> @@ -63,6 +63,9 @@ typedef struct PnvXScomInterfaceClass {
>  #define PNV_XSCOM_LPC_BASE        0xb0020
>  #define PNV_XSCOM_LPC_SIZE        0x4
>  
> +#define PNV_XSCOM_PSI_BASE        0x2010900
> +#define PNV_XSCOM_PSI_SIZE        0x20
> +
>  extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
>  extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
>  

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 18/20] ppc/pnv: Add OCC model stub with interrupt support
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 18/20] ppc/pnv: Add OCC model stub with interrupt support Cédric Le Goater
@ 2016-10-14  6:34   ` David Gibson
  0 siblings, 0 replies; 75+ messages in thread
From: David Gibson @ 2016-10-14  6:34 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 10737 bytes --]

On Mon, Oct 03, 2016 at 09:24:54AM +0200, Cédric Le Goater wrote:
> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> 
> The OCC is an on-chip microcontroller based on a ppc405 core used
> for various power management tasks. It comes with a pile of additional
> hardware sitting on the PIB (aka XSCOM bus). At this point we don't
> emulate it (nor plan to do so). However there is one facility which
> is provided by the surrounding hardware that we do need, which is the
> interrupt generation facility. OPAL uses it to send itself interrupts
> under some circumstances and there are other uses around the corner.
> 
> So this implement just enough to support this.
> 
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> [clg: - updated for qemu-2.7
>       - changed the XSCOM interface to fit new model
>       - QOMified the model ]
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

Reviewed-by: David Gibson <david@gibson.dropbear.id.au>

> ---
>  hw/ppc/Makefile.objs       |   2 +-
>  hw/ppc/pnv.c               |  11 ++++
>  hw/ppc/pnv_occ.c           | 135 +++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h       |   2 +
>  include/hw/ppc/pnv_occ.h   |  38 +++++++++++++
>  include/hw/ppc/pnv_xscom.h |   3 +
>  6 files changed, 190 insertions(+), 1 deletion(-)
>  create mode 100644 hw/ppc/pnv_occ.c
>  create mode 100644 include/hw/ppc/pnv_occ.h
> 
> diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
> index 4feb15b360c8..35b11cf887d5 100644
> --- a/hw/ppc/Makefile.objs
> +++ b/hw/ppc/Makefile.objs
> @@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
>  obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
>  obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
>  # IBM PowerNV
> -obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o
> +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o
>  ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
>  obj-y += spapr_pci_vfio.o
>  endif
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index b17e205c74db..e805e97d4d87 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -651,6 +651,11 @@ static void pnv_chip_init(Object *obj)
>  
>      object_initialize(&chip->psi, sizeof(chip->psi), TYPE_PNV_PSI);
>      object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL);
> +
> +    object_initialize(&chip->occ, sizeof(chip->occ), TYPE_PNV_OCC);
> +    object_property_add_child(obj, "occ", OBJECT(&chip->occ), NULL);
> +    object_property_add_const_link(OBJECT(&chip->occ), "psi",
> +                                   OBJECT(&chip->psi), &error_abort);
>  }
>  
>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
> @@ -740,6 +745,12 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>                               &error_fatal);
>      memory_region_add_subregion(&chip->xscom, PNV_XSCOM_LPC_BASE << 3,
>                                  &chip->lpc.xscom_regs);
> +
> +    /* Create the simplified OCC model */
> +    object_property_set_bool(OBJECT(&chip->occ), true, "realized",
> +                             &error_fatal);
> +    memory_region_add_subregion(&chip->xscom, PNV_XSCOM_OCC_BASE << 3,
> +                                &chip->occ.xscom_regs);
>  }
>  
>  static Property pnv_chip_properties[] = {
> diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c
> new file mode 100644
> index 000000000000..250517cca0ef
> --- /dev/null
> +++ b/hw/ppc/pnv_occ.c
> @@ -0,0 +1,135 @@
> +/*
> + * QEMU PowerNV Emulation of a few OCC related registers
> + *
> + * Copyright (c) 2016, IBM Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License, version 2, as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/hw.h"
> +#include "sysemu/sysemu.h"
> +#include "target-ppc/cpu.h"
> +#include "qapi/error.h"
> +#include "qemu/log.h"
> +
> +#include "hw/ppc/pnv.h"
> +#include "hw/ppc/pnv_occ.h"
> +
> +#define OCB_OCI_OCCMISC         0x4020
> +#define OCB_OCI_OCCMISC_AND     0x4021
> +#define OCB_OCI_OCCMISC_OR      0x4022
> +
> +static void pnv_occ_set_misc(PnvOCC *occ, uint64_t val)
> +{
> +    bool irq_state;
> +
> +    val &= 0xffff000000000000ull;
> +
> +    occ->occmisc = val;
> +    irq_state = !!(val >> 63);
> +    pnv_psi_irq_set(occ->psi, PSIHB_IRQ_OCC, irq_state);
> +}
> +
> +static uint64_t pnv_occ_xscom_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    PnvOCC *occ = PNV_OCC(opaque);
> +    uint32_t offset = addr >> 3;
> +    uint64_t val = 0;
> +
> +    switch (offset) {
> +    case OCB_OCI_OCCMISC:
> +        val = occ->occmisc;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%"
> +                      HWADDR_PRIx "\n", addr);
> +    }
> +    return val;
> +}
> +
> +static void pnv_occ_xscom_write(void *opaque, hwaddr addr,
> +                                uint64_t val, unsigned size)
> +{
> +    PnvOCC *occ = PNV_OCC(opaque);
> +    uint32_t offset = addr >> 3;
> +
> +    switch (offset) {
> +    case OCB_OCI_OCCMISC_AND:
> +        pnv_occ_set_misc(occ, occ->occmisc & val);
> +        break;
> +    case OCB_OCI_OCCMISC_OR:
> +        pnv_occ_set_misc(occ, occ->occmisc | val);
> +        break;
> +    case OCB_OCI_OCCMISC:
> +        pnv_occ_set_misc(occ, val);
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%"
> +                      HWADDR_PRIx "\n", addr);
> +    }
> +}
> +
> +static const MemoryRegionOps pnv_occ_xscom_ops = {
> +    .read = pnv_occ_xscom_read,
> +    .write = pnv_occ_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_occ_realize(DeviceState *dev, Error **errp)
> +{
> +    PnvOCC *occ = PNV_OCC(dev);
> +    Object *obj;
> +    Error *error = NULL;
> +
> +    occ->occmisc = 0;
> +
> +    /* get PSI object from chip */
> +    obj = object_property_get_link(OBJECT(dev), "psi", &error);
> +    if (!obj) {
> +        error_setg(errp, "%s: required link 'psi' not found: %s",
> +                   __func__, error_get_pretty(error));
> +        return;
> +    }
> +    occ->psi = PNV_PSI(obj);
> +
> +    /* XScom region for OCC registers */
> +    memory_region_init_io(&occ->xscom_regs, OBJECT(dev), &pnv_occ_xscom_ops,
> +                  occ, "xscom-occ", PNV_XSCOM_OCC_SIZE << 3);
> +}
> +
> +static void pnv_occ_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->realize = pnv_occ_realize;
> +}
> +
> +static const TypeInfo pnv_occ_type_info = {
> +    .name          = TYPE_PNV_OCC,
> +    .parent        = TYPE_DEVICE,
> +    .instance_size = sizeof(PnvOCC),
> +    .class_init    = pnv_occ_class_init,
> +};
> +
> +static void pnv_occ_register_types(void)
> +{
> +    type_register_static(&pnv_occ_type_info);
> +}
> +
> +type_init(pnv_occ_register_types)
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index 473fd2318d87..ed3316501326 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -25,6 +25,7 @@
>  #include "hw/ppc/pnv_lpc.h"
>  #include "hw/ppc/xics.h"
>  #include "hw/ppc/pnv_psi.h"
> +#include "hw/ppc/pnv_occ.h"
>  
>  #define TYPE_PNV_CHIP "powernv-chip"
>  #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
> @@ -59,6 +60,7 @@ typedef struct PnvChip {
>      PnvLpcController lpc;
>      XICSNative   xics;
>      PnvPsiController psi;
> +    PnvOCC           occ;
>  } PnvChip;
>  
>  typedef struct PnvChipClass {
> diff --git a/include/hw/ppc/pnv_occ.h b/include/hw/ppc/pnv_occ.h
> new file mode 100644
> index 000000000000..54e760df7c4f
> --- /dev/null
> +++ b/include/hw/ppc/pnv_occ.h
> @@ -0,0 +1,38 @@
> +/*
> + * QEMU PowerNV Emulation of a few OCC related registers
> + *
> + * Copyright (c) 2016, IBM Corporation.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + */
> +#ifndef _PPC_PNV_OCC_H
> +#define _PPC_PNV_OCC_H
> +
> +#define TYPE_PNV_OCC "pnv-occ"
> +#define PNV_OCC(obj) OBJECT_CHECK(PnvOCC, (obj), TYPE_PNV_OCC)
> +
> +typedef struct PnvPsiController PnvPsiController;
> +
> +typedef struct PnvOCC {
> +    DeviceState xd;
> +
> +    /* OCC Misc interrupt */
> +    uint64_t occmisc;
> +
> +    PnvPsiController *psi;
> +
> +    MemoryRegion xscom_regs;
> +} PnvOCC;
> +
> +#endif /* _PPC_PNV_OCC_H */
> diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
> index 28fbec8ee8e0..f22906dc2fff 100644
> --- a/include/hw/ppc/pnv_xscom.h
> +++ b/include/hw/ppc/pnv_xscom.h
> @@ -66,6 +66,9 @@ typedef struct PnvXScomInterfaceClass {
>  #define PNV_XSCOM_PSI_BASE        0x2010900
>  #define PNV_XSCOM_PSI_SIZE        0x20
>  
> +#define PNV_XSCOM_OCC_BASE        0x0066000
> +#define PNV_XSCOM_OCC_SIZE        0x6000
> +
>  extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
>  extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
>  

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 19/20] ppc/pnv: Add Naples chip support for LPC interrupts
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 19/20] ppc/pnv: Add Naples chip support for LPC interrupts Cédric Le Goater
@ 2016-10-14  6:36   ` David Gibson
  2016-10-14  7:47     ` Cédric Le Goater
  0 siblings, 1 reply; 75+ messages in thread
From: David Gibson @ 2016-10-14  6:36 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 6585 bytes --]

On Mon, Oct 03, 2016 at 09:24:55AM +0200, Cédric Le Goater wrote:
> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> 
> It adds the Naples chip which supports proper LPC interrupts via the
> LPC controller rather than via an external CPLD.
> 
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> [clg: - updated for qemu-2.7
>       - ported on latest PowerNV patchset (v3) ]
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>  hw/ppc/pnv.c             | 18 +++++++++++++++++-
>  hw/ppc/pnv_lpc.c         | 47 +++++++++++++++++++++++++++++++++++++++++++++--
>  include/hw/ppc/pnv_lpc.h |  7 +++++++
>  3 files changed, 69 insertions(+), 3 deletions(-)
> 
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index e805e97d4d87..5b70ccf66fac 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -340,7 +340,17 @@ static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
>  
>  static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
>  {
> -     /* XXX TODO */
> +    PnvLpcController *lpc = opaque;
> +
> +    if (n >= ISA_NUM_IRQS) {
> +        return;
> +    }

How could n >= ISA_NUM_IRQS arise?  Would it have to mean a bug
elsewhere in your code?  If so this should be an assert().

> +
> +    /* The Naples HW latches the 1 levels, clearing is done by SW */
> +    if (level) {
> +        lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SERIRQ0 >> n;
> +        pnv_lpc_eval_irqs(lpc);
> +    }
>  }
>  
>  static ISABus *pnv_isa_create(PnvChip *chip)
> @@ -656,6 +666,12 @@ static void pnv_chip_init(Object *obj)
>      object_property_add_child(obj, "occ", OBJECT(&chip->occ), NULL);
>      object_property_add_const_link(OBJECT(&chip->occ), "psi",
>                                     OBJECT(&chip->psi), &error_abort);
> +
> +    /*
> +     * The LPC controller needs PSI to generate interrupts
> +     */
> +    object_property_add_const_link(OBJECT(&chip->lpc), "psi",
> +                                   OBJECT(&chip->psi), &error_abort);
>  }
>  
>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
> diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
> index 210cc1cff167..8b78b0a1e414 100644
> --- a/hw/ppc/pnv_lpc.c
> +++ b/hw/ppc/pnv_lpc.c
> @@ -249,6 +249,34 @@ static const MemoryRegionOps pnv_lpc_xscom_ops = {
>      .endianness = DEVICE_BIG_ENDIAN,
>  };
>  
> +void pnv_lpc_eval_irqs(PnvLpcController *lpc)
> +{
> +    bool lpc_to_opb_irq = false;
> +
> +    /* Update LPC controller to OPB line */
> +    if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) {
> +        uint32_t irqs;
> +
> +        irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask;
> +        lpc_to_opb_irq = (irqs != 0);
> +    }
> +
> +    /* We don't honor the polarity register, it's pointless and unused
> +     * anyway
> +     */
> +    if (lpc_to_opb_irq) {
> +        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;
> +
> +    /* Reflect the interrupt */
> +    pnv_psi_irq_set(lpc->psi, PSIHB_IRQ_LPC_I2C, lpc->opb_irq_stat != 0);
> +}
> +
>  static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size)
>  {
>      PnvLpcController *lpc = opaque;
> @@ -299,12 +327,15 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val,
>          break;
>      case LPC_HC_IRQSER_CTRL:
>          lpc->lpc_hc_irqser_ctrl = val;
> +        pnv_lpc_eval_irqs(lpc);
>          break;
>      case LPC_HC_IRQMASK:
>          lpc->lpc_hc_irqmask = val;
> +        pnv_lpc_eval_irqs(lpc);
>          break;
>      case LPC_HC_IRQSTAT:
>          lpc->lpc_hc_irqstat &= ~val;
> +        pnv_lpc_eval_irqs(lpc);
>          break;
>      case LPC_HC_ERROR_ADDRESS:
>          break;
> @@ -362,14 +393,15 @@ static void opb_master_write(void *opaque, hwaddr addr,
>      switch (addr) {
>      case OPB_MASTER_LS_IRQ_STAT:
>          lpc->opb_irq_stat &= ~val;
> +        pnv_lpc_eval_irqs(lpc);
>          break;
>      case OPB_MASTER_LS_IRQ_MASK:
> -        /* XXX Filter out reserved bits */
>          lpc->opb_irq_mask = val;
> +        pnv_lpc_eval_irqs(lpc);
>          break;
>      case OPB_MASTER_LS_IRQ_POL:
> -        /* XXX Filter out reserved bits */
>          lpc->opb_irq_pol = val;
> +        pnv_lpc_eval_irqs(lpc);
>          break;
>      case OPB_MASTER_LS_IRQ_INPUT:
>          /* Read only */
> @@ -397,6 +429,8 @@ static const MemoryRegionOps opb_master_ops = {
>  static void pnv_lpc_realize(DeviceState *dev, Error **errp)
>  {
>      PnvLpcController *lpc = PNV_LPC(dev);
> +    Object *obj;
> +    Error *error = NULL;
>  
>      /* Reg inits */
>      lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B;
> @@ -440,6 +474,15 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp)
>      memory_region_init_io(&lpc->xscom_regs, OBJECT(dev),
>                            &pnv_lpc_xscom_ops, lpc, "xscom-lpc",
>                            PNV_XSCOM_LPC_SIZE << 3);
> +
> +    /* get PSI object from chip */
> +    obj = object_property_get_link(OBJECT(dev), "psi", &error);
> +    if (!obj) {
> +        error_setg(errp, "%s: required link 'psi' not found: %s",
> +                   __func__, error_get_pretty(error));
> +        return;
> +    }
> +    lpc->psi = PNV_PSI(obj);
>  }
>  
>  static void pnv_lpc_class_init(ObjectClass *klass, void *data)
> diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h
> index 38e5506975aa..fc348dca50ca 100644
> --- a/include/hw/ppc/pnv_lpc.h
> +++ b/include/hw/ppc/pnv_lpc.h
> @@ -23,9 +23,13 @@
>  #define PNV_LPC(obj) \
>       OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC)
>  
> +typedef struct PnvPsiController PnvPsiController;
> +
>  typedef struct PnvLpcController {
>      DeviceState parent;
>  
> +    PnvPsiController *psi;
> +
>      uint64_t eccb_stat_reg;
>      uint32_t eccb_data_reg;
>  
> @@ -64,4 +68,7 @@ typedef struct PnvLpcController {
>      MemoryRegion xscom_regs;
>  } PnvLpcController;
>  
> +#define   LPC_HC_IRQ_SERIRQ0            0x80000000 /* all bits down to ... */
> +void pnv_lpc_eval_irqs(PnvLpcController *lpc);
> +
>  #endif /* _PPC_PNV_LPC_H */

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 20/20] ppc/pnv: add support for POWER9 LPC Controller
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 20/20] ppc/pnv: add support for POWER9 LPC Controller Cédric Le Goater
@ 2016-10-14  6:43   ` David Gibson
  0 siblings, 0 replies; 75+ messages in thread
From: David Gibson @ 2016-10-14  6:43 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 18780 bytes --]

On Mon, Oct 03, 2016 at 09:24:56AM +0200, Cédric Le Goater wrote:
> The LPC Controller on POWER9 is very similar to the one found on
> POWER8 but accesses are now done via on MMIOs, without the XSCOM and
> ECCB logic. The device tree is populated differently so we add a
> PnvLpcClass and a couple of methods for the purpose.
> 
> LPC interrupts routing is missing. This is Work In Progress but it
> gives us a P9 console and, more important, a first idea of what
> changes we should consider for the following models.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>  hw/ppc/pnv.c             |  32 +++---
>  hw/ppc/pnv_lpc.c         | 255 ++++++++++++++++++++++++++++++++++++++++++++---
>  include/hw/ppc/pnv.h     |   9 +-
>  include/hw/ppc/pnv_lpc.h |  33 ++++++
>  4 files changed, 304 insertions(+), 25 deletions(-)
> 
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 5b70ccf66fac..d5ef4f8ddb53 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -237,6 +237,8 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
>          xics_native_populate_icp(chip, fdt, 0, pnv_core->pir, smt);
>      }
>  
> +    pnv_lpc_populate(chip, fdt, 0);
> +
>      /* Put all the memory in one node on chip 0 until we find a way to
>       * specify different ranges for each chip
>       */
> @@ -355,7 +357,7 @@ static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
>  
>  static ISABus *pnv_isa_create(PnvChip *chip)
>  {
> -    PnvLpcController *lpc = &chip->lpc;
> +    PnvLpcController *lpc = chip->lpc;
>      ISABus *isa_bus;
>      qemu_irq *irqs;
>      PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
> @@ -653,9 +655,6 @@ static void pnv_chip_init(Object *obj)
>  
>      chip->xscom_base = pcc->xscom_base;
>  
> -    object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
> -    object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
> -
>      object_initialize(&chip->xics, sizeof(chip->xics), TYPE_XICS_NATIVE);
>      object_property_add_child(obj, "xics", OBJECT(&chip->xics), NULL);
>  
> @@ -667,11 +666,6 @@ static void pnv_chip_init(Object *obj)
>      object_property_add_const_link(OBJECT(&chip->occ), "psi",
>                                     OBJECT(&chip->psi), &error_abort);
>  
> -    /*
> -     * The LPC controller needs PSI to generate interrupts
> -     */
> -    object_property_add_const_link(OBJECT(&chip->lpc), "psi",
> -                                   OBJECT(&chip->psi), &error_abort);
>  }
>  
>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
> @@ -757,10 +751,24 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>      xics_insert_ics(XICS_COMMON(&chip->xics), &chip->psi.ics);
>  
>      /* Create LPC controller */
> -    object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
> +    typename = g_strdup_printf(TYPE_PNV_LPC "-%s", pcc->cpu_model);
> +    chip->lpc = PNV_LPC(object_new(typename));

Urgh.  I think doing object_new() from realize is frowned upon.
Unfortunately, I don't know what the right way to do this is :/.

> +    object_property_add_child(OBJECT(chip), "lpc", OBJECT(chip->lpc), NULL);
> +
> +    /* The LPC controller needs PSI to generate interrupts  */
> +    object_property_add_const_link(OBJECT(chip->lpc), "psi",
> +                                   OBJECT(&chip->psi), &error_abort);
> +    object_property_set_bool(OBJECT(chip->lpc), true, "realized",
>                               &error_fatal);
> -    memory_region_add_subregion(&chip->xscom, PNV_XSCOM_LPC_BASE << 3,
> -                                &chip->lpc.xscom_regs);
> +
> +    if (PNV_CHIP_GET_CLASS(chip)->chip_type != PNV_CHIP_POWER9) {
> +        memory_region_add_subregion(&chip->xscom, PNV_XSCOM_LPC_BASE << 3,
> +                                    &chip->lpc->xscom_regs);
> +    } else {
> +        memory_region_add_subregion(get_system_memory(), PNV_POWER9_LPCM_BASE,
> +                                    &chip->lpc->xscom_regs);
> +    }
> +
>  
>      /* Create the simplified OCC model */
>      object_property_set_bool(OBJECT(&chip->occ), true, "realized",
> diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
> index 8b78b0a1e414..93ccc656120c 100644
> --- a/hw/ppc/pnv_lpc.c
> +++ b/hw/ppc/pnv_lpc.c
> @@ -28,6 +28,7 @@
>  #include "hw/ppc/fdt.h"
>  
>  #include <libfdt.h>
> +#include "exec/address-spaces.h"
>  
>  enum {
>      ECCB_CTL    = 0,
> @@ -91,6 +92,88 @@ enum {
>  #define LPC_HC_REGS_OPB_SIZE    0x00001000
>  
>  
> +int pnv_lpc_populate(PnvChip *chip, void *fdt, int offset)
> +{
> +    PnvLpcClass *plc = PNV_LPC_GET_CLASS(chip->lpc);
> +
> +    if (plc->populate) {
> +        return plc->populate(chip, fdt, offset);
> +    }
> +    return 0;
> +}
> +
> +static int pnv_lpc_power9_populate(PnvChip *chip, void *fdt, int root_offset)
> +{
> +    const char compat[] = "ibm,power9-lpcm-opb\0simple-bus";
> +    const char lpc_compat[] = "ibm,power9-lpc\0ibm,lpc";
> +    char *name;
> +    int offset, lpcm_offset;
> +    /* should depend on a MMIO base address of the chip */
> +    uint64_t lpcm_addr = PNV_POWER9_LPCM_BASE;
> +    /* TODO: build the ranges properly, array of :
> +     *              { offset, addr hi, addr low, size }
> +     */
> +    uint32_t ranges[4] = { 0,
> +                           cpu_to_be32(lpcm_addr >> 32),
> +                           cpu_to_be32((uint32_t)lpcm_addr),
> +                           cpu_to_be32(PNV_POWER9_LPCM_SIZE / 2),
> +    };
> +    uint32_t reg[2];
> +
> +    /*
> +     * OPB bus
> +     */
> +    name = g_strdup_printf("lpcm-opb@%"PRIx64, lpcm_addr);
> +    lpcm_offset = fdt_add_subnode(fdt, root_offset, name);
> +    _FDT(lpcm_offset);
> +    g_free(name);
> +    _FDT((fdt_setprop_cell(fdt, lpcm_offset, "#address-cells", 1)));
> +    _FDT((fdt_setprop_cell(fdt, lpcm_offset, "#size-cells", 1)));
> +    _FDT((fdt_setprop(fdt, lpcm_offset, "compatible", compat, sizeof(compat))));
> +    _FDT((fdt_setprop_cell(fdt, lpcm_offset, "ibm,chip-id", chip->chip_id)));
> +   _FDT((fdt_setprop(fdt, lpcm_offset, "ranges", ranges, sizeof(ranges))));
> +
> +    /*
> +     * OPB Master registers
> +     */
> +    name = g_strdup_printf("opb-master@%x", LPC_OPB_REGS_OPB_ADDR);
> +    offset = fdt_add_subnode(fdt, lpcm_offset, name);
> +    _FDT(offset);
> +    g_free(name);
> +
> +    reg[0] = cpu_to_be32(LPC_OPB_REGS_OPB_ADDR);
> +    reg[1] = cpu_to_be32(LPC_OPB_REGS_OPB_SIZE);
> +    _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
> +    _FDT((fdt_setprop_string(fdt, offset, "compatible",
> +                             "ibm,power9-lpcm-opb-master")));
> +
> +    /*
> +     * LPC Host Controller registers
> +     */
> +    name = g_strdup_printf("lpc-controller@%x", LPC_HC_REGS_OPB_ADDR);
> +    offset = fdt_add_subnode(fdt, lpcm_offset, name);
> +    _FDT(offset);
> +    g_free(name);
> +
> +    reg[0] = cpu_to_be32(LPC_HC_REGS_OPB_ADDR);
> +    reg[1] = cpu_to_be32(LPC_HC_REGS_OPB_SIZE);
> +    _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
> +    _FDT((fdt_setprop_string(fdt, offset, "compatible",
> +                             "ibm,power9-lpc-controller")));
> +
> +    name = g_strdup_printf("lpc@%x", LPC_FW_OPB_ADDR);
> +    offset = fdt_add_subnode(fdt, lpcm_offset, name);
> +    _FDT(offset);
> +    g_free(name);
> +    _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2)));
> +    _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1)));
> +    _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0)));
> +    _FDT((fdt_setprop(fdt, offset, "compatible", lpc_compat,
> +                      sizeof(lpc_compat))));
> +
> +    return 0;
> +}
> +
>  /*
>   * TODO: the "primary" cell should only be added on chip 0. This is
>   * how skiboot chooses the default LPC controller on multichip
> @@ -99,7 +182,8 @@ enum {
>   * It would be easly done if we can change the populate() interface to
>   * replace the PnvXScomInterface parameter by a PnvChip one
>   */
> -static int pnv_lpc_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset)
> +static int pnv_lpc_power8_populate(PnvXScomInterface *dev, void *fdt,
> +                                   int xscom_offset)
>  {
>      const char compat[] = "ibm,power8-lpc\0ibm,lpc";
>      char *name;
> @@ -249,6 +333,74 @@ static const MemoryRegionOps pnv_lpc_xscom_ops = {
>      .endianness = DEVICE_BIG_ENDIAN,
>  };
>  
> +static uint64_t pnv_lpc_mmio_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    PnvLpcController *lpc = PNV_LPC(opaque);
> +    uint64_t val = 0;
> +    uint32_t opb_addr = addr & ECCB_CTL_ADDR_MASK;
> +    MemTxResult result;
> +
> +    switch (size) {
> +    case 4:
> +        val = address_space_ldl(&lpc->opb_as, opb_addr, MEMTXATTRS_UNSPECIFIED,
> +                                &result);
> +        break;
> +    case 1:
> +        val = address_space_ldub(&lpc->opb_as, opb_addr, MEMTXATTRS_UNSPECIFIED,
> +                                 &result);
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "OPB read failed at @0x%"
> +                      HWADDR_PRIx " invalid size %d\n", addr, size);
> +        return 0;
> +    }
> +
> +    if (result != MEMTX_OK) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "OPB read failed at @0x%"
> +                      HWADDR_PRIx "\n", addr);
> +    }
> +
> +    return val;
> +}
> +
> +static void pnv_lpc_mmio_write(void *opaque, hwaddr addr,
> +                                uint64_t val, unsigned size)
> +{
> +    PnvLpcController *lpc = PNV_LPC(opaque);
> +    uint32_t opb_addr = addr & ECCB_CTL_ADDR_MASK;
> +    MemTxResult result;
> +
> +    switch (size) {
> +    case 4:
> +        address_space_stl(&lpc->opb_as, opb_addr, val, MEMTXATTRS_UNSPECIFIED,
> +                          &result);
> +         break;
> +    case 1:
> +        address_space_stb(&lpc->opb_as, opb_addr, val, MEMTXATTRS_UNSPECIFIED,
> +                          &result);
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "OPB write failed at @0x%"
> +                      HWADDR_PRIx " invalid size %d\n", addr, size);
> +        return;
> +    }
> +
> +    if (result != MEMTX_OK) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "OPB write failed at @0x%"
> +                      HWADDR_PRIx "\n", addr);
> +    }
> +}
> +
> +static const MemoryRegionOps pnv_lpc_mmio_ops = {
> +    .read = pnv_lpc_mmio_read,
> +    .write = pnv_lpc_mmio_write,
> +    .impl = {
> +        .min_access_size = 1,
> +        .max_access_size = 4,
> +    },
> +    .endianness = DEVICE_BIG_ENDIAN,
> +};
> +
>  void pnv_lpc_eval_irqs(PnvLpcController *lpc)
>  {
>      bool lpc_to_opb_irq = false;
> @@ -426,9 +578,29 @@ static const MemoryRegionOps opb_master_ops = {
>      },
>  };
>  
> +static void pnv_lpc_power8_realize(DeviceState *dev, Error **errp)
> +{
> +    PnvLpcController *lpc = PNV_LPC(dev);
> +
> +    /* XScom region for LPC registers */
> +    memory_region_init_io(&lpc->xscom_regs, OBJECT(dev),
> +                          &pnv_lpc_xscom_ops, lpc, "xscom-lpc",
> +                          PNV_XSCOM_LPC_SIZE << 3);
> +}
> +
> +static void pnv_lpc_power9_realize(DeviceState *dev, Error **errp)
> +{
> +    PnvLpcController *lpc = PNV_LPC(dev);
> +
> +    /* Initialize MMIO region */
> +    memory_region_init_io(&lpc->xscom_regs, OBJECT(dev), &pnv_lpc_mmio_ops, lpc,
> +                          "lpcm", PNV_POWER9_LPCM_SIZE);
> +}
> +
>  static void pnv_lpc_realize(DeviceState *dev, Error **errp)
>  {
>      PnvLpcController *lpc = PNV_LPC(dev);
> +    PnvLpcClass *plc = PNV_LPC_GET_CLASS(dev);
>      Object *obj;
>      Error *error = NULL;
>  
> @@ -470,11 +642,6 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp)
>      memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR,
>                                  &lpc->lpc_hc_regs);
>  
> -    /* XScom region for LPC registers */
> -    memory_region_init_io(&lpc->xscom_regs, OBJECT(dev),
> -                          &pnv_lpc_xscom_ops, lpc, "xscom-lpc",
> -                          PNV_XSCOM_LPC_SIZE << 3);
> -
>      /* get PSI object from chip */
>      obj = object_property_get_link(OBJECT(dev), "psi", &error);
>      if (!obj) {
> @@ -483,32 +650,96 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp)
>          return;
>      }
>      lpc->psi = PNV_PSI(obj);
> +
> +    plc->realize(dev, errp);
>  }
>  
> -static void pnv_lpc_class_init(ObjectClass *klass, void *data)
> +static void pnv_lpc_power8_class_init(ObjectClass *klass, void *data)
>  {
>      DeviceClass *dc = DEVICE_CLASS(klass);
>      PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
> +    PnvLpcClass *plc = PNV_LPC_CLASS(klass);
>  
> -    xdc->populate = pnv_lpc_populate;
> +    xdc->populate = pnv_lpc_power8_populate;
>  
>      dc->realize = pnv_lpc_realize;
> +    plc->realize = pnv_lpc_power8_realize;
>  }
>  
> -static const TypeInfo pnv_lpc_info = {
> -    .name          = TYPE_PNV_LPC,
> -    .parent        = TYPE_DEVICE,
> +static const TypeInfo pnv_lpc_power8e_info = {
> +    .name          = TYPE_PNV_LPC_POWER8E,
> +    .parent        = TYPE_PNV_LPC,
>      .instance_size = sizeof(PnvLpcController),
> -    .class_init    = pnv_lpc_class_init,
> +    .class_init    = pnv_lpc_power8_class_init,
> +    .interfaces = (InterfaceInfo[]) {
> +        { TYPE_PNV_XSCOM_INTERFACE },
> +        { }
> +    }
> +};
> +
> +static const TypeInfo pnv_lpc_power8_info = {
> +    .name          = TYPE_PNV_LPC_POWER8,
> +    .parent        = TYPE_PNV_LPC,
> +    .instance_size = sizeof(PnvLpcController),
> +    .class_init    = pnv_lpc_power8_class_init,
> +    .interfaces = (InterfaceInfo[]) {
> +        { TYPE_PNV_XSCOM_INTERFACE },
> +        { }
> +    }
> +};
> +
> +static const TypeInfo pnv_lpc_power8nvl_info = {
> +    .name          = TYPE_PNV_LPC_POWER8NVL,
> +    .parent        = TYPE_PNV_LPC,
> +    .instance_size = sizeof(PnvLpcController),
> +    .class_init    = pnv_lpc_power8_class_init,
>      .interfaces = (InterfaceInfo[]) {
>          { TYPE_PNV_XSCOM_INTERFACE },
>          { }
>      }
>  };
>  
> +static void pnv_lpc_power9_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PnvLpcClass *plc = PNV_LPC_CLASS(klass);
> +
> +    dc->realize = pnv_lpc_realize;
> +    plc->realize = pnv_lpc_power9_realize;
> +    plc->populate = pnv_lpc_power9_populate;
> +}
> +
> +static const TypeInfo pnv_lpc_power9_info = {
> +    .name          = TYPE_PNV_LPC_POWER9,
> +    .parent        = TYPE_PNV_LPC,
> +    .instance_size = sizeof(PnvLpcController),
> +    .class_init    = pnv_lpc_power9_class_init,
> +};
> +
> +static void pnv_lpc_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->realize = pnv_lpc_realize;
> +    dc->desc = "PowerNV LPC Controller";
> +}
> +
> +static const TypeInfo pnv_lpc_info = {
> +    .name          = TYPE_PNV_LPC,
> +    .parent        = TYPE_DEVICE,
> +    .class_init    = pnv_lpc_class_init,
> +    .class_size    = sizeof(PnvLpcClass),
> +    .abstract      = true,
> +};
> +
> +
>  static void pnv_lpc_register_types(void)
>  {
>      type_register_static(&pnv_lpc_info);
> +    type_register_static(&pnv_lpc_power8e_info);
> +    type_register_static(&pnv_lpc_power8_info);
> +    type_register_static(&pnv_lpc_power8nvl_info);
> +    type_register_static(&pnv_lpc_power9_info);
>  }
>  
>  type_init(pnv_lpc_register_types)
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index ed3316501326..361af311a92e 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -57,7 +57,7 @@ typedef struct PnvChip {
>      uint64_t  cores_mask;
>      void      *cores;
>  
> -    PnvLpcController lpc;
> +    PnvLpcController *lpc;
>      XICSNative   xics;
>      PnvPsiController psi;
>      PnvOCC           occ;
> @@ -154,4 +154,11 @@ typedef struct PnvMachineState {
>  #define PNV_PSIHB_BAR_SIZE    0x0000000000100000ull
>  
>  
> +/*
> + * POWER9 MMIO regions
> + */
> +#define PNV_POWER9_MMIO_BASE   0x006000000000000ull
> +#define PNV_POWER9_LPCM_BASE   (PNV_POWER9_MMIO_BASE + 0x30000000000ull)
> +#define PNV_POWER9_LPCM_SIZE   0x100000000ull
> +
>  #endif /* _PPC_PNV_H */
> diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h
> index fc348dca50ca..510fab2fd115 100644
> --- a/include/hw/ppc/pnv_lpc.h
> +++ b/include/hw/ppc/pnv_lpc.h
> @@ -22,8 +22,13 @@
>  #define TYPE_PNV_LPC "pnv-lpc"
>  #define PNV_LPC(obj) \
>       OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC)
> +#define PNV_LPC_CLASS(klass) \
> +     OBJECT_CLASS_CHECK(PnvLpcClass, (klass), TYPE_PNV_LPC)
> +#define PNV_LPC_GET_CLASS(obj) \
> +     OBJECT_GET_CLASS(PnvLpcClass, (obj), TYPE_PNV_LPC)
>  
>  typedef struct PnvPsiController PnvPsiController;
> +typedef struct PnvChip PnvChip;
>  
>  typedef struct PnvLpcController {
>      DeviceState parent;
> @@ -68,7 +73,35 @@ typedef struct PnvLpcController {
>      MemoryRegion xscom_regs;
>  } PnvLpcController;
>  
> +typedef struct PnvLpcClass {
> +    DeviceClass parent_class;
> +
> +    int (*populate)(PnvChip *chip, void *fdt, int offset);
> +    void (*realize)(DeviceState *dev, Error **errp);
> +} PnvLpcClass;
> +
> +
> +#define TYPE_PNV_LPC_POWER8E TYPE_PNV_LPC "-POWER8E"
> +#define PNV_LPC_POWER8E(obj) \
> +    OBJECT_CHECK(PnvLpc, (obj), TYPE_PNV_LPC_POWER8E)
> +
> +#define TYPE_PNV_LPC_POWER8 TYPE_PNV_LPC "-POWER8"
> +#define PNV_LPC_POWER8(obj) \
> +    OBJECT_CHECK(PnvLpc, (obj), TYPE_PNV_LPC_POWER8)
> +
> +#define TYPE_PNV_LPC_POWER8NVL TYPE_PNV_LPC "-POWER8NVL"
> +#define PNV_LPC_POWER8NVL(obj) \
> +    OBJECT_CHECK(PnvLpc, (obj), TYPE_PNV_LPC_POWER8NVL)
> +
> +#define TYPE_PNV_LPC_POWER9 TYPE_PNV_LPC "-POWER9"
> +#define PNV_LPC_POWER9(obj) \
> +    OBJECT_CHECK(PnvLpc, (obj), TYPE_PNV_LPC_POWER9)
> +
> +
> +
>  #define   LPC_HC_IRQ_SERIRQ0            0x80000000 /* all bits down to ... */
>  void pnv_lpc_eval_irqs(PnvLpcController *lpc);
>  
> +int pnv_lpc_populate(PnvChip *chip, void *fdt, int root_offset);
> +
>  #endif /* _PPC_PNV_LPC_H */

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 17/20] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt
  2016-10-14  6:32   ` David Gibson
@ 2016-10-14  7:13     ` Benjamin Herrenschmidt
  2016-10-14  8:07     ` Cédric Le Goater
  1 sibling, 0 replies; 75+ messages in thread
From: Benjamin Herrenschmidt @ 2016-10-14  7:13 UTC (permalink / raw)
  To: David Gibson, Cédric Le Goater; +Cc: qemu-ppc, qemu-devel

On Fri, 2016-10-14 at 17:32 +1100, David Gibson wrote:
> >  static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
> >  {
> > -    /* We don't yet emulate the PSI bridge which provides the external
> > -     * interrupt, so just drop interrupts on the floor
> > -     */
> > +    static uint32_t irqstate;
> 
> Hmm.. static local with important state?  That it's not clear whether
> it should be per-chip or not?
> 
> I'm not averse to hacks for early bringup, but it should at least have
> a FIXME comment on it.

This emulates the external FPGA no ? There's only ever one ... well I
suppose one could make a machine with more but that hasn't been done
yet.

Ideally it could be a whole device by itself who feeds the ISA irqs and
outputs the final IRQ but that's overkill.

Cheers,
Ben.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 10/20] ppc/xics: Make the ICSState a list
  2016-10-14  5:32   ` David Gibson
@ 2016-10-14  7:35     ` Cédric Le Goater
  2016-10-16 23:53       ` David Gibson
  0 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-14  7:35 UTC (permalink / raw)
  To: David Gibson
  Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel, Nikunj A Dadhania

On 10/14/2016 07:32 AM, David Gibson wrote:
> On Mon, Oct 03, 2016 at 09:24:46AM +0200, Cédric Le Goater wrote:
>> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>>
>> Instead of an array of fixed sized blocks, use a list, as we will need
>> to have sources with variable number of interrupts. SPAPR only uses
>> a single entry. Native will create more. If performance becomes an
>> issue we can add some hashed lookup but for now this will do fine.
>>
>> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>> [ move the initialization of list to xics_common_initfn,
>>   restore xirr_owner after migration and move restoring to
>>   icp_post_load]
>> Signed-off-by: Nikunj A Dadhania <nikunj@linux.vnet.ibm.com>
>> [ clg: removed the icp_post_load() changes from nikunj patchset v3:
>>        http://patchwork.ozlabs.org/patch/646008/ ]
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> 
> I think this and 11/20 are good enough and sufficiently standalone
> that I've merged them into ppc-for-2.8.

These are a prereq for the patchset.

Thanks,

C.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 12/20] ppc/xics: Add xics to the monitor "info pic" command
  2016-10-14  5:30   ` David Gibson
@ 2016-10-14  7:39     ` Cédric Le Goater
  0 siblings, 0 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-14  7:39 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 10/14/2016 07:30 AM, David Gibson wrote:
> On Mon, Oct 03, 2016 at 09:24:48AM +0200, Cédric Le Goater wrote:
>> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>>
>> Useful to debug interrupt problems.
>>
>> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>> [clg: - updated for qemu-2.7
>>       - added a test on ->irqs as it is not necessarily allocated
>>         (PHB3_MSI)
>>       - removed static variable g_xics and replace with a loop on all
>>         children to find the xics objects. ]
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> 
> So, this is fine in principle.  However info pic has recently been
> reworked to better allow different PICs to add stuff in there.  Your
> patches will need to be reworked on top of that.
> 
> See 61b97833 and surrounding commits.

Missed that. It seems I can get rid of the loop I introduced. I will 
rework.

Thanks,

C.

>> ---
>>  hmp-commands-info.hx  |  2 ++
>>  hw/intc/xics.c        | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++
>>  hw/ppc/ppc.c          | 14 ++++++++++++++
>>  include/hw/ppc/ppc.h  |  2 ++
>>  include/hw/ppc/xics.h |  2 ++
>>  monitor.c             |  4 ++++
>>  6 files changed, 76 insertions(+)
>>
>> diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
>> index 19729e55aea2..ab11eaf54348 100644
>> --- a/hmp-commands-info.hx
>> +++ b/hmp-commands-info.hx
>> @@ -203,6 +203,8 @@ ETEXI
>>          .cmd        = sun4m_hmp_info_pic,
>>  #elif defined(TARGET_LM32)
>>          .cmd        = lm32_hmp_info_pic,
>> +#elif defined(TARGET_PPC)
>> +        .cmd        = ppc_hmp_info_pic,
>>  #else
>>          .cmd        = hmp_info_pic,
>>  #endif
>> diff --git a/hw/intc/xics.c b/hw/intc/xics.c
>> index f40b00003a45..3bbbcc847791 100644
>> --- a/hw/intc/xics.c
>> +++ b/hw/intc/xics.c
>> @@ -35,6 +35,7 @@
>>  #include "hw/ppc/xics.h"
>>  #include "qemu/error-report.h"
>>  #include "qapi/visitor.h"
>> +#include "monitor/monitor.h"
>>  
>>  int xics_get_cpu_index_by_dt_id(int cpu_dt_id)
>>  {
>> @@ -633,6 +634,57 @@ static int ics_simple_dispatch_post_load(void *opaque, int version_id)
>>      return 0;
>>  }
>>  
>> +static int xics_hmp_info_pic_child(Object *child, void *opaque)
>> +{
>> +    Monitor *mon = opaque;
>> +
>> +    if (object_dynamic_cast(child, TYPE_XICS_COMMON)) {
>> +        XICSState *xics = XICS_COMMON(child);
>> +        ICSState *ics;
>> +        uint32_t i;
>> +
>> +        for (i = 0; i < xics->nr_servers; i++) {
>> +            ICPState *icp = &xics->ss[i];
>> +
>> +            if (!icp->output) {
>> +                continue;
>> +            }
>> +            monitor_printf(mon, "CPU %d XIRR=%08x (%p) PP=%02x MFRR=%02x\n",
>> +                           i, icp->xirr, icp->xirr_owner,
>> +                           icp->pending_priority, icp->mfrr);
>> +        }
>> +
>> +        QLIST_FOREACH(ics, &xics->ics, list) {
>> +            monitor_printf(mon, "ICS %4x..%4x %p\n",
>> +                           ics->offset, ics->offset + ics->nr_irqs - 1, ics);
>> +
>> +            if (!ics->irqs) {
>> +                continue;
>> +            }
>> +
>> +            for (i = 0; i < ics->nr_irqs; i++) {
>> +                ICSIRQState *irq = ics->irqs + i;
>> +
>> +                if (!(irq->flags & XICS_FLAGS_IRQ_MASK)) {
>> +                    continue;
>> +                }
>> +                monitor_printf(mon, "  %4x %s %02x %02x\n",
>> +                               ics->offset + i,
>> +                               (irq->flags & XICS_FLAGS_IRQ_LSI) ?
>> +                               "LSI" : "MSI",
>> +                               irq->priority, irq->status);
>> +            }
>> +        }
>> +    }
>> +    return 0;
>> +}
>> +
>> +void xics_hmp_info_pic(Monitor *mon, const QDict *qdict)
>> +{
>> +    object_child_foreach_recursive(OBJECT(qdev_get_machine()),
>> +                                   xics_hmp_info_pic_child, mon);
>> +}
>> +
>>  static const VMStateDescription vmstate_ics_simple_irq = {
>>      .name = "ics/irq",
>>      .version_id = 2,
>> diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
>> index 89458690097f..bc734281f509 100644
>> --- a/hw/ppc/ppc.c
>> +++ b/hw/ppc/ppc.c
>> @@ -27,6 +27,7 @@
>>  #include "hw/hw.h"
>>  #include "hw/ppc/ppc.h"
>>  #include "hw/ppc/ppc_e500.h"
>> +#include "hw/i386/pc.h"
>>  #include "qemu/timer.h"
>>  #include "sysemu/sysemu.h"
>>  #include "sysemu/cpus.h"
>> @@ -39,6 +40,10 @@
>>  #include "kvm_ppc.h"
>>  #include "trace.h"
>>  
>> +#if defined(TARGET_PPC64)
>> +#include "hw/ppc/xics.h"
>> +#endif
>> +
>>  //#define PPC_DEBUG_IRQ
>>  //#define PPC_DEBUG_TB
>>  
>> @@ -1376,3 +1381,12 @@ void ppc_cpu_parse_features(const char *cpu_model)
>>      cc->parse_features(typename, model_pieces[1], &error_fatal);
>>      g_strfreev(model_pieces);
>>  }
>> +
>> +void ppc_hmp_info_pic(Monitor *mon, const QDict *qdict)
>> +{
>> +    /* Call in turn every PIC around. OpenPIC doesn't have one yet */
>> +#ifdef TARGET_PPC64
>> +    xics_hmp_info_pic(mon, qdict);
>> +#endif
>> +    hmp_info_pic(mon, qdict);
>> +}
>> diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h
>> index 00c1fb1e720a..b36024a1213c 100644
>> --- a/include/hw/ppc/ppc.h
>> +++ b/include/hw/ppc/ppc.h
>> @@ -3,6 +3,8 @@
>>  
>>  #include "target-ppc/cpu-qom.h"
>>  
>> +void ppc_hmp_info_pic(Monitor *mon, const QDict *qdict);
>> +
>>  void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level);
>>  
>>  /* PowerPC hardware exceptions management helpers */
>> diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
>> index 66ae55ded387..ca9f8da542e0 100644
>> --- a/include/hw/ppc/xics.h
>> +++ b/include/hw/ppc/xics.h
>> @@ -205,4 +205,6 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi);
>>  
>>  ICSState *xics_find_source(XICSState *icp, int irq);
>>  
>> +void xics_hmp_info_pic(Monitor *mon, const QDict *qdict);
>> +
>>  #endif /* XICS_H */
>> diff --git a/monitor.c b/monitor.c
>> index 83c4edfce08e..70e17fa9f1fd 100644
>> --- a/monitor.c
>> +++ b/monitor.c
>> @@ -91,6 +91,10 @@
>>  #include "hw/s390x/storage-keys.h"
>>  #endif
>>  
>> +#if defined(TARGET_PPC)
>> +#include "hw/ppc/ppc.h"
>> +#endif
>> +
>>  /*
>>   * Supported types:
>>   *
> 

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 13/20] ppc/xics: introduce helpers to find an ICP from some (CPU) index
  2016-10-14  5:34   ` David Gibson
@ 2016-10-14  7:44     ` Cédric Le Goater
  0 siblings, 0 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-14  7:44 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 10/14/2016 07:34 AM, David Gibson wrote:
> On Mon, Oct 03, 2016 at 09:24:49AM +0200, Cédric Le Goater wrote:
>> Today, the Interrupt Presentation Controllers (ICP) are stored in an
>> array under the base class XICSState. They are simply indexed using
>> the CPU index field of CPUState. This made sense for the current
>> derived classes, spapr and kvm, as the CPU index are contiguous.
>> Nevertheless some problems have started to occur with CPU hotplug.
>>
>> With the PowerNV platform CPUs, this is not true anymore. Real HW ids
>> are being used and they are not contiguous. So we need a way to
>> customize the lookups in the array. Below is a proposal for this
>> purpose. A couple of helpers are introduced to hide the nature of the
>> underlying ICP array and also a new XICSStateClass method 'find_icp'
>> to let the derived classes customize the ICP lookups.
>>
>> A followup patch would be to let the derived class decide on the ICP
>> storage. They could use a hash table for instance. We would need to
>> introduce a new class method 'get_icp' for that. Or simply, change the
>> ICP array for a hash table and let the derived class decide on the key
>> to use.
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> 
> Uuuh.. IIRC this series has now been reworked (as suggested) to keep
> cpu_index contiguous and have a separate hardware id.  Doesn't that
> mean this patch can go away (though the callers might need to use a
> hwid->cpu_index helper).

Yes. This is how it is implemented. 

The pnv machine only handles cpu hwid and it uses a custom find handler
to look for an ICP. But I see you made comments on the implementation.

Thanks,

C.   


>> ---
>>  hw/intc/xics.c        | 48 ++++++++++++++++++++++++++++++++++++------------
>>  hw/intc/xics_kvm.c    |  7 ++-----
>>  hw/intc/xics_spapr.c  | 12 ++++++------
>>  include/hw/ppc/xics.h |  2 ++
>>  4 files changed, 46 insertions(+), 23 deletions(-)
>>
>> diff --git a/hw/intc/xics.c b/hw/intc/xics.c
>> index 3bbbcc847791..876c472aaa69 100644
>> --- a/hw/intc/xics.c
>> +++ b/hw/intc/xics.c
>> @@ -48,12 +48,32 @@ int xics_get_cpu_index_by_dt_id(int cpu_dt_id)
>>      return -1;
>>  }
>>  
>> +ICPState *xics_find_icp(XICSState *xics, int cpu_index)
>> +{
>> +    XICSStateClass *xsc = XICS_COMMON_GET_CLASS(xics);
>> +    ICPState *icp = xsc->find_icp(xics, cpu_index);
>> +
>> +    assert(icp);
>> +
>> +    return icp;
>> +}
>> +
>> +static ICPState *xics_get_icp(XICSState *xics, CPUState *cs)
>> +{
>> +    ICPState *ss;
>> +
>> +    assert(cs->cpu_index < xics->nr_servers);
>> +
>> +    ss = &xics->ss[cs->cpu_index];
>> +    ss->cs = cs;
>> +    return ss;
>> +}
>> +
>>  void xics_cpu_destroy(XICSState *xics, PowerPCCPU *cpu)
>>  {
>>      CPUState *cs = CPU(cpu);
>> -    ICPState *ss = &xics->ss[cs->cpu_index];
>> +    ICPState *ss = xics_find_icp(xics, cs->cpu_index);
>>  
>> -    assert(cs->cpu_index < xics->nr_servers);
>>      assert(cs == ss->cs);
>>  
>>      ss->output = NULL;
>> @@ -64,13 +84,9 @@ void xics_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
>>  {
>>      CPUState *cs = CPU(cpu);
>>      CPUPPCState *env = &cpu->env;
>> -    ICPState *ss = &xics->ss[cs->cpu_index];
>> +    ICPState *ss = xics_get_icp(xics, cs);
>>      XICSStateClass *info = XICS_COMMON_GET_CLASS(xics);
>>  
>> -    assert(cs->cpu_index < xics->nr_servers);
>> -
>> -    ss->cs = cs;
>> -
>>      if (info->cpu_setup) {
>>          info->cpu_setup(xics, cpu);
>>      }
>> @@ -94,6 +110,12 @@ void xics_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
>>  /*
>>   * XICS Common class - parent for emulated XICS and KVM-XICS
>>   */
>> +
>> +static ICPState *xics_common_find_icp(XICSState *xics, int cpu_index)
>> +{
>> +    return &xics->ss[cpu_index];
>> +}
>> +
>>  static void xics_common_reset(DeviceState *d)
>>  {
>>      XICSState *xics = XICS_COMMON(d);
>> @@ -191,8 +213,10 @@ static void xics_common_initfn(Object *obj)
>>  static void xics_common_class_init(ObjectClass *oc, void *data)
>>  {
>>      DeviceClass *dc = DEVICE_CLASS(oc);
>> +    XICSStateClass *xsc = XICS_COMMON_CLASS(oc);
>>  
>>      dc->reset = xics_common_reset;
>> +    xsc->find_icp = xics_common_find_icp;
>>  }
>>  
>>  static const TypeInfo xics_common_info = {
>> @@ -261,7 +285,7 @@ static void icp_check_ipi(ICPState *ss)
>>  
>>  static void icp_resend(XICSState *xics, int server)
>>  {
>> -    ICPState *ss = xics->ss + server;
>> +    ICPState *ss = xics_find_icp(xics, server);
>>      ICSState *ics;
>>  
>>      if (ss->mfrr < CPPR(ss)) {
>> @@ -274,7 +298,7 @@ static void icp_resend(XICSState *xics, int server)
>>  
>>  void icp_set_cppr(XICSState *xics, int server, uint8_t cppr)
>>  {
>> -    ICPState *ss = xics->ss + server;
>> +    ICPState *ss = xics_find_icp(xics, server);
>>      uint8_t old_cppr;
>>      uint32_t old_xisr;
>>  
>> @@ -301,7 +325,7 @@ void icp_set_cppr(XICSState *xics, int server, uint8_t cppr)
>>  
>>  void icp_set_mfrr(XICSState *xics, int server, uint8_t mfrr)
>>  {
>> -    ICPState *ss = xics->ss + server;
>> +    ICPState *ss = xics_find_icp(xics, server);
>>  
>>      ss->mfrr = mfrr;
>>      if (mfrr < CPPR(ss)) {
>> @@ -333,7 +357,7 @@ uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr)
>>  
>>  void icp_eoi(XICSState *xics, int server, uint32_t xirr)
>>  {
>> -    ICPState *ss = xics->ss + server;
>> +    ICPState *ss = xics_find_icp(xics, server);
>>      ICSState *ics;
>>      uint32_t irq;
>>  
>> @@ -354,7 +378,7 @@ void icp_eoi(XICSState *xics, int server, uint32_t xirr)
>>  static void icp_irq(ICSState *ics, int server, int nr, uint8_t priority)
>>  {
>>      XICSState *xics = ics->xics;
>> -    ICPState *ss = xics->ss + server;
>> +    ICPState *ss = xics_find_icp(xics, server);
>>  
>>      trace_xics_icp_irq(server, nr, priority);
>>  
>> diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c
>> index 9c2f198fd142..b666bb59fc24 100644
>> --- a/hw/intc/xics_kvm.c
>> +++ b/hw/intc/xics_kvm.c
>> @@ -326,14 +326,11 @@ static const TypeInfo ics_kvm_info = {
>>   */
>>  static void xics_kvm_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
>>  {
>> -    CPUState *cs;
>> -    ICPState *ss;
>> +    CPUState *cs = CPU(cpu);
>> +    ICPState *ss = xics_find_icp(xics, cs->cpu_index);
>>      KVMXICSState *xicskvm = XICS_SPAPR_KVM(xics);
>>      int ret;
>>  
>> -    cs = CPU(cpu);
>> -    ss = &xics->ss[cs->cpu_index];
>> -
>>      assert(cs->cpu_index < xics->nr_servers);
>>      if (xicskvm->kernel_xics_fd == -1) {
>>          abort();
>> diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c
>> index e8d0623c2cb5..af29998b1255 100644
>> --- a/hw/intc/xics_spapr.c
>> +++ b/hw/intc/xics_spapr.c
>> @@ -67,9 +67,9 @@ static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
>>                             target_ulong opcode, target_ulong *args)
>>  {
>>      CPUState *cs = CPU(cpu);
>> -    uint32_t xirr = icp_accept(spapr->xics->ss + cs->cpu_index);
>> +    ICPState *ss = xics_find_icp(spapr->xics, cs->cpu_index);
>>  
>> -    args[0] = xirr;
>> +    args[0] = icp_accept(ss);
>>      return H_SUCCESS;
>>  }
>>  
>> @@ -77,10 +77,9 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
>>                               target_ulong opcode, target_ulong *args)
>>  {
>>      CPUState *cs = CPU(cpu);
>> -    ICPState *ss = &spapr->xics->ss[cs->cpu_index];
>> -    uint32_t xirr = icp_accept(ss);
>> +    ICPState *ss = xics_find_icp(spapr->xics, cs->cpu_index);
>>  
>> -    args[0] = xirr;
>> +    args[0] = icp_accept(ss);
>>      args[1] = cpu_get_host_ticks();
>>      return H_SUCCESS;
>>  }
>> @@ -99,8 +98,9 @@ static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr,
>>                              target_ulong opcode, target_ulong *args)
>>  {
>>      CPUState *cs = CPU(cpu);
>> +    ICPState *ss = xics_find_icp(spapr->xics, cs->cpu_index);
>>      uint32_t mfrr;
>> -    uint32_t xirr = icp_ipoll(spapr->xics->ss + cs->cpu_index, &mfrr);
>> +    uint32_t xirr = icp_ipoll(ss, &mfrr);
>>  
>>      args[0] = xirr;
>>      args[1] = mfrr;
>> diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
>> index ca9f8da542e0..52c426d409c9 100644
>> --- a/include/hw/ppc/xics.h
>> +++ b/include/hw/ppc/xics.h
>> @@ -76,6 +76,7 @@ struct XICSStateClass {
>>      void (*cpu_setup)(XICSState *icp, PowerPCCPU *cpu);
>>      void (*set_nr_irqs)(XICSState *icp, uint32_t nr_irqs, Error **errp);
>>      void (*set_nr_servers)(XICSState *icp, uint32_t nr_servers, Error **errp);
>> +    ICPState *(*find_icp)(XICSState *xics, int cpu_index);
>>  };
>>  
>>  struct XICSState {
>> @@ -206,5 +207,6 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi);
>>  ICSState *xics_find_source(XICSState *icp, int irq);
>>  
>>  void xics_hmp_info_pic(Monitor *mon, const QDict *qdict);
>> +ICPState *xics_find_icp(XICSState *xics, int cpu_index);
>>  
>>  #endif /* XICS_H */
> 

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 19/20] ppc/pnv: Add Naples chip support for LPC interrupts
  2016-10-14  6:36   ` David Gibson
@ 2016-10-14  7:47     ` Cédric Le Goater
  0 siblings, 0 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-14  7:47 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 10/14/2016 08:36 AM, David Gibson wrote:
> On Mon, Oct 03, 2016 at 09:24:55AM +0200, Cédric Le Goater wrote:
>> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>>
>> It adds the Naples chip which supports proper LPC interrupts via the
>> LPC controller rather than via an external CPLD.
>>
>> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>> [clg: - updated for qemu-2.7
>>       - ported on latest PowerNV patchset (v3) ]
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>  hw/ppc/pnv.c             | 18 +++++++++++++++++-
>>  hw/ppc/pnv_lpc.c         | 47 +++++++++++++++++++++++++++++++++++++++++++++--
>>  include/hw/ppc/pnv_lpc.h |  7 +++++++
>>  3 files changed, 69 insertions(+), 3 deletions(-)
>>
>> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
>> index e805e97d4d87..5b70ccf66fac 100644
>> --- a/hw/ppc/pnv.c
>> +++ b/hw/ppc/pnv.c
>> @@ -340,7 +340,17 @@ static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
>>  
>>  static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
>>  {
>> -     /* XXX TODO */
>> +    PnvLpcController *lpc = opaque;
>> +
>> +    if (n >= ISA_NUM_IRQS) {
>> +        return;
>> +    }
> 
> How could n >= ISA_NUM_IRQS arise?  Would it have to mean a bug
> elsewhere in your code?  If so this should be an assert().

It cannot. I should remove it.

Thanks,

C. 

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 17/20] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt
  2016-10-14  6:32   ` David Gibson
  2016-10-14  7:13     ` Benjamin Herrenschmidt
@ 2016-10-14  8:07     ` Cédric Le Goater
  2016-10-16 23:52       ` David Gibson
  1 sibling, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-14  8:07 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

>> --- a/hw/ppc/pnv.c
>> +++ b/hw/ppc/pnv.c
>> @@ -318,15 +318,24 @@ static void ppc_powernv_reset(void)
>>   * have a CPLD that will collect the SerIRQ and shoot them as a
>>   * single level interrupt to the P8 chip. So let's setup a hook
>>   * for doing just that.
>> - *
>> - * Note: The actual interrupt input isn't emulated yet, this will
>> - * come with the PSI bridge model.
>>   */
>>  static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
>>  {
>> -    /* We don't yet emulate the PSI bridge which provides the external
>> -     * interrupt, so just drop interrupts on the floor
>> -     */
>> +    static uint32_t irqstate;
> 
> Hmm.. static local with important state?  That it's not clear whether
> it should be per-chip or not?
> 
> I'm not averse to hacks for early bringup, but it should at least have
> a FIXME comment on it.

yes. I will see if I can make a "irq_cpld' attribute of the chip instead. 
It should be cleaner.

Thanks,

C.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 15/20] ppc/xics: Add "native" XICS subclass
  2016-10-14  6:10   ` David Gibson
@ 2016-10-14  9:40     ` Cédric Le Goater
  2016-10-16 23:51       ` David Gibson
  0 siblings, 1 reply; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-14  9:40 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 10/14/2016 08:10 AM, David Gibson wrote:
> On Mon, Oct 03, 2016 at 09:24:51AM +0200, Cédric Le Goater wrote:
>> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>>
>> This provides access to the MMIO based Interrupt Presentation
>> Controllers (ICP) as found on a POWER8 system.
>>
>> A new XICSNative class is introduced to hold the MMIO region of the
>> ICPs. It also makes use of a hash table to associate the ICPState of a
>> CPU with a HW processor id, as this is the server number presented in
>> the XIVEs.
>>
>> The class routine 'find_icp' provide the way to do the lookups when
>> needed in the XICS base class.
>>
>> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>> [clg: - naming cleanups
>>       - replaced the use of xics_get_cpu_index_by_dt_id() by xics_find_icp()
>>       - added some qemu logging in case of error      
>>       - introduced a xics_native_find_icp routine to map icp index to
>>         cpu index      
>>       - moved sysbus mapping to chip ]
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>
>>  checkpatch complains on this one, but it seems to be a false positive :
>>  
>>  ERROR: spaces required around that '&' (ctx:WxV)
>>  #314: FILE: hw/intc/xics_native.c:246:
>>  +                        (gpointer) &xics->ss[cs->cpu_index]);
>>
>>  default-configs/ppc64-softmmu.mak |   3 +-
>>  hw/intc/Makefile.objs             |   1 +
>>  hw/intc/xics_native.c             | 327 ++++++++++++++++++++++++++++++++++++++
>>  include/hw/ppc/pnv.h              |  19 +++
>>  include/hw/ppc/xics.h             |  24 +++
>>  5 files changed, 373 insertions(+), 1 deletion(-)
>>  create mode 100644 hw/intc/xics_native.c
>>
>> diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak
>> index 67a9bcaa67fa..a22c93a48686 100644
>> --- a/default-configs/ppc64-softmmu.mak
>> +++ b/default-configs/ppc64-softmmu.mak
>> @@ -48,8 +48,9 @@ CONFIG_PLATFORM_BUS=y
>>  CONFIG_ETSEC=y
>>  CONFIG_LIBDECNUMBER=y
>>  # For pSeries
>> -CONFIG_XICS=$(CONFIG_PSERIES)
>> +CONFIG_XICS=$(or $(CONFIG_PSERIES),$(CONFIG_POWERNV))
>>  CONFIG_XICS_SPAPR=$(CONFIG_PSERIES)
>> +CONFIG_XICS_NATIVE=$(CONFIG_POWERNV)
>>  CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM))
>>  # For PReP
>>  CONFIG_MC146818RTC=y
>> diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
>> index 05ec21b21e0e..7be5dfc8347b 100644
>> --- a/hw/intc/Makefile.objs
>> +++ b/hw/intc/Makefile.objs
>> @@ -31,6 +31,7 @@ obj-$(CONFIG_RASPI) += bcm2835_ic.o bcm2836_control.o
>>  obj-$(CONFIG_SH4) += sh_intc.o
>>  obj-$(CONFIG_XICS) += xics.o
>>  obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o
>> +obj-$(CONFIG_XICS_NATIVE) += xics_native.o
>>  obj-$(CONFIG_XICS_KVM) += xics_kvm.o
>>  obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
>>  obj-$(CONFIG_S390_FLIC) += s390_flic.o
>> diff --git a/hw/intc/xics_native.c b/hw/intc/xics_native.c
>> new file mode 100644
>> index 000000000000..16413d807f65
>> --- /dev/null
>> +++ b/hw/intc/xics_native.c
>> @@ -0,0 +1,327 @@
>> +/*
>> + * QEMU PowerPC PowerNV machine model
>> + *
>> + * Native version of ICS/ICP
>> + *
>> + * Copyright (c) 2016, IBM Corporation.
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qapi/error.h"
>> +#include "qemu-common.h"
>> +#include "cpu.h"
>> +#include "hw/hw.h"
>> +#include "qemu/log.h"
>> +#include "qapi/error.h"
>> +
>> +#include "hw/ppc/fdt.h"
>> +#include "hw/ppc/xics.h"
>> +#include "hw/ppc/pnv.h"
>> +
>> +#include <libfdt.h>
>> +
>> +static void xics_native_reset(void *opaque)
>> +{
>> +    device_reset(DEVICE(opaque));
>> +}
>> +
>> +static void xics_native_initfn(Object *obj)
>> +{
>> +    XICSState *xics = XICS_COMMON(obj);
>> +
>> +    QLIST_INIT(&xics->ics);
> 
> This shouldn't be necessary, because it's done in the base class
> initfn (which is called before the subclass initfns).

True. This is a left over. 

>> +    /*
>> +     * Let's not forget to register a reset handler else the ICPs
>> +     * won't be initialized with the correct values. Trouble ahead !
>> +     */
>> +    qemu_register_reset(xics_native_reset, xics);
> 
> And. this shouldn't be necessary.  If you set dc->reset to the right
> thing in class_init (which the xics base class should already do) then
> the device model will automatically reset the device, you shouldn't
> need an extra reset handler.

ah ! That is what I thought but it seems that ->reset is only called when 
the device is under a bus. When creating devices with qdev_create(),
sysbus is used as a default but this is not the case for the xics_native 
instance. 

For the story, as the reset was not called, the ICP were bogus and xics 
was wrongly doing IPIs ... I pulled my hair on this.

>> +}
>> +
>> +static uint64_t xics_native_read(void *opaque, hwaddr addr, unsigned width)
>> +{
>> +    XICSState *s = opaque;
>> +    uint32_t cpu_id = (addr & (PNV_XICS_SIZE - 1)) >> 12;
>> +    bool byte0 = (width == 1 && (addr & 0x3) == 0);
>> +    uint64_t val = 0xffffffff;
>> +    ICPState *ss;
>> +
>> +    ss = xics_find_icp(s, cpu_id);
> 
> IIUC, cpu_id is the hardware id here... so why aren't you using the
> hash table you added to do this mapping?  

the lookup is done in the xics_find_icp() handler later.

> (although see comments elswhere, that I'm not sure that mapping belongs 
> within xics).

OK. I am open to any design :)

> Another option to avoid the lookup would be to register each icp page
> as a separate MR, then you could set the opaque pointer directly to ss
> instead of to the global xics.

Yes. This is much smarter. I like that !

In xics_native_realize(), we could loop on the array xics->ss and
create sub_memory regions for each ICP. cpu_index would be completely 
unused, which is even better.

We would need to change the API of the icp_set_*() calls to take 
an ICPState then, and not a XICSState as currently. The result would 
be more consistent I think but a backlink to the XICSState, or at 
least the ICSs, is needed for icp_resend().

What is your feeling on this ? before I start shuffling the code.

>> +    if (!ss) {
>> +        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP server %d\n", cpu_id);
>> +        return val;
>> +    }
>> +
>> +    switch (addr & 0xffc) {
>> +    case 0: /* poll */
>> +        val = icp_ipoll(ss, NULL);
>> +        if (byte0) {
>> +            val >>= 24;
>> +        } else if (width != 4) {
>> +            goto bad_access;
>> +        }
>> +        break;
>> +    case 4: /* xirr */
>> +        if (byte0) {
>> +            val = icp_ipoll(ss, NULL) >> 24;
>> +        } else if (width == 4) {
>> +            val = icp_accept(ss);
>> +        } else {
>> +            goto bad_access;
>> +        }
>> +        break;
>> +    case 12:
>> +        if (byte0) {
>> +            val = ss->mfrr;
>> +        } else {
>> +            goto bad_access;
>> +        }
>> +        break;
>> +    case 16:
>> +        if (width == 4) {
>> +            val = ss->links[0];
>> +        } else {
>> +            goto bad_access;
>> +        }
>> +        break;
>> +    case 20:
>> +        if (width == 4) {
>> +            val = ss->links[1];
>> +        } else {
>> +            goto bad_access;
>> +        }
>> +        break;
>> +    case 24:
>> +        if (width == 4) {
>> +            val = ss->links[2];
>> +        } else {
>> +            goto bad_access;
>> +        }
>> +        break;
>> +    default:
>> +bad_access:
>> +        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
>> +                      HWADDR_PRIx"/%d\n", addr, width);
>> +    }
>> +
>> +    return val;
>> +}
>> +
>> +static void xics_native_write(void *opaque, hwaddr addr, uint64_t val,
>> +                        unsigned width)
>> +{
>> +    XICSState *s = opaque;
>> +    uint32_t cpu_id = (addr & (PNV_XICS_SIZE - 1)) >> 12;
>> +    bool byte0 = (width == 1 && (addr & 0x3) == 0);
>> +    ICPState *ss;
>> +
>> +    ss = xics_find_icp(s, cpu_id);
>> +    if (!ss) {
>> +        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP server %d\n", cpu_id);
>> +        return;
>> +    }
>> +
>> +    switch (addr & 0xffc) {
>> +    case 4: /* xirr */
>> +        if (byte0) {
>> +            icp_set_cppr(s, cpu_id, val);
>> +        } else if (width == 4) {
>> +            icp_eoi(s, cpu_id, val);
>> +        } else {
>> +            goto bad_access;
>> +        }
>> +        break;
>> +    case 12:
>> +        if (byte0) {
>> +            icp_set_mfrr(s, cpu_id, val);
>> +        } else {
>> +            goto bad_access;
>> +        }
>> +        break;
>> +    case 16:
>> +        if (width == 4) {
>> +            ss->links[0] = val;
>> +        } else {
>> +            goto bad_access;
>> +        }
>> +        break;
>> +    case 20:
>> +        if (width == 4) {
>> +            ss->links[1] = val;
>> +        } else {
>> +            goto bad_access;
>> +        }
>> +        break;
>> +    case 24:
>> +        if (width == 4) {
>> +            ss->links[2] = val;
>> +        } else {
>> +            goto bad_access;
>> +        }
>> +        break;
>> +    default:
>> +bad_access:
>> +        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
>> +                      HWADDR_PRIx"/%d\n", addr, width);
>> +    }
>> +}
>> +
>> +static const MemoryRegionOps xics_native_ops = {
>> +    .read = xics_native_read,
>> +    .write = xics_native_write,
>> +    .valid.min_access_size = 1,
>> +    .valid.max_access_size = 4,
>> +    .impl.min_access_size = 1,
>> +    .impl.max_access_size = 4,
>> +    .endianness = DEVICE_BIG_ENDIAN,
>> +};
>> +
>> +static void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers,
>> +                                Error **errp)
>> +{
>> +    int i;
>> +
>> +    icp->nr_servers = nr_servers;
>> +
>> +    icp->ss = g_malloc0(icp->nr_servers * sizeof(ICPState));
>> +    for (i = 0; i < icp->nr_servers; i++) {
>> +        char name[32];
>> +        object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP);
>> +        snprintf(name, sizeof(name), "icp[%d]", i);
>> +        object_property_add_child(OBJECT(icp), name, OBJECT(&icp->ss[i]),
>> +                                  errp);
> 
> AFAICT this is identical to xics-spapr version and only difference to
> xics-kvm is it uses TYPE_KVM_ICP.  We should look at fusing these -
> maybe just having an icp typename in the xics class.

ok. I can add a preliminary patch for a common routine. 

>> +    }
>> +}
>> +
>> +static void xics_native_realize(DeviceState *dev, Error **errp)
>> +{
>> +    XICSState *xics = XICS_COMMON(dev);
>> +    XICSNative *xicsn = XICS_NATIVE(dev);
>> +    Error *error = NULL;
>> +    int i;
>> +
>> +    if (!xics->nr_servers) {
>> +        error_setg(errp, "Number of servers needs to be greater than 0");
>> +        return;
>> +    }
>> +
>> +    for (i = 0; i < xics->nr_servers; i++) {
>> +        object_property_set_bool(OBJECT(&xics->ss[i]), true, "realized",
>> +                                 &error);
>> +        if (error) {
>> +            error_propagate(errp, error);
>> +            return;
>> +        }
>> +    }
>> +
>> +    xicsn->pir_table = g_hash_table_new(g_direct_hash, g_direct_equal);
> 
> I'm not sure having this map inside xics makes sense.  Wouldn't it be
> more widely useful to have a fast PIR -> cpu_index map at the machine
> level? 

well, I would rather find a way of not using the cpu_index at all. We 
have HW cpu ids and ICPState to link together, if we could avoid the 
extra indexing layer, that would be better. If not, I would rather hide 
it deeply for people not to start using something which is a work around
IMO.

> That could also use the structure of the hwids to do a fast
> lookup neatly (collapse core id with a small table, recombine with
> chip and thread id).  A hash table seems a big hammer to throw at it.

yes. It was easy to use to start with but hopefully we will get rid of
it with your idea on the memory regions.

Thanks,

C. 


>> +    /* Register MMIO regions */
>> +    memory_region_init_io(&xicsn->icp_mmio, OBJECT(dev), &xics_native_ops,
>> +                          xicsn, "xics", PNV_XICS_SIZE);
>> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xicsn->icp_mmio);
>> +}
>> +
>> +static void xics_native_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
>> +{
>> +    CPUState *cs = CPU(cpu);
>> +    CPUPPCState *env = &cpu->env;
>> +    XICSNative *xicsn = XICS_NATIVE(xics);
>> +
>> +    assert(cs->cpu_index < xics->nr_servers);
>> +    g_hash_table_insert(xicsn->pir_table, GINT_TO_POINTER(env->spr[SPR_PIR]),
>> +                        (gpointer) &xics->ss[cs->cpu_index]);
>> +}
>> +
>> +static ICPState *xics_native_find_icp(XICSState *xics, int pir)
>> +{
>> +    XICSNative *xicsn = XICS_NATIVE(xics);
>> +    gpointer key, value;
>> +    gboolean found = g_hash_table_lookup_extended(xicsn->pir_table,
>> +                                GINT_TO_POINTER(pir), &key, &value);
>> +
>> +    assert(found);
>> +
>> +    return (ICPState *) value;
>> +}
>> +
>> +static void xics_native_class_init(ObjectClass *oc, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(oc);
>> +    XICSStateClass *xsc = XICS_NATIVE_CLASS(oc);
>> +
>> +    dc->realize = xics_native_realize;
>> +    xsc->set_nr_servers = xics_set_nr_servers;
>> +    xsc->cpu_setup = xics_native_cpu_setup;
>> +    xsc->find_icp = xics_native_find_icp;
>> +}
>> +
>> +static const TypeInfo xics_native_info = {
>> +    .name          = TYPE_XICS_NATIVE,
>> +    .parent        = TYPE_XICS_COMMON,
>> +    .instance_size = sizeof(XICSNative),
>> +    .class_size = sizeof(XICSStateClass),
>> +    .class_init    = xics_native_class_init,
>> +    .instance_init = xics_native_initfn,
>> +};
>> +
>> +static void xics_native_register_types(void)
>> +{
>> +    type_register_static(&xics_native_info);
>> +}
>> +
>> +type_init(xics_native_register_types)
>> +
>> +void xics_native_populate_icp(PnvChip *chip, void *fdt, int offset,
>> +                              uint32_t pir, uint32_t count)
>> +{
>> +    uint64_t addr;
>> +    char *name;
>> +    const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp";
>> +    uint32_t irange[2], i, rsize;
>> +    uint64_t *reg;
>> +
>> +    /*
>> +     * TODO: add multichip ICP BAR
>> +     */
>> +    addr = PNV_XICS_BASE | (pir << 12);
>> +
>> +    irange[0] = cpu_to_be32(pir);
>> +    irange[1] = cpu_to_be32(count);
>> +
>> +    rsize = sizeof(uint64_t) * 2 * count;
>> +    reg = g_malloc(rsize);
>> +    for (i = 0; i < count; i++) {
>> +        reg[i * 2] = cpu_to_be64(addr | ((pir + i) * 0x1000));
>> +        reg[i * 2 + 1] = cpu_to_be64(0x1000);
>> +    }
>> +
>> +    name = g_strdup_printf("interrupt-controller@%"PRIX64, addr);
>> +    offset = fdt_add_subnode(fdt, offset, name);
>> +    _FDT(offset);
>> +    g_free(name);
>> +
>> +    _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat))));
>> +    _FDT((fdt_setprop(fdt, offset, "reg", reg, rsize)));
>> +    _FDT((fdt_setprop_string(fdt, offset, "device_type",
>> +                              "PowerPC-External-Interrupt-Presentation")));
>> +    _FDT((fdt_setprop(fdt, offset, "interrupt-controller", NULL, 0)));
>> +    _FDT((fdt_setprop(fdt, offset, "ibm,interrupt-server-ranges",
>> +                       irange, sizeof(irange))));
>> +    _FDT((fdt_setprop_cell(fdt, offset, "#interrupt-cells", 1)));
>> +    _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0)));
>> +    g_free(reg);
>> +}
>> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
>> index 617c3fdd4f06..3f24b87d199b 100644
>> --- a/include/hw/ppc/pnv.h
>> +++ b/include/hw/ppc/pnv.h
>> @@ -125,4 +125,23 @@ typedef struct PnvMachineState {
>>  #define PNV_XSCOM_BASE(chip)                                            \
>>      (chip->xscom_base + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE)
>>  
>> +/*
>> + * XSCOM 0x20109CA defines the ICP BAR:
>> + *
>> + * 0:29   : bits 14 to 43 of address to define 1 MB region.
>> + * 30     : 1 to enable ICP to receive loads/stores against its BAR region
>> + * 31:63  : Constant 0
>> + *
>> + * Usually defined as :
>> + *
>> + *      0xffffe00200000000 -> 0x0003ffff80000000
>> + *      0xffffe00600000000 -> 0x0003ffff80100000
>> + *      0xffffe02200000000 -> 0x0003ffff80800000
>> + *      0xffffe02600000000 -> 0x0003ffff80900000
>> + *
>> + * TODO: make a macro using the chip hw id ?
>> + */
>> +#define PNV_XICS_BASE         0x0003ffff80000000ull
>> +#define PNV_XICS_SIZE         0x0000000000100000ull
>> +
>>  #endif /* _PPC_PNV_H */
>> diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
>> index dea9b92d4726..77ce786f000e 100644
>> --- a/include/hw/ppc/xics.h
>> +++ b/include/hw/ppc/xics.h
>> @@ -118,8 +118,27 @@ struct ICPState {
>>      uint8_t mfrr;
>>      qemu_irq output;
>>      bool cap_irq_xics_enabled;
>> +
>> +    /*
>> +     * for XICSNative (not used by Linux).
>> +     */
>> +    uint32_t links[3];
>>  };
>>  
>> +#define TYPE_XICS_NATIVE "xics-native"
>> +#define XICS_NATIVE(obj) OBJECT_CHECK(XICSNative, (obj), TYPE_XICS_NATIVE)
>> +#define XICS_NATIVE_CLASS(klass) \
>> +     OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS_NATIVE)
>> +#define XICS_NATIVE_GET_CLASS(obj) \
>> +     OBJECT_CLASS_CHECK(XICSStateClass, (obj), TYPE_XICS_NATIVE)
>> +
>> +typedef struct XICSNative {
>> +    XICSState parent_obj;
>> +
>> +    GHashTable *pir_table;
>> +    MemoryRegion icp_mmio;
>> +} XICSNative;
>> +
>>  #define TYPE_ICS_BASE "ics-base"
>>  #define ICS_BASE(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS_BASE)
>>  
>> @@ -210,4 +229,9 @@ void xics_hmp_info_pic(Monitor *mon, const QDict *qdict);
>>  ICPState *xics_find_icp(XICSState *xics, int cpu_index);
>>  void xics_insert_ics(XICSState *xics, ICSState *ics);
>>  
>> +typedef struct PnvChip PnvChip;
>> +
>> +void xics_native_populate_icp(PnvChip *chip, void *fdt, int offset,
>> +                              uint32_t base, uint32_t count);
>> +
>>  #endif /* XICS_H */
> 

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 15/20] ppc/xics: Add "native" XICS subclass
  2016-10-14  9:40     ` Cédric Le Goater
@ 2016-10-16 23:51       ` David Gibson
  0 siblings, 0 replies; 75+ messages in thread
From: David Gibson @ 2016-10-16 23:51 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 20963 bytes --]

On Fri, Oct 14, 2016 at 11:40:30AM +0200, Cédric Le Goater wrote:
> On 10/14/2016 08:10 AM, David Gibson wrote:
> > On Mon, Oct 03, 2016 at 09:24:51AM +0200, Cédric Le Goater wrote:
> >> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> >>
> >> This provides access to the MMIO based Interrupt Presentation
> >> Controllers (ICP) as found on a POWER8 system.
> >>
> >> A new XICSNative class is introduced to hold the MMIO region of the
> >> ICPs. It also makes use of a hash table to associate the ICPState of a
> >> CPU with a HW processor id, as this is the server number presented in
> >> the XIVEs.
> >>
> >> The class routine 'find_icp' provide the way to do the lookups when
> >> needed in the XICS base class.
> >>
> >> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> >> [clg: - naming cleanups
> >>       - replaced the use of xics_get_cpu_index_by_dt_id() by xics_find_icp()
> >>       - added some qemu logging in case of error      
> >>       - introduced a xics_native_find_icp routine to map icp index to
> >>         cpu index      
> >>       - moved sysbus mapping to chip ]
> >> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> >> ---
> >>
> >>  checkpatch complains on this one, but it seems to be a false positive :
> >>  
> >>  ERROR: spaces required around that '&' (ctx:WxV)
> >>  #314: FILE: hw/intc/xics_native.c:246:
> >>  +                        (gpointer) &xics->ss[cs->cpu_index]);
> >>
> >>  default-configs/ppc64-softmmu.mak |   3 +-
> >>  hw/intc/Makefile.objs             |   1 +
> >>  hw/intc/xics_native.c             | 327 ++++++++++++++++++++++++++++++++++++++
> >>  include/hw/ppc/pnv.h              |  19 +++
> >>  include/hw/ppc/xics.h             |  24 +++
> >>  5 files changed, 373 insertions(+), 1 deletion(-)
> >>  create mode 100644 hw/intc/xics_native.c
> >>
> >> diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak
> >> index 67a9bcaa67fa..a22c93a48686 100644
> >> --- a/default-configs/ppc64-softmmu.mak
> >> +++ b/default-configs/ppc64-softmmu.mak
> >> @@ -48,8 +48,9 @@ CONFIG_PLATFORM_BUS=y
> >>  CONFIG_ETSEC=y
> >>  CONFIG_LIBDECNUMBER=y
> >>  # For pSeries
> >> -CONFIG_XICS=$(CONFIG_PSERIES)
> >> +CONFIG_XICS=$(or $(CONFIG_PSERIES),$(CONFIG_POWERNV))
> >>  CONFIG_XICS_SPAPR=$(CONFIG_PSERIES)
> >> +CONFIG_XICS_NATIVE=$(CONFIG_POWERNV)
> >>  CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM))
> >>  # For PReP
> >>  CONFIG_MC146818RTC=y
> >> diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
> >> index 05ec21b21e0e..7be5dfc8347b 100644
> >> --- a/hw/intc/Makefile.objs
> >> +++ b/hw/intc/Makefile.objs
> >> @@ -31,6 +31,7 @@ obj-$(CONFIG_RASPI) += bcm2835_ic.o bcm2836_control.o
> >>  obj-$(CONFIG_SH4) += sh_intc.o
> >>  obj-$(CONFIG_XICS) += xics.o
> >>  obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o
> >> +obj-$(CONFIG_XICS_NATIVE) += xics_native.o
> >>  obj-$(CONFIG_XICS_KVM) += xics_kvm.o
> >>  obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
> >>  obj-$(CONFIG_S390_FLIC) += s390_flic.o
> >> diff --git a/hw/intc/xics_native.c b/hw/intc/xics_native.c
> >> new file mode 100644
> >> index 000000000000..16413d807f65
> >> --- /dev/null
> >> +++ b/hw/intc/xics_native.c
> >> @@ -0,0 +1,327 @@
> >> +/*
> >> + * QEMU PowerPC PowerNV machine model
> >> + *
> >> + * Native version of ICS/ICP
> >> + *
> >> + * Copyright (c) 2016, IBM Corporation.
> >> + *
> >> + * This library is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU Lesser General Public
> >> + * License as published by the Free Software Foundation; either
> >> + * version 2 of the License, or (at your option) any later version.
> >> + *
> >> + * This library is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> >> + * Lesser General Public License for more details.
> >> + *
> >> + * You should have received a copy of the GNU Lesser General Public
> >> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> >> + */
> >> +
> >> +#include "qemu/osdep.h"
> >> +#include "qapi/error.h"
> >> +#include "qemu-common.h"
> >> +#include "cpu.h"
> >> +#include "hw/hw.h"
> >> +#include "qemu/log.h"
> >> +#include "qapi/error.h"
> >> +
> >> +#include "hw/ppc/fdt.h"
> >> +#include "hw/ppc/xics.h"
> >> +#include "hw/ppc/pnv.h"
> >> +
> >> +#include <libfdt.h>
> >> +
> >> +static void xics_native_reset(void *opaque)
> >> +{
> >> +    device_reset(DEVICE(opaque));
> >> +}
> >> +
> >> +static void xics_native_initfn(Object *obj)
> >> +{
> >> +    XICSState *xics = XICS_COMMON(obj);
> >> +
> >> +    QLIST_INIT(&xics->ics);
> > 
> > This shouldn't be necessary, because it's done in the base class
> > initfn (which is called before the subclass initfns).
> 
> True. This is a left over. 
> 
> >> +    /*
> >> +     * Let's not forget to register a reset handler else the ICPs
> >> +     * won't be initialized with the correct values. Trouble ahead !
> >> +     */
> >> +    qemu_register_reset(xics_native_reset, xics);
> > 
> > And. this shouldn't be necessary.  If you set dc->reset to the right
> > thing in class_init (which the xics base class should already do) then
> > the device model will automatically reset the device, you shouldn't
> > need an extra reset handler.
> 
> ah ! That is what I thought but it seems that ->reset is only called when 
> the device is under a bus. When creating devices with qdev_create(),
> sysbus is used as a default but this is not the case for the xics_native 
> instance. 

Huh.. that's odd.  xics_native is a subclass of xics common which is a
subclass of sysbusdevice, which means it ought to appear on the
sysbus.

Ironically, spapr xics probably shouldn't be a sysbus device, because
it has no MMIO.  xics_native definitely should be though.

> For the story, as the reset was not called, the ICP were bogus and xics 
> was wrongly doing IPIs ... I pulled my hair on this.
> 
> >> +}
> >> +
> >> +static uint64_t xics_native_read(void *opaque, hwaddr addr, unsigned width)
> >> +{
> >> +    XICSState *s = opaque;
> >> +    uint32_t cpu_id = (addr & (PNV_XICS_SIZE - 1)) >> 12;
> >> +    bool byte0 = (width == 1 && (addr & 0x3) == 0);
> >> +    uint64_t val = 0xffffffff;
> >> +    ICPState *ss;
> >> +
> >> +    ss = xics_find_icp(s, cpu_id);
> > 
> > IIUC, cpu_id is the hardware id here... so why aren't you using the
> > hash table you added to do this mapping?  
> 
> the lookup is done in the xics_find_icp() handler later.

Ah, yes, that makes sense.

> > (although see comments elswhere, that I'm not sure that mapping belongs 
> > within xics).
> 
> OK. I am open to any design :)
> 
> > Another option to avoid the lookup would be to register each icp page
> > as a separate MR, then you could set the opaque pointer directly to ss
> > instead of to the global xics.
> 
> Yes. This is much smarter. I like that !
> 
> In xics_native_realize(), we could loop on the array xics->ss and
> create sub_memory regions for each ICP. cpu_index would be completely 
> unused, which is even better.
> 
> We would need to change the API of the icp_set_*() calls to take 
> an ICPState then, and not a XICSState as currently. The result would 
> be more consistent I think but a backlink to the XICSState, or at 
> least the ICSs, is needed for icp_resend().

> What is your feeling on this ? before I start shuffling the code.

Sounds good, I think it's worth adding the backlink for better
consistency across the pieces.

> >> +    if (!ss) {
> >> +        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP server %d\n", cpu_id);
> >> +        return val;
> >> +    }
> >> +
> >> +    switch (addr & 0xffc) {
> >> +    case 0: /* poll */
> >> +        val = icp_ipoll(ss, NULL);
> >> +        if (byte0) {
> >> +            val >>= 24;
> >> +        } else if (width != 4) {
> >> +            goto bad_access;
> >> +        }
> >> +        break;
> >> +    case 4: /* xirr */
> >> +        if (byte0) {
> >> +            val = icp_ipoll(ss, NULL) >> 24;
> >> +        } else if (width == 4) {
> >> +            val = icp_accept(ss);
> >> +        } else {
> >> +            goto bad_access;
> >> +        }
> >> +        break;
> >> +    case 12:
> >> +        if (byte0) {
> >> +            val = ss->mfrr;
> >> +        } else {
> >> +            goto bad_access;
> >> +        }
> >> +        break;
> >> +    case 16:
> >> +        if (width == 4) {
> >> +            val = ss->links[0];
> >> +        } else {
> >> +            goto bad_access;
> >> +        }
> >> +        break;
> >> +    case 20:
> >> +        if (width == 4) {
> >> +            val = ss->links[1];
> >> +        } else {
> >> +            goto bad_access;
> >> +        }
> >> +        break;
> >> +    case 24:
> >> +        if (width == 4) {
> >> +            val = ss->links[2];
> >> +        } else {
> >> +            goto bad_access;
> >> +        }
> >> +        break;
> >> +    default:
> >> +bad_access:
> >> +        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
> >> +                      HWADDR_PRIx"/%d\n", addr, width);
> >> +    }
> >> +
> >> +    return val;
> >> +}
> >> +
> >> +static void xics_native_write(void *opaque, hwaddr addr, uint64_t val,
> >> +                        unsigned width)
> >> +{
> >> +    XICSState *s = opaque;
> >> +    uint32_t cpu_id = (addr & (PNV_XICS_SIZE - 1)) >> 12;
> >> +    bool byte0 = (width == 1 && (addr & 0x3) == 0);
> >> +    ICPState *ss;
> >> +
> >> +    ss = xics_find_icp(s, cpu_id);
> >> +    if (!ss) {
> >> +        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP server %d\n", cpu_id);
> >> +        return;
> >> +    }
> >> +
> >> +    switch (addr & 0xffc) {
> >> +    case 4: /* xirr */
> >> +        if (byte0) {
> >> +            icp_set_cppr(s, cpu_id, val);
> >> +        } else if (width == 4) {
> >> +            icp_eoi(s, cpu_id, val);
> >> +        } else {
> >> +            goto bad_access;
> >> +        }
> >> +        break;
> >> +    case 12:
> >> +        if (byte0) {
> >> +            icp_set_mfrr(s, cpu_id, val);
> >> +        } else {
> >> +            goto bad_access;
> >> +        }
> >> +        break;
> >> +    case 16:
> >> +        if (width == 4) {
> >> +            ss->links[0] = val;
> >> +        } else {
> >> +            goto bad_access;
> >> +        }
> >> +        break;
> >> +    case 20:
> >> +        if (width == 4) {
> >> +            ss->links[1] = val;
> >> +        } else {
> >> +            goto bad_access;
> >> +        }
> >> +        break;
> >> +    case 24:
> >> +        if (width == 4) {
> >> +            ss->links[2] = val;
> >> +        } else {
> >> +            goto bad_access;
> >> +        }
> >> +        break;
> >> +    default:
> >> +bad_access:
> >> +        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
> >> +                      HWADDR_PRIx"/%d\n", addr, width);
> >> +    }
> >> +}
> >> +
> >> +static const MemoryRegionOps xics_native_ops = {
> >> +    .read = xics_native_read,
> >> +    .write = xics_native_write,
> >> +    .valid.min_access_size = 1,
> >> +    .valid.max_access_size = 4,
> >> +    .impl.min_access_size = 1,
> >> +    .impl.max_access_size = 4,
> >> +    .endianness = DEVICE_BIG_ENDIAN,
> >> +};
> >> +
> >> +static void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers,
> >> +                                Error **errp)
> >> +{
> >> +    int i;
> >> +
> >> +    icp->nr_servers = nr_servers;
> >> +
> >> +    icp->ss = g_malloc0(icp->nr_servers * sizeof(ICPState));
> >> +    for (i = 0; i < icp->nr_servers; i++) {
> >> +        char name[32];
> >> +        object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP);
> >> +        snprintf(name, sizeof(name), "icp[%d]", i);
> >> +        object_property_add_child(OBJECT(icp), name, OBJECT(&icp->ss[i]),
> >> +                                  errp);
> > 
> > AFAICT this is identical to xics-spapr version and only difference to
> > xics-kvm is it uses TYPE_KVM_ICP.  We should look at fusing these -
> > maybe just having an icp typename in the xics class.
> 
> ok. I can add a preliminary patch for a common routine. 
> 
> >> +    }
> >> +}
> >> +
> >> +static void xics_native_realize(DeviceState *dev, Error **errp)
> >> +{
> >> +    XICSState *xics = XICS_COMMON(dev);
> >> +    XICSNative *xicsn = XICS_NATIVE(dev);
> >> +    Error *error = NULL;
> >> +    int i;
> >> +
> >> +    if (!xics->nr_servers) {
> >> +        error_setg(errp, "Number of servers needs to be greater than 0");
> >> +        return;
> >> +    }
> >> +
> >> +    for (i = 0; i < xics->nr_servers; i++) {
> >> +        object_property_set_bool(OBJECT(&xics->ss[i]), true, "realized",
> >> +                                 &error);
> >> +        if (error) {
> >> +            error_propagate(errp, error);
> >> +            return;
> >> +        }
> >> +    }
> >> +
> >> +    xicsn->pir_table = g_hash_table_new(g_direct_hash, g_direct_equal);
> > 
> > I'm not sure having this map inside xics makes sense.  Wouldn't it be
> > more widely useful to have a fast PIR -> cpu_index map at the machine
> > level? 
> 
> well, I would rather find a way of not using the cpu_index at all. We 
> have HW cpu ids and ICPState to link together, if we could avoid the 
> extra indexing layer, that would be better. If not, I would rather hide 
> it deeply for people not to start using something which is a work around
> IMO.
> 
> > That could also use the structure of the hwids to do a fast
> > lookup neatly (collapse core id with a small table, recombine with
> > chip and thread id).  A hash table seems a big hammer to throw at it.
> 
> yes. It was easy to use to start with but hopefully we will get rid of
> it with your idea on the memory regions.

Ok, let's see how it turns out.

> 
> Thanks,
> 
> C. 
> 
> 
> >> +    /* Register MMIO regions */
> >> +    memory_region_init_io(&xicsn->icp_mmio, OBJECT(dev), &xics_native_ops,
> >> +                          xicsn, "xics", PNV_XICS_SIZE);
> >> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xicsn->icp_mmio);
> >> +}
> >> +
> >> +static void xics_native_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
> >> +{
> >> +    CPUState *cs = CPU(cpu);
> >> +    CPUPPCState *env = &cpu->env;
> >> +    XICSNative *xicsn = XICS_NATIVE(xics);
> >> +
> >> +    assert(cs->cpu_index < xics->nr_servers);
> >> +    g_hash_table_insert(xicsn->pir_table, GINT_TO_POINTER(env->spr[SPR_PIR]),
> >> +                        (gpointer) &xics->ss[cs->cpu_index]);
> >> +}
> >> +
> >> +static ICPState *xics_native_find_icp(XICSState *xics, int pir)
> >> +{
> >> +    XICSNative *xicsn = XICS_NATIVE(xics);
> >> +    gpointer key, value;
> >> +    gboolean found = g_hash_table_lookup_extended(xicsn->pir_table,
> >> +                                GINT_TO_POINTER(pir), &key, &value);
> >> +
> >> +    assert(found);
> >> +
> >> +    return (ICPState *) value;
> >> +}
> >> +
> >> +static void xics_native_class_init(ObjectClass *oc, void *data)
> >> +{
> >> +    DeviceClass *dc = DEVICE_CLASS(oc);
> >> +    XICSStateClass *xsc = XICS_NATIVE_CLASS(oc);
> >> +
> >> +    dc->realize = xics_native_realize;
> >> +    xsc->set_nr_servers = xics_set_nr_servers;
> >> +    xsc->cpu_setup = xics_native_cpu_setup;
> >> +    xsc->find_icp = xics_native_find_icp;
> >> +}
> >> +
> >> +static const TypeInfo xics_native_info = {
> >> +    .name          = TYPE_XICS_NATIVE,
> >> +    .parent        = TYPE_XICS_COMMON,
> >> +    .instance_size = sizeof(XICSNative),
> >> +    .class_size = sizeof(XICSStateClass),
> >> +    .class_init    = xics_native_class_init,
> >> +    .instance_init = xics_native_initfn,
> >> +};
> >> +
> >> +static void xics_native_register_types(void)
> >> +{
> >> +    type_register_static(&xics_native_info);
> >> +}
> >> +
> >> +type_init(xics_native_register_types)
> >> +
> >> +void xics_native_populate_icp(PnvChip *chip, void *fdt, int offset,
> >> +                              uint32_t pir, uint32_t count)
> >> +{
> >> +    uint64_t addr;
> >> +    char *name;
> >> +    const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp";
> >> +    uint32_t irange[2], i, rsize;
> >> +    uint64_t *reg;
> >> +
> >> +    /*
> >> +     * TODO: add multichip ICP BAR
> >> +     */
> >> +    addr = PNV_XICS_BASE | (pir << 12);
> >> +
> >> +    irange[0] = cpu_to_be32(pir);
> >> +    irange[1] = cpu_to_be32(count);
> >> +
> >> +    rsize = sizeof(uint64_t) * 2 * count;
> >> +    reg = g_malloc(rsize);
> >> +    for (i = 0; i < count; i++) {
> >> +        reg[i * 2] = cpu_to_be64(addr | ((pir + i) * 0x1000));
> >> +        reg[i * 2 + 1] = cpu_to_be64(0x1000);
> >> +    }
> >> +
> >> +    name = g_strdup_printf("interrupt-controller@%"PRIX64, addr);
> >> +    offset = fdt_add_subnode(fdt, offset, name);
> >> +    _FDT(offset);
> >> +    g_free(name);
> >> +
> >> +    _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat))));
> >> +    _FDT((fdt_setprop(fdt, offset, "reg", reg, rsize)));
> >> +    _FDT((fdt_setprop_string(fdt, offset, "device_type",
> >> +                              "PowerPC-External-Interrupt-Presentation")));
> >> +    _FDT((fdt_setprop(fdt, offset, "interrupt-controller", NULL, 0)));
> >> +    _FDT((fdt_setprop(fdt, offset, "ibm,interrupt-server-ranges",
> >> +                       irange, sizeof(irange))));
> >> +    _FDT((fdt_setprop_cell(fdt, offset, "#interrupt-cells", 1)));
> >> +    _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0)));
> >> +    g_free(reg);
> >> +}
> >> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> >> index 617c3fdd4f06..3f24b87d199b 100644
> >> --- a/include/hw/ppc/pnv.h
> >> +++ b/include/hw/ppc/pnv.h
> >> @@ -125,4 +125,23 @@ typedef struct PnvMachineState {
> >>  #define PNV_XSCOM_BASE(chip)                                            \
> >>      (chip->xscom_base + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE)
> >>  
> >> +/*
> >> + * XSCOM 0x20109CA defines the ICP BAR:
> >> + *
> >> + * 0:29   : bits 14 to 43 of address to define 1 MB region.
> >> + * 30     : 1 to enable ICP to receive loads/stores against its BAR region
> >> + * 31:63  : Constant 0
> >> + *
> >> + * Usually defined as :
> >> + *
> >> + *      0xffffe00200000000 -> 0x0003ffff80000000
> >> + *      0xffffe00600000000 -> 0x0003ffff80100000
> >> + *      0xffffe02200000000 -> 0x0003ffff80800000
> >> + *      0xffffe02600000000 -> 0x0003ffff80900000
> >> + *
> >> + * TODO: make a macro using the chip hw id ?
> >> + */
> >> +#define PNV_XICS_BASE         0x0003ffff80000000ull
> >> +#define PNV_XICS_SIZE         0x0000000000100000ull
> >> +
> >>  #endif /* _PPC_PNV_H */
> >> diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
> >> index dea9b92d4726..77ce786f000e 100644
> >> --- a/include/hw/ppc/xics.h
> >> +++ b/include/hw/ppc/xics.h
> >> @@ -118,8 +118,27 @@ struct ICPState {
> >>      uint8_t mfrr;
> >>      qemu_irq output;
> >>      bool cap_irq_xics_enabled;
> >> +
> >> +    /*
> >> +     * for XICSNative (not used by Linux).
> >> +     */
> >> +    uint32_t links[3];
> >>  };
> >>  
> >> +#define TYPE_XICS_NATIVE "xics-native"
> >> +#define XICS_NATIVE(obj) OBJECT_CHECK(XICSNative, (obj), TYPE_XICS_NATIVE)
> >> +#define XICS_NATIVE_CLASS(klass) \
> >> +     OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS_NATIVE)
> >> +#define XICS_NATIVE_GET_CLASS(obj) \
> >> +     OBJECT_CLASS_CHECK(XICSStateClass, (obj), TYPE_XICS_NATIVE)
> >> +
> >> +typedef struct XICSNative {
> >> +    XICSState parent_obj;
> >> +
> >> +    GHashTable *pir_table;
> >> +    MemoryRegion icp_mmio;
> >> +} XICSNative;
> >> +
> >>  #define TYPE_ICS_BASE "ics-base"
> >>  #define ICS_BASE(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS_BASE)
> >>  
> >> @@ -210,4 +229,9 @@ void xics_hmp_info_pic(Monitor *mon, const QDict *qdict);
> >>  ICPState *xics_find_icp(XICSState *xics, int cpu_index);
> >>  void xics_insert_ics(XICSState *xics, ICSState *ics);
> >>  
> >> +typedef struct PnvChip PnvChip;
> >> +
> >> +void xics_native_populate_icp(PnvChip *chip, void *fdt, int offset,
> >> +                              uint32_t base, uint32_t count);
> >> +
> >>  #endif /* XICS_H */
> > 
> 

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 17/20] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt
  2016-10-14  8:07     ` Cédric Le Goater
@ 2016-10-16 23:52       ` David Gibson
  2016-10-17  8:17         ` Cédric Le Goater
  0 siblings, 1 reply; 75+ messages in thread
From: David Gibson @ 2016-10-16 23:52 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 1397 bytes --]

On Fri, Oct 14, 2016 at 10:07:53AM +0200, Cédric Le Goater wrote:
> >> --- a/hw/ppc/pnv.c
> >> +++ b/hw/ppc/pnv.c
> >> @@ -318,15 +318,24 @@ static void ppc_powernv_reset(void)
> >>   * have a CPLD that will collect the SerIRQ and shoot them as a
> >>   * single level interrupt to the P8 chip. So let's setup a hook
> >>   * for doing just that.
> >> - *
> >> - * Note: The actual interrupt input isn't emulated yet, this will
> >> - * come with the PSI bridge model.
> >>   */
> >>  static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
> >>  {
> >> -    /* We don't yet emulate the PSI bridge which provides the external
> >> -     * interrupt, so just drop interrupts on the floor
> >> -     */
> >> +    static uint32_t irqstate;
> > 
> > Hmm.. static local with important state?  That it's not clear whether
> > it should be per-chip or not?
> > 
> > I'm not averse to hacks for early bringup, but it should at least have
> > a FIXME comment on it.
> 
> yes. I will see if I can make a "irq_cpld' attribute of the chip instead. 
> It should be cleaner.

Wouldn't it be in the machine, not the chip?  IIUC there's only one
CPLD on the whole board.

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 10/20] ppc/xics: Make the ICSState a list
  2016-10-14  7:35     ` Cédric Le Goater
@ 2016-10-16 23:53       ` David Gibson
  2016-10-17  8:13         ` Cédric Le Goater
  0 siblings, 1 reply; 75+ messages in thread
From: David Gibson @ 2016-10-16 23:53 UTC (permalink / raw)
  To: Cédric Le Goater
  Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel, Nikunj A Dadhania

[-- Attachment #1: Type: text/plain, Size: 1633 bytes --]

On Fri, Oct 14, 2016 at 09:35:46AM +0200, Cédric Le Goater wrote:
> On 10/14/2016 07:32 AM, David Gibson wrote:
> > On Mon, Oct 03, 2016 at 09:24:46AM +0200, Cédric Le Goater wrote:
> >> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> >>
> >> Instead of an array of fixed sized blocks, use a list, as we will need
> >> to have sources with variable number of interrupts. SPAPR only uses
> >> a single entry. Native will create more. If performance becomes an
> >> issue we can add some hashed lookup but for now this will do fine.
> >>
> >> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> >> [ move the initialization of list to xics_common_initfn,
> >>   restore xirr_owner after migration and move restoring to
> >>   icp_post_load]
> >> Signed-off-by: Nikunj A Dadhania <nikunj@linux.vnet.ibm.com>
> >> [ clg: removed the icp_post_load() changes from nikunj patchset v3:
> >>        http://patchwork.ozlabs.org/patch/646008/ ]
> >> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> > 
> > I think this and 11/20 are good enough and sufficiently standalone
> > that I've merged them into ppc-for-2.8.
> 
> These are a prereq for the patchset.

Sorry, I wasn't clear.  I realize they're prerequisites for the rest
of the patchset.  What I meant is that they don't require the rest of
the patchset to apply themselves, and they make some sense even
without the rest of the series.

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 10/20] ppc/xics: Make the ICSState a list
  2016-10-16 23:53       ` David Gibson
@ 2016-10-17  8:13         ` Cédric Le Goater
  0 siblings, 0 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-17  8:13 UTC (permalink / raw)
  To: David Gibson
  Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel, Nikunj A Dadhania

On 10/17/2016 01:53 AM, David Gibson wrote:
> On Fri, Oct 14, 2016 at 09:35:46AM +0200, Cédric Le Goater wrote:
>> On 10/14/2016 07:32 AM, David Gibson wrote:
>>> On Mon, Oct 03, 2016 at 09:24:46AM +0200, Cédric Le Goater wrote:
>>>> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>>>>
>>>> Instead of an array of fixed sized blocks, use a list, as we will need
>>>> to have sources with variable number of interrupts. SPAPR only uses
>>>> a single entry. Native will create more. If performance becomes an
>>>> issue we can add some hashed lookup but for now this will do fine.
>>>>
>>>> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>>>> [ move the initialization of list to xics_common_initfn,
>>>>   restore xirr_owner after migration and move restoring to
>>>>   icp_post_load]
>>>> Signed-off-by: Nikunj A Dadhania <nikunj@linux.vnet.ibm.com>
>>>> [ clg: removed the icp_post_load() changes from nikunj patchset v3:
>>>>        http://patchwork.ozlabs.org/patch/646008/ ]
>>>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>>>
>>> I think this and 11/20 are good enough and sufficiently standalone
>>> that I've merged them into ppc-for-2.8.
>>
>> These are a prereq for the patchset.
> 
> Sorry, I wasn't clear.  I realize they're prerequisites for the rest
> of the patchset.  What I meant is that they don't require the rest of
> the patchset to apply themselves, and they make some sense even
> without the rest of the series.

OK. I think there are a couple more cleanups we could do because the 
initialization is bit difficult to track. The way the ICPs are allocated 
is bit curious. I hope to have sometime for that after xics native is
merged.

I have made some migration tests on power8 tcg and kvm, we should be
fine.

Thanks,

C.
 

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 17/20] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt
  2016-10-16 23:52       ` David Gibson
@ 2016-10-17  8:17         ` Cédric Le Goater
  0 siblings, 0 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-17  8:17 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 10/17/2016 01:52 AM, David Gibson wrote:
> On Fri, Oct 14, 2016 at 10:07:53AM +0200, Cédric Le Goater wrote:
>>>> --- a/hw/ppc/pnv.c
>>>> +++ b/hw/ppc/pnv.c
>>>> @@ -318,15 +318,24 @@ static void ppc_powernv_reset(void)
>>>>   * have a CPLD that will collect the SerIRQ and shoot them as a
>>>>   * single level interrupt to the P8 chip. So let's setup a hook
>>>>   * for doing just that.
>>>> - *
>>>> - * Note: The actual interrupt input isn't emulated yet, this will
>>>> - * come with the PSI bridge model.
>>>>   */
>>>>  static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
>>>>  {
>>>> -    /* We don't yet emulate the PSI bridge which provides the external
>>>> -     * interrupt, so just drop interrupts on the floor
>>>> -     */
>>>> +    static uint32_t irqstate;
>>>
>>> Hmm.. static local with important state?  That it's not clear whether
>>> it should be per-chip or not?
>>>
>>> I'm not averse to hacks for early bringup, but it should at least have
>>> a FIXME comment on it.
>>
>> yes. I will see if I can make a "irq_cpld' attribute of the chip instead. 
>> It should be cleaner.
> 
> Wouldn't it be in the machine, not the chip?  IIUC there's only one
> CPLD on the whole board.

yes. You are right, it is a board device.

C.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 16/20] ppc/pnv: add a XICS native to each PowerNV chip
  2016-10-14  6:18   ` David Gibson
@ 2016-10-18 14:47     ` Cédric Le Goater
  0 siblings, 0 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-10-18 14:47 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 10/14/2016 08:18 AM, David Gibson wrote:
> On Mon, Oct 03, 2016 at 09:24:52AM +0200, Cédric Le Goater wrote:
>> and also link the XICS object to each core as it is needed to do the
>> CPU setup.
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>  hw/ppc/pnv.c         | 18 ++++++++++++++++++
>>  hw/ppc/pnv_core.c    | 25 +++++++++++++++++++++----
>>  include/hw/ppc/pnv.h |  2 ++
>>  3 files changed, 41 insertions(+), 4 deletions(-)
>>
>> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
>> index 4a71b18bf38b..6335ca11efe7 100644
>> --- a/hw/ppc/pnv.c
>> +++ b/hw/ppc/pnv.c
>> @@ -32,6 +32,7 @@
>>  #include "exec/address-spaces.h"
>>  #include "qemu/cutils.h"
>>  
>> +#include "hw/ppc/xics.h"
>>  #include "hw/ppc/pnv_xscom.h"
>>  
>>  #include "hw/isa/isa.h"
>> @@ -223,6 +224,7 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
>>      char *typename = pnv_core_typename(pcc->cpu_model);
>>      size_t typesize = object_type_get_instance_size(typename);
>>      int i;
>> +    int smt = 1; /* TCG does not support more for the moment */
>>  
>>      pnv_xscom_populate(chip, fdt, 0);
>>  
>> @@ -230,6 +232,9 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
>>          PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
>>  
>>          powernv_create_core_node(chip, pnv_core, fdt);
>> +
>> +        /* Interrupt presentation controllers (ICP). One per core. */
>> +        xics_native_populate_icp(chip, fdt, 0, pnv_core->pir, smt);
>>      }
>>  
>>      /* Put all the memory in one node on chip 0 until we find a way to
>> @@ -631,6 +636,9 @@ static void pnv_chip_init(Object *obj)
>>  
>>      object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
>>      object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
>> +
>> +    object_initialize(&chip->xics, sizeof(chip->xics), TYPE_XICS_NATIVE);
>> +    object_property_add_child(obj, "xics", OBJECT(&chip->xics), NULL);
>>  }
>>  
>>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
>> @@ -641,6 +649,7 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>      char *typename = pnv_core_typename(pcc->cpu_model);
>>      size_t typesize = object_type_get_instance_size(typename);
>>      int i, core_hwid;
>> +    int smt = 1; /* TCG does not support more for the moment */
>>  
>>      if (!object_class_by_name(typename)) {
>>          error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename);
>> @@ -662,6 +671,13 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>          return;
>>      }
>>  
>> +    /* Set up Interrupt Controller before we create the VCPUs */
>> +    object_property_set_int(OBJECT(&chip->xics), smp_cpus * smt / smp_threads,
>> +                            "nr_servers",  &error_fatal);
> 
> / smp_threads doesn't look right (more actual threads means less
> servers).  I think you just want smp_cpus * smp_threads.  Or actually 
> cores_per_chip * smp_threads.

yes this is a left over, working because everything is 1. So yes, it will be

	chip->nr_cores * smp_threads

Thanks,

C. 


>> +    object_property_set_bool(OBJECT(&chip->xics), true, "realized",
>> +                             &error_fatal);
>> +    sysbus_mmio_map(SYS_BUS_DEVICE(&chip->xics), 0, PNV_XICS_BASE);
>> +
>>      chip->cores = g_malloc0(typesize * chip->nr_cores);
>>  
>>      for (i = 0, core_hwid = 0; (core_hwid < sizeof(chip->cores_mask) * 8)
>> @@ -684,6 +700,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>          object_property_set_int(OBJECT(pnv_core),
>>                                  pcc->core_pir(chip, core_hwid),
>>                                  "pir", &error_fatal);
>> +        object_property_add_const_link(OBJECT(pnv_core), "xics",
>> +                                       OBJECT(&chip->xics), &error_fatal);
>>          object_property_set_bool(OBJECT(pnv_core), true, "realized",
>>                                   &error_fatal);
>>          object_unref(OBJECT(pnv_core));
>> diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
>> index a1c8a14f06b6..fe18e3150f78 100644
>> --- a/hw/ppc/pnv_core.c
>> +++ b/hw/ppc/pnv_core.c
>> @@ -24,6 +24,7 @@
>>  #include "hw/ppc/ppc.h"
>>  #include "hw/ppc/pnv.h"
>>  #include "hw/ppc/pnv_core.h"
>> +#include "hw/ppc/xics.h"
>>  
>>  static void powernv_cpu_reset(void *opaque)
>>  {
>> @@ -54,7 +55,7 @@ static void powernv_cpu_reset(void *opaque)
>>      env->msr |= MSR_HVB; /* Hypervisor mode */
>>  }
>>  
>> -static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
>> +static void powernv_cpu_init(PowerPCCPU *cpu, XICSState *xics, Error **errp)
>>  {
>>      CPUPPCState *env = &cpu->env;
>>  
>> @@ -63,6 +64,12 @@ static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
>>  
>>      qemu_register_reset(powernv_cpu_reset, cpu);
>>      powernv_cpu_reset(cpu);
>> +
>> +    /*
>> +     * XICS native cpu_setup() expects SPR_PIR to be set. So it needs
>> +     * to run after powernv_cpu_reset()
>> +     */
>> +    xics_cpu_setup(xics, cpu);
>>  }
>>  
>>  /*
>> @@ -110,7 +117,7 @@ static const MemoryRegionOps pnv_core_xscom_ops = {
>>      .endianness = DEVICE_BIG_ENDIAN,
>>  };
>>  
>> -static void pnv_core_realize_child(Object *child, Error **errp)
>> +static void pnv_core_realize_child(Object *child, XICSState *xics, Error **errp)
>>  {
>>      Error *local_err = NULL;
>>      CPUState *cs = CPU(child);
>> @@ -122,7 +129,7 @@ static void pnv_core_realize_child(Object *child, Error **errp)
>>          return;
>>      }
>>  
>> -    powernv_cpu_init(cpu, &local_err);
>> +    powernv_cpu_init(cpu, xics, &local_err);
>>      if (local_err) {
>>          error_propagate(errp, local_err);
>>          return;
>> @@ -140,6 +147,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
>>      void *obj;
>>      int i, j;
>>      char name[32];
>> +    XICSState *xics;
>>  
>>      pc->threads = g_malloc0(size * cc->nr_threads);
>>      for (i = 0; i < cc->nr_threads; i++) {
>> @@ -157,10 +165,19 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
>>          object_unref(obj);
>>      }
>>  
>> +    /* get XICS object from chip */
>> +    obj = object_property_get_link(OBJECT(dev), "xics", &local_err);
>> +    if (!obj) {
>> +        error_setg(errp, "%s: required link 'xics' not found: %s",
>> +                   __func__, error_get_pretty(local_err));
>> +        return;
>> +    }
>> +    xics = XICS_COMMON(obj);
>> +
>>      for (j = 0; j < cc->nr_threads; j++) {
>>          obj = pc->threads + j * size;
>>  
>> -        pnv_core_realize_child(obj, &local_err);
>> +        pnv_core_realize_child(obj, xics, &local_err);
>>          if (local_err) {
>>              goto err;
>>          }
>> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
>> index 3f24b87d199b..73d26c55d993 100644
>> --- a/include/hw/ppc/pnv.h
>> +++ b/include/hw/ppc/pnv.h
>> @@ -23,6 +23,7 @@
>>  #include "hw/sysbus.h"
>>  #include "hw/ppc/pnv_xscom.h"
>>  #include "hw/ppc/pnv_lpc.h"
>> +#include "hw/ppc/xics.h"
>>  
>>  #define TYPE_PNV_CHIP "powernv-chip"
>>  #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
>> @@ -55,6 +56,7 @@ typedef struct PnvChip {
>>      void      *cores;
>>  
>>      PnvLpcController lpc;
>> +    XICSNative   xics;
>>  } PnvChip;
>>  
>>  typedef struct PnvChipClass {
> 

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/20] ppc/pnv: add XSCOM infrastructure
  2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 06/20] ppc/pnv: add XSCOM infrastructure Cédric Le Goater
  2016-10-13  0:41   ` David Gibson
@ 2016-11-07  8:26   ` Olaf Hering
  2016-11-07  8:32     ` Cédric Le Goater
  1 sibling, 1 reply; 75+ messages in thread
From: Olaf Hering @ 2016-11-07  8:26 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, qemu-devel, David Gibson

[-- Attachment #1: Type: text/plain, Size: 769 bytes --]

On Mon, Oct 03, Cédric Le Goater wrote:

> +++ b/include/hw/ppc/pnv_xscom.h

> +typedef struct PnvChip PnvChip;

This causes a compile error with gcc 4.5.1:

...
[  552s] In file included from /usr/src/packages/BUILD/qemu-20161104T092624.9226682/hw/ppc/pnv.c:29:0:
[  552s] /usr/src/packages/BUILD/qemu-20161104T092624.9226682/include/hw/ppc/pnv.h:60:3: error: redefinition of typedef 'PnvChip'
[  552s] /usr/src/packages/BUILD/qemu-20161104T092624.9226682/include/hw/ppc/pnv_xscom.h:24:24: note: previous declaration of 'PnvChip' was here
[  552s] make[1]: *** [hw/ppc/pnv.o] Error 1
[  552s] make[1]: Leaving directory `/usr/src/packages/BUILD/qemu-20161104T092624.9226682/ppc64-softmmu'
[  552s] make: *** [subdir-ppc64-softmmu] Error 2
...

Olaf

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 163 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/20] ppc/pnv: add XSCOM infrastructure
  2016-11-07  8:26   ` Olaf Hering
@ 2016-11-07  8:32     ` Cédric Le Goater
  0 siblings, 0 replies; 75+ messages in thread
From: Cédric Le Goater @ 2016-11-07  8:32 UTC (permalink / raw)
  To: Olaf Hering; +Cc: qemu-ppc, qemu-devel, David Gibson

On 11/07/2016 09:26 AM, Olaf Hering wrote:
> On Mon, Oct 03, Cédric Le Goater wrote:
> 
>> +++ b/include/hw/ppc/pnv_xscom.h
> 
>> +typedef struct PnvChip PnvChip;
> 
> This causes a compile error with gcc 4.5.1:

Ah yes. That happens with "old" compilers. I will remove the extra definition. 

Thanks,

C. 

> ...
> [  552s] In file included from /usr/src/packages/BUILD/qemu-20161104T092624.9226682/hw/ppc/pnv.c:29:0:
> [  552s] /usr/src/packages/BUILD/qemu-20161104T092624.9226682/include/hw/ppc/pnv.h:60:3: error: redefinition of typedef 'PnvChip'
> [  552s] /usr/src/packages/BUILD/qemu-20161104T092624.9226682/include/hw/ppc/pnv_xscom.h:24:24: note: previous declaration of 'PnvChip' was here
> [  552s] make[1]: *** [hw/ppc/pnv.o] Error 1
> [  552s] make[1]: Leaving directory `/usr/src/packages/BUILD/qemu-20161104T092624.9226682/ppc64-softmmu'
> [  552s] make: *** [subdir-ppc64-softmmu] Error 2
> ...
> 
> Olaf
> 

^ permalink raw reply	[flat|nested] 75+ messages in thread

end of thread, other threads:[~2016-11-07  8:32 UTC | newest]

Thread overview: 75+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-10-03  7:24 [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space Cédric Le Goater
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 01/20] ppc/pnv: add skeleton PowerNV platform Cédric Le Goater
2016-10-07  4:14   ` David Gibson
2016-10-07  4:16     ` David Gibson
2016-10-07  7:38     ` Cédric Le Goater
2016-10-07 16:29       ` Jeff Cody
2016-10-07  8:36     ` Cédric Le Goater
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 02/20] ppc/pnv: add a PnvChip object Cédric Le Goater
2016-10-07  4:26   ` David Gibson
2016-10-07  9:16     ` Cédric Le Goater
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 03/20] ppc/pnv: add a core mask to PnvChip Cédric Le Goater
2016-10-07  4:32   ` David Gibson
2016-10-07  5:01     ` Benjamin Herrenschmidt
2016-10-07  5:11       ` David Gibson
2016-10-07  8:24         ` Cédric Le Goater
2016-10-10 12:56     ` Cédric Le Goater
2016-10-11 10:24       ` David Gibson
2016-10-12  8:53         ` Cédric Le Goater
2016-10-13  0:24           ` David Gibson
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 04/20] ppc/pnv: add a PIR handler " Cédric Le Goater
2016-10-07  4:34   ` David Gibson
2016-10-10  8:14     ` Cédric Le Goater
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 05/20] ppc/pnv: add a PnvCore object Cédric Le Goater
2016-10-07  4:52   ` David Gibson
2016-10-10  8:07     ` Cédric Le Goater
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 06/20] ppc/pnv: add XSCOM infrastructure Cédric Le Goater
2016-10-13  0:41   ` David Gibson
2016-10-13  6:26     ` Cédric Le Goater
2016-11-07  8:26   ` Olaf Hering
2016-11-07  8:32     ` Cédric Le Goater
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 07/20] ppc/pnv: add XSCOM handlers to PnvCore Cédric Le Goater
2016-10-13  0:51   ` David Gibson
2016-10-13  6:50     ` Cédric Le Goater
2016-10-13 22:24       ` David Gibson
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 08/20] ppc/pnv: add a LPC controller Cédric Le Goater
2016-10-13  2:52   ` David Gibson
2016-10-13  2:53     ` David Gibson
2016-10-13  6:31     ` Cédric Le Goater
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 09/20] ppc/pnv: add a ISA bus Cédric Le Goater
2016-10-13  2:58   ` David Gibson
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 10/20] ppc/xics: Make the ICSState a list Cédric Le Goater
2016-10-14  5:32   ` David Gibson
2016-10-14  7:35     ` Cédric Le Goater
2016-10-16 23:53       ` David Gibson
2016-10-17  8:13         ` Cédric Le Goater
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 11/20] ppc/xics: Split ICS into ics-base and ics class Cédric Le Goater
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 12/20] ppc/xics: Add xics to the monitor "info pic" command Cédric Le Goater
2016-10-14  5:30   ` David Gibson
2016-10-14  7:39     ` Cédric Le Goater
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 13/20] ppc/xics: introduce helpers to find an ICP from some (CPU) index Cédric Le Goater
2016-10-14  5:34   ` David Gibson
2016-10-14  7:44     ` Cédric Le Goater
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 14/20] ppc/xics: introduce a helper to insert a new ics Cédric Le Goater
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 15/20] ppc/xics: Add "native" XICS subclass Cédric Le Goater
2016-10-14  6:10   ` David Gibson
2016-10-14  9:40     ` Cédric Le Goater
2016-10-16 23:51       ` David Gibson
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 16/20] ppc/pnv: add a XICS native to each PowerNV chip Cédric Le Goater
2016-10-14  6:18   ` David Gibson
2016-10-18 14:47     ` Cédric Le Goater
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 17/20] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt Cédric Le Goater
2016-10-14  6:32   ` David Gibson
2016-10-14  7:13     ` Benjamin Herrenschmidt
2016-10-14  8:07     ` Cédric Le Goater
2016-10-16 23:52       ` David Gibson
2016-10-17  8:17         ` Cédric Le Goater
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 18/20] ppc/pnv: Add OCC model stub with interrupt support Cédric Le Goater
2016-10-14  6:34   ` David Gibson
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 19/20] ppc/pnv: Add Naples chip support for LPC interrupts Cédric Le Goater
2016-10-14  6:36   ` David Gibson
2016-10-14  7:47     ` Cédric Le Goater
2016-10-03  7:24 ` [Qemu-devel] [PATCH v4 20/20] ppc/pnv: add support for POWER9 LPC Controller Cédric Le Goater
2016-10-14  6:43   ` David Gibson
2016-10-03  7:59 ` [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space no-reply
2016-10-03  8:21   ` Cédric Le Goater

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.