qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

sifive_plic.c (17303B)


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