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/register.h"
  29#include "hw/ptimer.h"
  30#include "qemu/bitops.h"
  31#include "qapi/error.h"
  32#include "sysemu/dma.h"
  33#include "qemu/log.h"
  34#include "qemu/main-loop.h"
  35#include "qom/cpu.h"
  36
  37#include "hw/misc/xlnx-zynqmp-pmufw-cfg.h"
  38
  39#ifndef XILINX_ZYNQMP_BOOT_DEBUG
  40#define XILINX_ZYNQMP_BOOT_DEBUG 0
  41#endif
  42
  43#define TYPE_XILINX_ZYNQMP_BOOT "xlnx,zynqmp-boot"
  44
  45#define XILINX_ZYNQMP_BOOT(obj) \
  46     OBJECT_CHECK(ZynqMPBoot, (obj), TYPE_XILINX_ZYNQMP_BOOT)
  47
  48/* IPI message buffers */
  49#define IPI_BUFFER_BASEADDR     0xFF990000U
  50#define IPI_BUFFER_RPU_0_BASE   (IPI_BUFFER_BASEADDR + 0x0U)
  51#define IPI_BUFFER_RPU_1_BASE   (IPI_BUFFER_BASEADDR + 0x200U)
  52#define IPI_BUFFER_APU_BASE     (IPI_BUFFER_BASEADDR + 0x400U)
  53#define IPI_BUFFER_PMU_BASE     (IPI_BUFFER_BASEADDR + 0xE00U)
  54
  55#define IPI_BUFFER_TARGET_PMU_OFFSET    0x1C0U
  56
  57#define IPI_BUFFER_REQ_OFFSET   0x0U
  58#define IPI_BUFFER_RESP_OFFSET  0x20U
  59
  60/* IPI Base Address */
  61#define IPI_BASEADDR            0XFF300000
  62#define IPI_APU_IXR_PMU_0_MASK         (1 << 16)
  63
  64#define IPI_TRIG_OFFSET         0
  65#define IPI_OBS_OFFSET          4
  66
  67/* Power Management IPI interrupt number */
  68#define PM_INT_NUM              0
  69#define IPI_PMU_PM_INT_MASK     (IPI_APU_IXR_PMU_0_MASK << PM_INT_NUM)
  70
  71#define IPI_APU_MASK            1U
  72
  73#define PAYLOAD_ARG_CNT         6
  74#define PM_SET_CONFIGURATION    2
  75
  76#define CPU_NONE 0xFFFFFFFF
  77
  78#define DB_PRINT_L(lvl, fmt, args...) do { \
  79    if (XILINX_ZYNQMP_BOOT_DEBUG >= lvl) { \
  80        qemu_log("%s: " fmt, __func__, ## args); \
  81    } \
  82} while (0);
  83
  84#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
  85
  86typedef enum {
  87    STATE_WAIT_RST = 0,
  88    STATE_WAIT_PMUFW,
  89    STATE_PMUFW_SETCFG,
  90    STATE_WAIT_PMUFW_READY,
  91    STATE_RELEASE_CPU,
  92    STATE_DONE,
  93} BootState;
  94
  95typedef struct ZynqMPBoot {
  96    SysBusDevice parent_obj;
  97
  98    MemoryRegion *dma_mr;
  99    AddressSpace *dma_as;
 100
 101    QEMUBH *bh;
 102    ptimer_state *ptimer;
 103
 104    BootState state;
 105
 106    /* ZynqMP Boot reset is active-low.  */
 107    bool n_reset;
 108
 109    struct {
 110        uint32_t cpu_num;
 111        bool use_pmufw;
 112    } cfg;
 113
 114    unsigned char *buf;
 115} ZynqMPBoot;
 116
 117static const MemTxAttrs mattr_secure = { .secure = true };
 118
 119static void boot_store32(ZynqMPBoot *s, uint64_t addr, uint32_t v)
 120{
 121    address_space_write(s->dma_as, addr, mattr_secure, (void *) &v, sizeof v);
 122}
 123
 124static uint32_t boot_load32(ZynqMPBoot *s, uint64_t addr)
 125{
 126    uint32_t v;
 127    address_space_read(s->dma_as, addr, mattr_secure, (void *) &v, sizeof v);
 128    return v;
 129}
 130
 131/*
 132 * Check if the the PMU is ready.
 133 */
 134static bool pm_ipi_ready(ZynqMPBoot *s)
 135{
 136    uint32_t r;
 137
 138    r = boot_load32(s, IPI_BASEADDR + IPI_OBS_OFFSET);
 139    r &= IPI_PMU_PM_INT_MASK;
 140    return !r;
 141}
 142
 143/*
 144 * Send an IPI to the PMU.
 145 */
 146static void pm_ipi_send(ZynqMPBoot *s,
 147                        uint32_t payload[PAYLOAD_ARG_CNT])
 148{
 149    unsigned int i;
 150    unsigned int offset = 0;
 151    uintptr_t buffer_base = IPI_BUFFER_APU_BASE +
 152                            IPI_BUFFER_TARGET_PMU_OFFSET +
 153                            IPI_BUFFER_REQ_OFFSET;
 154
 155    assert(pm_ipi_ready(s));
 156
 157    /* Write payload into IPI buffer */
 158    for (i = 0; i < PAYLOAD_ARG_CNT; i++) {
 159            boot_store32(s, buffer_base + offset, payload[i]);
 160            offset += 4;
 161    }
 162    /* Generate IPI to PMU */
 163    boot_store32(s, IPI_BASEADDR + IPI_TRIG_OFFSET, IPI_PMU_PM_INT_MASK);
 164}
 165
 166static void release_cpu(ZynqMPBoot *s)
 167{
 168    CPUState *cpu = qemu_get_cpu(s->cfg.cpu_num);
 169    CPUClass *cc = CPU_GET_CLASS(cpu);
 170    uint64_t pc = 0;
 171    uint32_t r;
 172
 173    DB_PRINT("Starting CPU#%d release\n", s->cfg.cpu_num)
 174
 175    /*
 176     * Save and restore PC accross reset to keep ELF loaded entry point valid.
 177     */
 178    if (cc->get_pc) {
 179        pc = cc->get_pc(cpu);
 180    }
 181    if (s->cfg.cpu_num < 4) {
 182        /* Release the APU.  */
 183        r = boot_load32(s, 0xfd1a0104);
 184        r &= ~(1 << s->cfg.cpu_num);
 185        boot_store32(s, 0xfd1a0104, 0x8000000e);
 186    } else {
 187        /* FIXME: Not implemented yet.  */
 188    }
 189    if (cc->set_pc) {
 190        DB_PRINT("Setting CPU#%d PC to 0x%" PRIx64 "\n", s->cfg.cpu_num, pc)
 191        cc->set_pc(cpu, pc);
 192    }
 193}
 194
 195static bool check_for_pmufw(ZynqMPBoot *s)
 196{
 197    uint32_t r;
 198
 199    r = boot_load32(s, 0xFFD80000);
 200    return r & (1 << 4);
 201}
 202
 203static void roll_timer(ZynqMPBoot *s)
 204{
 205    ptimer_set_limit(s->ptimer, 200000, 1);
 206    ptimer_run(s->ptimer, 1);
 207}
 208
 209static void boot_sequence(void *opaque)
 210{
 211    ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(opaque);
 212    uint32_t pay[6] = {};
 213
 214    switch (s->state) {
 215    case STATE_WAIT_PMUFW:
 216        if (!s->cfg.use_pmufw) {
 217            s->state = STATE_RELEASE_CPU;
 218            boot_sequence(s);
 219            return;
 220        }
 221
 222        if (!check_for_pmufw(s)) {
 223            roll_timer(s);
 224            return;
 225        }
 226
 227        s->state = STATE_PMUFW_SETCFG;
 228        boot_sequence(s);
 229        break;
 230
 231    case STATE_PMUFW_SETCFG:
 232        if (!pm_ipi_ready(s)) {
 233            roll_timer(s);
 234            return;
 235        }
 236
 237        /* Save DDR contents.  */
 238        s->buf = g_malloc(sizeof pmufw_cfg);
 239        address_space_read(s->dma_as, 0, mattr_secure,
 240                           s->buf, sizeof pmufw_cfg);
 241        address_space_write(s->dma_as, 0, mattr_secure,
 242                            (void *) pmufw_cfg, sizeof pmufw_cfg);
 243        pay[0] = PM_SET_CONFIGURATION;
 244        pay[1] = 0;
 245        pm_ipi_send(s, pay);
 246        s->state = STATE_WAIT_PMUFW_READY;
 247        boot_sequence(s);
 248        break;
 249
 250    case STATE_WAIT_PMUFW_READY:
 251        if (!pm_ipi_ready(s)) {
 252            roll_timer(s);
 253            return;
 254        }
 255
 256        /* Restore DDR contents.  */
 257        address_space_write(s->dma_as, 0, mattr_secure,
 258                            s->buf, sizeof pmufw_cfg);
 259        g_free(s->buf);
 260        s->buf = NULL;
 261
 262        s->state = STATE_RELEASE_CPU;
 263        boot_sequence(s);
 264        break;
 265
 266    case STATE_RELEASE_CPU:
 267        release_cpu(s);
 268        s->state = STATE_DONE;
 269        break;
 270
 271    case STATE_DONE:
 272    case STATE_WAIT_RST:
 273        /* These states are not handled here.  */
 274        g_assert_not_reached();
 275        break;
 276    };
 277}
 278
 279static void irq_handler(void *opaque, int irq, int level)
 280{
 281    ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(opaque);
 282
 283    if (!s->n_reset && level) {
 284        /* Start the boot sequence.  */
 285        DB_PRINT("Starting the boot sequence\n");
 286        s->state = STATE_WAIT_PMUFW;
 287        boot_sequence(s);
 288    }
 289    s->n_reset = level;
 290}
 291
 292static void zynqmp_boot_realize(DeviceState *dev, Error **errp)
 293{
 294    ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(dev);
 295
 296    if (s->cfg.cpu_num > 3 && s->cfg.cpu_num != CPU_NONE) {
 297        error_setg(errp, "cpu-num %u is out of range\n", s->cfg.cpu_num);
 298    }
 299
 300    s->dma_as = s->dma_mr ? address_space_init_shareable(s->dma_mr, NULL)
 301                          : &address_space_memory;
 302
 303    s->bh = qemu_bh_new(boot_sequence, s);
 304    s->ptimer = ptimer_init(s->bh);
 305    ptimer_set_freq(s->ptimer, 1000000);
 306}
 307
 308static void zynqmp_boot_init(Object *obj)
 309{
 310    ZynqMPBoot *s = XILINX_ZYNQMP_BOOT(obj);
 311
 312    qdev_init_gpio_in(DEVICE(obj), irq_handler, 1);
 313    object_property_add_link(obj, "dma", TYPE_MEMORY_REGION,
 314                             (Object **)&s->dma_mr,
 315                             qdev_prop_allow_set_link_before_realize,
 316                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
 317                             &error_abort);
 318}
 319
 320static Property zynqmp_boot_props[] = {
 321    DEFINE_PROP_UINT32("cpu-num", ZynqMPBoot, cfg.cpu_num, CPU_NONE),
 322    DEFINE_PROP_BOOL("use-pmufw", ZynqMPBoot, cfg.use_pmufw, false),
 323    DEFINE_PROP_END_OF_LIST(),
 324};
 325
 326static void zynqmp_boot_class_init(ObjectClass *klass, void *data)
 327{
 328    DeviceClass *dc = DEVICE_CLASS(klass);
 329
 330    dc->realize = zynqmp_boot_realize;
 331    dc->props = zynqmp_boot_props;
 332}
 333
 334static const TypeInfo zynqmp_boot_info = {
 335    .name          = TYPE_XILINX_ZYNQMP_BOOT,
 336    .parent        = TYPE_SYS_BUS_DEVICE,
 337    .instance_size = sizeof(ZynqMPBoot),
 338    .class_init    = zynqmp_boot_class_init,
 339    .instance_init = zynqmp_boot_init,
 340};
 341
 342static void zynqmp_boot_register_types(void)
 343{
 344    type_register_static(&zynqmp_boot_info);
 345}
 346
 347type_init(zynqmp_boot_register_types)
 348