On 08/16/2016 07:02 AM, Benjamin Herrenschmidt wrote: > 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 ?
Sounds fun ! I need to dig in the P9 specs to have a better idea. I lack the knowledge right now. >>> 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 ok. I have will look into that. Thanks, C. >>> 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 */ >>