/* * 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 . */ #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" #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); /* * the skiboot firmware elects a primary thread to initialize the * system and it can be any. */ env->gpr[3] = PNV_FDT_ADDR; env->nip = 0x10; env->msr |= MSR_HVB; /* Hypervisor mode */ } static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp) { CPUPPCState *env = &cpu->env; int core_pir; int thread_index = 0; /* TODO: TCG supports only one thread */ ppc_spr_t *pir = &env->spr_cb[SPR_PIR]; core_pir = object_property_get_int(OBJECT(cpu), "core-pir", &error_abort); /* * The PIR of a thread is the core PIR + the thread index. We will * need to find a way to get the thread index when TCG supports * more than 1. We could use the object name ? */ pir->default_value = core_pir + thread_index; /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ); qemu_register_reset(powernv_cpu_reset, cpu); } /* * These values are read by the PowerNV HW monitors under Linux */ #define PNV_XSCOM_EX_DTS_RESULT0 0x50000 #define PNV_XSCOM_EX_DTS_RESULT1 0x50001 static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr, unsigned int width) { uint32_t offset = addr >> 3; uint64_t val = 0; /* The result should be 38 C */ switch (offset) { case PNV_XSCOM_EX_DTS_RESULT0: val = 0x26f024f023f0000ull; break; case PNV_XSCOM_EX_DTS_RESULT1: val = 0x24f000000000000ull; break; default: qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx, addr); } return val; } static void pnv_core_xscom_write(void *opaque, hwaddr addr, uint64_t val, unsigned int width) { qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx, addr); } static const MemoryRegionOps pnv_core_xscom_ops = { .read = pnv_core_xscom_read, .write = pnv_core_xscom_write, .valid.min_access_size = 8, .valid.max_access_size = 8, .impl.min_access_size = 8, .impl.max_access_size = 8, .endianness = DEVICE_BIG_ENDIAN, }; static void pnv_core_realize_child(Object *child, Error **errp) { Error *local_err = NULL; CPUState *cs = CPU(child); PowerPCCPU *cpu = POWERPC_CPU(cs); object_property_set_bool(child, true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); return; } powernv_cpu_init(cpu, &local_err); if (local_err) { error_propagate(errp, local_err); return; } } static void pnv_core_realize(DeviceState *dev, Error **errp) { PnvCore *pc = PNV_CORE(OBJECT(dev)); CPUCore *cc = CPU_CORE(OBJECT(dev)); PnvCoreClass *pcc = PNV_CORE_GET_CLASS(OBJECT(dev)); const char *typename = object_class_get_name(pcc->cpu_oc); size_t size = object_type_get_instance_size(typename); Error *local_err = NULL; void *obj; int i, j; char name[32]; pc->threads = g_malloc0(size * cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { obj = pc->threads + i * size; object_initialize(obj, size, typename); snprintf(name, sizeof(name), "thread[%d]", i); object_property_add_child(OBJECT(pc), name, obj, &local_err); object_property_add_alias(obj, "core-pir", OBJECT(pc), "pir", &local_err); if (local_err) { goto err; } object_unref(obj); } for (j = 0; j < cc->nr_threads; j++) { obj = pc->threads + j * size; pnv_core_realize_child(obj, &local_err); if (local_err) { goto err; } } snprintf(name, sizeof(name), "xscom-core.%d", cc->core_id); pnv_xscom_region_init(&pc->xscom_regs, OBJECT(dev), &pnv_core_xscom_ops, pc, name, PNV_XSCOM_EX_CORE_SIZE); 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, }; 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); }