On Tue, 2016-08-16 at 12:39 +1000, David Gibson wrote: > On Fri, Aug 05, 2016 at 11:15:37AM +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 PowerNVCPUCore objects is added to the PnvChip and the device > > tree is populated looping on these cores. Core ids in the device tree > > are still a little fuzy. To be checked.
What about P9 where cores are in pairs to form EX and in pairs of EX (ie, quads) to form EPs ? > > So, it's not immediately obvious to me if you want an actual core > object. You could potentially create the actual vcpu objects directly > from the chip object. That assumes that any hotplug will only be at > chip granularity, not core granularity, but I'm guessing that's the > case anyway. Well we want to add some of the core XSCOMs so it makes sense to have a core object that is a XSCOM device > > That said, if having the intermediate core object is helpful, you're > certainly free to have it. > > > > > > > Signed-off-by: Cédric Le Goater <c...@kaod.org> > > --- > > hw/ppc/Makefile.objs | 2 +- > > hw/ppc/pnv.c | 160 ++++++++++++++++++++++++++++++++++++++++++- > > hw/ppc/pnv_core.c | 171 > > ++++++++++++++++++++++++++++++++++++++++++++++ > > include/hw/ppc/pnv.h | 7 ++ > > include/hw/ppc/pnv_core.h | 47 +++++++++++++ > > 5 files changed, 383 insertions(+), 4 deletions(-) > > 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 a680780e9dea..1219493c7218 100644 > > --- a/hw/ppc/pnv.c > > +++ b/hw/ppc/pnv.c > > @@ -35,6 +35,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" > > @@ -112,6 +113,114 @@ static int powernv_populate_memory(void *fdt) > > return 0; > > } > > > > +static void powernv_create_core_node(void *fdt, CPUState *cs, uint32_t > > chip_id) > > +{ > > + PowerPCCPU *cpu = POWERPC_CPU(cs); > > + int smt_threads = ppc_get_compat_smt_threads(cpu); > > + CPUPPCState *env = &cpu->env; > > + DeviceClass *dc = DEVICE_GET_CLASS(cs); > > + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); > > + uint32_t servers_prop[smt_threads]; > > + uint32_t gservers_prop[smt_threads * 2]; > > + int i, index = ppc_get_vcpu_dt_id(cpu); > > + 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; > > + char *nodename; > > + > > + nodename = g_strdup_printf("%s@%x", dc->fw_name, index); > > + > > + _FDT((fdt_begin_node(fdt, nodename))); > > + > > + g_free(nodename); > > + > > + _FDT((fdt_property_cell(fdt, "reg", index))); > > + _FDT((fdt_property_string(fdt, "device_type", "cpu"))); > > The handling of dt_id is going to collide with cleanups I want to do > in this area (for spapr and the ppc cpu core). Not sure there's a lot > you can do to avoid that at this stage. > > > > > + _FDT((fdt_property_cell(fdt, "cpu-version", env->spr[SPR_PVR]))); > > + _FDT((fdt_property_cell(fdt, "d-cache-block-size", > > + env->dcache_line_size))); > > + _FDT((fdt_property_cell(fdt, "d-cache-line-size", > > + env->dcache_line_size))); > > + _FDT((fdt_property_cell(fdt, "i-cache-block-size", > > + env->icache_line_size))); > > + _FDT((fdt_property_cell(fdt, "i-cache-line-size", > > + env->icache_line_size))); > > + > > + if (pcc->l1_dcache_size) { > > + _FDT((fdt_property_cell(fdt, "d-cache-size", > > pcc->l1_dcache_size))); > > + } else { > > + error_report("Warning: Unknown L1 dcache size for cpu"); > > + } > > + if (pcc->l1_icache_size) { > > + _FDT((fdt_property_cell(fdt, "i-cache-size", > > pcc->l1_icache_size))); > > + } else { > > + error_report("Warning: Unknown L1 icache size for cpu"); > > + } > > + > > + _FDT((fdt_property_cell(fdt, "timebase-frequency", tbfreq))); > > + _FDT((fdt_property_cell(fdt, "clock-frequency", cpufreq))); > > + _FDT((fdt_property_cell(fdt, "ibm,slb-size", env->slb_nr))); > > + _FDT((fdt_property_string(fdt, "status", "okay"))); > > + _FDT((fdt_property(fdt, "64-bit", NULL, 0))); > > + > > + if (env->spr_cb[SPR_PURR].oea_read) { > > + _FDT((fdt_property(fdt, "ibm,purr", NULL, 0))); > > + } > > + > > + if (env->mmu_model & POWERPC_MMU_1TSEG) { > > + _FDT((fdt_property(fdt, "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_property_cell(fdt, "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_property_cell(fdt, "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_property(fdt, "ibm,segment-page-sizes", > > + page_sizes_prop, page_sizes_prop_size))); > > + } > > + > > + _FDT((fdt_property_cell(fdt, "ibm,chip-id", chip_id))); > > + > > + if (cpu->cpu_version) { > > + _FDT((fdt_property_cell(fdt, "cpu-version", cpu->cpu_version))); > > + } > > + > > + /* Build interrupt servers and gservers properties */ > > + for (i = 0; i < smt_threads; i++) { > > + servers_prop[i] = cpu_to_be32(index + i); > > + /* Hack, direct the group queues back to cpu 0 */ > > + gservers_prop[i * 2] = cpu_to_be32(index + i); > > + gservers_prop[i * 2 + 1] = 0; > > + } > > + _FDT((fdt_property(fdt, "ibm,ppc-interrupt-server#s", > > + servers_prop, sizeof(servers_prop)))); > > + _FDT((fdt_property(fdt, "ibm,ppc-interrupt-gserver#s", > > + gservers_prop, sizeof(gservers_prop)))); > > + > > + _FDT((fdt_end_node(fdt))); > > +} > > + > > static void *powernv_create_fdt(sPowerNVMachineState *pnv, > > const char *kernel_cmdline) > > { > > @@ -120,6 +229,7 @@ static void *powernv_create_fdt(sPowerNVMachineState > > *pnv, > > uint32_t end_prop = cpu_to_be32(pnv->initrd_base + pnv->initrd_size); > > char *buf; > > const char plat_compat[] = "qemu,powernv\0ibm,powernv"; > > + int i; > > > > fdt = g_malloc0(FDT_MAX_SIZE); > > _FDT((fdt_create(fdt, FDT_MAX_SIZE))); > > @@ -156,6 +266,23 @@ static void *powernv_create_fdt(sPowerNVMachineState > > *pnv, > > /* Memory */ > > _FDT((powernv_populate_memory(fdt))); > > > > + /* cpus */ > > + _FDT((fdt_begin_node(fdt, "cpus"))); > > + _FDT((fdt_property_cell(fdt, "#address-cells", 0x1))); > > + _FDT((fdt_property_cell(fdt, "#size-cells", 0x0))); > > + > > + for (i = 0; i < pnv->num_chips; i++) { > > + PnvChip *chip = &pnv->chips[i]; > > + int j; > > + > > + for (j = 0; j < chip->num_cores; j++) { > > + PowerNVCPUCore *pc = POWERNV_CPU_CORE(chip->cores[j]); > > + CPUState *cs = CPU(DEVICE(pc->threads)); > > + powernv_create_core_node(fdt, cs, chip->chip_id); > > I think it would be nicer to define the fdt creation function in terms > of the core object, and have it retrieve the representative thread itself. > > > > > + } > > + } > > + _FDT((fdt_end_node(fdt))); > > + > > _FDT((fdt_end_node(fdt))); /* close root node */ > > _FDT((fdt_finish(fdt))); > > > > @@ -168,11 +295,13 @@ static void ppc_powernv_reset(void) > > sPowerNVMachineState *pnv = POWERNV_MACHINE(machine); > > void *fdt; > > > > + pnv->fdt_addr = FDT_ADDR; > > + > > qemu_devices_reset(); > > > > fdt = powernv_create_fdt(pnv, machine->kernel_cmdline); > > > > - cpu_physical_memory_write(FDT_ADDR, fdt, fdt_totalsize(fdt)); > > + cpu_physical_memory_write(pnv->fdt_addr, fdt, fdt_totalsize(fdt)); > > These look like not really related changes, that should be maybe > folded into 1/3. > > > > > } > > > > static void ppc_powernv_init(MachineState *machine) > > @@ -252,6 +381,10 @@ static void ppc_powernv_init(MachineState *machine) > > > > object_initialize(chip, sizeof(*chip), TYPE_PNV_CHIP); > > object_property_set_int(OBJECT(chip), i, "chip-id", &error_abort); > > + object_property_set_int(OBJECT(chip), smp_cpus, "num-cores", > > + &error_abort); > > + object_property_set_str(OBJECT(chip), machine->cpu_model, > > "cpu-model", > > + &error_abort); > > So various drafts of the spapr cores had a cpu-model parameter, but we > eventually rejected it in favour of having different core types > corresponding to the different, well, core types. Unless there's a > compelling reason otherwise, I think it would be nicer to do the same > for the pnvchip objects - a p8pnvchip object will always create p8 > cores and so forth. > > > > > object_property_set_bool(OBJECT(chip), true, "realized", > > &error_abort); > > } > > } > > @@ -292,14 +425,35 @@ static const TypeInfo powernv_machine_2_8_info = { > > .class_init = powernv_machine_2_8_class_init, > > }; > > > > - > > static void pnv_chip_realize(DeviceState *dev, Error **errp) > > { > > - ; > > + int i; > > + PnvChip *chip = PNV_CHIP(dev); > > + char *typename = powernv_cpu_core_typename(chip->cpu_model); > > + > > + if (!object_class_by_name(typename)) { > > + error_setg(errp, "Unable to find PowerNV CPU Core definition"); > > + return; > > + } > > + > > + chip->cores = g_new0(Object *, chip->num_cores); > > + for (i = 0; i < chip->num_cores; i++) { > > + int core_id = i * smp_threads; > > + chip->cores[i] = object_new(typename); > > + object_property_set_int(chip->cores[i], smp_threads, "nr-threads", > > + &error_fatal); > > + object_property_set_int(chip->cores[i], core_id, > > CPU_CORE_PROP_CORE_ID, > > + &error_fatal); > > + object_property_set_bool(chip->cores[i], true, "realized", > > + &error_fatal); > > + } > > + g_free(typename); > > } > > > > static Property pnv_chip_properties[] = { > > DEFINE_PROP_UINT32("chip-id", PnvChip, chip_id, 0), > > + DEFINE_PROP_STRING("cpu-model", PnvChip, cpu_model), > > + DEFINE_PROP_UINT32("num-cores", PnvChip, num_cores, 1), > > DEFINE_PROP_END_OF_LIST(), > > }; > > > > diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c > > new file mode 100644 > > index 000000000000..1e36709db993 > > --- /dev/null > > +++ b/hw/ppc/pnv_core.c > > @@ -0,0 +1,171 @@ > > +/* > > + * QEMU PowerPC PowerNV CPU model > > + * > > + * Copyright (c) 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; > > + MachineState *machine = MACHINE(qdev_get_machine()); > > + sPowerNVMachineState *pnv = POWERNV_MACHINE(machine); > > + > > + cpu_reset(cs); > > + > > + env->spr[SPR_PIR] = ppc_get_vcpu_dt_id(cpu); > > Is the PIR writable? If not it might be better to move this to init > rather than reset time. > > > > > + env->spr[SPR_HIOR] = 0; > > + env->gpr[3] = pnv->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 powernv_cpu_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 powernv_cpu_core_realize(DeviceState *dev, Error **errp) > > +{ > > + PowerNVCPUCore *pc = POWERNV_CPU_CORE(OBJECT(dev)); > > + CPUCore *cc = CPU_CORE(OBJECT(dev)); > > + PowerNVCPUClass *pcc = POWERNV_CPU_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; > > + > > + > > + pc->threads = g_malloc0(size * cc->nr_threads); > > + for (i = 0; i < cc->nr_threads; i++) { > > + char id[32]; > > + CPUState *cs; > > + > > + obj = pc->threads + i * size; > > + > > + object_initialize(obj, size, typename); > > + cs = CPU(obj); > > + cs->cpu_index = cc->core_id + i; > > + snprintf(id, sizeof(id), "thread[%d]", i); > > + object_property_add_child(OBJECT(pc), id, obj, &local_err); > > + if (local_err) { > > + goto err; > > + } > > + object_unref(obj); > > + } > > + > > + for (j = 0; j < cc->nr_threads; j++) { > > + obj = pc->threads + j * size; > > + > > + powernv_cpu_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); > > +} > > + > > +/* > > + * Grow this list or merge with SPAPRCoreInfo which is very similar > > + */ > > +static const char *powernv_core_models[] = { "POWER8" }; > > + > > +static void powernv_cpu_core_class_init(ObjectClass *oc, void *data) > > +{ > > + DeviceClass *dc = DEVICE_CLASS(oc); > > + PowerNVCPUClass *pcc = POWERNV_CPU_CLASS(oc); > > + > > + dc->realize = powernv_cpu_core_realize; > > + pcc->cpu_oc = cpu_class_by_name(TYPE_POWERPC_CPU, data); > > +} > > + > > +static const TypeInfo powernv_cpu_core_info = { > > + .name = TYPE_POWERNV_CPU_CORE, > > + .parent = TYPE_CPU_CORE, > > + .instance_size = sizeof(PowerNVCPUCore), > > + .class_size = sizeof(PowerNVCPUClass), > > + .abstract = true, > > +}; > > + > > +static void powernv_cpu_core_register_types(void) > > +{ > > + int i ; > > + > > + type_register_static(&powernv_cpu_core_info); > > + for (i = 0; i < ARRAY_SIZE(powernv_core_models); ++i) { > > + TypeInfo ti = { > > + .parent = TYPE_POWERNV_CPU_CORE, > > + .instance_size = sizeof(PowerNVCPUCore), > > + .class_init = powernv_cpu_core_class_init, > > + .class_data = (void *) powernv_core_models[i], > > + }; > > + ti.name = powernv_cpu_core_typename(powernv_core_models[i]); > > + type_register(&ti); > > + g_free((void *)ti.name); > > + } > > +} > > + > > +type_init(powernv_cpu_core_register_types) > > + > > +char *powernv_cpu_core_typename(const char *model) > > +{ > > + return g_strdup_printf("%s-" TYPE_POWERNV_CPU_CORE, model); > > +} > > diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h > > index 6907dc9e5c3d..9eac4b34a9b0 100644 > > --- a/include/hw/ppc/pnv.h > > +++ b/include/hw/ppc/pnv.h > > @@ -31,6 +31,10 @@ typedef struct PnvChip { > > > > /*< public >*/ > > uint32_t chip_id; > > + uint32_t num_cores; > > + char *cpu_model; > > + > > + Object **cores; > > So, unlike the chips within the machine, the cores within the chip > should probably be done like the threads within the core - a single > array with object_initialize() rather than an array of pointers. AIUI > this is because having a (potentially) user instantiable object > automatically object_new() other things breaks QOM lifetime rules. I > can't say I understand this point terribly well though, but i know it > was something we went several rounds on during the spapr core work > though. > > > > > } PnvChip; > > > > #define TYPE_POWERNV_MACHINE "powernv-machine" > > @@ -43,9 +47,12 @@ typedef struct sPowerNVMachineState { > > > > uint32_t initrd_base; > > long initrd_size; > > + hwaddr fdt_addr; > > > > uint32_t num_chips; > > PnvChip *chips; > > } sPowerNVMachineState; > > > > +#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..88a09b0fd1c6 > > --- /dev/null > > +++ b/include/hw/ppc/pnv_core.h > > @@ -0,0 +1,47 @@ > > +/* > > + * QEMU PowerPC PowerNV CPU 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_POWERNV_CPU_CORE "powernv-cpu-core" > > +#define POWERNV_CPU_CORE(obj) \ > > + OBJECT_CHECK(PowerNVCPUCore, (obj), TYPE_POWERNV_CPU_CORE) > > +#define POWERNV_CPU_CLASS(klass) \ > > + OBJECT_CLASS_CHECK(PowerNVCPUClass, (klass), TYPE_POWERNV_CPU_CORE) > > +#define POWERNV_CPU_GET_CLASS(obj) \ > > + OBJECT_GET_CLASS(PowerNVCPUClass, (obj), TYPE_POWERNV_CPU_CORE) > > + > > +typedef struct PowerNVCPUCore { > > + /*< private >*/ > > + CPUCore parent_obj; > > + > > + /*< public >*/ > > + void *threads; > > +} PowerNVCPUCore; > > + > > +typedef struct PowerNVCPUClass { > > + DeviceClass parent_class; > > + ObjectClass *cpu_oc; > > +} PowerNVCPUClass; > > + > > +extern char *powernv_cpu_core_typename(const char *model); > > + > > +#endif /* _PPC_PNV_CORE_H */ >