qemu/hw/misc/xlnx-zynqmp-boot.c
<<
>>
Prefs
   1/*
   2 * QEMU replacement block for ZynqMP boot logic.
   3 *
   4 * Copyright (c) 2017 Xilinx Inc.
   5 * Written by Edgar E. Iglesias.
   6 *
   7 * Permission is hereby granted, free of charge, to any person obtaining a copy
   8 * of this software and associated documentation files (the "Software"), to deal
   9 * in the Software without restriction, including without limitation the rights
  10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 * copies of the Software, and to permit persons to whom the Software is
  12 * furnished to do so, subject to the following conditions:
  13 *
  14 * The above copyright notice and this permission notice shall be included in
  15 * all copies or substantial portions of the Software.
  16 *
  17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23 * THE SOFTWARE.
  24 */
  25
  26#include "qemu/osdep.h"
  27#include "hw/sysbus.h"
  28#include "hw/ptimer.h"
  29#include "qemu/bitops.h"
  30#include "qapi/error.h"
  31#include "sysemu/dma.h"
  32#include "qemu/log.h"
  33#include "qemu/main-loop.h"
  34#include "hw/core/cpu.h"
  35#include "migration/vmstate.h"
  36#include "hw/qdev-properties.h"
  37#include "hw/qdev-properties-system.h"
  38#include "sysemu/reset.h"
  39#include "sysemu/blockdev.h"
  40#include "sysemu/block-backend.h"
  41
  42#include "hw/misc/xlnx-zynqmp-pmufw-cfg.h"
  43
  44#ifndef XILINX_ZYNQMP_BOOT_DEBUG
  45#define XILINX_ZYNQMP_BOOT_DEBUG 0
  46#endif
  47
  48#define TYPE_XILINX_ZYNQMP_BOOT "xlnx,zynqmp-boot"
  49
  50#define XILINX_ZYNQMP_BOOT(obj) \
  51     OBJECT_CHECK(ZynqMPBoot, (obj), TYPE_XILINX_ZYNQMP_BOOT)
  52
  53/* IPI message buffers */
  54#define IPI_BUFFER_BASEADDR     0xFF990000U
  55#define IPI_BUFFER_RPU_0_BASE   (IPI_BUFFER_BASEADDR + 0x0U)
  56#define IPI_BUFFER_RPU_1_BASE   (IPI_BUFFER_BASEADDR + 0x200U)
  57#define IPI_BUFFER_APU_BASE     (IPI_BUFFER_BASEADDR + 0x400U)
  58#define IPI_BUFFER_PMU_BASE     (IPI_BUFFER_BASEADDR + 0xE00U)
  59
  60#define IPI_BUFFER_TARGET_PMU_OFFSET    0x1C0U
  61
  62#define IPI_BUFFER_REQ_OFFSET   0x0U
  63#define IPI_BUFFER_RESP_OFFSET  0x20U
  64
  65/* IPI Base Address */
  66#define IPI_BASEADDR            0XFF300000
  67#define IPI_APU_IXR_PMU_0_MASK         (1 << 16)
  68
  69#define IPI_TRIG_OFFSET         0
  70#define IPI_OBS_OFFSET          4
  71
  72/* Power Management IPI interrupt number */
  73#define PM_INT_NUM              0
  74#define IPI_PMU_PM_INT_MASK     (IPI_APU_IXR_PMU_0_MASK << PM_INT_NUM)
  75
  76#define IPI_APU_MASK            1U
  77
  78#define PAYLOAD_ARG_CNT         6
  79#define PM_SET_CONFIGURATION    2
  80
  81#define CPU_NONE 0xFFFFFFFF
  82
  83#define DB_PRINT_L(lvl, fmt, args...) do { \
  84    if (XILINX_ZYNQMP_BOOT_DEBUG >= lvl) { \
  85        qemu_log("%s: " fmt, __func__, ## args); \
  86    } \
  87} while (0);
  88
  89#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
  90
  91typedef enum {
  92    STATE_WAIT_RST = 0,
  93    STATE_WAIT_PMUFW,
  94    STATE_PMUFW_SETCFG,
  95    STATE_WAIT_PMUFW_READY,
  96    STATE_RELEASE_CPU,
  97    STATE_DONE,
  98} BootState;
  99
 100typedef struct ZynqMPBoot {
 101    SysBusDevice parent_obj;
 102
 103    MemoryRegion *dma_mr;
 104    AddressSpace *dma_as;
 105
 106    ptimer_state *ptimer;
 107
 108    BootState state;
 109
 110    /* ZynqMP Boot reset is active-low.  */
 111    bool n_reset;
 112
 113    bool boot_ready;
 114
 115    struct {
 116        uint32_t cpu_num;
 117        bool use_pmufw;
 118        bool load_pmufw_cfg;
 119    } cfg;
 120
 121    BlockBackend *blk;
 122    uint8_t *cfg_buf;
 123    uint32_t cfg_size;
 124    unsigned char *buf;
 125} ZynqMPBoot;
 126
 127static const MemTxAttrs mattr_secure = { .secure = true };
 128
 129static void boot_store32(ZynqMPBoot *s, uint64_t addr, uint32_t v)
 130{
 131    address_space_write(s->dma_as, addr, mattr_secure, (void *) &v, sizeof v);
 132}
 133
 134static uint32_t boot_load32(ZynqMPBoot *s, uint64_t addr)
 135{
 136    uint32_t v;
 137    address_space_read(s->dma_as, addr, mattr_secure, (void *) &v, sizeof v);
 138    return v;
 139}
 140
 141/*
 142 * Check if the the PMU is ready.
 143 */
 144static bool pm_ipi_ready(ZynqMPBoot *s)
 145{
 146    uint32_t r;
 147
 148    r = boot_load32(s, IPI_BASEADDR + IPI_OBS_OFFSET);
 149    r &= IPI_PMU_PM_INT_MASK;
 150    return !r;
 151}
 152
 153/*
 154 * Send an IPI to the PMU.
 155 */
 156static void pm_ipi_send(ZynqMPBoot *s,
 157                        uint32_t payload[PAYLOAD_ARG_CNT])
 158{
 159    unsigned int i;
 160    unsigned int offset = 0;
 161    uintptr_t buffer_base = IPI_BUFFER_APU_BASE +
 162                            IPI_BUFFER_TARGET_PMU_OFFSET +
 163                            IPI_BUFFER_REQ_OFFSET;
 164
 165    assert(pm_ipi_ready(s));
 166
 167    /* Write payload into IPI buffer */
 168    for (i = 0; i < PAYLOAD_ARG_CNT; i++) {
 169            boot_store32(s, buffer_base + offset, payload[i]);
 170            offset += 4;
 171    }
 172    /* Generate IPI to PMU */
 173    boot_store32(s, IPI_BASEADDR + IPI_TRIG_OFFSET, IPI_PMU_PM_INT_MASK);
 174}
 175
 176static void release_cpu_set_pc(CPUState *cpu, run_on_cpu_data arg)
 177{
 178    cpu_set_pc(cpu, arg.target_ptr);
 179}
 180
 181static void release_cpu(ZynqMPBoot *s)
 182{
 183    CPUState *cpu = qemu_get_cpu(s->cfg.cpu_num);
 184    CPUClass *cc = CPU_GET_CLASS(cpu);
 185    vaddr pc = 0;
 186    uint32_t r;
 187
 188    DB_PRINT("Starting CPU#%d release\n", s->cfg.cpu_num)
 189
 190    /*
 191     * Save and restore PC accross reset to keep ELF loaded entry point valid.
 192     */
 193    if (cc->get_pc) {
 194        pc = cc->get_pc(cpu);
 195    }
 196    if (s->cfg.cpu_num < 4) {
 197        /* Release the APU.  */
 198        r = boot_load32(s, 0xfd1a0104);
 199        r &= ~(1 << s->cfg.cpu_num);
 200        boot_store32(s, 0xfd1a0104, 0x80000000 | r);
 201    } else {
 202        /* FIXME: Not implemented yet.  */
 203    }
 204    if (cc->set_pc) {
 205        DB_PRINT("Setting CPU#%d PC to 0x%" PRIx64 "\n", s->cfg.cpu_num, pc)
 206        run_on_cpu(cpu, release_cpu_set_pc, RUN_ON_CPU_TARGET_PTR(pc));
 207    }
 208}
 209
 210static bool check_for_pmufw(ZynqMPBoot *s)
 211{
 212    uint32_t r;
 213
 214    r = boot_load32(s, 0xFFD80000);
 215    return r & (1 << 4);
 216}
 217
 218static void roll_timer(ZynqMPBoot *s)
 219{
 220    ptimer_set_limit(s->ptimer, 200000, 1);
 221    ptimer_run(s->ptimer, 1);
 222}
 223
 224static void boot_sequence(void *opaque)
 225{
 226    ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(opaque);
 227    uint32_t pay[6] = {};
 228
 229    switch (s->state) {
 230    case STATE_WAIT_PMUFW:
 231        if (!s->cfg.use_pmufw) {
 232            s->state = STATE_RELEASE_CPU;
 233            boot_sequence(s);
 234            return;
 235        }
 236
 237        if (!check_for_pmufw(s)) {
 238            roll_timer(s);
 239            return;
 240        }
 241
 242        if (s->cfg.load_pmufw_cfg) {
 243            s->state = STATE_PMUFW_SETCFG;
 244        } else {
 245            s->state = STATE_RELEASE_CPU;
 246        }
 247        boot_sequence(s);
 248        break;
 249
 250    case STATE_PMUFW_SETCFG:
 251        if (!pm_ipi_ready(s)) {
 252            roll_timer(s);
 253            return;
 254        }
 255
 256        s->cfg_size = s->blk ? blk_getlength(s->blk) :
 257                      sizeof(pmufw_cfg);
 258        s->cfg_buf = g_new0(uint8_t, s->cfg_size);
 259        if (s->blk) {
 260            blk_pread(s->blk, 0, s->cfg_size, s->cfg_buf, 0);
 261        } else {
 262            memcpy(s->cfg_buf, pmufw_cfg, s->cfg_size);
 263        }
 264
 265        /* Save DDR contents.  */
 266        s->buf = g_malloc(s->cfg_size);
 267        address_space_read(s->dma_as, 0, mattr_secure,
 268                           s->buf, s->cfg_size);
 269        address_space_write(s->dma_as, 0, mattr_secure,
 270                            (void *) s->cfg_buf, s->cfg_size);
 271        pay[0] = PM_SET_CONFIGURATION;
 272        pay[1] = 0;
 273        pm_ipi_send(s, pay);
 274        s->state = STATE_WAIT_PMUFW_READY;
 275        boot_sequence(s);
 276        break;
 277
 278    case STATE_WAIT_PMUFW_READY:
 279        if (!pm_ipi_ready(s)) {
 280            roll_timer(s);
 281            return;
 282        }
 283
 284        /* Restore DDR contents.  */
 285        address_space_write(s->dma_as, 0, mattr_secure,
 286                            s->buf, s->cfg_size);
 287        g_free(s->buf);
 288        s->buf = NULL;
 289        g_free(s->cfg_buf);
 290        s->cfg_buf = NULL;
 291
 292        s->state = STATE_RELEASE_CPU;
 293        boot_sequence(s);
 294        break;
 295
 296    case STATE_RELEASE_CPU:
 297        if (s->cfg.cpu_num != CPU_NONE) {
 298            release_cpu(s);
 299        }
 300        s->state = STATE_DONE;
 301        s->boot_ready = false;
 302        break;
 303
 304    case STATE_DONE:
 305    case STATE_WAIT_RST:
 306        /* These states are not handled here.  */
 307        g_assert_not_reached();
 308        break;
 309    };
 310}
 311
 312static void irq_handler(void *opaque, int irq, int level)
 313{
 314    ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(opaque);
 315
 316    if (!s->n_reset && level) {
 317        s->boot_ready = true;
 318    }
 319    s->n_reset = level;
 320}
 321
 322static void zynqmp_boot_reset(void *opaque)
 323{
 324    ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(opaque);
 325
 326    if (s->boot_ready) {
 327        /* Start the boot sequence.  */
 328        DB_PRINT("Starting the boot sequence\n");
 329        s->state = STATE_WAIT_PMUFW;
 330        ptimer_transaction_begin(s->ptimer);
 331        boot_sequence(s);
 332        ptimer_transaction_commit(s->ptimer);
 333    }
 334}
 335
 336static void zynqmp_boot_realize(DeviceState *dev, Error **errp)
 337{
 338    ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(dev);
 339
 340    if (s->cfg.cpu_num > 3 && s->cfg.cpu_num != CPU_NONE) {
 341        error_setg(errp, "cpu-num %u is out of range\n", s->cfg.cpu_num);
 342    }
 343
 344    s->dma_as = s->dma_mr ? address_space_init_shareable(s->dma_mr, NULL)
 345                          : &address_space_memory;
 346
 347    qemu_register_reset_loader(zynqmp_boot_reset, dev);
 348
 349    s->ptimer = ptimer_init(boot_sequence, s, PTIMER_POLICY_LEGACY);
 350    ptimer_transaction_begin(s->ptimer);
 351    ptimer_set_freq(s->ptimer, 1000000);
 352    ptimer_transaction_commit(s->ptimer);
 353}
 354
 355static void zynqmp_boot_unrealize(DeviceState *dev)
 356{
 357    qemu_unregister_reset_loader(zynqmp_boot_reset, dev);
 358}
 359
 360static void zynqmp_boot_init(Object *obj)
 361{
 362    ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(obj);
 363
 364    qdev_init_gpio_in(DEVICE(obj), irq_handler, 1);
 365    object_property_add_link(obj, "dma", TYPE_MEMORY_REGION,
 366                             (Object **)&s->dma_mr,
 367                             qdev_prop_allow_set_link_before_realize,
 368                             OBJ_PROP_LINK_STRONG);
 369}
 370
 371static Property zynqmp_boot_props[] = {
 372    DEFINE_PROP_UINT32("cpu-num", ZynqMPBoot, cfg.cpu_num, CPU_NONE),
 373    DEFINE_PROP_BOOL("use-pmufw", ZynqMPBoot, cfg.use_pmufw, false),
 374    DEFINE_PROP_BOOL("load-pmufw-cfg", ZynqMPBoot, cfg.load_pmufw_cfg, true),
 375    DEFINE_PROP_DRIVE("drive", ZynqMPBoot, blk),
 376    DEFINE_PROP_END_OF_LIST(),
 377};
 378
 379static void zynqmp_boot_class_init(ObjectClass *klass, void *data)
 380{
 381    DeviceClass *dc = DEVICE_CLASS(klass);
 382
 383    dc->realize = zynqmp_boot_realize;
 384    device_class_set_props(dc, zynqmp_boot_props);
 385    dc->unrealize = zynqmp_boot_unrealize;
 386}
 387
 388static const TypeInfo zynqmp_boot_info = {
 389    .name          = TYPE_XILINX_ZYNQMP_BOOT,
 390    .parent        = TYPE_SYS_BUS_DEVICE,
 391    .instance_size = sizeof(ZynqMPBoot),
 392    .class_init    = zynqmp_boot_class_init,
 393    .instance_init = zynqmp_boot_init,
 394};
 395
 396static void zynqmp_boot_register_types(void)
 397{
 398    type_register_static(&zynqmp_boot_info);
 399}
 400
 401type_init(zynqmp_boot_register_types)
 402