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