pnv_core.c 11.4 KB
Newer Older
C
Cédric Le Goater 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * 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"
22
#include "qemu/log.h"
23
#include "target/ppc/cpu.h"
C
Cédric Le Goater 已提交
24 25 26
#include "hw/ppc/ppc.h"
#include "hw/ppc/pnv.h"
#include "hw/ppc/pnv_core.h"
27
#include "hw/ppc/pnv_xscom.h"
28
#include "hw/ppc/xics.h"
C
Cédric Le Goater 已提交
29

30 31 32 33 34 35 36 37 38 39
static const char *pnv_core_cpu_typename(PnvCore *pc)
{
    const char *core_type = object_class_get_name(object_get_class(OBJECT(pc)));
    int len = strlen(core_type) - strlen(PNV_CORE_TYPE_SUFFIX);
    char *s = g_strdup_printf(POWERPC_CPU_TYPE_NAME("%.*s"), len, core_type);
    const char *cpu_type = object_class_get_name(object_class_by_name(s));
    g_free(s);
    return cpu_type;
}

40
static void pnv_cpu_reset(void *opaque)
C
Cédric Le Goater 已提交
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
{
    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 */
}

57 58 59 60 61 62
/*
 * 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

63 64
static uint64_t pnv_core_power8_xscom_read(void *opaque, hwaddr addr,
                                           unsigned int width)
65 66 67 68 69 70 71 72 73 74 75 76 77
{
    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:
78
        qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx "\n",
79 80 81 82 83 84
                  addr);
    }

    return val;
}

85 86
static void pnv_core_power8_xscom_write(void *opaque, hwaddr addr, uint64_t val,
                                        unsigned int width)
87
{
88
    qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx "\n",
89 90 91
                  addr);
}

92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
static const MemoryRegionOps pnv_core_power8_xscom_ops = {
    .read = pnv_core_power8_xscom_read,
    .write = pnv_core_power8_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,
};


/*
 * POWER9 core controls
 */
#define PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_HYP 0xf010d
#define PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_OTR 0xf010a

static uint64_t pnv_core_power9_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;
    case PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_HYP:
    case PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_OTR:
        val = 0x0;
        break;
    default:
        qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx "\n",
                  addr);
    }

    return val;
}

static void pnv_core_power9_xscom_write(void *opaque, hwaddr addr, uint64_t val,
                                        unsigned int width)
{
    uint32_t offset = addr >> 3;

    switch (offset) {
    case PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_HYP:
    case PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_OTR:
        break;
    default:
        qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx "\n",
                      addr);
    }
}

static const MemoryRegionOps pnv_core_power9_xscom_ops = {
    .read = pnv_core_power9_xscom_read,
    .write = pnv_core_power9_xscom_write,
153 154 155 156 157 158 159
    .valid.min_access_size = 8,
    .valid.max_access_size = 8,
    .impl.min_access_size = 8,
    .impl.max_access_size = 8,
    .endianness = DEVICE_BIG_ENDIAN,
};

160
static void pnv_realize_vcpu(PowerPCCPU *cpu, PnvChip *chip, Error **errp)
C
Cédric Le Goater 已提交
161
{
D
David Gibson 已提交
162 163 164 165
    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];
C
Cédric Le Goater 已提交
166
    Error *local_err = NULL;
167
    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
168

D
David Gibson 已提交
169
    object_property_set_bool(OBJECT(cpu), true, "realized", &local_err);
170 171 172 173
    if (local_err) {
        error_propagate(errp, local_err);
        return;
    }
C
Cédric Le Goater 已提交
174

175
    pcc->intc_create(chip, cpu, &local_err);
C
Cédric Le Goater 已提交
176 177 178 179 180
    if (local_err) {
        error_propagate(errp, local_err);
        return;
    }

D
David Gibson 已提交
181 182 183 184 185 186 187 188 189 190 191 192 193
    core_pir = object_property_get_uint(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(pnv_cpu_reset, cpu);
C
Cédric Le Goater 已提交
194 195 196 197 198
}

static void pnv_core_realize(DeviceState *dev, Error **errp)
{
    PnvCore *pc = PNV_CORE(OBJECT(dev));
199
    PnvCoreClass *pcc = PNV_CORE_GET_CLASS(pc);
C
Cédric Le Goater 已提交
200
    CPUCore *cc = CPU_CORE(OBJECT(dev));
201
    const char *typename = pnv_core_cpu_typename(pc);
C
Cédric Le Goater 已提交
202 203 204 205
    Error *local_err = NULL;
    void *obj;
    int i, j;
    char name[32];
206
    Object *chip;
207

208 209
    chip = object_property_get_link(OBJECT(dev), "chip", &local_err);
    if (!chip) {
210 211
        error_propagate_prepend(errp, local_err,
                                "required link 'chip' not found: ");
212
        return;
213
    }
C
Cédric Le Goater 已提交
214

215
    pc->threads = g_new(PowerPCCPU *, cc->nr_threads);
C
Cédric Le Goater 已提交
216
    for (i = 0; i < cc->nr_threads; i++) {
217 218
        PowerPCCPU *cpu;

219
        obj = object_new(typename);
220
        cpu = POWERPC_CPU(obj);
C
Cédric Le Goater 已提交
221

222
        pc->threads[i] = POWERPC_CPU(obj);
C
Cédric Le Goater 已提交
223 224

        snprintf(name, sizeof(name), "thread[%d]", i);
225
        object_property_add_child(OBJECT(pc), name, obj, &error_abort);
C
Cédric Le Goater 已提交
226
        object_property_add_alias(obj, "core-pir", OBJECT(pc),
227
                                  "pir", &error_abort);
228 229 230

        cpu->machine_data = g_new0(PnvCPUState, 1);

C
Cédric Le Goater 已提交
231 232 233 234
        object_unref(obj);
    }

    for (j = 0; j < cc->nr_threads; j++) {
235
        pnv_realize_vcpu(pc->threads[j], PNV_CHIP(chip), &local_err);
C
Cédric Le Goater 已提交
236 237 238 239
        if (local_err) {
            goto err;
        }
    }
240 241

    snprintf(name, sizeof(name), "xscom-core.%d", cc->core_id);
242
    pnv_xscom_region_init(&pc->xscom_regs, OBJECT(dev), pcc->xscom_ops,
243
                          pc, name, PNV_XSCOM_EX_SIZE);
C
Cédric Le Goater 已提交
244 245 246 247
    return;

err:
    while (--i >= 0) {
248
        obj = OBJECT(pc->threads[i]);
C
Cédric Le Goater 已提交
249 250 251 252 253 254
        object_unparent(obj);
    }
    g_free(pc->threads);
    error_propagate(errp, local_err);
}

D
David Gibson 已提交
255 256
static void pnv_unrealize_vcpu(PowerPCCPU *cpu)
{
257 258
    PnvCPUState *pnv_cpu = pnv_cpu_state(cpu);

D
David Gibson 已提交
259
    qemu_unregister_reset(pnv_cpu_reset, cpu);
260
    object_unparent(OBJECT(pnv_cpu_state(cpu)->intc));
D
David Gibson 已提交
261
    cpu_remove_sync(CPU(cpu));
262 263
    cpu->machine_data = NULL;
    g_free(pnv_cpu);
D
David Gibson 已提交
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
    object_unparent(OBJECT(cpu));
}

static void pnv_core_unrealize(DeviceState *dev, Error **errp)
{
    PnvCore *pc = PNV_CORE(dev);
    CPUCore *cc = CPU_CORE(dev);
    int i;

    for (i = 0; i < cc->nr_threads; i++) {
        pnv_unrealize_vcpu(pc->threads[i]);
    }
    g_free(pc->threads);
}

C
Cédric Le Goater 已提交
279 280 281 282 283
static Property pnv_core_properties[] = {
    DEFINE_PROP_UINT32("pir", PnvCore, pir, 0),
    DEFINE_PROP_END_OF_LIST(),
};

284 285 286 287 288 289 290 291 292 293 294 295 296 297
static void pnv_core_power8_class_init(ObjectClass *oc, void *data)
{
    PnvCoreClass *pcc = PNV_CORE_CLASS(oc);

    pcc->xscom_ops = &pnv_core_power8_xscom_ops;
}

static void pnv_core_power9_class_init(ObjectClass *oc, void *data)
{
    PnvCoreClass *pcc = PNV_CORE_CLASS(oc);

    pcc->xscom_ops = &pnv_core_power9_xscom_ops;
}

C
Cédric Le Goater 已提交
298 299 300 301 302
static void pnv_core_class_init(ObjectClass *oc, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(oc);

    dc->realize = pnv_core_realize;
D
David Gibson 已提交
303
    dc->unrealize = pnv_core_unrealize;
C
Cédric Le Goater 已提交
304 305 306
    dc->props = pnv_core_properties;
}

307
#define DEFINE_PNV_CORE_TYPE(family, cpu_model) \
308 309 310
    {                                           \
        .parent = TYPE_PNV_CORE,                \
        .name = PNV_CORE_TYPE_NAME(cpu_model),  \
311
        .class_init = pnv_core_##family##_class_init, \
C
Cédric Le Goater 已提交
312 313
    }

314 315 316 317 318 319 320 321 322
static const TypeInfo pnv_core_infos[] = {
    {
        .name           = TYPE_PNV_CORE,
        .parent         = TYPE_CPU_CORE,
        .instance_size  = sizeof(PnvCore),
        .class_size     = sizeof(PnvCoreClass),
        .class_init = pnv_core_class_init,
        .abstract       = true,
    },
323 324 325 326
    DEFINE_PNV_CORE_TYPE(power8, "power8e_v2.1"),
    DEFINE_PNV_CORE_TYPE(power8, "power8_v2.0"),
    DEFINE_PNV_CORE_TYPE(power8, "power8nvl_v1.0"),
    DEFINE_PNV_CORE_TYPE(power9, "power9_v2.0"),
327
};
C
Cédric Le Goater 已提交
328

329
DEFINE_TYPES(pnv_core_infos)
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416

/*
 * POWER9 Quads
 */

#define P9X_EX_NCU_SPEC_BAR                     0x11010

static uint64_t pnv_quad_xscom_read(void *opaque, hwaddr addr,
                                    unsigned int width)
{
    uint32_t offset = addr >> 3;
    uint64_t val = -1;

    switch (offset) {
    case P9X_EX_NCU_SPEC_BAR:
    case P9X_EX_NCU_SPEC_BAR + 0x400: /* Second EX */
        val = 0;
        break;
    default:
        qemu_log_mask(LOG_UNIMP, "%s: writing @0x%08x\n", __func__,
                      offset);
    }

    return val;
}

static void pnv_quad_xscom_write(void *opaque, hwaddr addr, uint64_t val,
                                 unsigned int width)
{
    uint32_t offset = addr >> 3;

    switch (offset) {
    case P9X_EX_NCU_SPEC_BAR:
    case P9X_EX_NCU_SPEC_BAR + 0x400: /* Second EX */
        break;
    default:
        qemu_log_mask(LOG_UNIMP, "%s: writing @0x%08x\n", __func__,
                  offset);
    }
}

static const MemoryRegionOps pnv_quad_xscom_ops = {
    .read = pnv_quad_xscom_read,
    .write = pnv_quad_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_quad_realize(DeviceState *dev, Error **errp)
{
    PnvQuad *eq = PNV_QUAD(dev);
    char name[32];

    snprintf(name, sizeof(name), "xscom-quad.%d", eq->id);
    pnv_xscom_region_init(&eq->xscom_regs, OBJECT(dev), &pnv_quad_xscom_ops,
                          eq, name, PNV9_XSCOM_EQ_SIZE);
}

static Property pnv_quad_properties[] = {
    DEFINE_PROP_UINT32("id", PnvQuad, id, 0),
    DEFINE_PROP_END_OF_LIST(),
};

static void pnv_quad_class_init(ObjectClass *oc, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(oc);

    dc->realize = pnv_quad_realize;
    dc->props = pnv_quad_properties;
}

static const TypeInfo pnv_quad_info = {
    .name          = TYPE_PNV_QUAD,
    .parent        = TYPE_DEVICE,
    .instance_size = sizeof(PnvQuad),
    .class_init    = pnv_quad_class_init,
};

static void pnv_core_register_types(void)
{
    type_register_static(&pnv_quad_info);
}

type_init(pnv_core_register_types)