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