qemu/hw/riscv/sifive_plic.c
<<
>>
Prefs
   1/*
   2 * SiFive PLIC (Platform Level Interrupt Controller)
   3 *
   4 * Copyright (c) 2017 SiFive, Inc.
   5 *
   6 * This provides a parameterizable interrupt controller based on SiFive's PLIC.
   7 *
   8 * This program is free software; you can redistribute it and/or modify it
   9 * under the terms and conditions of the GNU General Public License,
  10 * version 2 or later, as published by the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope it will be useful, but WITHOUT
  13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  15 * more details.
  16 *
  17 * You should have received a copy of the GNU General Public License along with
  18 * this program.  If not, see <http://www.gnu.org/licenses/>.
  19 */
  20
  21#include "qemu/osdep.h"
  22#include "qapi/error.h"
  23#include "qemu/log.h"
  24#include "qemu/module.h"
  25#include "qemu/error-report.h"
  26#include "hw/sysbus.h"
  27#include "hw/pci/msi.h"
  28#include "hw/boards.h"
  29#include "hw/qdev-properties.h"
  30#include "target/riscv/cpu.h"
  31#include "sysemu/sysemu.h"
  32#include "hw/riscv/sifive_plic.h"
  33
  34#define RISCV_DEBUG_PLIC 0
  35
  36static PLICMode char_to_mode(char c)
  37{
  38    switch (c) {
  39    case 'U': return PLICMode_U;
  40    case 'S': return PLICMode_S;
  41    case 'H': return PLICMode_H;
  42    case 'M': return PLICMode_M;
  43    default:
  44        error_report("plic: invalid mode '%c'", c);
  45        exit(1);
  46    }
  47}
  48
  49static char mode_to_char(PLICMode m)
  50{
  51    switch (m) {
  52    case PLICMode_U: return 'U';
  53    case PLICMode_S: return 'S';
  54    case PLICMode_H: return 'H';
  55    case PLICMode_M: return 'M';
  56    default: return '?';
  57    }
  58}
  59
  60static void sifive_plic_print_state(SiFivePLICState *plic)
  61{
  62    int i;
  63    int addrid;
  64
  65    /* pending */
  66    qemu_log("pending       : ");
  67    for (i = plic->bitfield_words - 1; i >= 0; i--) {
  68        qemu_log("%08x", plic->pending[i]);
  69    }
  70    qemu_log("\n");
  71
  72    /* pending */
  73    qemu_log("claimed       : ");
  74    for (i = plic->bitfield_words - 1; i >= 0; i--) {
  75        qemu_log("%08x", plic->claimed[i]);
  76    }
  77    qemu_log("\n");
  78
  79    for (addrid = 0; addrid < plic->num_addrs; addrid++) {
  80        qemu_log("hart%d-%c enable: ",
  81            plic->addr_config[addrid].hartid,
  82            mode_to_char(plic->addr_config[addrid].mode));
  83        for (i = plic->bitfield_words - 1; i >= 0; i--) {
  84            qemu_log("%08x", plic->enable[addrid * plic->bitfield_words + i]);
  85        }
  86        qemu_log("\n");
  87    }
  88}
  89
  90static uint32_t atomic_set_masked(uint32_t *a, uint32_t mask, uint32_t value)
  91{
  92    uint32_t old, new, cmp = atomic_read(a);
  93
  94    do {
  95        old = cmp;
  96        new = (old & ~mask) | (value & mask);
  97        cmp = atomic_cmpxchg(a, old, new);
  98    } while (old != cmp);
  99
 100    return old;
 101}
 102
 103static void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool level)
 104{
 105    atomic_set_masked(&plic->pending[irq >> 5], 1 << (irq & 31), -!!level);
 106}
 107
 108static void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool level)
 109{
 110    atomic_set_masked(&plic->claimed[irq >> 5], 1 << (irq & 31), -!!level);
 111}
 112
 113static int sifive_plic_irqs_pending(SiFivePLICState *plic, uint32_t addrid)
 114{
 115    int i, j;
 116    for (i = 0; i < plic->bitfield_words; i++) {
 117        uint32_t pending_enabled_not_claimed =
 118            (plic->pending[i] & ~plic->claimed[i]) &
 119            plic->enable[addrid * plic->bitfield_words + i];
 120        if (!pending_enabled_not_claimed) {
 121            continue;
 122        }
 123        for (j = 0; j < 32; j++) {
 124            int irq = (i << 5) + j;
 125            uint32_t prio = plic->source_priority[irq];
 126            int enabled = pending_enabled_not_claimed & (1 << j);
 127            if (enabled && prio > plic->target_priority[addrid]) {
 128                return 1;
 129            }
 130        }
 131    }
 132    return 0;
 133}
 134
 135static void sifive_plic_update(SiFivePLICState *plic)
 136{
 137    int addrid;
 138
 139    /* raise irq on harts where this irq is enabled */
 140    for (addrid = 0; addrid < plic->num_addrs; addrid++) {
 141        uint32_t hartid = plic->addr_config[addrid].hartid;
 142        PLICMode mode = plic->addr_config[addrid].mode;
 143        CPUState *cpu = qemu_get_cpu(hartid);
 144        CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
 145        if (!env) {
 146            continue;
 147        }
 148        int level = sifive_plic_irqs_pending(plic, addrid);
 149        switch (mode) {
 150        case PLICMode_M:
 151            riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MEIP, BOOL_TO_MASK(level));
 152            break;
 153        case PLICMode_S:
 154            riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_SEIP, BOOL_TO_MASK(level));
 155            break;
 156        default:
 157            break;
 158        }
 159    }
 160
 161    if (RISCV_DEBUG_PLIC) {
 162        sifive_plic_print_state(plic);
 163    }
 164}
 165
 166static uint32_t sifive_plic_claim(SiFivePLICState *plic, uint32_t addrid)
 167{
 168    int i, j;
 169    uint32_t max_irq = 0;
 170    uint32_t max_prio = plic->target_priority[addrid];
 171
 172    for (i = 0; i < plic->bitfield_words; i++) {
 173        uint32_t pending_enabled_not_claimed =
 174            (plic->pending[i] & ~plic->claimed[i]) &
 175            plic->enable[addrid * plic->bitfield_words + i];
 176        if (!pending_enabled_not_claimed) {
 177            continue;
 178        }
 179        for (j = 0; j < 32; j++) {
 180            int irq = (i << 5) + j;
 181            uint32_t prio = plic->source_priority[irq];
 182            int enabled = pending_enabled_not_claimed & (1 << j);
 183            if (enabled && prio > max_prio) {
 184                max_irq = irq;
 185                max_prio = prio;
 186            }
 187        }
 188    }
 189
 190    if (max_irq) {
 191        sifive_plic_set_pending(plic, max_irq, false);
 192        sifive_plic_set_claimed(plic, max_irq, true);
 193    }
 194    return max_irq;
 195}
 196
 197static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size)
 198{
 199    SiFivePLICState *plic = opaque;
 200
 201    /* writes must be 4 byte words */
 202    if ((addr & 0x3) != 0) {
 203        goto err;
 204    }
 205
 206    if (addr >= plic->priority_base && /* 4 bytes per source */
 207        addr < plic->priority_base + (plic->num_sources << 2))
 208    {
 209        uint32_t irq = ((addr - plic->priority_base) >> 2) + 1;
 210        if (RISCV_DEBUG_PLIC) {
 211            qemu_log("plic: read priority: irq=%d priority=%d\n",
 212                irq, plic->source_priority[irq]);
 213        }
 214        return plic->source_priority[irq];
 215    } else if (addr >= plic->pending_base && /* 1 bit per source */
 216               addr < plic->pending_base + (plic->num_sources >> 3))
 217    {
 218        uint32_t word = (addr - plic->pending_base) >> 2;
 219        if (RISCV_DEBUG_PLIC) {
 220            qemu_log("plic: read pending: word=%d value=%d\n",
 221                word, plic->pending[word]);
 222        }
 223        return plic->pending[word];
 224    } else if (addr >= plic->enable_base && /* 1 bit per source */
 225             addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
 226    {
 227        uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
 228        uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
 229        if (wordid < plic->bitfield_words) {
 230            if (RISCV_DEBUG_PLIC) {
 231                qemu_log("plic: read enable: hart%d-%c word=%d value=%x\n",
 232                    plic->addr_config[addrid].hartid,
 233                    mode_to_char(plic->addr_config[addrid].mode), wordid,
 234                    plic->enable[addrid * plic->bitfield_words + wordid]);
 235            }
 236            return plic->enable[addrid * plic->bitfield_words + wordid];
 237        }
 238    } else if (addr >= plic->context_base && /* 1 bit per source */
 239             addr < plic->context_base + plic->num_addrs * plic->context_stride)
 240    {
 241        uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
 242        uint32_t contextid = (addr & (plic->context_stride - 1));
 243        if (contextid == 0) {
 244            if (RISCV_DEBUG_PLIC) {
 245                qemu_log("plic: read priority: hart%d-%c priority=%x\n",
 246                    plic->addr_config[addrid].hartid,
 247                    mode_to_char(plic->addr_config[addrid].mode),
 248                    plic->target_priority[addrid]);
 249            }
 250            return plic->target_priority[addrid];
 251        } else if (contextid == 4) {
 252            uint32_t value = sifive_plic_claim(plic, addrid);
 253            if (RISCV_DEBUG_PLIC) {
 254                qemu_log("plic: read claim: hart%d-%c irq=%x\n",
 255                    plic->addr_config[addrid].hartid,
 256                    mode_to_char(plic->addr_config[addrid].mode),
 257                    value);
 258            }
 259            sifive_plic_update(plic);
 260            return value;
 261        }
 262    }
 263
 264err:
 265    qemu_log_mask(LOG_GUEST_ERROR,
 266                  "%s: Invalid register read 0x%" HWADDR_PRIx "\n",
 267                  __func__, addr);
 268    return 0;
 269}
 270
 271static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value,
 272        unsigned size)
 273{
 274    SiFivePLICState *plic = opaque;
 275
 276    /* writes must be 4 byte words */
 277    if ((addr & 0x3) != 0) {
 278        goto err;
 279    }
 280
 281    if (addr >= plic->priority_base && /* 4 bytes per source */
 282        addr < plic->priority_base + (plic->num_sources << 2))
 283    {
 284        uint32_t irq = ((addr - plic->priority_base) >> 2) + 1;
 285        plic->source_priority[irq] = value & 7;
 286        if (RISCV_DEBUG_PLIC) {
 287            qemu_log("plic: write priority: irq=%d priority=%d\n",
 288                irq, plic->source_priority[irq]);
 289        }
 290        sifive_plic_update(plic);
 291        return;
 292    } else if (addr >= plic->pending_base && /* 1 bit per source */
 293               addr < plic->pending_base + (plic->num_sources >> 3))
 294    {
 295        qemu_log_mask(LOG_GUEST_ERROR,
 296                      "%s: invalid pending write: 0x%" HWADDR_PRIx "",
 297                      __func__, addr);
 298        return;
 299    } else if (addr >= plic->enable_base && /* 1 bit per source */
 300        addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
 301    {
 302        uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
 303        uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
 304        if (wordid < plic->bitfield_words) {
 305            plic->enable[addrid * plic->bitfield_words + wordid] = value;
 306            if (RISCV_DEBUG_PLIC) {
 307                qemu_log("plic: write enable: hart%d-%c word=%d value=%x\n",
 308                    plic->addr_config[addrid].hartid,
 309                    mode_to_char(plic->addr_config[addrid].mode), wordid,
 310                    plic->enable[addrid * plic->bitfield_words + wordid]);
 311            }
 312            return;
 313        }
 314    } else if (addr >= plic->context_base && /* 4 bytes per reg */
 315        addr < plic->context_base + plic->num_addrs * plic->context_stride)
 316    {
 317        uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
 318        uint32_t contextid = (addr & (plic->context_stride - 1));
 319        if (contextid == 0) {
 320            if (RISCV_DEBUG_PLIC) {
 321                qemu_log("plic: write priority: hart%d-%c priority=%x\n",
 322                    plic->addr_config[addrid].hartid,
 323                    mode_to_char(plic->addr_config[addrid].mode),
 324                    plic->target_priority[addrid]);
 325            }
 326            if (value <= plic->num_priorities) {
 327                plic->target_priority[addrid] = value;
 328                sifive_plic_update(plic);
 329            }
 330            return;
 331        } else if (contextid == 4) {
 332            if (RISCV_DEBUG_PLIC) {
 333                qemu_log("plic: write claim: hart%d-%c irq=%x\n",
 334                    plic->addr_config[addrid].hartid,
 335                    mode_to_char(plic->addr_config[addrid].mode),
 336                    (uint32_t)value);
 337            }
 338            if (value < plic->num_sources) {
 339                sifive_plic_set_claimed(plic, value, false);
 340                sifive_plic_update(plic);
 341            }
 342            return;
 343        }
 344    }
 345
 346err:
 347    qemu_log_mask(LOG_GUEST_ERROR,
 348                  "%s: Invalid register write 0x%" HWADDR_PRIx "\n",
 349                  __func__, addr);
 350}
 351
 352static const MemoryRegionOps sifive_plic_ops = {
 353    .read = sifive_plic_read,
 354    .write = sifive_plic_write,
 355    .endianness = DEVICE_LITTLE_ENDIAN,
 356    .valid = {
 357        .min_access_size = 4,
 358        .max_access_size = 4
 359    }
 360};
 361
 362static Property sifive_plic_properties[] = {
 363    DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config),
 364    DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0),
 365    DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0),
 366    DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0),
 367    DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0),
 368    DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0),
 369    DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0),
 370    DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0),
 371    DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0),
 372    DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0),
 373    DEFINE_PROP_END_OF_LIST(),
 374};
 375
 376/*
 377 * parse PLIC hart/mode address offset config
 378 *
 379 * "M"              1 hart with M mode
 380 * "MS,MS"          2 harts, 0-1 with M and S mode
 381 * "M,MS,MS,MS,MS"  5 harts, 0 with M mode, 1-5 with M and S mode
 382 */
 383static void parse_hart_config(SiFivePLICState *plic)
 384{
 385    int addrid, hartid, modes;
 386    const char *p;
 387    char c;
 388
 389    /* count and validate hart/mode combinations */
 390    addrid = 0, hartid = 0, modes = 0;
 391    p = plic->hart_config;
 392    while ((c = *p++)) {
 393        if (c == ',') {
 394            addrid += ctpop8(modes);
 395            modes = 0;
 396            hartid++;
 397        } else {
 398            int m = 1 << char_to_mode(c);
 399            if (modes == (modes | m)) {
 400                error_report("plic: duplicate mode '%c' in config: %s",
 401                             c, plic->hart_config);
 402                exit(1);
 403            }
 404            modes |= m;
 405        }
 406    }
 407    if (modes) {
 408        addrid += ctpop8(modes);
 409    }
 410    hartid++;
 411
 412    /* store hart/mode combinations */
 413    plic->num_addrs = addrid;
 414    plic->addr_config = g_new(PLICAddr, plic->num_addrs);
 415    addrid = 0, hartid = 0;
 416    p = plic->hart_config;
 417    while ((c = *p++)) {
 418        if (c == ',') {
 419            hartid++;
 420        } else {
 421            plic->addr_config[addrid].addrid = addrid;
 422            plic->addr_config[addrid].hartid = hartid;
 423            plic->addr_config[addrid].mode = char_to_mode(c);
 424            addrid++;
 425        }
 426    }
 427}
 428
 429static void sifive_plic_irq_request(void *opaque, int irq, int level)
 430{
 431    SiFivePLICState *plic = opaque;
 432    if (RISCV_DEBUG_PLIC) {
 433        qemu_log("sifive_plic_irq_request: irq=%d level=%d\n", irq, level);
 434    }
 435    sifive_plic_set_pending(plic, irq, level > 0);
 436    sifive_plic_update(plic);
 437}
 438
 439static void sifive_plic_realize(DeviceState *dev, Error **errp)
 440{
 441    MachineState *ms = MACHINE(qdev_get_machine());
 442    unsigned int smp_cpus = ms->smp.cpus;
 443    SiFivePLICState *plic = SIFIVE_PLIC(dev);
 444    int i;
 445
 446    memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic,
 447                          TYPE_SIFIVE_PLIC, plic->aperture_size);
 448    parse_hart_config(plic);
 449    plic->bitfield_words = (plic->num_sources + 31) >> 5;
 450    plic->source_priority = g_new0(uint32_t, plic->num_sources);
 451    plic->target_priority = g_new(uint32_t, plic->num_addrs);
 452    plic->pending = g_new0(uint32_t, plic->bitfield_words);
 453    plic->claimed = g_new0(uint32_t, plic->bitfield_words);
 454    plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs);
 455    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio);
 456    qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources);
 457
 458    /* We can't allow the supervisor to control SEIP as this would allow the
 459     * supervisor to clear a pending external interrupt which will result in
 460     * lost a interrupt in the case a PLIC is attached. The SEIP bit must be
 461     * hardware controlled when a PLIC is attached.
 462     */
 463    for (i = 0; i < smp_cpus; i++) {
 464        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
 465        if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) {
 466            error_report("SEIP already claimed");
 467            exit(1);
 468        }
 469    }
 470
 471    msi_nonbroken = true;
 472}
 473
 474static void sifive_plic_class_init(ObjectClass *klass, void *data)
 475{
 476    DeviceClass *dc = DEVICE_CLASS(klass);
 477
 478    device_class_set_props(dc, sifive_plic_properties);
 479    dc->realize = sifive_plic_realize;
 480}
 481
 482static const TypeInfo sifive_plic_info = {
 483    .name          = TYPE_SIFIVE_PLIC,
 484    .parent        = TYPE_SYS_BUS_DEVICE,
 485    .instance_size = sizeof(SiFivePLICState),
 486    .class_init    = sifive_plic_class_init,
 487};
 488
 489static void sifive_plic_register_types(void)
 490{
 491    type_register_static(&sifive_plic_info);
 492}
 493
 494type_init(sifive_plic_register_types)
 495
 496/*
 497 * Create PLIC device.
 498 */
 499DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
 500    uint32_t num_sources, uint32_t num_priorities,
 501    uint32_t priority_base, uint32_t pending_base,
 502    uint32_t enable_base, uint32_t enable_stride,
 503    uint32_t context_base, uint32_t context_stride,
 504    uint32_t aperture_size)
 505{
 506    DeviceState *dev = qdev_new(TYPE_SIFIVE_PLIC);
 507    assert(enable_stride == (enable_stride & -enable_stride));
 508    assert(context_stride == (context_stride & -context_stride));
 509    qdev_prop_set_string(dev, "hart-config", hart_config);
 510    qdev_prop_set_uint32(dev, "num-sources", num_sources);
 511    qdev_prop_set_uint32(dev, "num-priorities", num_priorities);
 512    qdev_prop_set_uint32(dev, "priority-base", priority_base);
 513    qdev_prop_set_uint32(dev, "pending-base", pending_base);
 514    qdev_prop_set_uint32(dev, "enable-base", enable_base);
 515    qdev_prop_set_uint32(dev, "enable-stride", enable_stride);
 516    qdev_prop_set_uint32(dev, "context-base", context_base);
 517    qdev_prop_set_uint32(dev, "context-stride", context_stride);
 518    qdev_prop_set_uint32(dev, "aperture-size", aperture_size);
 519    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
 520    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
 521    return dev;
 522}
 523