All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v3 00/10] ppc/pnv: loading skiboot and booting the kernel
@ 2016-09-15 12:45 Cédric Le Goater
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 01/10] ppc/pnv: add skeleton PowerNV platform Cédric Le Goater
                   ` (9 more replies)
  0 siblings, 10 replies; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-15 12:45 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

Hello,

Here is a new version addressing all comments from v2 plus a couple of
important changes :

 - QOM'ification of all the models. should be clean.
 - some P9 basic support
 - PnvChip has its own routine to populate the device tree
 - rework of the XSCOM model to use an address space. see below.
 - LPC controller support and an ISA bus for a console 

The patchset is more or less organised the same way, 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 a proposal for a new model of XSCOM, the sideband bus which
gives controls to all the units in the POWER8 chip. It now uses a
specific address space to dispatch the accesses on the different chips
but clearly, I am not satisfied with it. I either got it all wrong or
the memory region do not permit what we are trying to achieve. Please
check out patch 7, I took ownership of the model and did my grumbling
there.

Last is an initial LPC controller, which gives us a console ! The
PowerNV platform now has enough support to start skiboot and see what
is happening. Linux also loads but stops quite early in xmon when
trying to setup the irqs :

    [    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

This is the next step : XICS native and support for real CPU
ids. There are some initial patches and hacks for that in my dev
branch. If you feel adventurous, you can give it a try here :

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

Just add on the command line :

     -smp cores=8

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


Thanks,

C. 

Benjamin Herrenschmidt (2):
  ppc/pnv: add skeleton PowerNV platform
  ppc/pnv: Add LPC controller and RTC

Cédric Le Goater (8):
  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
  monitor: fix crash for platforms without a CPU 0
  ppc/pnv: Add XSCOM infrastructure
  ppc/pnv: add a XScomDevice to PnvCore
  ppc/pnv: add a ISA bus

 default-configs/ppc64-softmmu.mak |   1 +
 hw/ppc/Makefile.objs              |   2 +
 hw/ppc/pnv.c                      | 804 ++++++++++++++++++++++++++++++++++++++
 hw/ppc/pnv_core.c                 | 239 +++++++++++
 hw/ppc/pnv_lpc.c                  | 455 +++++++++++++++++++++
 hw/ppc/pnv_xscom.c                | 308 +++++++++++++++
 include/hw/ppc/pnv.h              | 134 +++++++
 include/hw/ppc/pnv_core.h         |  50 +++
 include/hw/ppc/pnv_lpc.h          |  63 +++
 include/hw/ppc/pnv_xscom.h        |  96 +++++
 monitor.c                         |   2 +-
 11 files changed, 2153 insertions(+), 1 deletion(-)
 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_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_xscom.h

-- 
2.7.4

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

* [Qemu-devel] [PATCH v3 01/10] ppc/pnv: add skeleton PowerNV platform
  2016-09-15 12:45 [Qemu-devel] [PATCH v3 00/10] ppc/pnv: loading skiboot and booting the kernel Cédric Le Goater
@ 2016-09-15 12:45 ` Cédric Le Goater
  2016-09-20  7:53   ` David Gibson
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 02/10] ppc/pnv: add a PnvChip object Cédric Le Goater
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-15 12:45 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 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                      | 222 ++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h              |  38 +++++++
 4 files changed, 263 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 c4be59f638ed..516a6e25aba3 100644
--- a/default-configs/ppc64-softmmu.mak
+++ b/default-configs/ppc64-softmmu.mak
@@ -40,6 +40,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..ee78422b2eae
--- /dev/null
+++ b/hw/ppc/pnv.c
@@ -0,0 +1,222 @@
+/*
+ * 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@"TARGET_FMT_lx, 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 = g_strdup_printf(UUID_FMT, qemu_uuid[0], qemu_uuid[1],
+                          qemu_uuid[2], qemu_uuid[3], qemu_uuid[4],
+                          qemu_uuid[5], qemu_uuid[6], qemu_uuid[7],
+                          qemu_uuid[8], qemu_uuid[9], qemu_uuid[10],
+                          qemu_uuid[11], qemu_uuid[12], qemu_uuid[13],
+                          qemu_uuid[14], qemu_uuid[15]);
+    _FDT((fdt_setprop_string(fdt, 0, "vm,uuid", 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);
+
+    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] 53+ messages in thread

* [Qemu-devel] [PATCH v3 02/10] ppc/pnv: add a PnvChip object
  2016-09-15 12:45 [Qemu-devel] [PATCH v3 00/10] ppc/pnv: loading skiboot and booting the kernel Cédric Le Goater
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 01/10] ppc/pnv: add skeleton PowerNV platform Cédric Le Goater
@ 2016-09-15 12:45 ` Cédric Le Goater
  2016-09-20 13:50   ` David Gibson
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 03/10] ppc/pnv: add a core mask to PnvChip Cédric Le Goater
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-15 12:45 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.
 Maybe this object deserves its own file hw/ppc/pnv_chip.c ? 

 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         | 192 +++++++++++++++++++++++++++++++++++++++++++++++++--
 include/hw/ppc/pnv.h |  79 +++++++++++++++++++++
 2 files changed, 266 insertions(+), 5 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index ee78422b2eae..2aa5be56c8dc 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)));
@@ -117,11 +128,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;
 }
 
@@ -146,6 +156,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)) {
@@ -190,6 +202,170 @@ 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(PnvChipPower8E),
+    .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(PnvChipPower8),
+    .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(PnvChipPower8NVL),
+    .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(PnvChipPower9),
+    .class_init    = pnv_chip_power9_class_init,
+};
+
+static void pnv_chip_realize(DeviceState *dev, Error **errp)
+{
+    PnvChip *chip = PNV_CHIP(dev);
+    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+
+    if (pcc->realize) {
+        pcc->realize(chip, errp);
+    }
+}
+
+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);
+    int num_chips;
+
+    if (sscanf(value, "%d", &num_chips) != 1) {
+        error_setg(errp, "invalid num_chips property: '%s'", value);
+    }
+
+    /*
+     * FIXME: should we decide on how many chips we can create based
+     * on #cores and Venice vs. Murano vs. Naples chip type etc...,
+     */
+    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)
@@ -211,12 +387,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..6e6628edcf6a 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -20,6 +20,82 @@
 #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;
+
+    void (*realize)(PnvChip *dev, Error **errp);
+} PnvChipClass;
+
+#define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E"
+#define PNV_CHIP_POWER8E(obj) \
+    OBJECT_CHECK(PnvChipPower8E, (obj), TYPE_PNV_CHIP_POWER8E)
+
+typedef struct PnvChipPower8E {
+    PnvChip pnv_chip;
+} PnvChipPower8E;
+
+#define TYPE_PNV_CHIP_POWER8 TYPE_PNV_CHIP "-POWER8"
+#define PNV_CHIP_POWER8(obj) \
+    OBJECT_CHECK(PnvChipPower8, (obj), TYPE_PNV_CHIP_POWER8)
+
+typedef struct PnvChipPower8 {
+    PnvChip pnv_chip;
+} PnvChipPower8;
+
+#define TYPE_PNV_CHIP_POWER8NVL TYPE_PNV_CHIP "-POWER8NVL"
+#define PNV_CHIP_POWER8NVL(obj) \
+    OBJECT_CHECK(PnvChipPower8NVL, (obj), TYPE_PNV_CHIP_POWER8NVL)
+
+typedef struct PnvChipPower8NVL {
+    PnvChip pnv_chip;
+} PnvChipPower8NVL;
+
+#define TYPE_PNV_CHIP_POWER9 TYPE_PNV_CHIP "-POWER9"
+#define PNV_CHIP_POWER9(obj) \
+    OBJECT_CHECK(PnvChipPower9, (obj), TYPE_PNV_CHIP_POWER9)
+
+typedef struct PnvChipPower9 {
+    PnvChip pnv_chip;
+} PnvChipPower9;
+
+/*
+ * This generates a HW chip id depending on an index:
+ *
+ *    0x0, 0x1, 0x10, 0x11, 0x20, 0x21, ...
+ *
+ * Is this correct ?
+ */
+#define CHIP_HWID(i) ((((i) & 0x3e) << 3) | ((i) & 0x1))
 
 #define TYPE_POWERNV_MACHINE       MACHINE_TYPE_NAME("powernv")
 #define POWERNV_MACHINE(obj) \
@@ -31,6 +107,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] 53+ messages in thread

* [Qemu-devel] [PATCH v3 03/10] ppc/pnv: add a core mask to PnvChip
  2016-09-15 12:45 [Qemu-devel] [PATCH v3 00/10] ppc/pnv: loading skiboot and booting the kernel Cédric Le Goater
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 01/10] ppc/pnv: add skeleton PowerNV platform Cédric Le Goater
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 02/10] ppc/pnv: add a PnvChip object Cédric Le Goater
@ 2016-09-15 12:45 ` Cédric Le Goater
  2016-09-20 13:57   ` David Gibson
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 04/10] ppc/pnv: add a PIR handler " Cédric Le Goater
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-15 12:45 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 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         | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h |  4 ++++
 2 files changed, 70 insertions(+)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 2aa5be56c8dc..ec7dd6ac5ea1 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -226,11 +226,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);
@@ -239,6 +272,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";
 }
 
@@ -257,6 +291,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";
 }
 
@@ -275,6 +310,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";
 }
 
@@ -293,6 +329,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";
 }
 
@@ -303,11 +340,38 @@ static const TypeInfo pnv_chip_power9_info = {
     .class_init    = pnv_chip_power9_class_init,
 };
 
+static void pnv_chip_core_sanitize(PnvChip *chip)
+{
+    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+    int cores_max = hweight_long(pcc->cores_mask);
+
+    if (chip->nr_cores > cores_max) {
+        error_report("warning: too many cores for chip ! Limiting to %d",
+                     cores_max);
+        chip->nr_cores = 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_report("warning: invalid core mask for chip !");
+    }
+    chip->cores_mask &= pcc->cores_mask;
+}
+
 static void pnv_chip_realize(DeviceState *dev, Error **errp)
 {
     PnvChip *chip = PNV_CHIP(dev);
     PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
 
+    /* Early checks on the core settings */
+    pnv_chip_core_sanitize(chip);
+
     if (pcc->realize) {
         pcc->realize(chip, errp);
     }
@@ -315,6 +379,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
 
 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 6e6628edcf6a..cfc32586320f 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;
 
     void (*realize)(PnvChip *dev, Error **errp);
 } PnvChipClass;
-- 
2.7.4

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

* [Qemu-devel] [PATCH v3 04/10] ppc/pnv: add a PIR handler to PnvChip
  2016-09-15 12:45 [Qemu-devel] [PATCH v3 00/10] ppc/pnv: loading skiboot and booting the kernel Cédric Le Goater
                   ` (2 preceding siblings ...)
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 03/10] ppc/pnv: add a core mask to PnvChip Cédric Le Goater
@ 2016-09-15 12:45 ` Cédric Le Goater
  2016-09-21  1:29   ` David Gibson
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 05/10] ppc/pnv: add a PnvCore object Cédric Le Goater
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-15 12:45 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

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

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

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index ec7dd6ac5ea1..f4c125503249 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -238,6 +238,16 @@ static void ppc_powernv_init(MachineState *machine)
     g_free(chip_typename);
 }
 
+static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t core_id)
+{
+    return (chip->chip_id << 7) | (core_id << 3);
+}
+
+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>
@@ -273,6 +283,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";
 }
 
@@ -292,6 +303,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";
 }
 
@@ -311,6 +323,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";
 }
 
@@ -330,6 +343,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 cfc32586320f..2bd2294ac2a3 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -58,6 +58,7 @@ typedef struct PnvChipClass {
     uint64_t     cores_mask;
 
     void (*realize)(PnvChip *dev, Error **errp);
+    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] 53+ messages in thread

* [Qemu-devel] [PATCH v3 05/10] ppc/pnv: add a PnvCore object
  2016-09-15 12:45 [Qemu-devel] [PATCH v3 00/10] ppc/pnv: loading skiboot and booting the kernel Cédric Le Goater
                   ` (3 preceding siblings ...)
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 04/10] ppc/pnv: add a PIR handler " Cédric Le Goater
@ 2016-09-15 12:45 ` Cédric Le Goater
  2016-09-21  1:51   ` David Gibson
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 06/10] monitor: fix crash for platforms without a CPU 0 Cédric Le Goater
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-15 12:45 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. This id is stored in CPUState->cpu_index and
in PnvCore->pir. It is used to populate the device tree.

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

 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              | 186 ++++++++++++++++++++++++++++++++++++++++++++++
 hw/ppc/pnv_core.c         | 184 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h      |   3 +
 include/hw/ppc/pnv_core.h |  48 ++++++++++++
 5 files changed, 422 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 f4c125503249..7bc98f15f14b 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,
@@ -382,10 +531,47 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
 {
     PnvChip *chip = PNV_CHIP(dev);
     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);
 
+    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);
+
     if (pcc->realize) {
         pcc->realize(chip, errp);
     }
diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
new file mode 100644
index 000000000000..6fed5a208536
--- /dev/null
+++ b/hw/ppc/pnv_core.c
@@ -0,0 +1,184 @@
+/*
+ * 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;
+
+    cpu_reset(cs);
+
+    /*
+     * FIXME: cpu_index is non contiguous but xics native requires it
+     * to find its icp
+     */
+    env->spr[SPR_PIR] = cs->cpu_index;
+    env->spr[SPR_HIOR] = 0;
+    env->gpr[3] = POWERNV_FDT_ADDR;
+    env->nip = 0x10;
+    env->msr |= MSR_HVB;
+}
+
+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);
+
+    /* MSR[IP] doesn't exist nowadays */
+    env->msr_mask &= ~(1 << 6);
+
+    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++) {
+        CPUState *cs;
+
+        obj = pc->threads + i * size;
+
+        object_initialize(obj, size, typename);
+        cs = CPU(obj);
+        /*
+         * FIXME: cpu_index is non contiguous but xics native requires
+         * it to find its icp
+         */
+        cs->cpu_index = pc->pir + i;
+        snprintf(name, sizeof(name), "thread[%d]", i);
+        object_property_add_child(OBJECT(pc), name, obj, &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 2bd2294ac2a3..262faa59a75f 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 {
@@ -119,4 +120,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] 53+ messages in thread

* [Qemu-devel] [PATCH v3 06/10] monitor: fix crash for platforms without a CPU 0
  2016-09-15 12:45 [Qemu-devel] [PATCH v3 00/10] ppc/pnv: loading skiboot and booting the kernel Cédric Le Goater
                   ` (4 preceding siblings ...)
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 05/10] ppc/pnv: add a PnvCore object Cédric Le Goater
@ 2016-09-15 12:45 ` Cédric Le Goater
  2016-09-21  5:30   ` David Gibson
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure Cédric Le Goater
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-15 12:45 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

On PowerNV, CPU ids start at 0x8 or 0x20, we don't have a CPU 0
anymore. So let's use the first_cpu index to initialize the monitor.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 monitor.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/monitor.c b/monitor.c
index 5c003731e288..0192209a7915 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1023,7 +1023,7 @@ int monitor_set_cpu(int cpu_index)
 CPUState *mon_get_cpu(void)
 {
     if (!cur_mon->mon_cpu) {
-        monitor_set_cpu(0);
+        monitor_set_cpu(first_cpu->cpu_index);
     }
     cpu_synchronize_state(cur_mon->mon_cpu);
     return cur_mon->mon_cpu;
-- 
2.7.4

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

* [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure
  2016-09-15 12:45 [Qemu-devel] [PATCH v3 00/10] ppc/pnv: loading skiboot and booting the kernel Cédric Le Goater
                   ` (5 preceding siblings ...)
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 06/10] monitor: fix crash for platforms without a CPU 0 Cédric Le Goater
@ 2016-09-15 12:45 ` Cédric Le Goater
  2016-09-15 22:11   ` Benjamin Herrenschmidt
  2016-09-21  6:08   ` David Gibson
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 08/10] ppc/pnv: add a XScomDevice to PnvCore Cédric Le Goater
                   ` (2 subsequent siblings)
  9 siblings, 2 replies; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-15 12:45 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.

The PnvXScom object represents the ADU of a real system, it dispatches
XSCOM accesses to the targeted chiplets using a specific XSCOM address
space introduced for this purpose. A set of handlers to translate of
an XSCOM address into a PCB register address is added to the PnvChip
class to handle the differences between P9 and P8.

Unfortunately, due to the nature of the translation, we can not
translate before the dispatch in the address space. So an address
relative to the XSCOM mapping in main memory is used and the
translation is done in the XSCOM memory subregions with helpers.

To customize the device tree, a QOM InterfaceClass, PnvXScomInterface,
is provided with a devnode() handler. The device tree is populated by
simply looping on the PnvXScom children. Therefore, each model needing
custom nodes should declare itself as a child of the PnvXScom object
at instantiation time.

The PnvXScomInterface is also used to hold the address translation
handlers required by models having XSCOM memory subregions.

*Caveats*

 - I kept a standalone for PnvXScom object in this model to isolate
   the feature but it should be merged with the PnvChip and remove
   quite a few lines at the same time. This is minor.

 - The PCB translation is too much of a constraint for a specific
   XSCOM address space, unless someone can explain me how to address 8
   bytes at 0xb0021 and another 8 different bytes at 0xb0022. I don't
   think the address space and the memory regions were designed with
   this in mind. Please advise !

   So we should problably just kill the idea of a specific address
   space and use helpers in the XSCOM memory subregions to translate
   XSCOM addresses to PCB registers.

   We can improve the calls to initialize and to map the XSCOM subregions,
   memory_region_add_subregion() and memory_region_init_io(), with
   wrappers and make the translation a bit less painful.

 - The way the translation helpers of the PnvXScomInterface are
   initialized is a bit of a hack. Check :

      uint32_t pnv_xscom_pcba(PnvXScomInterface *dev, uint64_t addr)
      uint64_t pnv_xscom_addr(PnvXScomInterface *dev, uint32_t pcba)

   Ideas welcomed !

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

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

 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               |  51 ++++++++
 hw/ppc/pnv_xscom.c         | 308 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h       |   4 +
 include/hw/ppc/pnv_xscom.h |  74 +++++++++++
 5 files changed, 438 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 7bc98f15f14b..7dcdf18a9e6b 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_fdt(&chip->xscom, fdt, 0);
+
     for (i = 0; i < chip->nr_cores; i++) {
         PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
 
@@ -397,6 +401,28 @@ static uint32_t pnv_chip_core_pir_p9(PnvChip *chip, uint32_t core_id)
     return (chip->chip_id << 8) | (core_id << 2);
 }
 
+static uint64_t pnv_chip_xscom_addr_p8(uint32_t pcba)
+{
+    return (((uint64_t) pcba << 4) & ~0xfful) | ((pcba << 3) & 0x78);
+}
+
+static uint64_t pnv_chip_xscom_addr_p9(uint32_t pcba)
+{
+    return (uint64_t) pcba << 3;
+}
+
+static uint32_t pnv_chip_xscom_pcba_p8(uint64_t addr)
+{
+        addr &= (PNV_XSCOM_SIZE - 1);
+        return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf);
+}
+
+static uint32_t pnv_chip_xscom_pcba_p9(uint64_t addr)
+{
+        addr &= (PNV_XSCOM_SIZE - 1);
+        return addr >> 3;
+}
+
 /* Allowed core identifiers on a POWER8 Processor Chip :
  *
  * <EX0 reserved>
@@ -433,6 +459,8 @@ 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_addr = pnv_chip_xscom_addr_p8;
+    k->xscom_pcba = pnv_chip_xscom_pcba_p8;
     dc->desc = "PowerNV Chip POWER8E";
 }
 
@@ -453,6 +481,8 @@ 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_addr = pnv_chip_xscom_addr_p8;
+    k->xscom_pcba = pnv_chip_xscom_pcba_p8;
     dc->desc = "PowerNV Chip POWER8";
 }
 
@@ -473,6 +503,8 @@ 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_addr = pnv_chip_xscom_addr_p8;
+    k->xscom_pcba = pnv_chip_xscom_pcba_p8;
     dc->desc = "PowerNV Chip POWER8NVL";
 }
 
@@ -493,6 +525,8 @@ 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_addr = pnv_chip_xscom_addr_p9;
+    k->xscom_pcba = pnv_chip_xscom_pcba_p9;
     dc->desc = "PowerNV Chip POWER9";
 }
 
@@ -527,6 +561,16 @@ static void pnv_chip_core_sanitize(PnvChip *chip)
     chip->cores_mask &= pcc->cores_mask;
 }
 
+static void pnv_chip_init(Object *obj)
+{
+    PnvChip *chip = PNV_CHIP(obj);
+
+    object_initialize(&chip->xscom, sizeof(chip->xscom), TYPE_PNV_XSCOM);
+    object_property_add_child(obj, "xscom", OBJECT(&chip->xscom), NULL);
+    object_property_add_const_link(OBJECT(&chip->xscom), "chip",
+                                   OBJECT(chip), &error_abort);
+}
+
 static void pnv_chip_realize(DeviceState *dev, Error **errp)
 {
     PnvChip *chip = PNV_CHIP(dev);
@@ -540,6 +584,12 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
         return;
     }
 
+    /* XSCOM bridge */
+    object_property_set_bool(OBJECT(&chip->xscom), true, "realized",
+                             &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&chip->xscom), 0,
+                    PNV_XSCOM_BASE(chip->chip_id));
+
     /* Early checks on the core settings */
     pnv_chip_core_sanitize(chip);
 
@@ -597,6 +647,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..019cd85428de
--- /dev/null
+++ b/hw/ppc/pnv_xscom.c
@@ -0,0 +1,308 @@
+/*
+ * 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;
+    PowerPCCPU *cpu = POWERPC_CPU(cs);
+    CPUPPCState *env = &cpu->env;
+
+    cpu_synchronize_state(cs);
+    env->spr[SPR_HMER] |= hmer_bits;
+
+    /* XXX Need a CPU helper to set HMER, also handle gneeration
+     * of HMIs
+     */
+}
+
+static bool xscom_dispatch_read(PnvXScom *xscom, hwaddr addr, uint64_t *val)
+{
+    uint32_t success;
+    uint8_t data[8];
+
+    success = !address_space_rw(&xscom->xscom_as, addr, MEMTXATTRS_UNSPECIFIED,
+                                data, 8, false);
+    *val = (((uint64_t) data[0]) << 56 |
+            ((uint64_t) data[1]) << 48 |
+            ((uint64_t) data[2]) << 40 |
+            ((uint64_t) data[3]) << 32 |
+            ((uint64_t) data[4]) << 24 |
+            ((uint64_t) data[5]) << 16 |
+            ((uint64_t) data[6]) << 8  |
+            ((uint64_t) data[7]));
+    return success;
+}
+
+static bool xscom_dispatch_write(PnvXScom *xscom, hwaddr addr, uint64_t val)
+{
+    uint32_t success;
+    uint8_t data[8];
+
+    data[0] = (val >> 56) & 0xff;
+    data[1] = (val >> 48) & 0xff;
+    data[2] = (val >> 40) & 0xff;
+    data[3] = (val >> 32) & 0xff;
+    data[4] = (val >> 24) & 0xff;
+    data[5] = (val >> 16) & 0xff;
+    data[6] = (val >> 8) & 0xff;
+    data[7] = val & 0xff;
+
+    success = !address_space_rw(&xscom->xscom_as, addr, MEMTXATTRS_UNSPECIFIED,
+                           data, 8, true);
+    return success;
+}
+
+static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)
+{
+    PnvXScom *s = opaque;
+    uint32_t pcba = s->chip_class->xscom_pcba(addr);
+    uint64_t val = 0;
+
+    /* Handle some SCOMs here before dispatch */
+    switch (pcba) {
+    case 0xf000f:
+        val = s->chip_class->chip_cfam_id;
+        break;
+    case 0x1010c00:     /* PIBAM FIR */
+    case 0x1010c03:     /* PIBAM FIR MASK */
+    case 0x2020007:     /* ADU stuff */
+    case 0x2020009:     /* ADU stuff */
+    case 0x202000f:     /* ADU stuff */
+        val = 0;
+        break;
+    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 */
+        val = 0;
+        break;
+    default:
+        if (!xscom_dispatch_read(s, addr, &val)) {
+            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;
+        }
+    }
+
+    xscom_complete(HMER_XSCOM_DONE);
+    return val;
+}
+
+static void xscom_write(void *opaque, hwaddr addr, uint64_t val,
+                        unsigned width)
+{
+    PnvXScom *s = opaque;
+    uint32_t pcba = s->chip_class->xscom_pcba(addr);
+
+    /* Handle some SCOMs here before dispatch */
+    switch (pcba) {
+        /* We ignore writes to these */
+    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 */
+        break;
+    default:
+        if (!xscom_dispatch_write(s, addr, val)) {
+            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;
+        }
+    }
+
+    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,
+};
+
+static void pnv_xscom_realize(DeviceState *dev, Error **errp)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    PnvXScom *s = PNV_XSCOM(dev);
+    char *name;
+    Object *obj;
+    Error *err = NULL;
+
+    obj = object_property_get_link(OBJECT(s), "chip", &err);
+    if (!obj) {
+        error_setg(errp, "%s: required link 'chip' not found: %s",
+                     __func__, error_get_pretty(err));
+        return;
+    }
+
+    s->chip_class = PNV_CHIP_GET_CLASS(obj);
+    s->chip_id = PNV_CHIP(obj)->chip_id;
+
+    if (s->chip_id < 0) {
+        error_setg(errp, "invalid chip id '%d'", s->chip_id);
+        return;
+    }
+
+    name = g_strdup_printf("xscom-%x", s->chip_id);
+    memory_region_init_io(&s->mem, OBJECT(s), &pnv_xscom_ops, s, name,
+                          PNV_XSCOM_SIZE);
+    sysbus_init_mmio(sbd, &s->mem);
+
+    memory_region_init(&s->xscom_mr, OBJECT(s), name, PNV_XSCOM_SIZE);
+    address_space_init(&s->xscom_as, &s->xscom_mr, name);
+    g_free(name);
+
+}
+
+static void pnv_xscom_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = pnv_xscom_realize;
+}
+
+static const TypeInfo pnv_xscom_info = {
+    .name          = TYPE_PNV_XSCOM,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(PnvXScom),
+    .class_init    = pnv_xscom_class_init,
+};
+
+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_info);
+    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->devnode) {
+            _FDT((xc->devnode(xd, args->fdt, args->xscom_offset)));
+        }
+    }
+    return 0;
+}
+
+int pnv_xscom_populate_fdt(PnvXScom *adu, void *fdt, int root_offset)
+{
+    const char compat[] = "ibm,power8-xscom\0ibm,xscom";
+    uint64_t reg[] = { cpu_to_be64(PNV_XSCOM_BASE(adu->chip_id)),
+                       cpu_to_be64(PNV_XSCOM_SIZE) };
+    int xscom_offset;
+    ForeachPopulateArgs args;
+    char *name;
+
+    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", adu->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))));
+    _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat,
+                      sizeof(compat))));
+    _FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0)));
+
+    args.fdt = fdt;
+    args.xscom_offset = xscom_offset;
+
+    object_child_foreach(OBJECT(adu), xscom_populate_child, &args);
+    return 0;
+}
+
+/*
+ * XScom address translation depends on the chip type and not all
+ * objects have backlink to it. Here's a helper to handle this case.
+ * To be improved.
+ */
+uint32_t pnv_xscom_pcba(PnvXScomInterface *dev, uint64_t addr)
+{
+    PnvXScomInterfaceClass *xc = PNV_XSCOM_INTERFACE_GET_CLASS(dev);
+
+    if (!xc->xscom_pcba) {
+        PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
+        PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pnv->chips[0]);
+
+        xc->xscom_pcba = pcc->xscom_pcba;
+    }
+
+    return xc->xscom_pcba(addr);
+}
+
+uint64_t pnv_xscom_addr(PnvXScomInterface *dev, uint32_t pcba)
+{
+     PnvXScomInterfaceClass *xc = PNV_XSCOM_INTERFACE_GET_CLASS(dev);
+
+    if (!xc->xscom_addr) {
+        PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
+        PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pnv->chips[0]);
+
+        xc->xscom_addr = pcc->xscom_addr;
+    }
+
+    return xc->xscom_addr(pcba);
+}
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index 262faa59a75f..0371710b1882 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)
@@ -42,6 +43,7 @@ typedef struct PnvChip {
 
     /*< public >*/
     uint32_t     chip_id;
+    PnvXScom     xscom;
 
     uint32_t  nr_cores;
     uint64_t  cores_mask;
@@ -60,6 +62,8 @@ typedef struct PnvChipClass {
 
     void (*realize)(PnvChip *dev, Error **errp);
     uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
+    uint64_t (*xscom_addr)(uint32_t pcba);
+    uint32_t (*xscom_pcba)(uint64_t addr);
 } PnvChipClass;
 
 #define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E"
diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
new file mode 100644
index 000000000000..0a03d533db59
--- /dev/null
+++ b/include/hw/ppc/pnv_xscom.h
@@ -0,0 +1,74 @@
+/*
+ * 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 "hw/sysbus.h"
+
+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 (*devnode)(PnvXScomInterface *dev, void *fdt, int offset);
+
+    uint64_t (*xscom_addr)(uint32_t pcba);
+    uint32_t (*xscom_pcba)(uint64_t addr);
+} PnvXScomInterfaceClass;
+
+#define TYPE_PNV_XSCOM "pnv-xscom"
+#define PNV_XSCOM(obj) OBJECT_CHECK(PnvXScom, (obj), TYPE_PNV_XSCOM)
+
+typedef struct PnvChipClass PnvChipClass;
+
+typedef struct PnvXScom {
+    /*< private >*/
+    SysBusDevice parent_obj;
+    /*< public >*/
+
+    MemoryRegion mem;
+    int32_t chip_id;
+    PnvChipClass *chip_class;
+    MemoryRegion xscom_mr;
+    AddressSpace xscom_as;
+} PnvXScom;
+
+#define PNV_XSCOM_SIZE        0x800000000ull
+#define PNV_XSCOM_BASE(chip)                                    \
+    (0x3fc0000000000ull + ((uint64_t)(chip)) * PNV_XSCOM_SIZE)
+
+extern int pnv_xscom_populate_fdt(PnvXScom *xscom, void *fdt, int offset);
+
+/*
+ * helpers to translate to XScomm PCB addresses
+ */
+extern uint32_t pnv_xscom_pcba(PnvXScomInterface *dev, uint64_t addr);
+extern uint64_t pnv_xscom_addr(PnvXScomInterface *dev, uint32_t pcba);
+
+#endif /* _PPC_PNV_XSCOM_H */
-- 
2.7.4

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

* [Qemu-devel] [PATCH v3 08/10] ppc/pnv: add a XScomDevice to PnvCore
  2016-09-15 12:45 [Qemu-devel] [PATCH v3 00/10] ppc/pnv: loading skiboot and booting the kernel Cédric Le Goater
                   ` (6 preceding siblings ...)
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure Cédric Le Goater
@ 2016-09-15 12:45 ` Cédric Le Goater
  2016-09-21  6:12   ` David Gibson
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 09/10] ppc/pnv: add a LPC controller Cédric Le Goater
  2016-09-15 12:46 ` [Qemu-devel] [PATCH v3 10/10] ppc/pnv: add a ISA bus Cédric Le Goater
  9 siblings, 1 reply; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-15 12:45 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 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          | 55 ++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv_core.h  |  2 ++
 include/hw/ppc/pnv_xscom.h | 19 ++++++++++++++++
 4 files changed, 80 insertions(+)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 7dcdf18a9e6b..6a3d1fbf8403 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -619,6 +619,10 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
                                  &error_fatal);
         object_unref(OBJECT(pnv_core));
         i++;
+
+        memory_region_add_subregion(&chip->xscom.xscom_mr,
+                         pcc->xscom_addr(PNV_XSCOM_EX_CORE_BASE(core_hwid)),
+                         &PNV_CORE(pnv_core)->xscom_regs);
     }
     g_free(typename);
 
diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
index 6fed5a208536..81b83d0f41b3 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"
@@ -57,6 +58,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 DTS_RESULT0     0x50000
+#define DTS_RESULT1     0x50001
+
+static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr,
+                                    unsigned int width)
+{
+    uint32_t offset = pnv_xscom_pcba(opaque, addr);
+    uint64_t val = 0;
+
+    /* The result should be 38 C */
+    switch (offset) {
+    case DTS_RESULT0:
+        val = 0x26f024f023f0000ull;
+        break;
+    case 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;
@@ -117,6 +163,11 @@ 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_addr(PNV_XSCOM_INTERFACE(dev),
+                                                   PNV_XSCOM_EX_CORE_SIZE));
     return;
 
 err:
@@ -169,6 +220,10 @@ static void pnv_core_register_types(void)
             .instance_size = sizeof(PnvCore),
             .class_init = pnv_core_class_init,
             .class_data = (void *) pnv_core_models[i],
+            .interfaces    = (InterfaceInfo[]) {
+                { TYPE_PNV_XSCOM_INTERFACE },
+                { }
+            }
         };
         ti.name = pnv_core_typename(pnv_core_models[i]);
         type_register(&ti);
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 0a03d533db59..31e5e8847b90 100644
--- a/include/hw/ppc/pnv_xscom.h
+++ b/include/hw/ppc/pnv_xscom.h
@@ -63,6 +63,25 @@ typedef struct PnvXScom {
 #define PNV_XSCOM_BASE(chip)                                    \
     (0x3fc0000000000ull + ((uint64_t)(chip)) * PNV_XSCOM_SIZE)
 
+/*
+ * Layout of Xscom PCB addresses for 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 int pnv_xscom_populate_fdt(PnvXScom *xscom, void *fdt, int offset);
 
 /*
-- 
2.7.4

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

* [Qemu-devel] [PATCH v3 09/10] ppc/pnv: add a LPC controller
  2016-09-15 12:45 [Qemu-devel] [PATCH v3 00/10] ppc/pnv: loading skiboot and booting the kernel Cédric Le Goater
                   ` (7 preceding siblings ...)
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 08/10] ppc/pnv: add a XScomDevice to PnvCore Cédric Le Goater
@ 2016-09-15 12:45 ` Cédric Le Goater
  2016-09-15 22:13   ` Benjamin Herrenschmidt
  2016-09-21  6:23   ` David Gibson
  2016-09-15 12:46 ` [Qemu-devel] [PATCH v3 10/10] ppc/pnv: add a ISA bus Cédric Le Goater
  9 siblings, 2 replies; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-15 12:45 UTC (permalink / raw)
  To: qemu-ppc
  Cc: David Gibson, Benjamin Herrenschmidt, qemu-devel, Cedric Le Goater

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

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 (v3)
      - 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 ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/ppc/Makefile.objs       |   2 +-
 hw/ppc/pnv.c               |  15 ++
 hw/ppc/pnv_lpc.c           | 455 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h       |   3 +
 include/hw/ppc/pnv_lpc.h   |  63 +++++++
 include/hw/ppc/pnv_xscom.h |   3 +
 6 files changed, 540 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 6a3d1fbf8403..1aa7b8ee8903 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -569,6 +569,14 @@ static void pnv_chip_init(Object *obj)
     object_property_add_child(obj, "xscom", OBJECT(&chip->xscom), NULL);
     object_property_add_const_link(OBJECT(&chip->xscom), "chip",
                                    OBJECT(chip), &error_abort);
+
+    /*
+     * Add the lpc controller as an XScom child as we need to populate
+     * the device tree for the isa bus.
+     */
+    object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
+    object_property_add_child(OBJECT(&chip->xscom), "lpc",
+                              OBJECT(&chip->lpc), NULL);
 }
 
 static void pnv_chip_realize(DeviceState *dev, Error **errp)
@@ -626,6 +634,13 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
     }
     g_free(typename);
 
+    /* Create LPC controller */
+    object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
+                             &error_fatal);
+    memory_region_add_subregion(&chip->xscom.xscom_mr,
+                                pcc->xscom_addr(PNV_XSCOM_LPC_BASE),
+                                &chip->lpc.xscom_regs);
+
     if (pcc->realize) {
         pcc->realize(chip, errp);
     }
diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
new file mode 100644
index 000000000000..de4bab0ca085
--- /dev/null
+++ b/hw/ppc/pnv_lpc.c
@@ -0,0 +1,455 @@
+/*
+ * 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 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
+
+
+static int pnv_lpc_devnode(PnvXScomInterface *dev, void *fdt, int xscom_offset)
+{
+    const char compat[] = "ibm,power8-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_appendprop(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;
+}
+
+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 = pnv_xscom_pcba(opaque, addr);
+    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 = pnv_xscom_pcba(opaque, addr);
+
+    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_addr(PNV_XSCOM_INTERFACE(dev),
+                                         PNV_XSCOM_LPC_SIZE));
+}
+
+static void pnv_lpc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
+
+    xdc->devnode = pnv_lpc_devnode;
+
+    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 0371710b1882..a30579a5817f 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)
@@ -48,6 +49,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..3741e573b200
--- /dev/null
+++ b/include/hw/ppc/pnv_lpc.h
@@ -0,0 +1,63 @@
+/*
+ * 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;
+
+    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 31e5e8847b90..46b6ce7767c7 100644
--- a/include/hw/ppc/pnv_xscom.h
+++ b/include/hw/ppc/pnv_xscom.h
@@ -82,6 +82,9 @@ typedef struct PnvXScom {
 #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 int pnv_xscom_populate_fdt(PnvXScom *xscom, void *fdt, int offset);
 
 /*
-- 
2.7.4

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

* [Qemu-devel] [PATCH v3 10/10] ppc/pnv: add a ISA bus
  2016-09-15 12:45 [Qemu-devel] [PATCH v3 00/10] ppc/pnv: loading skiboot and booting the kernel Cédric Le Goater
                   ` (8 preceding siblings ...)
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 09/10] ppc/pnv: add a LPC controller Cédric Le Goater
@ 2016-09-15 12:46 ` Cédric Le Goater
  2016-09-21  6:30   ` David Gibson
  9 siblings, 1 reply; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-15 12:46 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         | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h |  2 ++
 2 files changed, 66 insertions(+)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 1aa7b8ee8903..fd6e4917133b 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
@@ -301,6 +305,57 @@ 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);
+
+    /* Instanciate ISA bus. let isa_bus_new() create its own bridge on
+     * sysbus otherwise devices speficied on the command line 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, 16);
+    } else {
+        irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, NULL, 16);
+    }
+
+    isa_bus_irqs(isa_bus, irqs);
+    return isa_bus;
+}
+
 static void ppc_powernv_init(MachineState *machine)
 {
     PnvMachineState *pnv = POWERNV_MACHINE(machine);
@@ -389,6 +444,15 @@ static void ppc_powernv_init(MachineState *machine)
         object_property_set_bool(chip, true, "realized", &error_fatal);
     }
     g_free(chip_typename);
+
+    /* Instanciate 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);
 }
 
 static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t core_id)
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index a30579a5817f..e75f937d40dd 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -123,6 +123,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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure Cédric Le Goater
@ 2016-09-15 22:11   ` Benjamin Herrenschmidt
  2016-09-21  5:56     ` David Gibson
  2016-09-21  6:08   ` David Gibson
  1 sibling, 1 reply; 53+ messages in thread
From: Benjamin Herrenschmidt @ 2016-09-15 22:11 UTC (permalink / raw)
  To: Cédric Le Goater, qemu-ppc; +Cc: David Gibson, qemu-devel

On Thu, 2016-09-15 at 14:45 +0200, Cédric Le Goater wrote:
>  - The PCB translation is too much of a constraint for a specific
>    XSCOM address space, unless someone can explain me how to address 8
>    bytes at 0xb0021 and another 8 different bytes at 0xb0022. I don't
>    think the address space and the memory regions were designed with
>    this in mind. Please advise !

I'd say just dispatch pcb_addr << 3 to the memory regions (which is
also the P9 translation iirc).

Just add a quirk to the ADU/XSCOM dispatch object to do the additional
unmangling needed on P7/P8

Cheers,
Ben.

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

* Re: [Qemu-devel] [PATCH v3 09/10] ppc/pnv: add a LPC controller
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 09/10] ppc/pnv: add a LPC controller Cédric Le Goater
@ 2016-09-15 22:13   ` Benjamin Herrenschmidt
  2016-09-16 17:35     ` Cédric Le Goater
  2016-09-21  6:23   ` David Gibson
  1 sibling, 1 reply; 53+ messages in thread
From: Benjamin Herrenschmidt @ 2016-09-15 22:13 UTC (permalink / raw)
  To: Cédric Le Goater, qemu-ppc; +Cc: David Gibson, qemu-devel

On Thu, 2016-09-15 at 14:45 +0200, Cédric Le Goater wrote:
> 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.

The version in my branch has this support btw.

Cheers,
Ben.

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

* Re: [Qemu-devel] [PATCH v3 09/10] ppc/pnv: add a LPC controller
  2016-09-15 22:13   ` Benjamin Herrenschmidt
@ 2016-09-16 17:35     ` Cédric Le Goater
  0 siblings, 0 replies; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-16 17:35 UTC (permalink / raw)
  To: Benjamin Herrenschmidt, qemu-ppc; +Cc: David Gibson, qemu-devel

On 09/16/2016 12:13 AM, Benjamin Herrenschmidt wrote:
> On Thu, 2016-09-15 at 14:45 +0200, Cédric Le Goater wrote:
>> 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.
> 
> The version in my branch has this support btw.

Yes. there is some of it in patch 10 : 

+    if (pcc->chip_type == PNV_CHIP_POWER8NVL) {
+        irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler, lpc, 16);
+    } else {
+        irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, NULL, 16);
+    }

but these are empty shell for the moment as I need PSI, and so XICS 
native, to go forward. Working on it.

Cheers,

C.

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

* Re: [Qemu-devel] [PATCH v3 01/10] ppc/pnv: add skeleton PowerNV platform
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 01/10] ppc/pnv: add skeleton PowerNV platform Cédric Le Goater
@ 2016-09-20  7:53   ` David Gibson
  2016-09-21  7:32     ` Cédric Le Goater
  0 siblings, 1 reply; 53+ messages in thread
From: David Gibson @ 2016-09-20  7:53 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

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

On Thu, Sep 15, 2016 at 02:45:51PM +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>

Looking pretty good, just a couple of minor details noted below.

> ---
> 
>  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                      | 222 ++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h              |  38 +++++++
>  4 files changed, 263 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 c4be59f638ed..516a6e25aba3 100644
> --- a/default-configs/ppc64-softmmu.mak
> +++ b/default-configs/ppc64-softmmu.mak
> @@ -40,6 +40,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..ee78422b2eae
> --- /dev/null
> +++ b/hw/ppc/pnv.c
> @@ -0,0 +1,222 @@
> +/*
> + * 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@"TARGET_FMT_lx, start);

This should be HWADDR_PRIx, to match the type of 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 = g_strdup_printf(UUID_FMT, qemu_uuid[0], qemu_uuid[1],
> +                          qemu_uuid[2], qemu_uuid[3], qemu_uuid[4],
> +                          qemu_uuid[5], qemu_uuid[6], qemu_uuid[7],
> +                          qemu_uuid[8], qemu_uuid[9], qemu_uuid[10],
> +                          qemu_uuid[11], qemu_uuid[12], qemu_uuid[13],
> +                          qemu_uuid[14], qemu_uuid[15]);
> +    _FDT((fdt_setprop_string(fdt, 0, "vm,uuid", 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);

It's not essential, but it's probably a good idea to put an fdt_pack()
at the end of this, to set the fdt's size header to the actual size
rather than the maximum 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);
> +
> +    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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 02/10] ppc/pnv: add a PnvChip object
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 02/10] ppc/pnv: add a PnvChip object Cédric Le Goater
@ 2016-09-20 13:50   ` David Gibson
  2016-09-21  7:44     ` Cédric Le Goater
  0 siblings, 1 reply; 53+ messages in thread
From: David Gibson @ 2016-09-20 13:50 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

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

On Thu, Sep 15, 2016 at 02:45:52PM +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.

Thanks for the above - that's basically exactly the sort of
description I was looking for.

> 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.
>  Maybe this object deserves its own file hw/ppc/pnv_chip.c ? 
> 
>  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         | 192 +++++++++++++++++++++++++++++++++++++++++++++++++--
>  include/hw/ppc/pnv.h |  79 +++++++++++++++++++++
>  2 files changed, 266 insertions(+), 5 deletions(-)
> 
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index ee78422b2eae..2aa5be56c8dc 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)));
> @@ -117,11 +128,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;
>  }
>  
> @@ -146,6 +156,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)) {
> @@ -190,6 +202,170 @@ 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(PnvChipPower8E),
> +    .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(PnvChipPower8),
> +    .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(PnvChipPower8NVL),
> +    .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(PnvChipPower9),
> +    .class_init    = pnv_chip_power9_class_init,
> +};
> +
> +static void pnv_chip_realize(DeviceState *dev, Error **errp)
> +{
> +    PnvChip *chip = PNV_CHIP(dev);
> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
> +
> +    if (pcc->realize) {
> +        pcc->realize(chip, errp);

Is there actually a need for this PnvChipClass::realize callback?
Couldn't the class_inits just direclty set DeviceClass::realize?

> +    }
> +}
> +
> +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);
> +    int num_chips;
> +
> +    if (sscanf(value, "%d", &num_chips) != 1) {
> +        error_setg(errp, "invalid num_chips property: '%s'", value);
> +    }

As a rule, I'd recommend strtol() instead of sscanf() - less
variations in the semantics by platform.

> +
> +    /*
> +     * FIXME: should we decide on how many chips we can create based
> +     * on #cores and Venice vs. Murano vs. Naples chip type etc...,
> +     */
> +    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);

Although come to that, why not use object_property_add_uint32_ptr() or
similar instead of property_add_str()?

> +    object_property_set_description(obj, "num-chips",
> +                                    "Specifies the number of processor chips",
> +                                    NULL);
>  }
>  
>  static void powernv_machine_class_init(ObjectClass *oc, void *data)
> @@ -211,12 +387,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..6e6628edcf6a 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -20,6 +20,82 @@
>  #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;
> +
> +    void (*realize)(PnvChip *dev, Error **errp);
> +} PnvChipClass;
> +
> +#define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E"
> +#define PNV_CHIP_POWER8E(obj) \
> +    OBJECT_CHECK(PnvChipPower8E, (obj), TYPE_PNV_CHIP_POWER8E)
> +
> +typedef struct PnvChipPower8E {
> +    PnvChip pnv_chip;
> +} PnvChipPower8E;

If you're not adding any fields you don't need to define an actual
type for the subclass.

And if you *are* defining a type, then you need to make sure you set
the instance size in the type definition to match it.

I suspect you're going to want P8 family and P9 family intermediate
classes fairly early on.

> +
> +#define TYPE_PNV_CHIP_POWER8 TYPE_PNV_CHIP "-POWER8"
> +#define PNV_CHIP_POWER8(obj) \
> +    OBJECT_CHECK(PnvChipPower8, (obj), TYPE_PNV_CHIP_POWER8)
> +
> +typedef struct PnvChipPower8 {
> +    PnvChip pnv_chip;
> +} PnvChipPower8;
> +
> +#define TYPE_PNV_CHIP_POWER8NVL TYPE_PNV_CHIP "-POWER8NVL"
> +#define PNV_CHIP_POWER8NVL(obj) \
> +    OBJECT_CHECK(PnvChipPower8NVL, (obj), TYPE_PNV_CHIP_POWER8NVL)
> +
> +typedef struct PnvChipPower8NVL {
> +    PnvChip pnv_chip;
> +} PnvChipPower8NVL;
> +
> +#define TYPE_PNV_CHIP_POWER9 TYPE_PNV_CHIP "-POWER9"
> +#define PNV_CHIP_POWER9(obj) \
> +    OBJECT_CHECK(PnvChipPower9, (obj), TYPE_PNV_CHIP_POWER9)
> +
> +typedef struct PnvChipPower9 {
> +    PnvChip pnv_chip;
> +} PnvChipPower9;
> +
> +/*
> + * This generates a HW chip id depending on an index:
> + *
> + *    0x0, 0x1, 0x10, 0x11, 0x20, 0x21, ...
> + *
> + * Is this correct ?
> + */
> +#define CHIP_HWID(i) ((((i) & 0x3e) << 3) | ((i) & 0x1))
>  
>  #define TYPE_POWERNV_MACHINE       MACHINE_TYPE_NAME("powernv")
>  #define POWERNV_MACHINE(obj) \
> @@ -31,6 +107,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] 53+ messages in thread

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

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

On Thu, Sep 15, 2016 at 02:45:53PM +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.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
> 
>  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         | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h |  4 ++++
>  2 files changed, 70 insertions(+)
> 
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 2aa5be56c8dc..ec7dd6ac5ea1 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -226,11 +226,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);

If you're selecting the default, why set it at all?

>          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);
> @@ -239,6 +272,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";
>  }
>  
> @@ -257,6 +291,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";
>  }
>  
> @@ -275,6 +310,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";
>  }
>  
> @@ -293,6 +329,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";
>  }
>  
> @@ -303,11 +340,38 @@ static const TypeInfo pnv_chip_power9_info = {
>      .class_init    = pnv_chip_power9_class_init,
>  };
>  
> +static void pnv_chip_core_sanitize(PnvChip *chip)
> +{
> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
> +    int cores_max = hweight_long(pcc->cores_mask);
> +
> +    if (chip->nr_cores > cores_max) {
> +        error_report("warning: too many cores for chip ! Limiting to %d",
> +                     cores_max);
> +        chip->nr_cores = cores_max;

This is called from realize() which takes an errp argument.  It would
be better to pass that in and actually report the error up the chain
here, rather than assuming a warning is the right answer.

Also.. shouldn't you actually check nr_cores against the chip local
mask instead of the class one?

> +    }
> +
> +    /* 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_report("warning: invalid core mask for chip !");

Likewise here you should propagate the error.

> +    }
> +    chip->cores_mask &= pcc->cores_mask;
> +}
> +
>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
>  {
>      PnvChip *chip = PNV_CHIP(dev);
>      PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
>  
> +    /* Early checks on the core settings */
> +    pnv_chip_core_sanitize(chip);
> +
>      if (pcc->realize) {
>          pcc->realize(chip, errp);
>      }
> @@ -315,6 +379,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>  
>  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 6e6628edcf6a..cfc32586320f 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;
>  
>      void (*realize)(PnvChip *dev, Error **errp);
>  } 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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 04/10] ppc/pnv: add a PIR handler to PnvChip
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 04/10] ppc/pnv: add a PIR handler " Cédric Le Goater
@ 2016-09-21  1:29   ` David Gibson
  2016-09-21  1:52     ` Benjamin Herrenschmidt
  2016-09-21  7:05     ` Cédric Le Goater
  0 siblings, 2 replies; 53+ messages in thread
From: David Gibson @ 2016-09-21  1:29 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

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

On Thu, Sep 15, 2016 at 02:45:54PM +0200, Cédric Le Goater wrote:
> P9 and P8 have some differences in the CPU PIR encoding.

The thread id isn't in the PIR at all?

> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>  hw/ppc/pnv.c         | 14 ++++++++++++++
>  include/hw/ppc/pnv.h |  1 +
>  2 files changed, 15 insertions(+)
> 
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index ec7dd6ac5ea1..f4c125503249 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -238,6 +238,16 @@ static void ppc_powernv_init(MachineState *machine)
>      g_free(chip_typename);
>  }
>  
> +static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t core_id)
> +{
> +    return (chip->chip_id << 7) | (core_id << 3);
> +}
> +
> +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>
> @@ -273,6 +283,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";
>  }
>  
> @@ -292,6 +303,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";
>  }
>  
> @@ -311,6 +323,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";
>  }
>  
> @@ -330,6 +343,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 cfc32586320f..2bd2294ac2a3 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -58,6 +58,7 @@ typedef struct PnvChipClass {
>      uint64_t     cores_mask;
>  
>      void (*realize)(PnvChip *dev, Error **errp);
> +    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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 05/10] ppc/pnv: add a PnvCore object
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 05/10] ppc/pnv: add a PnvCore object Cédric Le Goater
@ 2016-09-21  1:51   ` David Gibson
  2016-09-21  2:05     ` Benjamin Herrenschmidt
                       ` (2 more replies)
  0 siblings, 3 replies; 53+ messages in thread
From: David Gibson @ 2016-09-21  1:51 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

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

On Thu, Sep 15, 2016 at 02:45:55PM +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. This id is stored in CPUState->cpu_index and
> in PnvCore->pir. It is used to populate the device tree.

Ok, as noted elsewhere, I think you need to disassociate the PIR value
from the cpu_index.  It may be a little less elegant, but it'll make
your life much easier in the short and medium term.

Apart from that, this looks pretty good, though I have one suggested
cleanup below.

> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
> 
>  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              | 186 ++++++++++++++++++++++++++++++++++++++++++++++
>  hw/ppc/pnv_core.c         | 184 +++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h      |   3 +
>  include/hw/ppc/pnv_core.h |  48 ++++++++++++
>  5 files changed, 422 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 f4c125503249..7bc98f15f14b 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,
> @@ -382,10 +531,47 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>  {
>      PnvChip *chip = PNV_CHIP(dev);
>      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);
>  
> +    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);
> +
>      if (pcc->realize) {
>          pcc->realize(chip, errp);
>      }
> diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
> new file mode 100644
> index 000000000000..6fed5a208536
> --- /dev/null
> +++ b/hw/ppc/pnv_core.c
> @@ -0,0 +1,184 @@
> +/*
> + * 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;
> +
> +    cpu_reset(cs);
> +
> +    /*
> +     * FIXME: cpu_index is non contiguous but xics native requires it
> +     * to find its icp
> +     */
> +    env->spr[SPR_PIR] = cs->cpu_index;
> +    env->spr[SPR_HIOR] = 0;
> +    env->gpr[3] = POWERNV_FDT_ADDR;
> +    env->nip = 0x10;
> +    env->msr |= MSR_HVB;
> +}
> +
> +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);
> +
> +    /* MSR[IP] doesn't exist nowadays */
> +    env->msr_mask &= ~(1 << 6);

If MSR[IP] is gone, shouldn't that be reflected in the MSR masks
stored for the actual vcpu models, rather than imposed from the
machine or core code?

> +    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++) {
> +        CPUState *cs;
> +
> +        obj = pc->threads + i * size;
> +
> +        object_initialize(obj, size, typename);
> +        cs = CPU(obj);
> +        /*
> +         * FIXME: cpu_index is non contiguous but xics native requires
> +         * it to find its icp
> +         */
> +        cs->cpu_index = pc->pir + i;
> +        snprintf(name, sizeof(name), "thread[%d]", i);
> +        object_property_add_child(OBJECT(pc), name, obj, &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);
> +    }
> +}

I think you might be able to use a single chip type table and
construct both the chip types and the corresponding core types from
it.

> +
> +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 2bd2294ac2a3..262faa59a75f 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 {
> @@ -119,4 +120,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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 04/10] ppc/pnv: add a PIR handler to PnvChip
  2016-09-21  1:29   ` David Gibson
@ 2016-09-21  1:52     ` Benjamin Herrenschmidt
  2016-09-21  7:05     ` Cédric Le Goater
  1 sibling, 0 replies; 53+ messages in thread
From: Benjamin Herrenschmidt @ 2016-09-21  1:52 UTC (permalink / raw)
  To: David Gibson, Cédric Le Goater; +Cc: qemu-ppc, qemu-devel

On Wed, 2016-09-21 at 11:29 +1000, David Gibson wrote:
> On Thu, Sep 15, 2016 at 02:45:54PM +0200, Cédric Le Goater wrote:
> > 
> > P9 and P8 have some differences in the CPU PIR encoding.
> 
> The thread id isn't in the PIR at all?

Yes it is. Though on P9 there could be different encodings depending on
some kind of "mode" I can't quite yet get into details about.

Cheers,
Ben.

> > 
> > 
> > Signed-off-by: Cédric Le Goater <clg@kaod.org>
> > ---
> >  hw/ppc/pnv.c         | 14 ++++++++++++++
> >  include/hw/ppc/pnv.h |  1 +
> >  2 files changed, 15 insertions(+)
> > 
> > diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> > index ec7dd6ac5ea1..f4c125503249 100644
> > --- a/hw/ppc/pnv.c
> > +++ b/hw/ppc/pnv.c
> > @@ -238,6 +238,16 @@ static void ppc_powernv_init(MachineState
> > *machine)
> >      g_free(chip_typename);
> >  }
> >  
> > +static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t
> > core_id)
> > +{
> > +    return (chip->chip_id << 7) | (core_id << 3);
> > +}
> > +
> > +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>
> > @@ -273,6 +283,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";
> >  }
> >  
> > @@ -292,6 +303,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";
> >  }
> >  
> > @@ -311,6 +323,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";
> >  }
> >  
> > @@ -330,6 +343,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 cfc32586320f..2bd2294ac2a3 100644
> > --- a/include/hw/ppc/pnv.h
> > +++ b/include/hw/ppc/pnv.h
> > @@ -58,6 +58,7 @@ typedef struct PnvChipClass {
> >      uint64_t     cores_mask;
> >  
> >      void (*realize)(PnvChip *dev, Error **errp);
> > +    uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
> >  } PnvChipClass;
> >  
> >  #define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E"
> 

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

* Re: [Qemu-devel] [PATCH v3 05/10] ppc/pnv: add a PnvCore object
  2016-09-21  1:51   ` David Gibson
@ 2016-09-21  2:05     ` Benjamin Herrenschmidt
  2016-09-21  2:15       ` David Gibson
  2016-09-21  7:15       ` Cédric Le Goater
  2016-09-21  7:09     ` Cédric Le Goater
  2016-09-21 14:24     ` Cédric Le Goater
  2 siblings, 2 replies; 53+ messages in thread
From: Benjamin Herrenschmidt @ 2016-09-21  2:05 UTC (permalink / raw)
  To: David Gibson, Cédric Le Goater; +Cc: qemu-ppc, qemu-devel

On Wed, 2016-09-21 at 11:51 +1000, David Gibson wrote:
> Ok, as noted elsewhere, I think you need to disassociate the PIR value
> from the cpu_index.  It may be a little less elegant, but it'll make
> your life much easier in the short and medium term.
> 
> Apart from that, this looks pretty good, though I have one suggested
> cleanup below.

As long as the PIR value is what is used everywhere, ie, interrupt
controller, XSCOM addressing of the cores, Doorbells, etc...

Cheers,
Ben.

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

* Re: [Qemu-devel] [PATCH v3 05/10] ppc/pnv: add a PnvCore object
  2016-09-21  2:05     ` Benjamin Herrenschmidt
@ 2016-09-21  2:15       ` David Gibson
  2016-09-21  7:15       ` Cédric Le Goater
  1 sibling, 0 replies; 53+ messages in thread
From: David Gibson @ 2016-09-21  2:15 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Cédric Le Goater, qemu-ppc, qemu-devel

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

On Wed, Sep 21, 2016 at 12:05:47PM +1000, Benjamin Herrenschmidt wrote:
> On Wed, 2016-09-21 at 11:51 +1000, David Gibson wrote:
> > Ok, as noted elsewhere, I think you need to disassociate the PIR value
> > from the cpu_index.  It may be a little less elegant, but it'll make
> > your life much easier in the short and medium term.
> > 
> > Apart from that, this looks pretty good, though I have one suggested
> > cleanup below.
> 
> As long as the PIR value is what is used everywhere, ie, interrupt
> controller, XSCOM addressing of the cores, Doorbells, etc...

Right, obviously the physical ID needs to be used for all the guest
visible interfaces.

-- 
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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 06/10] monitor: fix crash for platforms without a CPU 0
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 06/10] monitor: fix crash for platforms without a CPU 0 Cédric Le Goater
@ 2016-09-21  5:30   ` David Gibson
  2016-09-21  8:06     ` Cédric Le Goater
  0 siblings, 1 reply; 53+ messages in thread
From: David Gibson @ 2016-09-21  5:30 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

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

On Thu, Sep 15, 2016 at 02:45:56PM +0200, Cédric Le Goater wrote:
> On PowerNV, CPU ids start at 0x8 or 0x20, we don't have a CPU 0
> anymore. So let's use the first_cpu index to initialize the monitor.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

I've rewritten the commit message to make the case for this without
the context of pnv and posted upstream.

> ---
>  monitor.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/monitor.c b/monitor.c
> index 5c003731e288..0192209a7915 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -1023,7 +1023,7 @@ int monitor_set_cpu(int cpu_index)
>  CPUState *mon_get_cpu(void)
>  {
>      if (!cur_mon->mon_cpu) {
> -        monitor_set_cpu(0);
> +        monitor_set_cpu(first_cpu->cpu_index);
>      }
>      cpu_synchronize_state(cur_mon->mon_cpu);
>      return cur_mon->mon_cpu;

-- 
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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure
  2016-09-15 22:11   ` Benjamin Herrenschmidt
@ 2016-09-21  5:56     ` David Gibson
  2016-09-21  7:44       ` Benjamin Herrenschmidt
  0 siblings, 1 reply; 53+ messages in thread
From: David Gibson @ 2016-09-21  5:56 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Cédric Le Goater, qemu-ppc, qemu-devel

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

On Fri, Sep 16, 2016 at 08:11:45AM +1000, Benjamin Herrenschmidt wrote:
> On Thu, 2016-09-15 at 14:45 +0200, Cédric Le Goater wrote:
> >  - The PCB translation is too much of a constraint for a specific
> >    XSCOM address space, unless someone can explain me how to address 8
> >    bytes at 0xb0021 and another 8 different bytes at 0xb0022. I don't
> >    think the address space and the memory regions were designed with
> >    this in mind. Please advise !
> 
> I'd say just dispatch pcb_addr << 3 to the memory regions (which is
> also the P9 translation iirc).

Yes, I think that's the way to go.

That also means on P9 you can potentially just map the scom address
space directly into address_space_memory, instead of requiring a
redispatcher to do the address mangling.

> Just add a quirk to the ADU/XSCOM dispatch object to do the additional
> unmangling needed on P7/P8
> 

-- 
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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure Cédric Le Goater
  2016-09-15 22:11   ` Benjamin Herrenschmidt
@ 2016-09-21  6:08   ` David Gibson
  2016-09-22  8:25     ` Cédric Le Goater
  2016-09-27  9:10     ` Cédric Le Goater
  1 sibling, 2 replies; 53+ messages in thread
From: David Gibson @ 2016-09-21  6:08 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

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

On Thu, Sep 15, 2016 at 02:45:57PM +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.
> 
> The PnvXScom object represents the ADU of a real system, it dispatches
> XSCOM accesses to the targeted chiplets using a specific XSCOM address
> space introduced for this purpose. A set of handlers to translate of
> an XSCOM address into a PCB register address is added to the PnvChip
> class to handle the differences between P9 and P8.
> 
> Unfortunately, due to the nature of the translation, we can not
> translate before the dispatch in the address space. So an address
> relative to the XSCOM mapping in main memory is used and the
> translation is done in the XSCOM memory subregions with helpers.
> 
> To customize the device tree, a QOM InterfaceClass, PnvXScomInterface,
> is provided with a devnode() handler. The device tree is populated by
> simply looping on the PnvXScom children. Therefore, each model needing
> custom nodes should declare itself as a child of the PnvXScom object
> at instantiation time.
> 
> The PnvXScomInterface is also used to hold the address translation
> handlers required by models having XSCOM memory subregions.
> 
> *Caveats*
> 
>  - I kept a standalone for PnvXScom object in this model to isolate
>    the feature but it should be merged with the PnvChip and remove
>    quite a few lines at the same time. This is minor.

Ok.

>  - The PCB translation is too much of a constraint for a specific
>    XSCOM address space, unless someone can explain me how to address 8
>    bytes at 0xb0021 and another 8 different bytes at 0xb0022. I don't
>    think the address space and the memory regions were designed with
>    this in mind. Please advise !
> 
>    So we should problably just kill the idea of a specific address
>    space and use helpers in the XSCOM memory subregions to translate
>    XSCOM addresses to PCB registers.
> 
>    We can improve the calls to initialize and to map the XSCOM subregions,
>    memory_region_add_subregion() and memory_region_init_io(), with
>    wrappers and make the translation a bit less painful.
> 
>  - The way the translation helpers of the PnvXScomInterface are
>    initialized is a bit of a hack. Check :
> 
>       uint32_t pnv_xscom_pcba(PnvXScomInterface *dev, uint64_t addr)
>       uint64_t pnv_xscom_addr(PnvXScomInterface *dev, uint32_t pcba)
> 
>    Ideas welcomed !
> 
> Based on previous work done by :
>       Benjamin Herrenschmidt <benh@kernel.crashing.org>
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
> 
>  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               |  51 ++++++++
>  hw/ppc/pnv_xscom.c         | 308 +++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h       |   4 +
>  include/hw/ppc/pnv_xscom.h |  74 +++++++++++
>  5 files changed, 438 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 7bc98f15f14b..7dcdf18a9e6b 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_fdt(&chip->xscom, fdt, 0);
> +
>      for (i = 0; i < chip->nr_cores; i++) {
>          PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
>  
> @@ -397,6 +401,28 @@ static uint32_t pnv_chip_core_pir_p9(PnvChip *chip, uint32_t core_id)
>      return (chip->chip_id << 8) | (core_id << 2);
>  }
>  
> +static uint64_t pnv_chip_xscom_addr_p8(uint32_t pcba)
> +{
> +    return (((uint64_t) pcba << 4) & ~0xfful) | ((pcba << 3) & 0x78);
> +}
> +
> +static uint64_t pnv_chip_xscom_addr_p9(uint32_t pcba)
> +{
> +    return (uint64_t) pcba << 3;
> +}
> +
> +static uint32_t pnv_chip_xscom_pcba_p8(uint64_t addr)
> +{
> +        addr &= (PNV_XSCOM_SIZE - 1);
> +        return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf);
> +}
> +
> +static uint32_t pnv_chip_xscom_pcba_p9(uint64_t addr)
> +{
> +        addr &= (PNV_XSCOM_SIZE - 1);
> +        return addr >> 3;
> +}
> +
>  /* Allowed core identifiers on a POWER8 Processor Chip :
>   *
>   * <EX0 reserved>
> @@ -433,6 +459,8 @@ 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_addr = pnv_chip_xscom_addr_p8;
> +    k->xscom_pcba = pnv_chip_xscom_pcba_p8;
>      dc->desc = "PowerNV Chip POWER8E";
>  }
>  
> @@ -453,6 +481,8 @@ 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_addr = pnv_chip_xscom_addr_p8;
> +    k->xscom_pcba = pnv_chip_xscom_pcba_p8;
>      dc->desc = "PowerNV Chip POWER8";
>  }
>  
> @@ -473,6 +503,8 @@ 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_addr = pnv_chip_xscom_addr_p8;
> +    k->xscom_pcba = pnv_chip_xscom_pcba_p8;
>      dc->desc = "PowerNV Chip POWER8NVL";
>  }
>  
> @@ -493,6 +525,8 @@ 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_addr = pnv_chip_xscom_addr_p9;
> +    k->xscom_pcba = pnv_chip_xscom_pcba_p9;

So if you do as BenH (and I) suggested and have the "scom address
space" actually be addressed by (pcba << 3), I think you can probably
avoid these.  Instead you can handle it in the chip or ADU realize
function by either:

    P8: * map one big subregion for the ADU into &address_space_memory
        * have the handler for that subregion do the address mangling,
          then redispatch into the xscom address space
    P9: * Map the appropriate chunk of the xscom address space
          directly into address_space_memory

>      dc->desc = "PowerNV Chip POWER9";
>  }
>  
> @@ -527,6 +561,16 @@ static void pnv_chip_core_sanitize(PnvChip *chip)
>      chip->cores_mask &= pcc->cores_mask;
>  }
>  
> +static void pnv_chip_init(Object *obj)
> +{
> +    PnvChip *chip = PNV_CHIP(obj);
> +
> +    object_initialize(&chip->xscom, sizeof(chip->xscom), TYPE_PNV_XSCOM);
> +    object_property_add_child(obj, "xscom", OBJECT(&chip->xscom), NULL);
> +    object_property_add_const_link(OBJECT(&chip->xscom), "chip",
> +                                   OBJECT(chip), &error_abort);
> +}
> +
>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
>  {
>      PnvChip *chip = PNV_CHIP(dev);
> @@ -540,6 +584,12 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>          return;
>      }
>  
> +    /* XSCOM bridge */
> +    object_property_set_bool(OBJECT(&chip->xscom), true, "realized",
> +                             &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&chip->xscom), 0,
> +                    PNV_XSCOM_BASE(chip->chip_id));
> +
>      /* Early checks on the core settings */
>      pnv_chip_core_sanitize(chip);
>  
> @@ -597,6 +647,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..019cd85428de
> --- /dev/null
> +++ b/hw/ppc/pnv_xscom.c
> @@ -0,0 +1,308 @@
> +/*
> + * 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;

Hmm.. is current_cpu a safe thing to use in the case of KVM or MTTCG?

> +    PowerPCCPU *cpu = POWERPC_CPU(cs);
> +    CPUPPCState *env = &cpu->env;
> +
> +    cpu_synchronize_state(cs);
> +    env->spr[SPR_HMER] |= hmer_bits;
> +
> +    /* XXX Need a CPU helper to set HMER, also handle gneeration
> +     * of HMIs
> +     */
> +}
> +
> +static bool xscom_dispatch_read(PnvXScom *xscom, hwaddr addr, uint64_t *val)
> +{
> +    uint32_t success;
> +    uint8_t data[8];
> +
> +    success = !address_space_rw(&xscom->xscom_as, addr, MEMTXATTRS_UNSPECIFIED,
> +                                data, 8, false);
> +    *val = (((uint64_t) data[0]) << 56 |
> +            ((uint64_t) data[1]) << 48 |
> +            ((uint64_t) data[2]) << 40 |
> +            ((uint64_t) data[3]) << 32 |
> +            ((uint64_t) data[4]) << 24 |
> +            ((uint64_t) data[5]) << 16 |
> +            ((uint64_t) data[6]) << 8  |
> +            ((uint64_t) data[7]));

AFAICT this is basically assuming data is always encoded BE.  With the
right choice of endian flags on the individual SCOM device
registrations with the scom address space, I think you should be able
to avoid this mangling.

> +    return success;
> +}
> +
> +static bool xscom_dispatch_write(PnvXScom *xscom, hwaddr addr, uint64_t val)
> +{
> +    uint32_t success;
> +    uint8_t data[8];
> +
> +    data[0] = (val >> 56) & 0xff;
> +    data[1] = (val >> 48) & 0xff;
> +    data[2] = (val >> 40) & 0xff;
> +    data[3] = (val >> 32) & 0xff;
> +    data[4] = (val >> 24) & 0xff;
> +    data[5] = (val >> 16) & 0xff;
> +    data[6] = (val >> 8) & 0xff;
> +    data[7] = val & 0xff;
> +
> +    success = !address_space_rw(&xscom->xscom_as, addr, MEMTXATTRS_UNSPECIFIED,
> +                           data, 8, true);
> +    return success;
> +}
> +
> +static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)
> +{
> +    PnvXScom *s = opaque;
> +    uint32_t pcba = s->chip_class->xscom_pcba(addr);
> +    uint64_t val = 0;
> +
> +    /* Handle some SCOMs here before dispatch */
> +    switch (pcba) {
> +    case 0xf000f:
> +        val = s->chip_class->chip_cfam_id;
> +        break;
> +    case 0x1010c00:     /* PIBAM FIR */
> +    case 0x1010c03:     /* PIBAM FIR MASK */
> +    case 0x2020007:     /* ADU stuff */
> +    case 0x2020009:     /* ADU stuff */
> +    case 0x202000f:     /* ADU stuff */
> +        val = 0;
> +        break;
> +    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 */
> +        val = 0;
> +        break;

It'd be theoretically nicer to actually register regions for these
special case addresses, but handling it here is a reasonable hack to
get things working quickly for the time being.

> +    default:
> +        if (!xscom_dispatch_read(s, addr, &val)) {
> +            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;
> +        }
> +    }
> +
> +    xscom_complete(HMER_XSCOM_DONE);
> +    return val;
> +}
> +
> +static void xscom_write(void *opaque, hwaddr addr, uint64_t val,
> +                        unsigned width)
> +{
> +    PnvXScom *s = opaque;
> +    uint32_t pcba = s->chip_class->xscom_pcba(addr);
> +
> +    /* Handle some SCOMs here before dispatch */
> +    switch (pcba) {
> +        /* We ignore writes to these */
> +    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 */
> +        break;
> +    default:
> +        if (!xscom_dispatch_write(s, addr, val)) {
> +            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;
> +        }
> +    }
> +
> +    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,
> +};
> +
> +static void pnv_xscom_realize(DeviceState *dev, Error **errp)
> +{
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> +    PnvXScom *s = PNV_XSCOM(dev);
> +    char *name;
> +    Object *obj;
> +    Error *err = NULL;
> +
> +    obj = object_property_get_link(OBJECT(s), "chip", &err);
> +    if (!obj) {
> +        error_setg(errp, "%s: required link 'chip' not found: %s",
> +                     __func__, error_get_pretty(err));
> +        return;
> +    }
> +
> +    s->chip_class = PNV_CHIP_GET_CLASS(obj);
> +    s->chip_id = PNV_CHIP(obj)->chip_id;
> +
> +    if (s->chip_id < 0) {
> +        error_setg(errp, "invalid chip id '%d'", s->chip_id);
> +        return;
> +    }
> +
> +    name = g_strdup_printf("xscom-%x", s->chip_id);
> +    memory_region_init_io(&s->mem, OBJECT(s), &pnv_xscom_ops, s, name,
> +                          PNV_XSCOM_SIZE);
> +    sysbus_init_mmio(sbd, &s->mem);
> +
> +    memory_region_init(&s->xscom_mr, OBJECT(s), name, PNV_XSCOM_SIZE);
> +    address_space_init(&s->xscom_as, &s->xscom_mr, name);
> +    g_free(name);
> +
> +}
> +
> +static void pnv_xscom_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->realize = pnv_xscom_realize;
> +}
> +
> +static const TypeInfo pnv_xscom_info = {
> +    .name          = TYPE_PNV_XSCOM,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(PnvXScom),
> +    .class_init    = pnv_xscom_class_init,
> +};
> +
> +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_info);
> +    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->devnode) {
> +            _FDT((xc->devnode(xd, args->fdt, args->xscom_offset)));
> +        }
> +    }
> +    return 0;
> +}
> +
> +int pnv_xscom_populate_fdt(PnvXScom *adu, void *fdt, int root_offset)
> +{
> +    const char compat[] = "ibm,power8-xscom\0ibm,xscom";
> +    uint64_t reg[] = { cpu_to_be64(PNV_XSCOM_BASE(adu->chip_id)),
> +                       cpu_to_be64(PNV_XSCOM_SIZE) };
> +    int xscom_offset;
> +    ForeachPopulateArgs args;
> +    char *name;
> +
> +    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", adu->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))));
> +    _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat,
> +                      sizeof(compat))));
> +    _FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0)));
> +
> +    args.fdt = fdt;
> +    args.xscom_offset = xscom_offset;
> +
> +    object_child_foreach(OBJECT(adu), xscom_populate_child, &args);
> +    return 0;
> +}
> +
> +/*
> + * XScom address translation depends on the chip type and not all
> + * objects have backlink to it. Here's a helper to handle this case.
> + * To be improved.
> + */

Yeah.. this seems a bit hacky as you say.  Passing the individual
device to these helpers just seems wrong, since it's a chip / machine
dependent mapping.  Does this go away if you use the (pcba << 3)
encoding?

> +uint32_t pnv_xscom_pcba(PnvXScomInterface *dev, uint64_t addr)
> +{
> +    PnvXScomInterfaceClass *xc = PNV_XSCOM_INTERFACE_GET_CLASS(dev);
> +
> +    if (!xc->xscom_pcba) {
> +        PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
> +        PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pnv->chips[0]);
> +
> +        xc->xscom_pcba = pcc->xscom_pcba;
> +    }
> +
> +    return xc->xscom_pcba(addr);
> +}
> +
> +uint64_t pnv_xscom_addr(PnvXScomInterface *dev, uint32_t pcba)
> +{
> +     PnvXScomInterfaceClass *xc = PNV_XSCOM_INTERFACE_GET_CLASS(dev);
> +
> +    if (!xc->xscom_addr) {
> +        PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
> +        PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pnv->chips[0]);
> +
> +        xc->xscom_addr = pcc->xscom_addr;
> +    }
> +
> +    return xc->xscom_addr(pcba);
> +}
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index 262faa59a75f..0371710b1882 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)
> @@ -42,6 +43,7 @@ typedef struct PnvChip {
>  
>      /*< public >*/
>      uint32_t     chip_id;
> +    PnvXScom     xscom;
>  
>      uint32_t  nr_cores;
>      uint64_t  cores_mask;
> @@ -60,6 +62,8 @@ typedef struct PnvChipClass {
>  
>      void (*realize)(PnvChip *dev, Error **errp);
>      uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
> +    uint64_t (*xscom_addr)(uint32_t pcba);
> +    uint32_t (*xscom_pcba)(uint64_t addr);
>  } PnvChipClass;
>  
>  #define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E"
> diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
> new file mode 100644
> index 000000000000..0a03d533db59
> --- /dev/null
> +++ b/include/hw/ppc/pnv_xscom.h
> @@ -0,0 +1,74 @@
> +/*
> + * 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 "hw/sysbus.h"
> +
> +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 (*devnode)(PnvXScomInterface *dev, void *fdt, int offset);
> +
> +    uint64_t (*xscom_addr)(uint32_t pcba);
> +    uint32_t (*xscom_pcba)(uint64_t addr);

Allowing the SCOM device to override the mapping just seems bogus.
Surely this should always go via the chip.

> +} PnvXScomInterfaceClass;
> +
> +#define TYPE_PNV_XSCOM "pnv-xscom"
> +#define PNV_XSCOM(obj) OBJECT_CHECK(PnvXScom, (obj), TYPE_PNV_XSCOM)
> +
> +typedef struct PnvChipClass PnvChipClass;
> +
> +typedef struct PnvXScom {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +    /*< public >*/
> +
> +    MemoryRegion mem;
> +    int32_t chip_id;
> +    PnvChipClass *chip_class;

Just having a pointer to the chip seems simpler than storing both id
and class here.  But I guess it'll go away anyway if you merge the ADU
into the chip object itself.

> +    MemoryRegion xscom_mr;
> +    AddressSpace xscom_as;
> +} PnvXScom;
> +
> +#define PNV_XSCOM_SIZE        0x800000000ull
> +#define PNV_XSCOM_BASE(chip)                                    \
> +    (0x3fc0000000000ull + ((uint64_t)(chip)) * PNV_XSCOM_SIZE)
> +
> +extern int pnv_xscom_populate_fdt(PnvXScom *xscom, void *fdt, int offset);
> +
> +/*
> + * helpers to translate to XScomm PCB addresses
> + */
> +extern uint32_t pnv_xscom_pcba(PnvXScomInterface *dev, uint64_t addr);
> +extern uint64_t pnv_xscom_addr(PnvXScomInterface *dev, uint32_t pcba);
> +
> +#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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 08/10] ppc/pnv: add a XScomDevice to PnvCore
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 08/10] ppc/pnv: add a XScomDevice to PnvCore Cédric Le Goater
@ 2016-09-21  6:12   ` David Gibson
  2016-09-22  8:33     ` Cédric Le Goater
  0 siblings, 1 reply; 53+ messages in thread
From: David Gibson @ 2016-09-21  6:12 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

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

On Thu, Sep 15, 2016 at 02:45:58PM +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 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          | 55 ++++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv_core.h  |  2 ++
>  include/hw/ppc/pnv_xscom.h | 19 ++++++++++++++++
>  4 files changed, 80 insertions(+)
> 
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 7dcdf18a9e6b..6a3d1fbf8403 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -619,6 +619,10 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>                                   &error_fatal);
>          object_unref(OBJECT(pnv_core));
>          i++;
> +
> +        memory_region_add_subregion(&chip->xscom.xscom_mr,
> +                         pcc->xscom_addr(PNV_XSCOM_EX_CORE_BASE(core_hwid)),
> +                         &PNV_CORE(pnv_core)->xscom_regs);

I think the core realize function should be doing this itself.

>      }
>      g_free(typename);
>  
> diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
> index 6fed5a208536..81b83d0f41b3 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"
> @@ -57,6 +58,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 DTS_RESULT0     0x50000
> +#define DTS_RESULT1     0x50001
> +
> +static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr,
> +                                    unsigned int width)
> +{
> +    uint32_t offset = pnv_xscom_pcba(opaque, addr);
> +    uint64_t val = 0;
> +
> +    /* The result should be 38 C */
> +    switch (offset) {
> +    case DTS_RESULT0:
> +        val = 0x26f024f023f0000ull;
> +        break;
> +    case 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);
> +}

You should double check, but I think you can implement an RO region in
an address space by just leaving the write function as NULL.

> +
> +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;
> @@ -117,6 +163,11 @@ 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_addr(PNV_XSCOM_INTERFACE(dev),
> +                                                   PNV_XSCOM_EX_CORE_SIZE));
>      return;
>  
>  err:
> @@ -169,6 +220,10 @@ static void pnv_core_register_types(void)
>              .instance_size = sizeof(PnvCore),
>              .class_init = pnv_core_class_init,
>              .class_data = (void *) pnv_core_models[i],
> +            .interfaces    = (InterfaceInfo[]) {
> +                { TYPE_PNV_XSCOM_INTERFACE },
> +                { }
> +            }
>          };
>          ti.name = pnv_core_typename(pnv_core_models[i]);
>          type_register(&ti);
> 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 0a03d533db59..31e5e8847b90 100644
> --- a/include/hw/ppc/pnv_xscom.h
> +++ b/include/hw/ppc/pnv_xscom.h
> @@ -63,6 +63,25 @@ typedef struct PnvXScom {
>  #define PNV_XSCOM_BASE(chip)                                    \
>      (0x3fc0000000000ull + ((uint64_t)(chip)) * PNV_XSCOM_SIZE)
>  
> +/*
> + * Layout of Xscom PCB addresses for 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 int pnv_xscom_populate_fdt(PnvXScom *xscom, 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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 09/10] ppc/pnv: add a LPC controller
  2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 09/10] ppc/pnv: add a LPC controller Cédric Le Goater
  2016-09-15 22:13   ` Benjamin Herrenschmidt
@ 2016-09-21  6:23   ` David Gibson
  1 sibling, 0 replies; 53+ messages in thread
From: David Gibson @ 2016-09-21  6:23 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

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

On Thu, Sep 15, 2016 at 02:45:59PM +0200, Cédric Le Goater wrote:
> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> 
> 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 (v3)
>       - 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 ]
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>  hw/ppc/Makefile.objs       |   2 +-
>  hw/ppc/pnv.c               |  15 ++
>  hw/ppc/pnv_lpc.c           | 455 +++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h       |   3 +
>  include/hw/ppc/pnv_lpc.h   |  63 +++++++
>  include/hw/ppc/pnv_xscom.h |   3 +
>  6 files changed, 540 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 6a3d1fbf8403..1aa7b8ee8903 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -569,6 +569,14 @@ static void pnv_chip_init(Object *obj)
>      object_property_add_child(obj, "xscom", OBJECT(&chip->xscom), NULL);
>      object_property_add_const_link(OBJECT(&chip->xscom), "chip",
>                                     OBJECT(chip), &error_abort);
> +
> +    /*
> +     * Add the lpc controller as an XScom child as we need to populate
> +     * the device tree for the isa bus.
> +     */
> +    object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
> +    object_property_add_child(OBJECT(&chip->xscom), "lpc",
> +                              OBJECT(&chip->lpc), NULL);
>  }
>  
>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
> @@ -626,6 +634,13 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>      }
>      g_free(typename);
>  
> +    /* Create LPC controller */
> +    object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
> +                             &error_fatal);
> +    memory_region_add_subregion(&chip->xscom.xscom_mr,
> +                                pcc->xscom_addr(PNV_XSCOM_LPC_BASE),
> +                                &chip->lpc.xscom_regs);

Again, I think the memory region construction should be in the actual
LPC device's realize().

> +
>      if (pcc->realize) {
>          pcc->realize(chip, errp);
>      }
> diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
> new file mode 100644
> index 000000000000..de4bab0ca085
> --- /dev/null
> +++ b/hw/ppc/pnv_lpc.c
> @@ -0,0 +1,455 @@
> +/*
> + * 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 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
> +
> +
> +static int pnv_lpc_devnode(PnvXScomInterface *dev, void *fdt, int xscom_offset)
> +{
> +    const char compat[] = "ibm,power8-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_appendprop(fdt, offset, "reg", reg, sizeof(reg))));

Any particular reason for the appendprop instead of setprop?

> +    _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;
> +}
> +
> +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 */

Are the access size limits built into the address_space mechanism not
sufficient?

> +    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 = pnv_xscom_pcba(opaque, addr);
> +    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 = pnv_xscom_pcba(opaque, addr);
> +
> +    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_addr(PNV_XSCOM_INTERFACE(dev),
> +                                         PNV_XSCOM_LPC_SIZE));
> +}
> +
> +static void pnv_lpc_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
> +
> +    xdc->devnode = pnv_lpc_devnode;
> +
> +    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 0371710b1882..a30579a5817f 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)
> @@ -48,6 +49,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..3741e573b200
> --- /dev/null
> +++ b/include/hw/ppc/pnv_lpc.h
> @@ -0,0 +1,63 @@
> +/*
> + * 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;
> +
> +    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 31e5e8847b90..46b6ce7767c7 100644
> --- a/include/hw/ppc/pnv_xscom.h
> +++ b/include/hw/ppc/pnv_xscom.h
> @@ -82,6 +82,9 @@ typedef struct PnvXScom {
>  #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 int pnv_xscom_populate_fdt(PnvXScom *xscom, 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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 10/10] ppc/pnv: add a ISA bus
  2016-09-15 12:46 ` [Qemu-devel] [PATCH v3 10/10] ppc/pnv: add a ISA bus Cédric Le Goater
@ 2016-09-21  6:30   ` David Gibson
  2016-09-22  8:44     ` Cédric Le Goater
  0 siblings, 1 reply; 53+ messages in thread
From: David Gibson @ 2016-09-21  6:30 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

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

On Thu, Sep 15, 2016 at 02:46:00PM +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>

This looks pretty good, just a couple of minor queries below.

> ---
>  hw/ppc/pnv.c         | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ppc/pnv.h |  2 ++
>  2 files changed, 66 insertions(+)
> 
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 1aa7b8ee8903..fd6e4917133b 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
> @@ -301,6 +305,57 @@ 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);
> +
> +    /* Instanciate ISA bus. let isa_bus_new() create its own bridge on

Instantiate has 3 't's and no 'c's; English orthography strikes again.

> +     * sysbus otherwise devices speficied on the command line will
> +     * fail to create.
> +     */
> +    isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io,
> +                          &error_fatal);

It's not clear to me if this belongs in the chip code or on the lpc
code - the lpc does create a device node as 'isa@', although it also
does some other stuff.

> +
> +    /* 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, 16);
> +    } else {
> +        irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, NULL, 16);
> +    }
> +
> +    isa_bus_irqs(isa_bus, irqs);
> +    return isa_bus;
> +}
> +
>  static void ppc_powernv_init(MachineState *machine)
>  {
>      PnvMachineState *pnv = POWERNV_MACHINE(machine);
> @@ -389,6 +444,15 @@ static void ppc_powernv_init(MachineState *machine)
>          object_property_set_bool(chip, true, "realized", &error_fatal);
>      }
>      g_free(chip_typename);
> +
> +    /* Instanciate 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);
>  }
>  
>  static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t core_id)
> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> index a30579a5817f..e75f937d40dd 100644
> --- a/include/hw/ppc/pnv.h
> +++ b/include/hw/ppc/pnv.h
> @@ -123,6 +123,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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 04/10] ppc/pnv: add a PIR handler to PnvChip
  2016-09-21  1:29   ` David Gibson
  2016-09-21  1:52     ` Benjamin Herrenschmidt
@ 2016-09-21  7:05     ` Cédric Le Goater
  1 sibling, 0 replies; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-21  7:05 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 09/21/2016 03:29 AM, David Gibson wrote:
> On Thu, Sep 15, 2016 at 02:45:54PM +0200, Cédric Le Goater wrote:
>> P9 and P8 have some differences in the CPU PIR encoding.
> 
> The thread id isn't in the PIR at all?

The thread id from what I have seen is basically a +1. So this is why in
PnvCore there is : 

    for (i = 0; i < cc->nr_threads; i++) {
	...
    	cs->cpu_index = pc->pir + i;
    }

and 

    env->spr[SPR_PIR] = cs->cpu_index;

But, yes I should not use cpu_index that way. working on it.

Thanks,
C.


> 
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>  hw/ppc/pnv.c         | 14 ++++++++++++++
>>  include/hw/ppc/pnv.h |  1 +
>>  2 files changed, 15 insertions(+)
>>
>> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
>> index ec7dd6ac5ea1..f4c125503249 100644
>> --- a/hw/ppc/pnv.c
>> +++ b/hw/ppc/pnv.c
>> @@ -238,6 +238,16 @@ static void ppc_powernv_init(MachineState *machine)
>>      g_free(chip_typename);
>>  }
>>  
>> +static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t core_id)
>> +{
>> +    return (chip->chip_id << 7) | (core_id << 3);
>> +}
>> +
>> +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>
>> @@ -273,6 +283,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";
>>  }
>>  
>> @@ -292,6 +303,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";
>>  }
>>  
>> @@ -311,6 +323,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";
>>  }
>>  
>> @@ -330,6 +343,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 cfc32586320f..2bd2294ac2a3 100644
>> --- a/include/hw/ppc/pnv.h
>> +++ b/include/hw/ppc/pnv.h
>> @@ -58,6 +58,7 @@ typedef struct PnvChipClass {
>>      uint64_t     cores_mask;
>>  
>>      void (*realize)(PnvChip *dev, Error **errp);
>> +    uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
>>  } PnvChipClass;
>>  
>>  #define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E"
> 

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

* Re: [Qemu-devel] [PATCH v3 05/10] ppc/pnv: add a PnvCore object
  2016-09-21  1:51   ` David Gibson
  2016-09-21  2:05     ` Benjamin Herrenschmidt
@ 2016-09-21  7:09     ` Cédric Le Goater
  2016-09-21 14:24     ` Cédric Le Goater
  2 siblings, 0 replies; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-21  7:09 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 09/21/2016 03:51 AM, David Gibson wrote:
> On Thu, Sep 15, 2016 at 02:45:55PM +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. This id is stored in CPUState->cpu_index and
>> in PnvCore->pir. It is used to populate the device tree.
> 
> Ok, as noted elsewhere, I think you need to disassociate the PIR value
> from the cpu_index.  It may be a little less elegant, but it'll make
> your life much easier in the short and medium term.

yes I agree. cpu_index was just easy to use mostly because of the ICPs
indexes but I am changing that. Hopefully, at the end, the code should 
not use cpu_dt_id (this is the case right now) nor cpu_index.

> Apart from that, this looks pretty good, though I have one suggested
> cleanup below.
> 
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>
>>  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              | 186 ++++++++++++++++++++++++++++++++++++++++++++++
>>  hw/ppc/pnv_core.c         | 184 +++++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/ppc/pnv.h      |   3 +
>>  include/hw/ppc/pnv_core.h |  48 ++++++++++++
>>  5 files changed, 422 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 f4c125503249..7bc98f15f14b 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,
>> @@ -382,10 +531,47 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>  {
>>      PnvChip *chip = PNV_CHIP(dev);
>>      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);
>>  
>> +    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);
>> +
>>      if (pcc->realize) {
>>          pcc->realize(chip, errp);
>>      }
>> diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
>> new file mode 100644
>> index 000000000000..6fed5a208536
>> --- /dev/null
>> +++ b/hw/ppc/pnv_core.c
>> @@ -0,0 +1,184 @@
>> +/*
>> + * 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;
>> +
>> +    cpu_reset(cs);
>> +
>> +    /*
>> +     * FIXME: cpu_index is non contiguous but xics native requires it
>> +     * to find its icp
>> +     */
>> +    env->spr[SPR_PIR] = cs->cpu_index;
>> +    env->spr[SPR_HIOR] = 0;
>> +    env->gpr[3] = POWERNV_FDT_ADDR;
>> +    env->nip = 0x10;
>> +    env->msr |= MSR_HVB;
>> +}
>> +
>> +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);
>> +
>> +    /* MSR[IP] doesn't exist nowadays */
>> +    env->msr_mask &= ~(1 << 6);
> 
> If MSR[IP] is gone, shouldn't that be reflected in the MSR masks
> stored for the actual vcpu models, rather than imposed from the
> machine or core code?
> 
>> +    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++) {
>> +        CPUState *cs;
>> +
>> +        obj = pc->threads + i * size;
>> +
>> +        object_initialize(obj, size, typename);
>> +        cs = CPU(obj);
>> +        /*
>> +         * FIXME: cpu_index is non contiguous but xics native requires
>> +         * it to find its icp
>> +         */
>> +        cs->cpu_index = pc->pir + i;
>> +        snprintf(name, sizeof(name), "thread[%d]", i);
>> +        object_property_add_child(OBJECT(pc), name, obj, &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);
>> +    }
>> +}
> 
> I think you might be able to use a single chip type table and
> construct both the chip types and the corresponding core types from
> it.

Indeed and It would be better to keep them in sync. I will look
into that.

Thanks,

C. 


>> +
>> +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 2bd2294ac2a3..262faa59a75f 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 {
>> @@ -119,4 +120,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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 05/10] ppc/pnv: add a PnvCore object
  2016-09-21  2:05     ` Benjamin Herrenschmidt
  2016-09-21  2:15       ` David Gibson
@ 2016-09-21  7:15       ` Cédric Le Goater
  1 sibling, 0 replies; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-21  7:15 UTC (permalink / raw)
  To: Benjamin Herrenschmidt, David Gibson; +Cc: qemu-ppc, qemu-devel

On 09/21/2016 04:05 AM, Benjamin Herrenschmidt wrote:
> On Wed, 2016-09-21 at 11:51 +1000, David Gibson wrote:
>> Ok, as noted elsewhere, I think you need to disassociate the PIR value
>> from the cpu_index.  It may be a little less elegant, but it'll make
>> your life much easier in the short and medium term.
>>
>> Apart from that, this looks pretty good, though I have one suggested
>> cleanup below.
> 
> As long as the PIR value is what is used everywhere, ie, interrupt
> controller, XSCOM addressing of the cores, Doorbells, etc...

Yes. PowerNV needs the PIR that's all. 

I need to get rid of all the different cpu identifiers which are used 
under qemu. It should be possible with a "cpu hwid" -> "ipc index" 
mapping table, like David proposed.

Thanks,
C.

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

* Re: [Qemu-devel] [PATCH v3 01/10] ppc/pnv: add skeleton PowerNV platform
  2016-09-20  7:53   ` David Gibson
@ 2016-09-21  7:32     ` Cédric Le Goater
  0 siblings, 0 replies; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-21  7:32 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 09/20/2016 09:53 AM, David Gibson wrote:
> On Thu, Sep 15, 2016 at 02:45:51PM +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>
> 
> Looking pretty good, just a couple of minor details noted below.

I will add the fixes in v4.

Thanks,

C. 

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

* Re: [Qemu-devel] [PATCH v3 02/10] ppc/pnv: add a PnvChip object
  2016-09-20 13:50   ` David Gibson
@ 2016-09-21  7:44     ` Cédric Le Goater
  0 siblings, 0 replies; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-21  7:44 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 09/20/2016 03:50 PM, David Gibson wrote:
> On Thu, Sep 15, 2016 at 02:45:52PM +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.
> 
> Thanks for the above - that's basically exactly the sort of
> description I was looking for.
> 
>> 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.
>>  Maybe this object deserves its own file hw/ppc/pnv_chip.c ? 
>>
>>  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         | 192 +++++++++++++++++++++++++++++++++++++++++++++++++--
>>  include/hw/ppc/pnv.h |  79 +++++++++++++++++++++
>>  2 files changed, 266 insertions(+), 5 deletions(-)
>>
>> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
>> index ee78422b2eae..2aa5be56c8dc 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)));
>> @@ -117,11 +128,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;
>>  }
>>  
>> @@ -146,6 +156,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)) {
>> @@ -190,6 +202,170 @@ 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(PnvChipPower8E),
>> +    .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(PnvChipPower8),
>> +    .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(PnvChipPower8NVL),
>> +    .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(PnvChipPower9),
>> +    .class_init    = pnv_chip_power9_class_init,
>> +};
>> +
>> +static void pnv_chip_realize(DeviceState *dev, Error **errp)
>> +{
>> +    PnvChip *chip = PNV_CHIP(dev);
>> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
>> +
>> +    if (pcc->realize) {
>> +        pcc->realize(chip, errp);
> 
> Is there actually a need for this PnvChipClass::realize callback?

There is no real need for the moment. I guess I will remove it in
v4, we can add it back later if required.


> Couldn't the class_inits just direclty set DeviceClass::realize?
> 
>> +    }
>> +}
>> +
>> +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);
>> +    int num_chips;
>> +
>> +    if (sscanf(value, "%d", &num_chips) != 1) {
>> +        error_setg(errp, "invalid num_chips property: '%s'", value);
>> +    }
> 
> As a rule, I'd recommend strtol() instead of sscanf() - less
> variations in the semantics by platform.

ok.

>> +
>> +    /*
>> +     * FIXME: should we decide on how many chips we can create based
>> +     * on #cores and Venice vs. Murano vs. Naples chip type etc...,
>> +     */
>> +    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);
> 
> Although come to that, why not use object_property_add_uint32_ptr() or
> similar instead of property_add_str()?

I think I tried that already and got trapped by the fact that pnv->num_chips 
needed to be static. I will take a closer look because it makes sense.

>> +    object_property_set_description(obj, "num-chips",
>> +                                    "Specifies the number of processor chips",
>> +                                    NULL);
>>  }
>>  
>>  static void powernv_machine_class_init(ObjectClass *oc, void *data)
>> @@ -211,12 +387,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..6e6628edcf6a 100644
>> --- a/include/hw/ppc/pnv.h
>> +++ b/include/hw/ppc/pnv.h
>> @@ -20,6 +20,82 @@
>>  #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;
>> +
>> +    void (*realize)(PnvChip *dev, Error **errp);
>> +} PnvChipClass;
>> +
>> +#define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E"
>> +#define PNV_CHIP_POWER8E(obj) \
>> +    OBJECT_CHECK(PnvChipPower8E, (obj), TYPE_PNV_CHIP_POWER8E)
>> +
>> +typedef struct PnvChipPower8E {
>> +    PnvChip pnv_chip;
>> +} PnvChipPower8E;
> 
> If you're not adding any fields you don't need to define an actual
> type for the subclass.

this is true. I was planning to add fields but the need did not arise,
I will remove the PnvChipPower* types. We can readd them when needed,
possibly when we introduce a really incompatible device model that 
needs more than an PnvChipClass to handle differences.

> And if you *are* defining a type, then you need to make sure you set
> the instance size in the type definition to match it.

yes. I got a bad failure because of that recently. I will recheck
all of it after the above removal.

> I suspect you're going to want P8 family and P9 family intermediate
> classes fairly early on.

yes. the core definitions depends on it. 

Thanks,

C.

>> +
>> +#define TYPE_PNV_CHIP_POWER8 TYPE_PNV_CHIP "-POWER8"
>> +#define PNV_CHIP_POWER8(obj) \
>> +    OBJECT_CHECK(PnvChipPower8, (obj), TYPE_PNV_CHIP_POWER8)
>> +
>> +typedef struct PnvChipPower8 {
>> +    PnvChip pnv_chip;
>> +} PnvChipPower8;
>> +
>> +#define TYPE_PNV_CHIP_POWER8NVL TYPE_PNV_CHIP "-POWER8NVL"
>> +#define PNV_CHIP_POWER8NVL(obj) \
>> +    OBJECT_CHECK(PnvChipPower8NVL, (obj), TYPE_PNV_CHIP_POWER8NVL)
>> +
>> +typedef struct PnvChipPower8NVL {
>> +    PnvChip pnv_chip;
>> +} PnvChipPower8NVL;
>> +
>> +#define TYPE_PNV_CHIP_POWER9 TYPE_PNV_CHIP "-POWER9"
>> +#define PNV_CHIP_POWER9(obj) \
>> +    OBJECT_CHECK(PnvChipPower9, (obj), TYPE_PNV_CHIP_POWER9)
>> +
>> +typedef struct PnvChipPower9 {
>> +    PnvChip pnv_chip;
>> +} PnvChipPower9;
>> +
>> +/*
>> + * This generates a HW chip id depending on an index:
>> + *
>> + *    0x0, 0x1, 0x10, 0x11, 0x20, 0x21, ...
>> + *
>> + * Is this correct ?
>> + */
>> +#define CHIP_HWID(i) ((((i) & 0x3e) << 3) | ((i) & 0x1))
>>  
>>  #define TYPE_POWERNV_MACHINE       MACHINE_TYPE_NAME("powernv")
>>  #define POWERNV_MACHINE(obj) \
>> @@ -31,6 +107,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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure
  2016-09-21  5:56     ` David Gibson
@ 2016-09-21  7:44       ` Benjamin Herrenschmidt
  0 siblings, 0 replies; 53+ messages in thread
From: Benjamin Herrenschmidt @ 2016-09-21  7:44 UTC (permalink / raw)
  To: David Gibson; +Cc: Cédric Le Goater, qemu-ppc, qemu-devel

On Wed, 2016-09-21 at 15:56 +1000, David Gibson wrote:
> 
> Yes, I think that's the way to go.
> 
> That also means on P9 you can potentially just map the scom address
> space directly into address_space_memory, instead of requiring a
> redispatcher to do the address mangling.

No. You still need a redispatcher to do the HMER business

Cheers,
Ben.

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

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

On 09/20/2016 03:57 PM, David Gibson wrote:
> On Thu, Sep 15, 2016 at 02:45:53PM +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.
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>
>>  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         | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/ppc/pnv.h |  4 ++++
>>  2 files changed, 70 insertions(+)
>>
>> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
>> index 2aa5be56c8dc..ec7dd6ac5ea1 100644
>> --- a/hw/ppc/pnv.c
>> +++ b/hw/ppc/pnv.c
>> @@ -226,11 +226,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);
> 
> If you're selecting the default, why set it at all?

To remind that we could add a machine property to define the layout of the
cores in each chip on the command line, something like :

-machine powernv,accel=tcg,num-chips=2,cores-mask="0xdeadbeef,0xbadc0ffee"

That would be nice to test different layouts.

> 
>>          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);
>> @@ -239,6 +272,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";
>>  }
>>  
>> @@ -257,6 +291,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";
>>  }
>>  
>> @@ -275,6 +310,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";
>>  }
>>  
>> @@ -293,6 +329,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";
>>  }
>>  
>> @@ -303,11 +340,38 @@ static const TypeInfo pnv_chip_power9_info = {
>>      .class_init    = pnv_chip_power9_class_init,
>>  };
>>  
>> +static void pnv_chip_core_sanitize(PnvChip *chip)
>> +{
>> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
>> +    int cores_max = hweight_long(pcc->cores_mask);
>> +
>> +    if (chip->nr_cores > cores_max) {
>> +        error_report("warning: too many cores for chip ! Limiting to %d",
>> +                     cores_max);
>> +        chip->nr_cores = cores_max;
> 
> This is called from realize() which takes an errp argument.  It would
> be better to pass that in and actually report the error up the chain
> here, rather than assuming a warning is the right answer.

yes. I forgot that.

> Also.. shouldn't you actually check nr_cores against the chip local
> mask instead of the class one?

I need to rework a bit that part because I want to use the mask for 
the "cpu hwid" -> "icp index" mapping table. So I would need to modify 
chip->cores_mask depending on chip->nr_cores in the case we ask for 
less cores than there is in the mask.

Thanks,

C.


>> +    }
>> +
>> +    /* 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_report("warning: invalid core mask for chip !");
> 
> Likewise here you should propagate the error.
> 
>> +    }
>> +    chip->cores_mask &= pcc->cores_mask;
>> +}
>> +
>>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>  {
>>      PnvChip *chip = PNV_CHIP(dev);
>>      PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
>>  
>> +    /* Early checks on the core settings */
>> +    pnv_chip_core_sanitize(chip);
>> +
>>      if (pcc->realize) {
>>          pcc->realize(chip, errp);
>>      }
>> @@ -315,6 +379,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>  
>>  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 6e6628edcf6a..cfc32586320f 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;
>>  
>>      void (*realize)(PnvChip *dev, Error **errp);
>>  } PnvChipClass;
> 

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

* Re: [Qemu-devel] [PATCH v3 06/10] monitor: fix crash for platforms without a CPU 0
  2016-09-21  5:30   ` David Gibson
@ 2016-09-21  8:06     ` Cédric Le Goater
  0 siblings, 0 replies; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-21  8:06 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 09/21/2016 07:30 AM, David Gibson wrote:
> On Thu, Sep 15, 2016 at 02:45:56PM +0200, Cédric Le Goater wrote:
>> On PowerNV, CPU ids start at 0x8 or 0x20, we don't have a CPU 0
>> anymore. So let's use the first_cpu index to initialize the monitor.
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> 
> I've rewritten the commit message to make the case for this without
> the context of pnv and posted upstream.

Yes this is better. Thanks.

C.

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

* Re: [Qemu-devel] [PATCH v3 05/10] ppc/pnv: add a PnvCore object
  2016-09-21  1:51   ` David Gibson
  2016-09-21  2:05     ` Benjamin Herrenschmidt
  2016-09-21  7:09     ` Cédric Le Goater
@ 2016-09-21 14:24     ` Cédric Le Goater
  2 siblings, 0 replies; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-21 14:24 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

>> +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);
>> +
>> +    /* MSR[IP] doesn't exist nowadays */
>> +    env->msr_mask &= ~(1 << 6);
> 
> If MSR[IP] is gone, shouldn't that be reflected in the MSR masks
> stored for the actual vcpu models, rather than imposed from the
> machine or core code?

So, this is MSR[EP] and it is not in the msr mask of the CPUs PowerNV
runs on. We can just drop the line.

C.

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

* Re: [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure
  2016-09-21  6:08   ` David Gibson
@ 2016-09-22  8:25     ` Cédric Le Goater
  2016-09-23  2:46       ` David Gibson
  2016-09-27  9:10     ` Cédric Le Goater
  1 sibling, 1 reply; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-22  8:25 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

>> @@ -493,6 +525,8 @@ 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_addr = pnv_chip_xscom_addr_p9;
>> +    k->xscom_pcba = pnv_chip_xscom_pcba_p9;
> 
> So if you do as BenH (and I) suggested and have the "scom address
> space" actually be addressed by (pcba << 3), I think you can probably
> avoid these.  

I will look at that option again. 

I was trying to untangle a few things at the same time. I have better
view of the problem to solve now. The bus is gone, that's was one 
thing. How we map these xscom regions is the next. 

Ben suggested to add some P7/P8 mangling before the dispatch in 
the &address_space_xscom. This should make things cleaner. I had 
not thought of doing that and this is why I introduced these helpers :

+uint32_t pnv_xscom_pcba(PnvXScomInterface *dev, uint64_t addr)
+uint64_t pnv_xscom_addr(PnvXScomInterface *dev, uint32_t pcba)

which I don't really like ...

but we must make sure that we can do the mapping of the xscom 
subregions in the &address_space_xscom using (pcba << 3)


> Instead you can handle it in the chip or ADU realize function by either:
> 
>     P8: * map one big subregion for the ADU into &address_space_memory
>         * have the handler for that subregion do the address mangling,
>           then redispatch into the xscom address space
>
>     P9: * Map the appropriate chunk of the xscom address space
>           directly into address_space_memory

Yes that was my feeling for a better solution but Ben chimed in with the 
HMER topic. I need to look at that.


>>      dc->desc = "PowerNV Chip POWER9";
>>  }
>>  
>> @@ -527,6 +561,16 @@ static void pnv_chip_core_sanitize(PnvChip *chip)
>>      chip->cores_mask &= pcc->cores_mask;
>>  }
>>  
>> +static void pnv_chip_init(Object *obj)
>> +{
>> +    PnvChip *chip = PNV_CHIP(obj);
>> +
>> +    object_initialize(&chip->xscom, sizeof(chip->xscom), TYPE_PNV_XSCOM);
>> +    object_property_add_child(obj, "xscom", OBJECT(&chip->xscom), NULL);
>> +    object_property_add_const_link(OBJECT(&chip->xscom), "chip",
>> +                                   OBJECT(chip), &error_abort);
>> +}
>> +
>>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>  {
>>      PnvChip *chip = PNV_CHIP(dev);
>> @@ -540,6 +584,12 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>          return;
>>      }
>>  
>> +    /* XSCOM bridge */
>> +    object_property_set_bool(OBJECT(&chip->xscom), true, "realized",
>> +                             &error_fatal);
>> +    sysbus_mmio_map(SYS_BUS_DEVICE(&chip->xscom), 0,
>> +                    PNV_XSCOM_BASE(chip->chip_id));
>> +
>>      /* Early checks on the core settings */
>>      pnv_chip_core_sanitize(chip);
>>  
>> @@ -597,6 +647,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..019cd85428de
>> --- /dev/null
>> +++ b/hw/ppc/pnv_xscom.c
>> @@ -0,0 +1,308 @@
>> +/*
>> + * 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;
> 
> Hmm.. is current_cpu a safe thing to use in the case of KVM or MTTCG?

hmm, indeed. I need to look at that.

>> +    PowerPCCPU *cpu = POWERPC_CPU(cs);
>> +    CPUPPCState *env = &cpu->env;
>> +
>> +    cpu_synchronize_state(cs);
>> +    env->spr[SPR_HMER] |= hmer_bits;
>> +
>> +    /* XXX Need a CPU helper to set HMER, also handle gneeration
>> +     * of HMIs
>> +     */
>> +}
>> +
>> +static bool xscom_dispatch_read(PnvXScom *xscom, hwaddr addr, uint64_t *val)
>> +{
>> +    uint32_t success;
>> +    uint8_t data[8];
>> +
>> +    success = !address_space_rw(&xscom->xscom_as, addr, MEMTXATTRS_UNSPECIFIED,
>> +                                data, 8, false);
>> +    *val = (((uint64_t) data[0]) << 56 |
>> +            ((uint64_t) data[1]) << 48 |
>> +            ((uint64_t) data[2]) << 40 |
>> +            ((uint64_t) data[3]) << 32 |
>> +            ((uint64_t) data[4]) << 24 |
>> +            ((uint64_t) data[5]) << 16 |
>> +            ((uint64_t) data[6]) << 8  |
>> +            ((uint64_t) data[7]));
> 
> AFAICT this is basically assuming data is always encoded BE.  With the
> right choice of endian flags on the individual SCOM device
> registrations with the scom address space, I think you should be able
> to avoid this mangling.

yes. I should but curiously I had to do this, and this works the same on
an intel host or a ppc64 host.


>> +    return success;
>> +}
>> +
>> +static bool xscom_dispatch_write(PnvXScom *xscom, hwaddr addr, uint64_t val)
>> +{
>> +    uint32_t success;
>> +    uint8_t data[8];
>> +
>> +    data[0] = (val >> 56) & 0xff;
>> +    data[1] = (val >> 48) & 0xff;
>> +    data[2] = (val >> 40) & 0xff;
>> +    data[3] = (val >> 32) & 0xff;
>> +    data[4] = (val >> 24) & 0xff;
>> +    data[5] = (val >> 16) & 0xff;
>> +    data[6] = (val >> 8) & 0xff;
>> +    data[7] = val & 0xff;
>> +
>> +    success = !address_space_rw(&xscom->xscom_as, addr, MEMTXATTRS_UNSPECIFIED,
>> +                           data, 8, true);
>> +    return success;
>> +}
>> +
>> +static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)
>> +{
>> +    PnvXScom *s = opaque;
>> +    uint32_t pcba = s->chip_class->xscom_pcba(addr);
>> +    uint64_t val = 0;
>> +
>> +    /* Handle some SCOMs here before dispatch */
>> +    switch (pcba) {
>> +    case 0xf000f:
>> +        val = s->chip_class->chip_cfam_id;
>> +        break;
>> +    case 0x1010c00:     /* PIBAM FIR */
>> +    case 0x1010c03:     /* PIBAM FIR MASK */
>> +    case 0x2020007:     /* ADU stuff */
>> +    case 0x2020009:     /* ADU stuff */
>> +    case 0x202000f:     /* ADU stuff */
>> +        val = 0;
>> +        break;
>> +    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 */
>> +        val = 0;
>> +        break;
> 
> It'd be theoretically nicer to actually register regions for these
> special case addresses, but handling it here is a reasonable hack to
> get things working quickly for the time being.

I will make a default region on the whole xscomm address space to catch 
these.

>> +    default:
>> +        if (!xscom_dispatch_read(s, addr, &val)) {
>> +            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;
>> +        }
>> +    }
>> +
>> +    xscom_complete(HMER_XSCOM_DONE);
>> +    return val;
>> +}
>> +
>> +static void xscom_write(void *opaque, hwaddr addr, uint64_t val,
>> +                        unsigned width)
>> +{
>> +    PnvXScom *s = opaque;
>> +    uint32_t pcba = s->chip_class->xscom_pcba(addr);
>> +
>> +    /* Handle some SCOMs here before dispatch */
>> +    switch (pcba) {
>> +        /* We ignore writes to these */
>> +    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 */
>> +        break;
>> +    default:
>> +        if (!xscom_dispatch_write(s, addr, val)) {
>> +            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;
>> +        }
>> +    }
>> +
>> +    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,
>> +};
>> +
>> +static void pnv_xscom_realize(DeviceState *dev, Error **errp)
>> +{
>> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
>> +    PnvXScom *s = PNV_XSCOM(dev);
>> +    char *name;
>> +    Object *obj;
>> +    Error *err = NULL;
>> +
>> +    obj = object_property_get_link(OBJECT(s), "chip", &err);
>> +    if (!obj) {
>> +        error_setg(errp, "%s: required link 'chip' not found: %s",
>> +                     __func__, error_get_pretty(err));
>> +        return;
>> +    }
>> +
>> +    s->chip_class = PNV_CHIP_GET_CLASS(obj);
>> +    s->chip_id = PNV_CHIP(obj)->chip_id;
>> +
>> +    if (s->chip_id < 0) {
>> +        error_setg(errp, "invalid chip id '%d'", s->chip_id);
>> +        return;
>> +    }
>> +
>> +    name = g_strdup_printf("xscom-%x", s->chip_id);
>> +    memory_region_init_io(&s->mem, OBJECT(s), &pnv_xscom_ops, s, name,
>> +                          PNV_XSCOM_SIZE);
>> +    sysbus_init_mmio(sbd, &s->mem);
>> +
>> +    memory_region_init(&s->xscom_mr, OBJECT(s), name, PNV_XSCOM_SIZE);
>> +    address_space_init(&s->xscom_as, &s->xscom_mr, name);
>> +    g_free(name);
>> +
>> +}
>> +
>> +static void pnv_xscom_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    dc->realize = pnv_xscom_realize;
>> +}
>> +
>> +static const TypeInfo pnv_xscom_info = {
>> +    .name          = TYPE_PNV_XSCOM,
>> +    .parent        = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size = sizeof(PnvXScom),
>> +    .class_init    = pnv_xscom_class_init,
>> +};
>> +
>> +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_info);
>> +    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->devnode) {
>> +            _FDT((xc->devnode(xd, args->fdt, args->xscom_offset)));
>> +        }
>> +    }
>> +    return 0;
>> +}
>> +
>> +int pnv_xscom_populate_fdt(PnvXScom *adu, void *fdt, int root_offset)
>> +{
>> +    const char compat[] = "ibm,power8-xscom\0ibm,xscom";
>> +    uint64_t reg[] = { cpu_to_be64(PNV_XSCOM_BASE(adu->chip_id)),
>> +                       cpu_to_be64(PNV_XSCOM_SIZE) };
>> +    int xscom_offset;
>> +    ForeachPopulateArgs args;
>> +    char *name;
>> +
>> +    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", adu->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))));
>> +    _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat,
>> +                      sizeof(compat))));
>> +    _FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0)));
>> +
>> +    args.fdt = fdt;
>> +    args.xscom_offset = xscom_offset;
>> +
>> +    object_child_foreach(OBJECT(adu), xscom_populate_child, &args);
>> +    return 0;
>> +}
>> +
>> +/*
>> + * XScom address translation depends on the chip type and not all
>> + * objects have backlink to it. Here's a helper to handle this case.
>> + * To be improved.
>> + */
> 
> Yeah.. this seems a bit hacky as you say.  Passing the individual
> device to these helpers just seems wrong, since it's a chip / machine
> dependent mapping.  Does this go away if you use the (pcba << 3)
> encoding?

Hopefully. the goal is to solve that in v4.

>> +uint32_t pnv_xscom_pcba(PnvXScomInterface *dev, uint64_t addr)
>> +{
>> +    PnvXScomInterfaceClass *xc = PNV_XSCOM_INTERFACE_GET_CLASS(dev);
>> +
>> +    if (!xc->xscom_pcba) {
>> +        PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
>> +        PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pnv->chips[0]);
>> +
>> +        xc->xscom_pcba = pcc->xscom_pcba;
>> +    }
>> +
>> +    return xc->xscom_pcba(addr);
>> +}
>> +
>> +uint64_t pnv_xscom_addr(PnvXScomInterface *dev, uint32_t pcba)
>> +{
>> +     PnvXScomInterfaceClass *xc = PNV_XSCOM_INTERFACE_GET_CLASS(dev);
>> +
>> +    if (!xc->xscom_addr) {
>> +        PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
>> +        PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pnv->chips[0]);
>> +
>> +        xc->xscom_addr = pcc->xscom_addr;
>> +    }
>> +
>> +    return xc->xscom_addr(pcba);
>> +}
>> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
>> index 262faa59a75f..0371710b1882 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)
>> @@ -42,6 +43,7 @@ typedef struct PnvChip {
>>  
>>      /*< public >*/
>>      uint32_t     chip_id;
>> +    PnvXScom     xscom;
>>  
>>      uint32_t  nr_cores;
>>      uint64_t  cores_mask;
>> @@ -60,6 +62,8 @@ typedef struct PnvChipClass {
>>  
>>      void (*realize)(PnvChip *dev, Error **errp);
>>      uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
>> +    uint64_t (*xscom_addr)(uint32_t pcba);
>> +    uint32_t (*xscom_pcba)(uint64_t addr);
>>  } PnvChipClass;
>>  
>>  #define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E"
>> diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
>> new file mode 100644
>> index 000000000000..0a03d533db59
>> --- /dev/null
>> +++ b/include/hw/ppc/pnv_xscom.h
>> @@ -0,0 +1,74 @@
>> +/*
>> + * 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 "hw/sysbus.h"
>> +
>> +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 (*devnode)(PnvXScomInterface *dev, void *fdt, int offset);
>> +
>> +    uint64_t (*xscom_addr)(uint32_t pcba);
>> +    uint32_t (*xscom_pcba)(uint64_t addr);
> 
> Allowing the SCOM device to override the mapping just seems bogus.
> Surely this should always go via the chip.

yes.

>> +} PnvXScomInterfaceClass;
>> +
>> +#define TYPE_PNV_XSCOM "pnv-xscom"
>> +#define PNV_XSCOM(obj) OBJECT_CHECK(PnvXScom, (obj), TYPE_PNV_XSCOM)
>> +
>> +typedef struct PnvChipClass PnvChipClass;
>> +
>> +typedef struct PnvXScom {
>> +    /*< private >*/
>> +    SysBusDevice parent_obj;
>> +    /*< public >*/
>> +
>> +    MemoryRegion mem;
>> +    int32_t chip_id;
>> +    PnvChipClass *chip_class;
> 
> Just having a pointer to the chip seems simpler than storing both id
> and class here.  But I guess it'll go away anyway if you merge the ADU
> into the chip object itself.

I will merge in v4.

Thanks,

C. 

>> +    MemoryRegion xscom_mr;
>> +    AddressSpace xscom_as;
>> +} PnvXScom;
>> +
>> +#define PNV_XSCOM_SIZE        0x800000000ull
>> +#define PNV_XSCOM_BASE(chip)                                    \
>> +    (0x3fc0000000000ull + ((uint64_t)(chip)) * PNV_XSCOM_SIZE)
>> +
>> +extern int pnv_xscom_populate_fdt(PnvXScom *xscom, void *fdt, int offset);
>> +
>> +/*
>> + * helpers to translate to XScomm PCB addresses
>> + */
>> +extern uint32_t pnv_xscom_pcba(PnvXScomInterface *dev, uint64_t addr);
>> +extern uint64_t pnv_xscom_addr(PnvXScomInterface *dev, uint32_t pcba);
>> +
>> +#endif /* _PPC_PNV_XSCOM_H */
> 

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

* Re: [Qemu-devel] [PATCH v3 08/10] ppc/pnv: add a XScomDevice to PnvCore
  2016-09-21  6:12   ` David Gibson
@ 2016-09-22  8:33     ` Cédric Le Goater
  2016-09-23  2:50       ` David Gibson
  0 siblings, 1 reply; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-22  8:33 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 09/21/2016 08:12 AM, David Gibson wrote:
> On Thu, Sep 15, 2016 at 02:45:58PM +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 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          | 55 ++++++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/ppc/pnv_core.h  |  2 ++
>>  include/hw/ppc/pnv_xscom.h | 19 ++++++++++++++++
>>  4 files changed, 80 insertions(+)
>>
>> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
>> index 7dcdf18a9e6b..6a3d1fbf8403 100644
>> --- a/hw/ppc/pnv.c
>> +++ b/hw/ppc/pnv.c
>> @@ -619,6 +619,10 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
>>                                   &error_fatal);
>>          object_unref(OBJECT(pnv_core));
>>          i++;
>> +
>> +        memory_region_add_subregion(&chip->xscom.xscom_mr,
>> +                         pcc->xscom_addr(PNV_XSCOM_EX_CORE_BASE(core_hwid)),
>> +                         &PNV_CORE(pnv_core)->xscom_regs);
> 
> I think the core realize function should be doing this itself.

When working on the support of the AST2{4,5}00 SoC for qemu, these 
are the BMC chips for the OpenPOWER systems, we were asked to do all 
the mmio mappings for the devices at the board level. 

I think we can consider that the powernv chip is the board level for 
the xscom address space and to all the mapping there.

This has some benefits on the view of the address space as it is 
located in one file and not spread in multiple areas of the code.

> 
>>      }
>>      g_free(typename);
>>  
>> diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
>> index 6fed5a208536..81b83d0f41b3 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"
>> @@ -57,6 +58,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 DTS_RESULT0     0x50000
>> +#define DTS_RESULT1     0x50001
>> +
>> +static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr,
>> +                                    unsigned int width)
>> +{
>> +    uint32_t offset = pnv_xscom_pcba(opaque, addr);
>> +    uint64_t val = 0;
>> +
>> +    /* The result should be 38 C */
>> +    switch (offset) {
>> +    case DTS_RESULT0:
>> +        val = 0x26f024f023f0000ull;
>> +        break;
>> +    case 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);
>> +}
> 
> You should double check, but I think you can implement an RO region in
> an address space by just leaving the write function as NULL.

OK.

Thanks,

C.

>> +
>> +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;
>> @@ -117,6 +163,11 @@ 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_addr(PNV_XSCOM_INTERFACE(dev),
>> +                                                   PNV_XSCOM_EX_CORE_SIZE));
>>      return;
>>  
>>  err:
>> @@ -169,6 +220,10 @@ static void pnv_core_register_types(void)
>>              .instance_size = sizeof(PnvCore),
>>              .class_init = pnv_core_class_init,
>>              .class_data = (void *) pnv_core_models[i],
>> +            .interfaces    = (InterfaceInfo[]) {
>> +                { TYPE_PNV_XSCOM_INTERFACE },
>> +                { }
>> +            }
>>          };
>>          ti.name = pnv_core_typename(pnv_core_models[i]);
>>          type_register(&ti);
>> 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 0a03d533db59..31e5e8847b90 100644
>> --- a/include/hw/ppc/pnv_xscom.h
>> +++ b/include/hw/ppc/pnv_xscom.h
>> @@ -63,6 +63,25 @@ typedef struct PnvXScom {
>>  #define PNV_XSCOM_BASE(chip)                                    \
>>      (0x3fc0000000000ull + ((uint64_t)(chip)) * PNV_XSCOM_SIZE)
>>  
>> +/*
>> + * Layout of Xscom PCB addresses for 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 int pnv_xscom_populate_fdt(PnvXScom *xscom, void *fdt, int offset);
>>  
>>  /*
> 

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

* Re: [Qemu-devel] [PATCH v3 10/10] ppc/pnv: add a ISA bus
  2016-09-21  6:30   ` David Gibson
@ 2016-09-22  8:44     ` Cédric Le Goater
  2016-09-23  2:54       ` David Gibson
  0 siblings, 1 reply; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-22  8:44 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel


>> +static ISABus *pnv_isa_create(PnvChip *chip)
>> +{
>> +    PnvLpcController *lpc = &chip->lpc;
>> +    ISABus *isa_bus;
>> +    qemu_irq *irqs;
>> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
>> +
>> +    /* Instanciate ISA bus. let isa_bus_new() create its own bridge on
> 
> Instantiate has 3 't's and no 'c's; English orthography strikes again.

he :) thanks. 

>> +     * sysbus otherwise devices speficied on the command line will
>> +     * fail to create.
>> +     */
>> +    isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io,
>> +                          &error_fatal);
> 
> It's not clear to me if this belongs in the chip code or on the lpc
> code - the lpc does create a device node as 'isa@', although it also
> does some other stuff.

In fact, the isabus in the qemu model is at the machine level, see below,
next to the 'Instanc^Htiate'.

each chip has a lpc controller but skiboot use a default one to route
the traffic. So we choose the chip[0] one for that. 

Looking closer, I should make sure the "primary" cell is not added in the 
device tree for chip_id != 0.  

Thanks,

C. 


>> +
>> +    /* 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, 16);
>> +    } else {
>> +        irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, NULL, 16);
>> +    }
>> +
>> +    isa_bus_irqs(isa_bus, irqs);
>> +    return isa_bus;
>> +}
>> +
>>  static void ppc_powernv_init(MachineState *machine)
>>  {
>>      PnvMachineState *pnv = POWERNV_MACHINE(machine);
>> @@ -389,6 +444,15 @@ static void ppc_powernv_init(MachineState *machine)
>>          object_property_set_bool(chip, true, "realized", &error_fatal);
>>      }
>>      g_free(chip_typename);
>> +
>> +    /* Instanciate 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);
>>  }
>>  
>>  static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t core_id)
>> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
>> index a30579a5817f..e75f937d40dd 100644
>> --- a/include/hw/ppc/pnv.h
>> +++ b/include/hw/ppc/pnv.h
>> @@ -123,6 +123,8 @@ typedef struct PnvMachineState {
>>  
>>      uint32_t  num_chips;
>>      PnvChip   **chips;
>> +
>> +    ISABus *isa_bus;
>>  } PnvMachineState;
>>  
>>  #define POWERNV_FDT_ADDR                0x01000000
> 

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

* Re: [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure
  2016-09-22  8:25     ` Cédric Le Goater
@ 2016-09-23  2:46       ` David Gibson
  2016-09-26 16:11         ` Cédric Le Goater
  0 siblings, 1 reply; 53+ messages in thread
From: David Gibson @ 2016-09-23  2:46 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

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

On Thu, Sep 22, 2016 at 10:25:59AM +0200, Cédric Le Goater wrote:
> >> @@ -493,6 +525,8 @@ 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_addr = pnv_chip_xscom_addr_p9;
> >> +    k->xscom_pcba = pnv_chip_xscom_pcba_p9;
> > 
> > So if you do as BenH (and I) suggested and have the "scom address
> > space" actually be addressed by (pcba << 3), I think you can probably
> > avoid these.  
> 
> I will look at that option again. 
> 
> I was trying to untangle a few things at the same time. I have better
> view of the problem to solve now. The bus is gone, that's was one 
> thing. How we map these xscom regions is the next. 
> 
> Ben suggested to add some P7/P8 mangling before the dispatch in 
> the &address_space_xscom. This should make things cleaner. I had 
> not thought of doing that and this is why I introduced these helpers :
> 
> +uint32_t pnv_xscom_pcba(PnvXScomInterface *dev, uint64_t addr)
> +uint64_t pnv_xscom_addr(PnvXScomInterface *dev, uint32_t pcba)
> 
> which I don't really like ...
> 
> but we must make sure that we can do the mapping of the xscom 
> subregions in the &address_space_xscom using (pcba << 3)
> 
> 
> > Instead you can handle it in the chip or ADU realize function by either:
> > 
> >     P8: * map one big subregion for the ADU into &address_space_memory
> >         * have the handler for that subregion do the address mangling,
> >           then redispatch into the xscom address space
> >
> >     P9: * Map the appropriate chunk of the xscom address space
> >           directly into address_space_memory
> 
> Yes that was my feeling for a better solution but Ben chimed in with the 
> HMER topic. I need to look at that.

Right.  Doesn't change the basic concept though - it just means you
need (slightly different) redispatchers for both P8 and P9.

> 
> 
> >>      dc->desc = "PowerNV Chip POWER9";
> >>  }
> >>  
> >> @@ -527,6 +561,16 @@ static void pnv_chip_core_sanitize(PnvChip *chip)
> >>      chip->cores_mask &= pcc->cores_mask;
> >>  }
> >>  
> >> +static void pnv_chip_init(Object *obj)
> >> +{
> >> +    PnvChip *chip = PNV_CHIP(obj);
> >> +
> >> +    object_initialize(&chip->xscom, sizeof(chip->xscom), TYPE_PNV_XSCOM);
> >> +    object_property_add_child(obj, "xscom", OBJECT(&chip->xscom), NULL);
> >> +    object_property_add_const_link(OBJECT(&chip->xscom), "chip",
> >> +                                   OBJECT(chip), &error_abort);
> >> +}
> >> +
> >>  static void pnv_chip_realize(DeviceState *dev, Error **errp)
> >>  {
> >>      PnvChip *chip = PNV_CHIP(dev);
> >> @@ -540,6 +584,12 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
> >>          return;
> >>      }
> >>  
> >> +    /* XSCOM bridge */
> >> +    object_property_set_bool(OBJECT(&chip->xscom), true, "realized",
> >> +                             &error_fatal);
> >> +    sysbus_mmio_map(SYS_BUS_DEVICE(&chip->xscom), 0,
> >> +                    PNV_XSCOM_BASE(chip->chip_id));
> >> +
> >>      /* Early checks on the core settings */
> >>      pnv_chip_core_sanitize(chip);
> >>  
> >> @@ -597,6 +647,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..019cd85428de
> >> --- /dev/null
> >> +++ b/hw/ppc/pnv_xscom.c
> >> @@ -0,0 +1,308 @@
> >> +/*
> >> + * 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;
> > 
> > Hmm.. is current_cpu a safe thing to use in the case of KVM or MTTCG?
> 
> hmm, indeed. I need to look at that.
> 
> >> +    PowerPCCPU *cpu = POWERPC_CPU(cs);
> >> +    CPUPPCState *env = &cpu->env;
> >> +
> >> +    cpu_synchronize_state(cs);
> >> +    env->spr[SPR_HMER] |= hmer_bits;
> >> +
> >> +    /* XXX Need a CPU helper to set HMER, also handle gneeration
> >> +     * of HMIs
> >> +     */
> >> +}
> >> +
> >> +static bool xscom_dispatch_read(PnvXScom *xscom, hwaddr addr, uint64_t *val)
> >> +{
> >> +    uint32_t success;
> >> +    uint8_t data[8];
> >> +
> >> +    success = !address_space_rw(&xscom->xscom_as, addr, MEMTXATTRS_UNSPECIFIED,
> >> +                                data, 8, false);
> >> +    *val = (((uint64_t) data[0]) << 56 |
> >> +            ((uint64_t) data[1]) << 48 |
> >> +            ((uint64_t) data[2]) << 40 |
> >> +            ((uint64_t) data[3]) << 32 |
> >> +            ((uint64_t) data[4]) << 24 |
> >> +            ((uint64_t) data[5]) << 16 |
> >> +            ((uint64_t) data[6]) << 8  |
> >> +            ((uint64_t) data[7]));
> > 
> > AFAICT this is basically assuming data is always encoded BE.  With the
> > right choice of endian flags on the individual SCOM device
> > registrations with the scom address space, I think you should be able
> > to avoid this mangling.
> 
> yes. I should but curiously I had to do this, and this works the same on
> an intel host or a ppc64 host.

Hmm.. I suspect what you actually need is NATIVE_ENDIAN on the
individual SCOM devices, with BIG_ENDIAN on the redispatcher region.

> >> +    return success;
> >> +}
> >> +
> >> +static bool xscom_dispatch_write(PnvXScom *xscom, hwaddr addr, uint64_t val)
> >> +{
> >> +    uint32_t success;
> >> +    uint8_t data[8];
> >> +
> >> +    data[0] = (val >> 56) & 0xff;
> >> +    data[1] = (val >> 48) & 0xff;
> >> +    data[2] = (val >> 40) & 0xff;
> >> +    data[3] = (val >> 32) & 0xff;
> >> +    data[4] = (val >> 24) & 0xff;
> >> +    data[5] = (val >> 16) & 0xff;
> >> +    data[6] = (val >> 8) & 0xff;
> >> +    data[7] = val & 0xff;
> >> +
> >> +    success = !address_space_rw(&xscom->xscom_as, addr, MEMTXATTRS_UNSPECIFIED,
> >> +                           data, 8, true);
> >> +    return success;
> >> +}
> >> +
> >> +static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)
> >> +{
> >> +    PnvXScom *s = opaque;
> >> +    uint32_t pcba = s->chip_class->xscom_pcba(addr);
> >> +    uint64_t val = 0;
> >> +
> >> +    /* Handle some SCOMs here before dispatch */
> >> +    switch (pcba) {
> >> +    case 0xf000f:
> >> +        val = s->chip_class->chip_cfam_id;
> >> +        break;
> >> +    case 0x1010c00:     /* PIBAM FIR */
> >> +    case 0x1010c03:     /* PIBAM FIR MASK */
> >> +    case 0x2020007:     /* ADU stuff */
> >> +    case 0x2020009:     /* ADU stuff */
> >> +    case 0x202000f:     /* ADU stuff */
> >> +        val = 0;
> >> +        break;
> >> +    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 */
> >> +        val = 0;
> >> +        break;
> > 
> > It'd be theoretically nicer to actually register regions for these
> > special case addresses, but handling it here is a reasonable hack to
> > get things working quickly for the time being.
> 
> I will make a default region on the whole xscomm address space to catch 
> these.

Ok.

> 
> >> +    default:
> >> +        if (!xscom_dispatch_read(s, addr, &val)) {
> >> +            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;
> >> +        }
> >> +    }
> >> +
> >> +    xscom_complete(HMER_XSCOM_DONE);
> >> +    return val;
> >> +}
> >> +
> >> +static void xscom_write(void *opaque, hwaddr addr, uint64_t val,
> >> +                        unsigned width)
> >> +{
> >> +    PnvXScom *s = opaque;
> >> +    uint32_t pcba = s->chip_class->xscom_pcba(addr);
> >> +
> >> +    /* Handle some SCOMs here before dispatch */
> >> +    switch (pcba) {
> >> +        /* We ignore writes to these */
> >> +    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 */
> >> +        break;
> >> +    default:
> >> +        if (!xscom_dispatch_write(s, addr, val)) {
> >> +            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;
> >> +        }
> >> +    }
> >> +
> >> +    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,
> >> +};
> >> +
> >> +static void pnv_xscom_realize(DeviceState *dev, Error **errp)
> >> +{
> >> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> >> +    PnvXScom *s = PNV_XSCOM(dev);
> >> +    char *name;
> >> +    Object *obj;
> >> +    Error *err = NULL;
> >> +
> >> +    obj = object_property_get_link(OBJECT(s), "chip", &err);
> >> +    if (!obj) {
> >> +        error_setg(errp, "%s: required link 'chip' not found: %s",
> >> +                     __func__, error_get_pretty(err));
> >> +        return;
> >> +    }
> >> +
> >> +    s->chip_class = PNV_CHIP_GET_CLASS(obj);
> >> +    s->chip_id = PNV_CHIP(obj)->chip_id;
> >> +
> >> +    if (s->chip_id < 0) {
> >> +        error_setg(errp, "invalid chip id '%d'", s->chip_id);
> >> +        return;
> >> +    }
> >> +
> >> +    name = g_strdup_printf("xscom-%x", s->chip_id);
> >> +    memory_region_init_io(&s->mem, OBJECT(s), &pnv_xscom_ops, s, name,
> >> +                          PNV_XSCOM_SIZE);
> >> +    sysbus_init_mmio(sbd, &s->mem);
> >> +
> >> +    memory_region_init(&s->xscom_mr, OBJECT(s), name, PNV_XSCOM_SIZE);
> >> +    address_space_init(&s->xscom_as, &s->xscom_mr, name);
> >> +    g_free(name);
> >> +
> >> +}
> >> +
> >> +static void pnv_xscom_class_init(ObjectClass *klass, void *data)
> >> +{
> >> +    DeviceClass *dc = DEVICE_CLASS(klass);
> >> +
> >> +    dc->realize = pnv_xscom_realize;
> >> +}
> >> +
> >> +static const TypeInfo pnv_xscom_info = {
> >> +    .name          = TYPE_PNV_XSCOM,
> >> +    .parent        = TYPE_SYS_BUS_DEVICE,
> >> +    .instance_size = sizeof(PnvXScom),
> >> +    .class_init    = pnv_xscom_class_init,
> >> +};
> >> +
> >> +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_info);
> >> +    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->devnode) {
> >> +            _FDT((xc->devnode(xd, args->fdt, args->xscom_offset)));
> >> +        }
> >> +    }
> >> +    return 0;
> >> +}
> >> +
> >> +int pnv_xscom_populate_fdt(PnvXScom *adu, void *fdt, int root_offset)
> >> +{
> >> +    const char compat[] = "ibm,power8-xscom\0ibm,xscom";
> >> +    uint64_t reg[] = { cpu_to_be64(PNV_XSCOM_BASE(adu->chip_id)),
> >> +                       cpu_to_be64(PNV_XSCOM_SIZE) };
> >> +    int xscom_offset;
> >> +    ForeachPopulateArgs args;
> >> +    char *name;
> >> +
> >> +    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", adu->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))));
> >> +    _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat,
> >> +                      sizeof(compat))));
> >> +    _FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0)));
> >> +
> >> +    args.fdt = fdt;
> >> +    args.xscom_offset = xscom_offset;
> >> +
> >> +    object_child_foreach(OBJECT(adu), xscom_populate_child, &args);
> >> +    return 0;
> >> +}
> >> +
> >> +/*
> >> + * XScom address translation depends on the chip type and not all
> >> + * objects have backlink to it. Here's a helper to handle this case.
> >> + * To be improved.
> >> + */
> > 
> > Yeah.. this seems a bit hacky as you say.  Passing the individual
> > device to these helpers just seems wrong, since it's a chip / machine
> > dependent mapping.  Does this go away if you use the (pcba << 3)
> > encoding?
> 
> Hopefully. the goal is to solve that in v4.
> 
> >> +uint32_t pnv_xscom_pcba(PnvXScomInterface *dev, uint64_t addr)
> >> +{
> >> +    PnvXScomInterfaceClass *xc = PNV_XSCOM_INTERFACE_GET_CLASS(dev);
> >> +
> >> +    if (!xc->xscom_pcba) {
> >> +        PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
> >> +        PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pnv->chips[0]);
> >> +
> >> +        xc->xscom_pcba = pcc->xscom_pcba;
> >> +    }
> >> +
> >> +    return xc->xscom_pcba(addr);
> >> +}
> >> +
> >> +uint64_t pnv_xscom_addr(PnvXScomInterface *dev, uint32_t pcba)
> >> +{
> >> +     PnvXScomInterfaceClass *xc = PNV_XSCOM_INTERFACE_GET_CLASS(dev);
> >> +
> >> +    if (!xc->xscom_addr) {
> >> +        PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
> >> +        PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pnv->chips[0]);
> >> +
> >> +        xc->xscom_addr = pcc->xscom_addr;
> >> +    }
> >> +
> >> +    return xc->xscom_addr(pcba);
> >> +}
> >> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
> >> index 262faa59a75f..0371710b1882 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)
> >> @@ -42,6 +43,7 @@ typedef struct PnvChip {
> >>  
> >>      /*< public >*/
> >>      uint32_t     chip_id;
> >> +    PnvXScom     xscom;
> >>  
> >>      uint32_t  nr_cores;
> >>      uint64_t  cores_mask;
> >> @@ -60,6 +62,8 @@ typedef struct PnvChipClass {
> >>  
> >>      void (*realize)(PnvChip *dev, Error **errp);
> >>      uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
> >> +    uint64_t (*xscom_addr)(uint32_t pcba);
> >> +    uint32_t (*xscom_pcba)(uint64_t addr);
> >>  } PnvChipClass;
> >>  
> >>  #define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E"
> >> diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
> >> new file mode 100644
> >> index 000000000000..0a03d533db59
> >> --- /dev/null
> >> +++ b/include/hw/ppc/pnv_xscom.h
> >> @@ -0,0 +1,74 @@
> >> +/*
> >> + * 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 "hw/sysbus.h"
> >> +
> >> +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 (*devnode)(PnvXScomInterface *dev, void *fdt, int offset);
> >> +
> >> +    uint64_t (*xscom_addr)(uint32_t pcba);
> >> +    uint32_t (*xscom_pcba)(uint64_t addr);
> > 
> > Allowing the SCOM device to override the mapping just seems bogus.
> > Surely this should always go via the chip.
> 
> yes.
> 
> >> +} PnvXScomInterfaceClass;
> >> +
> >> +#define TYPE_PNV_XSCOM "pnv-xscom"
> >> +#define PNV_XSCOM(obj) OBJECT_CHECK(PnvXScom, (obj), TYPE_PNV_XSCOM)
> >> +
> >> +typedef struct PnvChipClass PnvChipClass;
> >> +
> >> +typedef struct PnvXScom {
> >> +    /*< private >*/
> >> +    SysBusDevice parent_obj;
> >> +    /*< public >*/
> >> +
> >> +    MemoryRegion mem;
> >> +    int32_t chip_id;
> >> +    PnvChipClass *chip_class;
> > 
> > Just having a pointer to the chip seems simpler than storing both id
> > and class here.  But I guess it'll go away anyway if you merge the ADU
> > into the chip object itself.
> 
> I will merge in v4.
> 
> Thanks,
> 
> C. 
> 
> >> +    MemoryRegion xscom_mr;
> >> +    AddressSpace xscom_as;
> >> +} PnvXScom;
> >> +
> >> +#define PNV_XSCOM_SIZE        0x800000000ull
> >> +#define PNV_XSCOM_BASE(chip)                                    \
> >> +    (0x3fc0000000000ull + ((uint64_t)(chip)) * PNV_XSCOM_SIZE)
> >> +
> >> +extern int pnv_xscom_populate_fdt(PnvXScom *xscom, void *fdt, int offset);
> >> +
> >> +/*
> >> + * helpers to translate to XScomm PCB addresses
> >> + */
> >> +extern uint32_t pnv_xscom_pcba(PnvXScomInterface *dev, uint64_t addr);
> >> +extern uint64_t pnv_xscom_addr(PnvXScomInterface *dev, uint32_t pcba);
> >> +
> >> +#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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 08/10] ppc/pnv: add a XScomDevice to PnvCore
  2016-09-22  8:33     ` Cédric Le Goater
@ 2016-09-23  2:50       ` David Gibson
  0 siblings, 0 replies; 53+ messages in thread
From: David Gibson @ 2016-09-23  2:50 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

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

On Thu, Sep 22, 2016 at 10:33:21AM +0200, Cédric Le Goater wrote:
> On 09/21/2016 08:12 AM, David Gibson wrote:
> > On Thu, Sep 15, 2016 at 02:45:58PM +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 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          | 55 ++++++++++++++++++++++++++++++++++++++++++++++
> >>  include/hw/ppc/pnv_core.h  |  2 ++
> >>  include/hw/ppc/pnv_xscom.h | 19 ++++++++++++++++
> >>  4 files changed, 80 insertions(+)
> >>
> >> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> >> index 7dcdf18a9e6b..6a3d1fbf8403 100644
> >> --- a/hw/ppc/pnv.c
> >> +++ b/hw/ppc/pnv.c
> >> @@ -619,6 +619,10 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
> >>                                   &error_fatal);
> >>          object_unref(OBJECT(pnv_core));
> >>          i++;
> >> +
> >> +        memory_region_add_subregion(&chip->xscom.xscom_mr,
> >> +                         pcc->xscom_addr(PNV_XSCOM_EX_CORE_BASE(core_hwid)),
> >> +                         &PNV_CORE(pnv_core)->xscom_regs);
> > 
> > I think the core realize function should be doing this itself.
> 
> When working on the support of the AST2{4,5}00 SoC for qemu, these 
> are the BMC chips for the OpenPOWER systems, we were asked to do all 
> the mmio mappings for the devices at the board level. 

After a bit of thought, I agree.  Doing as you're doing here and
building any internal structure into a single MR within the device
which then gets mapped into the global address space by the machine is
a good approach.

> I think we can consider that the powernv chip is the board level for 
> the xscom address space and to all the mapping there.
> 
> This has some benefits on the view of the address space as it is 
> located in one file and not spread in multiple areas of the code.



> 
> > 
> >>      }
> >>      g_free(typename);
> >>  
> >> diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
> >> index 6fed5a208536..81b83d0f41b3 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"
> >> @@ -57,6 +58,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 DTS_RESULT0     0x50000
> >> +#define DTS_RESULT1     0x50001
> >> +
> >> +static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr,
> >> +                                    unsigned int width)
> >> +{
> >> +    uint32_t offset = pnv_xscom_pcba(opaque, addr);
> >> +    uint64_t val = 0;
> >> +
> >> +    /* The result should be 38 C */
> >> +    switch (offset) {
> >> +    case DTS_RESULT0:
> >> +        val = 0x26f024f023f0000ull;
> >> +        break;
> >> +    case 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);
> >> +}
> > 
> > You should double check, but I think you can implement an RO region in
> > an address space by just leaving the write function as NULL.
> 
> OK.
> 
> Thanks,
> 
> C.
> 
> >> +
> >> +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;
> >> @@ -117,6 +163,11 @@ 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_addr(PNV_XSCOM_INTERFACE(dev),
> >> +                                                   PNV_XSCOM_EX_CORE_SIZE));
> >>      return;
> >>  
> >>  err:
> >> @@ -169,6 +220,10 @@ static void pnv_core_register_types(void)
> >>              .instance_size = sizeof(PnvCore),
> >>              .class_init = pnv_core_class_init,
> >>              .class_data = (void *) pnv_core_models[i],
> >> +            .interfaces    = (InterfaceInfo[]) {
> >> +                { TYPE_PNV_XSCOM_INTERFACE },
> >> +                { }
> >> +            }
> >>          };
> >>          ti.name = pnv_core_typename(pnv_core_models[i]);
> >>          type_register(&ti);
> >> 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 0a03d533db59..31e5e8847b90 100644
> >> --- a/include/hw/ppc/pnv_xscom.h
> >> +++ b/include/hw/ppc/pnv_xscom.h
> >> @@ -63,6 +63,25 @@ typedef struct PnvXScom {
> >>  #define PNV_XSCOM_BASE(chip)                                    \
> >>      (0x3fc0000000000ull + ((uint64_t)(chip)) * PNV_XSCOM_SIZE)
> >>  
> >> +/*
> >> + * Layout of Xscom PCB addresses for 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 int pnv_xscom_populate_fdt(PnvXScom *xscom, 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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 10/10] ppc/pnv: add a ISA bus
  2016-09-22  8:44     ` Cédric Le Goater
@ 2016-09-23  2:54       ` David Gibson
  0 siblings, 0 replies; 53+ messages in thread
From: David Gibson @ 2016-09-23  2:54 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

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

On Thu, Sep 22, 2016 at 10:44:13AM +0200, Cédric Le Goater wrote:
> 
> >> +static ISABus *pnv_isa_create(PnvChip *chip)
> >> +{
> >> +    PnvLpcController *lpc = &chip->lpc;
> >> +    ISABus *isa_bus;
> >> +    qemu_irq *irqs;
> >> +    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
> >> +
> >> +    /* Instanciate ISA bus. let isa_bus_new() create its own bridge on
> > 
> > Instantiate has 3 't's and no 'c's; English orthography strikes again.
> 
> he :) thanks. 
> 
> >> +     * sysbus otherwise devices speficied on the command line will
> >> +     * fail to create.
> >> +     */
> >> +    isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io,
> >> +                          &error_fatal);
> > 
> > It's not clear to me if this belongs in the chip code or on the lpc
> > code - the lpc does create a device node as 'isa@', although it also
> > does some other stuff.
> 
> In fact, the isabus in the qemu model is at the machine level, see below,
> next to the 'Instanc^Htiate'.
> 
> each chip has a lpc controller but skiboot use a default one to route
> the traffic. So we choose the chip[0] one for that. 
> 
> Looking closer, I should make sure the "primary" cell is not added in the 
> device tree for chip_id != 0.

Ok, that seens sensible.

-- 
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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure
  2016-09-23  2:46       ` David Gibson
@ 2016-09-26 16:11         ` Cédric Le Goater
  2016-09-27  2:35           ` David Gibson
  0 siblings, 1 reply; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-26 16:11 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 09/23/2016 04:46 AM, David Gibson wrote:
> On Thu, Sep 22, 2016 at 10:25:59AM +0200, Cédric Le Goater wrote:
>>>> @@ -493,6 +525,8 @@ 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_addr = pnv_chip_xscom_addr_p9;
>>>> +    k->xscom_pcba = pnv_chip_xscom_pcba_p9;
>>>
>>> So if you do as BenH (and I) suggested and have the "scom address
>>> space" actually be addressed by (pcba << 3), I think you can probably
>>> avoid these.  
>>
>> I will look at that option again. 
>>
>> I was trying to untangle a few things at the same time. I have better
>> view of the problem to solve now. The bus is gone, that's was one 
>> thing. How we map these xscom regions is the next. 
>>
>> Ben suggested to add some P7/P8 mangling before the dispatch in 
>> the &address_space_xscom. This should make things cleaner. I had 
>> not thought of doing that and this is why I introduced these helpers :
>>
>> +uint32_t pnv_xscom_pcba(PnvXScomInterface *dev, uint64_t addr)
>> +uint64_t pnv_xscom_addr(PnvXScomInterface *dev, uint32_t pcba)
>>
>> which I don't really like ...
>>
>> but we must make sure that we can do the mapping of the xscom 
>> subregions in the &address_space_xscom using (pcba << 3)
>>
>>
>>> Instead you can handle it in the chip or ADU realize function by either:
>>>
>>>     P8: * map one big subregion for the ADU into &address_space_memory
>>>         * have the handler for that subregion do the address mangling,
>>>           then redispatch into the xscom address space
>>>
>>>     P9: * Map the appropriate chunk of the xscom address space
>>>           directly into address_space_memory
>>
>> Yes that was my feeling for a better solution but Ben chimed in with the 
>> HMER topic. I need to look at that.
> 
> Right.  Doesn't change the basic concept though - it just means you
> need (slightly different) redispatchers for both P8 and P9.

In fact they are the same, you only need an "addr to pcba" handler at the
chip class level : 

static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)
{
	PnvChip *chip = opaque;
	uint32_t pcba = PNV_CHIP_GET_CLASS(chip)->xscom_pcba(addr);
	uint64_t val = 0;
	MemTxResult result;

	...

        val = address_space_ldq(&chip->xscom_as, pcba << 3,
                                MEMTXATTRS_UNSPECIFIED, &result);
        if (result != MEMTX_OK) {

  

And so, the result is pretty clean. I killed the proxy object and merged 
the regions in the chip but I have kept the pnv_xscom.c file because the 
code related to xscom is rather large : ~250 lines. 

The objects declaring a xscom region need to do some register shifting but 
this is usual in mmio regions.

You will see in v4.

>>>> +static bool xscom_dispatch_read(PnvXScom *xscom, hwaddr addr, uint64_t *val)
>>>> +{
>>>> +    uint32_t success;
>>>> +    uint8_t data[8];
>>>> +
>>>> +    success = !address_space_rw(&xscom->xscom_as, addr, MEMTXATTRS_UNSPECIFIED,
>>>> +                                data, 8, false);
>>>> +    *val = (((uint64_t) data[0]) << 56 |
>>>> +            ((uint64_t) data[1]) << 48 |
>>>> +            ((uint64_t) data[2]) << 40 |
>>>> +            ((uint64_t) data[3]) << 32 |
>>>> +            ((uint64_t) data[4]) << 24 |
>>>> +            ((uint64_t) data[5]) << 16 |
>>>> +            ((uint64_t) data[6]) << 8  |
>>>> +            ((uint64_t) data[7]));
>>>
>>> AFAICT this is basically assuming data is always encoded BE.  With the
>>> right choice of endian flags on the individual SCOM device
>>> registrations with the scom address space, I think you should be able
>>> to avoid this mangling.
>>
>> yes. I should but curiously I had to do this, and this works the same on
>> an intel host or a ppc64 host.
> 
> Hmm.. I suspect what you actually need is NATIVE_ENDIAN on the
> individual SCOM devices, with BIG_ENDIAN on the redispatcher region.

we should be using address_space_ldq and address_space_stq.

>>>> +
>>>> +    success = !address_space_rw(&xscom->xscom_as, addr, MEMTXATTRS_UNSPECIFIED,
>>>> +                           data, 8, true);
>>>> +    return success;
>>>> +}
>>>> +
>>>> +static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)
>>>> +{
>>>> +    PnvXScom *s = opaque;
>>>> +    uint32_t pcba = s->chip_class->xscom_pcba(addr);
>>>> +    uint64_t val = 0;
>>>> +
>>>> +    /* Handle some SCOMs here before dispatch */
>>>> +    switch (pcba) {
>>>> +    case 0xf000f:
>>>> +        val = s->chip_class->chip_cfam_id;
>>>> +        break;
>>>> +    case 0x1010c00:     /* PIBAM FIR */
>>>> +    case 0x1010c03:     /* PIBAM FIR MASK */
>>>> +    case 0x2020007:     /* ADU stuff */
>>>> +    case 0x2020009:     /* ADU stuff */
>>>> +    case 0x202000f:     /* ADU stuff */
>>>> +        val = 0;
>>>> +        break;
>>>> +    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 */
>>>> +        val = 0;
>>>> +        break;
>>>
>>> It'd be theoretically nicer to actually register regions for these
>>> special case addresses, but handling it here is a reasonable hack to
>>> get things working quickly for the time being.
>>
>> I will make a default region on the whole xscomm address space to catch 
>> these.
> 
> Ok.

Well, it does not bring much and we loose the ability to catch errors. 
I will leave it that way.

Thanks,

C. 

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

* Re: [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure
  2016-09-26 16:11         ` Cédric Le Goater
@ 2016-09-27  2:35           ` David Gibson
  2016-09-27  5:54             ` Cédric Le Goater
  0 siblings, 1 reply; 53+ messages in thread
From: David Gibson @ 2016-09-27  2:35 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

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

On Mon, Sep 26, 2016 at 06:11:36PM +0200, Cédric Le Goater wrote:
> On 09/23/2016 04:46 AM, David Gibson wrote:
> > On Thu, Sep 22, 2016 at 10:25:59AM +0200, Cédric Le Goater wrote:
> >>>> @@ -493,6 +525,8 @@ 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_addr = pnv_chip_xscom_addr_p9;
> >>>> +    k->xscom_pcba = pnv_chip_xscom_pcba_p9;
> >>>
> >>> So if you do as BenH (and I) suggested and have the "scom address
> >>> space" actually be addressed by (pcba << 3), I think you can probably
> >>> avoid these.  
> >>
> >> I will look at that option again. 
> >>
> >> I was trying to untangle a few things at the same time. I have better
> >> view of the problem to solve now. The bus is gone, that's was one 
> >> thing. How we map these xscom regions is the next. 
> >>
> >> Ben suggested to add some P7/P8 mangling before the dispatch in 
> >> the &address_space_xscom. This should make things cleaner. I had 
> >> not thought of doing that and this is why I introduced these helpers :
> >>
> >> +uint32_t pnv_xscom_pcba(PnvXScomInterface *dev, uint64_t addr)
> >> +uint64_t pnv_xscom_addr(PnvXScomInterface *dev, uint32_t pcba)
> >>
> >> which I don't really like ...
> >>
> >> but we must make sure that we can do the mapping of the xscom 
> >> subregions in the &address_space_xscom using (pcba << 3)
> >>
> >>
> >>> Instead you can handle it in the chip or ADU realize function by either:
> >>>
> >>>     P8: * map one big subregion for the ADU into &address_space_memory
> >>>         * have the handler for that subregion do the address mangling,
> >>>           then redispatch into the xscom address space
> >>>
> >>>     P9: * Map the appropriate chunk of the xscom address space
> >>>           directly into address_space_memory
> >>
> >> Yes that was my feeling for a better solution but Ben chimed in with the 
> >> HMER topic. I need to look at that.
> > 
> > Right.  Doesn't change the basic concept though - it just means you
> > need (slightly different) redispatchers for both P8 and P9.
> 
> In fact they are the same, you only need an "addr to pcba" handler at the
> chip class level : 

Ok.  I'd been thinking of using different dispatchers as an
alternative to using the chip class translator hook, but I guess if
you have the decoding of those "core" registers here as well, then
that doesn't make so much sense.

> static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)
> {
> 	PnvChip *chip = opaque;
> 	uint32_t pcba = PNV_CHIP_GET_CLASS(chip)->xscom_pcba(addr);
> 	uint64_t val = 0;
> 	MemTxResult result;
> 
> 	...
> 
>         val = address_space_ldq(&chip->xscom_as, pcba << 3,
>                                 MEMTXATTRS_UNSPECIFIED, &result);
>         if (result != MEMTX_OK) {
> 
>   
> 
> And so, the result is pretty clean. I killed the proxy object and merged 
> the regions in the chip but I have kept the pnv_xscom.c file because the 
> code related to xscom is rather large : ~250 lines. 

Sure, makes sense.

> The objects declaring a xscom region need to do some register shifting but 
> this is usual in mmio regions.
> 
> You will see in v4.

Ok.

> >>>> +static bool xscom_dispatch_read(PnvXScom *xscom, hwaddr addr, uint64_t *val)
> >>>> +{
> >>>> +    uint32_t success;
> >>>> +    uint8_t data[8];
> >>>> +
> >>>> +    success = !address_space_rw(&xscom->xscom_as, addr, MEMTXATTRS_UNSPECIFIED,
> >>>> +                                data, 8, false);
> >>>> +    *val = (((uint64_t) data[0]) << 56 |
> >>>> +            ((uint64_t) data[1]) << 48 |
> >>>> +            ((uint64_t) data[2]) << 40 |
> >>>> +            ((uint64_t) data[3]) << 32 |
> >>>> +            ((uint64_t) data[4]) << 24 |
> >>>> +            ((uint64_t) data[5]) << 16 |
> >>>> +            ((uint64_t) data[6]) << 8  |
> >>>> +            ((uint64_t) data[7]));
> >>>
> >>> AFAICT this is basically assuming data is always encoded BE.  With the
> >>> right choice of endian flags on the individual SCOM device
> >>> registrations with the scom address space, I think you should be able
> >>> to avoid this mangling.
> >>
> >> yes. I should but curiously I had to do this, and this works the same on
> >> an intel host or a ppc64 host.
> > 
> > Hmm.. I suspect what you actually need is NATIVE_ENDIAN on the
> > individual SCOM devices, with BIG_ENDIAN on the redispatcher region.
> 
> we should be using address_space_ldq and address_space_stq.

Ok.

> >>>> +
> >>>> +    success = !address_space_rw(&xscom->xscom_as, addr, MEMTXATTRS_UNSPECIFIED,
> >>>> +                           data, 8, true);
> >>>> +    return success;
> >>>> +}
> >>>> +
> >>>> +static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)
> >>>> +{
> >>>> +    PnvXScom *s = opaque;
> >>>> +    uint32_t pcba = s->chip_class->xscom_pcba(addr);
> >>>> +    uint64_t val = 0;
> >>>> +
> >>>> +    /* Handle some SCOMs here before dispatch */
> >>>> +    switch (pcba) {
> >>>> +    case 0xf000f:
> >>>> +        val = s->chip_class->chip_cfam_id;
> >>>> +        break;
> >>>> +    case 0x1010c00:     /* PIBAM FIR */
> >>>> +    case 0x1010c03:     /* PIBAM FIR MASK */
> >>>> +    case 0x2020007:     /* ADU stuff */
> >>>> +    case 0x2020009:     /* ADU stuff */
> >>>> +    case 0x202000f:     /* ADU stuff */
> >>>> +        val = 0;
> >>>> +        break;
> >>>> +    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 */
> >>>> +        val = 0;
> >>>> +        break;
> >>>
> >>> It'd be theoretically nicer to actually register regions for these
> >>> special case addresses, but handling it here is a reasonable hack to
> >>> get things working quickly for the time being.
> >>
> >> I will make a default region on the whole xscomm address space to catch 
> >> these.
> > 
> > Ok.
> 
> Well, it does not bring much and we loose the ability to catch errors. 
> I will leave it that way.
> 
> Thanks,
> 
> C. 
> 

-- 
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] 53+ messages in thread

* Re: [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure
  2016-09-27  2:35           ` David Gibson
@ 2016-09-27  5:54             ` Cédric Le Goater
  2016-09-27  6:10               ` Benjamin Herrenschmidt
  2016-09-28  1:40               ` David Gibson
  0 siblings, 2 replies; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-27  5:54 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 09/27/2016 04:35 AM, David Gibson wrote:
> On Mon, Sep 26, 2016 at 06:11:36PM +0200, Cédric Le Goater wrote:
>> On 09/23/2016 04:46 AM, David Gibson wrote:
>>> On Thu, Sep 22, 2016 at 10:25:59AM +0200, Cédric Le Goater wrote:
>>>>>> @@ -493,6 +525,8 @@ 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_addr = pnv_chip_xscom_addr_p9;
>>>>>> +    k->xscom_pcba = pnv_chip_xscom_pcba_p9;
>>>>>
>>>>> So if you do as BenH (and I) suggested and have the "scom address
>>>>> space" actually be addressed by (pcba << 3), I think you can probably
>>>>> avoid these.  
>>>>
>>>> I will look at that option again. 
>>>>
>>>> I was trying to untangle a few things at the same time. I have better
>>>> view of the problem to solve now. The bus is gone, that's was one 
>>>> thing. How we map these xscom regions is the next. 
>>>>
>>>> Ben suggested to add some P7/P8 mangling before the dispatch in 
>>>> the &address_space_xscom. This should make things cleaner. I had 
>>>> not thought of doing that and this is why I introduced these helpers :
>>>>
>>>> +uint32_t pnv_xscom_pcba(PnvXScomInterface *dev, uint64_t addr)
>>>> +uint64_t pnv_xscom_addr(PnvXScomInterface *dev, uint32_t pcba)
>>>>
>>>> which I don't really like ...
>>>>
>>>> but we must make sure that we can do the mapping of the xscom 
>>>> subregions in the &address_space_xscom using (pcba << 3)
>>>>
>>>>
>>>>> Instead you can handle it in the chip or ADU realize function by either:
>>>>>
>>>>>     P8: * map one big subregion for the ADU into &address_space_memory
>>>>>         * have the handler for that subregion do the address mangling,
>>>>>           then redispatch into the xscom address space
>>>>>
>>>>>     P9: * Map the appropriate chunk of the xscom address space
>>>>>           directly into address_space_memory
>>>>
>>>> Yes that was my feeling for a better solution but Ben chimed in with the 
>>>> HMER topic. I need to look at that.
>>>
>>> Right.  Doesn't change the basic concept though - it just means you
>>> need (slightly different) redispatchers for both P8 and P9.
>>
>> In fact they are the same, you only need an "addr to pcba" handler at the
>> chip class level : 
> 
> Ok.  I'd been thinking of using different dispatchers as an
> alternative to using the chip class translator hook, 

ah. yes, why not. We could have per-chip dispatchers but they 
would have a lot in common. However, I think we can get rid of 
the xscom_pcba' handlers, they should not be needed any where 
else than in the XSCOM dispatchers. 

> but I guess if you have the decoding of those "core" registers 
> here as well, then that doesn't make so much sense.

yes and there is also the handling of the XSCOM failures.

I can add some prologue handler to cover those "core" registers
but adding a MemoryRegion, ops, init and mapping would be a lot 
of churn just to return 0.

Thanks,

C. 


>> static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)
>> {
>> 	PnvChip *chip = opaque;
>> 	uint32_t pcba = PNV_CHIP_GET_CLASS(chip)->xscom_pcba(addr);
>> 	uint64_t val = 0;
>> 	MemTxResult result;
>>
>> 	...
>>
>>         val = address_space_ldq(&chip->xscom_as, pcba << 3,
>>                                 MEMTXATTRS_UNSPECIFIED, &result);
>>         if (result != MEMTX_OK) {
>>
>>   
>>
>> And so, the result is pretty clean. I killed the proxy object and merged 
>> the regions in the chip but I have kept the pnv_xscom.c file because the 
>> code related to xscom is rather large : ~250 lines. 
> 
> Sure, makes sense.
> 
>> The objects declaring a xscom region need to do some register shifting but 
>> this is usual in mmio regions.
>>
>> You will see in v4.
> 
> Ok.
> 
>>>>>> +static bool xscom_dispatch_read(PnvXScom *xscom, hwaddr addr, uint64_t *val)
>>>>>> +{
>>>>>> +    uint32_t success;
>>>>>> +    uint8_t data[8];
>>>>>> +
>>>>>> +    success = !address_space_rw(&xscom->xscom_as, addr, MEMTXATTRS_UNSPECIFIED,
>>>>>> +                                data, 8, false);
>>>>>> +    *val = (((uint64_t) data[0]) << 56 |
>>>>>> +            ((uint64_t) data[1]) << 48 |
>>>>>> +            ((uint64_t) data[2]) << 40 |
>>>>>> +            ((uint64_t) data[3]) << 32 |
>>>>>> +            ((uint64_t) data[4]) << 24 |
>>>>>> +            ((uint64_t) data[5]) << 16 |
>>>>>> +            ((uint64_t) data[6]) << 8  |
>>>>>> +            ((uint64_t) data[7]));
>>>>>
>>>>> AFAICT this is basically assuming data is always encoded BE.  With the
>>>>> right choice of endian flags on the individual SCOM device
>>>>> registrations with the scom address space, I think you should be able
>>>>> to avoid this mangling.
>>>>
>>>> yes. I should but curiously I had to do this, and this works the same on
>>>> an intel host or a ppc64 host.
>>>
>>> Hmm.. I suspect what you actually need is NATIVE_ENDIAN on the
>>> individual SCOM devices, with BIG_ENDIAN on the redispatcher region.
>>
>> we should be using address_space_ldq and address_space_stq.
> 
> Ok.
> 
>>>>>> +
>>>>>> +    success = !address_space_rw(&xscom->xscom_as, addr, MEMTXATTRS_UNSPECIFIED,
>>>>>> +                           data, 8, true);
>>>>>> +    return success;
>>>>>> +}
>>>>>> +
>>>>>> +static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)
>>>>>> +{
>>>>>> +    PnvXScom *s = opaque;
>>>>>> +    uint32_t pcba = s->chip_class->xscom_pcba(addr);
>>>>>> +    uint64_t val = 0;
>>>>>> +
>>>>>> +    /* Handle some SCOMs here before dispatch */
>>>>>> +    switch (pcba) {
>>>>>> +    case 0xf000f:
>>>>>> +        val = s->chip_class->chip_cfam_id;
>>>>>> +        break;
>>>>>> +    case 0x1010c00:     /* PIBAM FIR */
>>>>>> +    case 0x1010c03:     /* PIBAM FIR MASK */
>>>>>> +    case 0x2020007:     /* ADU stuff */
>>>>>> +    case 0x2020009:     /* ADU stuff */
>>>>>> +    case 0x202000f:     /* ADU stuff */
>>>>>> +        val = 0;
>>>>>> +        break;
>>>>>> +    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 */
>>>>>> +        val = 0;
>>>>>> +        break;
>>>>>
>>>>> It'd be theoretically nicer to actually register regions for these
>>>>> special case addresses, but handling it here is a reasonable hack to
>>>>> get things working quickly for the time being.
>>>>
>>>> I will make a default region on the whole xscomm address space to catch 
>>>> these.
>>>
>>> Ok.
>>
>> Well, it does not bring much and we loose the ability to catch errors. 
>> I will leave it that way.
>>
>> Thanks,
>>
>> C. 
>>
> 

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

* Re: [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure
  2016-09-27  5:54             ` Cédric Le Goater
@ 2016-09-27  6:10               ` Benjamin Herrenschmidt
  2016-09-27  7:16                 ` Cédric Le Goater
  2016-09-28  1:40               ` David Gibson
  1 sibling, 1 reply; 53+ messages in thread
From: Benjamin Herrenschmidt @ 2016-09-27  6:10 UTC (permalink / raw)
  To: Cédric Le Goater, David Gibson; +Cc: qemu-ppc, qemu-devel

On Tue, 2016-09-27 at 07:54 +0200, Cédric Le Goater wrote:
> 
> > but I guess if you have the decoding of those "core" registers 
> > here as well, then that doesn't make so much sense.

Those core registers may well change with P9, we havne't looked closely
yet...

> yes and there is also the handling of the XSCOM failures.
> 
> I can add some prologue handler to cover those "core" registers
> but adding a MemoryRegion, ops, init and mapping would be a lot 
> of churn just to return 0.
> 
> Thanks,

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

* Re: [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure
  2016-09-27  6:10               ` Benjamin Herrenschmidt
@ 2016-09-27  7:16                 ` Cédric Le Goater
  0 siblings, 0 replies; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-27  7:16 UTC (permalink / raw)
  To: Benjamin Herrenschmidt, David Gibson; +Cc: qemu-ppc, qemu-devel

On 09/27/2016 08:10 AM, Benjamin Herrenschmidt wrote:
> On Tue, 2016-09-27 at 07:54 +0200, Cédric Le Goater wrote:
>>
>>> but I guess if you have the decoding of those "core" registers 
>>> here as well, then that doesn't make so much sense.
> 
> Those core registers may well change with P9, we havne't looked closely
> yet...

Neither have I ... qemu/pnv reaches the kernel but this is really a 
"Frankenstein P9". The interrupt controller is missing. 

C. 

>> yes and there is also the handling of the XSCOM failures.
>>
>> I can add some prologue handler to cover those "core" registers
>> but adding a MemoryRegion, ops, init and mapping would be a lot 
>> of churn just to return 0.
>>
>> Thanks,

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

* Re: [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure
  2016-09-21  6:08   ` David Gibson
  2016-09-22  8:25     ` Cédric Le Goater
@ 2016-09-27  9:10     ` Cédric Le Goater
  2016-09-27  9:30       ` Cédric Le Goater
  2016-09-27 10:17       ` Benjamin Herrenschmidt
  1 sibling, 2 replies; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-27  9:10 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

>> +#include <libfdt.h>
>> +
>> +static void xscom_complete(uint64_t hmer_bits)
>> +{
>> +    CPUState *cs = current_cpu;
> 
> Hmm.. is current_cpu a safe thing to use in the case of KVM or MTTCG?

yes, as we are running under cpu_exec when doing this call.

>> +    PowerPCCPU *cpu = POWERPC_CPU(cs);
>> +    CPUPPCState *env = &cpu->env;
>> +
>> +    cpu_synchronize_state(cs);
>> +    env->spr[SPR_HMER] |= hmer_bits;
>> +
>> +    /* XXX Need a CPU helper to set HMER, also handle gneeration
>> +     * of HMIs
>> +     */

Ben, 

The CPU helper would be to replicate the value of the SPR_HMER in all
the threads of the core I guess ? 

Thanks,

C.

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

* Re: [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure
  2016-09-27  9:10     ` Cédric Le Goater
@ 2016-09-27  9:30       ` Cédric Le Goater
  2016-09-27 10:18         ` Benjamin Herrenschmidt
  2016-09-27 10:17       ` Benjamin Herrenschmidt
  1 sibling, 1 reply; 53+ messages in thread
From: Cédric Le Goater @ 2016-09-27  9:30 UTC (permalink / raw)
  To: David Gibson; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

On 09/27/2016 11:10 AM, Cédric Le Goater wrote:
>>> +#include <libfdt.h>
>>> +
>>> +static void xscom_complete(uint64_t hmer_bits)
>>> +{
>>> +    CPUState *cs = current_cpu;
>>
>> Hmm.. is current_cpu a safe thing to use in the case of KVM or MTTCG?
> 
> yes, as we are running under cpu_exec when doing this call.

well, this is not true under the monitor. 

So we will have to come up with something to handle xscom read/writes 
from the monitor. Could we use first_cpu in that case ? 

Thanks,

C. 

>>> +    PowerPCCPU *cpu = POWERPC_CPU(cs);
>>> +    CPUPPCState *env = &cpu->env;
>>> +
>>> +    cpu_synchronize_state(cs);
>>> +    env->spr[SPR_HMER] |= hmer_bits;
>>> +
>>> +    /* XXX Need a CPU helper to set HMER, also handle gneeration
>>> +     * of HMIs
>>> +     */
> 
> Ben, 
> 
> The CPU helper would be to replicate the value of the SPR_HMER in all
> the threads of the core I guess ? 
> 
> Thanks,
> 
> C.
> 

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

* Re: [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure
  2016-09-27  9:10     ` Cédric Le Goater
  2016-09-27  9:30       ` Cédric Le Goater
@ 2016-09-27 10:17       ` Benjamin Herrenschmidt
  1 sibling, 0 replies; 53+ messages in thread
From: Benjamin Herrenschmidt @ 2016-09-27 10:17 UTC (permalink / raw)
  To: Cédric Le Goater, David Gibson; +Cc: qemu-ppc, qemu-devel

On Tue, 2016-09-27 at 11:10 +0200, Cédric Le Goater wrote:
> > 
> > > 
> > > +    PowerPCCPU *cpu = POWERPC_CPU(cs);
> > > +    CPUPPCState *env = &cpu->env;
> > > +
> > > +    cpu_synchronize_state(cs);
> > > +    env->spr[SPR_HMER] |= hmer_bits;
> > > +
> > > +    /* XXX Need a CPU helper to set HMER, also handle gneeration
> > > +     * of HMIs
> > > +     */
> 
> Ben, 
> 
> The CPU helper would be to replicate the value of the SPR_HMER in all
> the threads of the core I guess ? 

Nope, those HMER bits are per-thread afaik, but I dislike having a
device whack at CPU state...

Also when we do PR KVM of powernv, we'll want to sync the SPR with KVM
I suppose.

Cheers,
Ben.
 

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

* Re: [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure
  2016-09-27  9:30       ` Cédric Le Goater
@ 2016-09-27 10:18         ` Benjamin Herrenschmidt
  0 siblings, 0 replies; 53+ messages in thread
From: Benjamin Herrenschmidt @ 2016-09-27 10:18 UTC (permalink / raw)
  To: Cédric Le Goater, David Gibson; +Cc: qemu-ppc, qemu-devel

On Tue, 2016-09-27 at 11:30 +0200, Cédric Le Goater wrote:
> On 09/27/2016 11:10 AM, Cédric Le Goater wrote:
> > 
> > > 
> > > > 
> > > > +#include <libfdt.h>
> > > > +
> > > > +static void xscom_complete(uint64_t hmer_bits)
> > > > +{
> > > > +    CPUState *cs = current_cpu;
> > > 
> > > Hmm.. is current_cpu a safe thing to use in the case of KVM or MTTCG?
> > 
> > yes, as we are running under cpu_exec when doing this call.
> 
> well, this is not true under the monitor. 
> 
> So we will have to come up with something to handle xscom read/writes 
> from the monitor. Could we use first_cpu in that case ? 

Well, we'll need to find the chip etc... I think the XSCOM bridge (or bus)
should have special methods for the monitor that don't update HMER but
print out the status instead.

Cheers,
Ben.

> Thanks,
> 
> C. 
> 
> > 
> > > 
> > > > 
> > > > +    PowerPCCPU *cpu = POWERPC_CPU(cs);
> > > > +    CPUPPCState *env = &cpu->env;
> > > > +
> > > > +    cpu_synchronize_state(cs);
> > > > +    env->spr[SPR_HMER] |= hmer_bits;
> > > > +
> > > > +    /* XXX Need a CPU helper to set HMER, also handle gneeration
> > > > +     * of HMIs
> > > > +     */
> > 
> > Ben, 
> > 
> > The CPU helper would be to replicate the value of the SPR_HMER in all
> > the threads of the core I guess ? 
> > 
> > Thanks,
> > 
> > C.
> > 

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

* Re: [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure
  2016-09-27  5:54             ` Cédric Le Goater
  2016-09-27  6:10               ` Benjamin Herrenschmidt
@ 2016-09-28  1:40               ` David Gibson
  1 sibling, 0 replies; 53+ messages in thread
From: David Gibson @ 2016-09-28  1:40 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: qemu-ppc, Benjamin Herrenschmidt, qemu-devel

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

On Tue, Sep 27, 2016 at 07:54:37AM +0200, Cédric Le Goater wrote:
> On 09/27/2016 04:35 AM, David Gibson wrote:
> > On Mon, Sep 26, 2016 at 06:11:36PM +0200, Cédric Le Goater wrote:
> >> On 09/23/2016 04:46 AM, David Gibson wrote:
> >>> On Thu, Sep 22, 2016 at 10:25:59AM +0200, Cédric Le Goater wrote:
> >>>>>> @@ -493,6 +525,8 @@ 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_addr = pnv_chip_xscom_addr_p9;
> >>>>>> +    k->xscom_pcba = pnv_chip_xscom_pcba_p9;
> >>>>>
> >>>>> So if you do as BenH (and I) suggested and have the "scom address
> >>>>> space" actually be addressed by (pcba << 3), I think you can probably
> >>>>> avoid these.  
> >>>>
> >>>> I will look at that option again. 
> >>>>
> >>>> I was trying to untangle a few things at the same time. I have better
> >>>> view of the problem to solve now. The bus is gone, that's was one 
> >>>> thing. How we map these xscom regions is the next. 
> >>>>
> >>>> Ben suggested to add some P7/P8 mangling before the dispatch in 
> >>>> the &address_space_xscom. This should make things cleaner. I had 
> >>>> not thought of doing that and this is why I introduced these helpers :
> >>>>
> >>>> +uint32_t pnv_xscom_pcba(PnvXScomInterface *dev, uint64_t addr)
> >>>> +uint64_t pnv_xscom_addr(PnvXScomInterface *dev, uint32_t pcba)
> >>>>
> >>>> which I don't really like ...
> >>>>
> >>>> but we must make sure that we can do the mapping of the xscom 
> >>>> subregions in the &address_space_xscom using (pcba << 3)
> >>>>
> >>>>
> >>>>> Instead you can handle it in the chip or ADU realize function by either:
> >>>>>
> >>>>>     P8: * map one big subregion for the ADU into &address_space_memory
> >>>>>         * have the handler for that subregion do the address mangling,
> >>>>>           then redispatch into the xscom address space
> >>>>>
> >>>>>     P9: * Map the appropriate chunk of the xscom address space
> >>>>>           directly into address_space_memory
> >>>>
> >>>> Yes that was my feeling for a better solution but Ben chimed in with the 
> >>>> HMER topic. I need to look at that.
> >>>
> >>> Right.  Doesn't change the basic concept though - it just means you
> >>> need (slightly different) redispatchers for both P8 and P9.
> >>
> >> In fact they are the same, you only need an "addr to pcba" handler at the
> >> chip class level : 
> > 
> > Ok.  I'd been thinking of using different dispatchers as an
> > alternative to using the chip class translator hook, 
> 
> ah. yes, why not. We could have per-chip dispatchers but they 
> would have a lot in common.

Would they?  Unless you're counting the core register dispatch - and
it sounds like splitting that for P8 vs. P9 would be a good idea
anyway - I don't see that there's much in common besides the address
translation.

Note of course, that you can add a helper function that both
dispatchers can use if it's useful.

> However, I think we can get rid of 
> the xscom_pcba' handlers, they should not be needed any where 
> else than in the XSCOM dispatchers. 
> 
> > but I guess if you have the decoding of those "core" registers 
> > here as well, then that doesn't make so much sense.
> 
> yes and there is also the handling of the XSCOM failures.

Hm, ok.

> I can add some prologue handler to cover those "core" registers
> but adding a MemoryRegion, ops, init and mapping would be a lot 
> of churn just to return 0.
> 
> Thanks,
> 
> C. 
> 
> 
> >> static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)
> >> {
> >> 	PnvChip *chip = opaque;
> >> 	uint32_t pcba = PNV_CHIP_GET_CLASS(chip)->xscom_pcba(addr);
> >> 	uint64_t val = 0;
> >> 	MemTxResult result;
> >>
> >> 	...
> >>
> >>         val = address_space_ldq(&chip->xscom_as, pcba << 3,
> >>                                 MEMTXATTRS_UNSPECIFIED, &result);
> >>         if (result != MEMTX_OK) {
> >>
> >>   
> >>
> >> And so, the result is pretty clean. I killed the proxy object and merged 
> >> the regions in the chip but I have kept the pnv_xscom.c file because the 
> >> code related to xscom is rather large : ~250 lines. 
> > 
> > Sure, makes sense.
> > 
> >> The objects declaring a xscom region need to do some register shifting but 
> >> this is usual in mmio regions.
> >>
> >> You will see in v4.
> > 
> > Ok.
> > 
> >>>>>> +static bool xscom_dispatch_read(PnvXScom *xscom, hwaddr addr, uint64_t *val)
> >>>>>> +{
> >>>>>> +    uint32_t success;
> >>>>>> +    uint8_t data[8];
> >>>>>> +
> >>>>>> +    success = !address_space_rw(&xscom->xscom_as, addr, MEMTXATTRS_UNSPECIFIED,
> >>>>>> +                                data, 8, false);
> >>>>>> +    *val = (((uint64_t) data[0]) << 56 |
> >>>>>> +            ((uint64_t) data[1]) << 48 |
> >>>>>> +            ((uint64_t) data[2]) << 40 |
> >>>>>> +            ((uint64_t) data[3]) << 32 |
> >>>>>> +            ((uint64_t) data[4]) << 24 |
> >>>>>> +            ((uint64_t) data[5]) << 16 |
> >>>>>> +            ((uint64_t) data[6]) << 8  |
> >>>>>> +            ((uint64_t) data[7]));
> >>>>>
> >>>>> AFAICT this is basically assuming data is always encoded BE.  With the
> >>>>> right choice of endian flags on the individual SCOM device
> >>>>> registrations with the scom address space, I think you should be able
> >>>>> to avoid this mangling.
> >>>>
> >>>> yes. I should but curiously I had to do this, and this works the same on
> >>>> an intel host or a ppc64 host.
> >>>
> >>> Hmm.. I suspect what you actually need is NATIVE_ENDIAN on the
> >>> individual SCOM devices, with BIG_ENDIAN on the redispatcher region.
> >>
> >> we should be using address_space_ldq and address_space_stq.
> > 
> > Ok.
> > 
> >>>>>> +
> >>>>>> +    success = !address_space_rw(&xscom->xscom_as, addr, MEMTXATTRS_UNSPECIFIED,
> >>>>>> +                           data, 8, true);
> >>>>>> +    return success;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)
> >>>>>> +{
> >>>>>> +    PnvXScom *s = opaque;
> >>>>>> +    uint32_t pcba = s->chip_class->xscom_pcba(addr);
> >>>>>> +    uint64_t val = 0;
> >>>>>> +
> >>>>>> +    /* Handle some SCOMs here before dispatch */
> >>>>>> +    switch (pcba) {
> >>>>>> +    case 0xf000f:
> >>>>>> +        val = s->chip_class->chip_cfam_id;
> >>>>>> +        break;
> >>>>>> +    case 0x1010c00:     /* PIBAM FIR */
> >>>>>> +    case 0x1010c03:     /* PIBAM FIR MASK */
> >>>>>> +    case 0x2020007:     /* ADU stuff */
> >>>>>> +    case 0x2020009:     /* ADU stuff */
> >>>>>> +    case 0x202000f:     /* ADU stuff */
> >>>>>> +        val = 0;
> >>>>>> +        break;
> >>>>>> +    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 */
> >>>>>> +        val = 0;
> >>>>>> +        break;
> >>>>>
> >>>>> It'd be theoretically nicer to actually register regions for these
> >>>>> special case addresses, but handling it here is a reasonable hack to
> >>>>> get things working quickly for the time being.
> >>>>
> >>>> I will make a default region on the whole xscomm address space to catch 
> >>>> these.
> >>>
> >>> Ok.
> >>
> >> Well, it does not bring much and we loose the ability to catch errors. 
> >> I will leave it that way.
> >>
> >> Thanks,
> >>
> >> C. 
> >>
> > 
> 

-- 
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] 53+ messages in thread

end of thread, other threads:[~2016-09-28  2:32 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-15 12:45 [Qemu-devel] [PATCH v3 00/10] ppc/pnv: loading skiboot and booting the kernel Cédric Le Goater
2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 01/10] ppc/pnv: add skeleton PowerNV platform Cédric Le Goater
2016-09-20  7:53   ` David Gibson
2016-09-21  7:32     ` Cédric Le Goater
2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 02/10] ppc/pnv: add a PnvChip object Cédric Le Goater
2016-09-20 13:50   ` David Gibson
2016-09-21  7:44     ` Cédric Le Goater
2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 03/10] ppc/pnv: add a core mask to PnvChip Cédric Le Goater
2016-09-20 13:57   ` David Gibson
2016-09-21  7:57     ` Cédric Le Goater
2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 04/10] ppc/pnv: add a PIR handler " Cédric Le Goater
2016-09-21  1:29   ` David Gibson
2016-09-21  1:52     ` Benjamin Herrenschmidt
2016-09-21  7:05     ` Cédric Le Goater
2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 05/10] ppc/pnv: add a PnvCore object Cédric Le Goater
2016-09-21  1:51   ` David Gibson
2016-09-21  2:05     ` Benjamin Herrenschmidt
2016-09-21  2:15       ` David Gibson
2016-09-21  7:15       ` Cédric Le Goater
2016-09-21  7:09     ` Cédric Le Goater
2016-09-21 14:24     ` Cédric Le Goater
2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 06/10] monitor: fix crash for platforms without a CPU 0 Cédric Le Goater
2016-09-21  5:30   ` David Gibson
2016-09-21  8:06     ` Cédric Le Goater
2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 07/10] ppc/pnv: add XSCOM infrastructure Cédric Le Goater
2016-09-15 22:11   ` Benjamin Herrenschmidt
2016-09-21  5:56     ` David Gibson
2016-09-21  7:44       ` Benjamin Herrenschmidt
2016-09-21  6:08   ` David Gibson
2016-09-22  8:25     ` Cédric Le Goater
2016-09-23  2:46       ` David Gibson
2016-09-26 16:11         ` Cédric Le Goater
2016-09-27  2:35           ` David Gibson
2016-09-27  5:54             ` Cédric Le Goater
2016-09-27  6:10               ` Benjamin Herrenschmidt
2016-09-27  7:16                 ` Cédric Le Goater
2016-09-28  1:40               ` David Gibson
2016-09-27  9:10     ` Cédric Le Goater
2016-09-27  9:30       ` Cédric Le Goater
2016-09-27 10:18         ` Benjamin Herrenschmidt
2016-09-27 10:17       ` Benjamin Herrenschmidt
2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 08/10] ppc/pnv: add a XScomDevice to PnvCore Cédric Le Goater
2016-09-21  6:12   ` David Gibson
2016-09-22  8:33     ` Cédric Le Goater
2016-09-23  2:50       ` David Gibson
2016-09-15 12:45 ` [Qemu-devel] [PATCH v3 09/10] ppc/pnv: add a LPC controller Cédric Le Goater
2016-09-15 22:13   ` Benjamin Herrenschmidt
2016-09-16 17:35     ` Cédric Le Goater
2016-09-21  6:23   ` David Gibson
2016-09-15 12:46 ` [Qemu-devel] [PATCH v3 10/10] ppc/pnv: add a ISA bus Cédric Le Goater
2016-09-21  6:30   ` David Gibson
2016-09-22  8:44     ` Cédric Le Goater
2016-09-23  2:54       ` David Gibson

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.