qemu

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

riscv_imsic.c (13998B)


      1 /*
      2  * RISC-V IMSIC (Incoming Message Signaled Interrupt Controller)
      3  *
      4  * Copyright (c) 2021 Western Digital Corporation or its affiliates.
      5  *
      6  * This program is free software; you can redistribute it and/or modify it
      7  * under the terms and conditions of the GNU General Public License,
      8  * version 2 or later, as published by the Free Software Foundation.
      9  *
     10  * This program is distributed in the hope it will be useful, but WITHOUT
     11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
     13  * more details.
     14  *
     15  * You should have received a copy of the GNU General Public License along with
     16  * this program.  If not, see <http://www.gnu.org/licenses/>.
     17  */
     18 
     19 #include "qemu/osdep.h"
     20 #include "qapi/error.h"
     21 #include "qemu/log.h"
     22 #include "qemu/module.h"
     23 #include "qemu/error-report.h"
     24 #include "qemu/bswap.h"
     25 #include "exec/address-spaces.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 "hw/intc/riscv_imsic.h"
     31 #include "hw/irq.h"
     32 #include "target/riscv/cpu.h"
     33 #include "target/riscv/cpu_bits.h"
     34 #include "sysemu/sysemu.h"
     35 #include "migration/vmstate.h"
     36 
     37 #define IMSIC_MMIO_PAGE_LE             0x00
     38 #define IMSIC_MMIO_PAGE_BE             0x04
     39 
     40 #define IMSIC_MIN_ID                   ((IMSIC_EIPx_BITS * 2) - 1)
     41 #define IMSIC_MAX_ID                   (IMSIC_TOPEI_IID_MASK)
     42 
     43 #define IMSIC_EISTATE_PENDING          (1U << 0)
     44 #define IMSIC_EISTATE_ENABLED          (1U << 1)
     45 #define IMSIC_EISTATE_ENPEND           (IMSIC_EISTATE_ENABLED | \
     46                                         IMSIC_EISTATE_PENDING)
     47 
     48 static uint32_t riscv_imsic_topei(RISCVIMSICState *imsic, uint32_t page)
     49 {
     50     uint32_t i, max_irq, base;
     51 
     52     base = page * imsic->num_irqs;
     53     max_irq = (imsic->eithreshold[page] &&
     54                (imsic->eithreshold[page] <= imsic->num_irqs)) ?
     55                imsic->eithreshold[page] : imsic->num_irqs;
     56     for (i = 1; i < max_irq; i++) {
     57         if ((imsic->eistate[base + i] & IMSIC_EISTATE_ENPEND) ==
     58                 IMSIC_EISTATE_ENPEND) {
     59             return (i << IMSIC_TOPEI_IID_SHIFT) | i;
     60         }
     61     }
     62 
     63     return 0;
     64 }
     65 
     66 static void riscv_imsic_update(RISCVIMSICState *imsic, uint32_t page)
     67 {
     68     if (imsic->eidelivery[page] && riscv_imsic_topei(imsic, page)) {
     69         qemu_irq_raise(imsic->external_irqs[page]);
     70     } else {
     71         qemu_irq_lower(imsic->external_irqs[page]);
     72     }
     73 }
     74 
     75 static int riscv_imsic_eidelivery_rmw(RISCVIMSICState *imsic, uint32_t page,
     76                                       target_ulong *val,
     77                                       target_ulong new_val,
     78                                       target_ulong wr_mask)
     79 {
     80     target_ulong old_val = imsic->eidelivery[page];
     81 
     82     if (val) {
     83         *val = old_val;
     84     }
     85 
     86     wr_mask &= 0x1;
     87     imsic->eidelivery[page] = (old_val & ~wr_mask) | (new_val & wr_mask);
     88 
     89     riscv_imsic_update(imsic, page);
     90     return 0;
     91 }
     92 
     93 static int riscv_imsic_eithreshold_rmw(RISCVIMSICState *imsic, uint32_t page,
     94                                       target_ulong *val,
     95                                       target_ulong new_val,
     96                                       target_ulong wr_mask)
     97 {
     98     target_ulong old_val = imsic->eithreshold[page];
     99 
    100     if (val) {
    101         *val = old_val;
    102     }
    103 
    104     wr_mask &= IMSIC_MAX_ID;
    105     imsic->eithreshold[page] = (old_val & ~wr_mask) | (new_val & wr_mask);
    106 
    107     riscv_imsic_update(imsic, page);
    108     return 0;
    109 }
    110 
    111 static int riscv_imsic_topei_rmw(RISCVIMSICState *imsic, uint32_t page,
    112                                  target_ulong *val, target_ulong new_val,
    113                                  target_ulong wr_mask)
    114 {
    115     uint32_t base, topei = riscv_imsic_topei(imsic, page);
    116 
    117     /* Read pending and enabled interrupt with highest priority */
    118     if (val) {
    119         *val = topei;
    120     }
    121 
    122     /* Writes ignore value and clear top pending interrupt */
    123     if (topei && wr_mask) {
    124         topei >>= IMSIC_TOPEI_IID_SHIFT;
    125         base = page * imsic->num_irqs;
    126         if (topei) {
    127             imsic->eistate[base + topei] &= ~IMSIC_EISTATE_PENDING;
    128         }
    129 
    130         riscv_imsic_update(imsic, page);
    131     }
    132 
    133     return 0;
    134 }
    135 
    136 static int riscv_imsic_eix_rmw(RISCVIMSICState *imsic,
    137                                uint32_t xlen, uint32_t page,
    138                                uint32_t num, bool pend, target_ulong *val,
    139                                target_ulong new_val, target_ulong wr_mask)
    140 {
    141     uint32_t i, base;
    142     target_ulong mask;
    143     uint32_t state = (pend) ? IMSIC_EISTATE_PENDING : IMSIC_EISTATE_ENABLED;
    144 
    145     if (xlen != 32) {
    146         if (num & 0x1) {
    147             return -EINVAL;
    148         }
    149         num >>= 1;
    150     }
    151     if (num >= (imsic->num_irqs / xlen)) {
    152         return -EINVAL;
    153     }
    154 
    155     base = (page * imsic->num_irqs) + (num * xlen);
    156 
    157     if (val) {
    158         *val = 0;
    159         for (i = 0; i < xlen; i++) {
    160             mask = (target_ulong)1 << i;
    161             *val |= (imsic->eistate[base + i] & state) ? mask : 0;
    162         }
    163     }
    164 
    165     for (i = 0; i < xlen; i++) {
    166         /* Bit0 of eip0 and eie0 are read-only zero */
    167         if (!num && !i) {
    168             continue;
    169         }
    170 
    171         mask = (target_ulong)1 << i;
    172         if (wr_mask & mask) {
    173             if (new_val & mask) {
    174                 imsic->eistate[base + i] |= state;
    175             } else {
    176                 imsic->eistate[base + i] &= ~state;
    177             }
    178         }
    179     }
    180 
    181     riscv_imsic_update(imsic, page);
    182     return 0;
    183 }
    184 
    185 static int riscv_imsic_rmw(void *arg, target_ulong reg, target_ulong *val,
    186                            target_ulong new_val, target_ulong wr_mask)
    187 {
    188     RISCVIMSICState *imsic = arg;
    189     uint32_t isel, priv, virt, vgein, xlen, page;
    190 
    191     priv = AIA_IREG_PRIV(reg);
    192     virt = AIA_IREG_VIRT(reg);
    193     isel = AIA_IREG_ISEL(reg);
    194     vgein = AIA_IREG_VGEIN(reg);
    195     xlen = AIA_IREG_XLEN(reg);
    196 
    197     if (imsic->mmode) {
    198         if (priv == PRV_M && !virt) {
    199             page = 0;
    200         } else {
    201             goto err;
    202         }
    203     } else {
    204         if (priv == PRV_S) {
    205             if (virt) {
    206                 if (vgein && vgein < imsic->num_pages) {
    207                     page = vgein;
    208                 } else {
    209                     goto err;
    210                 }
    211             } else {
    212                 page = 0;
    213             }
    214         } else {
    215             goto err;
    216         }
    217     }
    218 
    219     switch (isel) {
    220     case ISELECT_IMSIC_EIDELIVERY:
    221         return riscv_imsic_eidelivery_rmw(imsic, page, val,
    222                                           new_val, wr_mask);
    223     case ISELECT_IMSIC_EITHRESHOLD:
    224         return riscv_imsic_eithreshold_rmw(imsic, page, val,
    225                                            new_val, wr_mask);
    226     case ISELECT_IMSIC_TOPEI:
    227         return riscv_imsic_topei_rmw(imsic, page, val, new_val, wr_mask);
    228     case ISELECT_IMSIC_EIP0 ... ISELECT_IMSIC_EIP63:
    229         return riscv_imsic_eix_rmw(imsic, xlen, page,
    230                                    isel - ISELECT_IMSIC_EIP0,
    231                                    true, val, new_val, wr_mask);
    232     case ISELECT_IMSIC_EIE0 ... ISELECT_IMSIC_EIE63:
    233         return riscv_imsic_eix_rmw(imsic, xlen, page,
    234                                    isel - ISELECT_IMSIC_EIE0,
    235                                    false, val, new_val, wr_mask);
    236     default:
    237         break;
    238     };
    239 
    240 err:
    241     qemu_log_mask(LOG_GUEST_ERROR,
    242                   "%s: Invalid register priv=%d virt=%d isel=%d vgein=%d\n",
    243                   __func__, priv, virt, isel, vgein);
    244     return -EINVAL;
    245 }
    246 
    247 static uint64_t riscv_imsic_read(void *opaque, hwaddr addr, unsigned size)
    248 {
    249     RISCVIMSICState *imsic = opaque;
    250 
    251     /* Reads must be 4 byte words */
    252     if ((addr & 0x3) != 0) {
    253         goto err;
    254     }
    255 
    256     /* Reads cannot be out of range */
    257     if (addr > IMSIC_MMIO_SIZE(imsic->num_pages)) {
    258         goto err;
    259     }
    260 
    261     return 0;
    262 
    263 err:
    264     qemu_log_mask(LOG_GUEST_ERROR,
    265                   "%s: Invalid register read 0x%" HWADDR_PRIx "\n",
    266                   __func__, addr);
    267     return 0;
    268 }
    269 
    270 static void riscv_imsic_write(void *opaque, hwaddr addr, uint64_t value,
    271         unsigned size)
    272 {
    273     RISCVIMSICState *imsic = opaque;
    274     uint32_t page;
    275 
    276     /* Writes must be 4 byte words */
    277     if ((addr & 0x3) != 0) {
    278         goto err;
    279     }
    280 
    281     /* Writes cannot be out of range */
    282     if (addr > IMSIC_MMIO_SIZE(imsic->num_pages)) {
    283         goto err;
    284     }
    285 
    286     /* Writes only supported for MSI little-endian registers */
    287     page = addr >> IMSIC_MMIO_PAGE_SHIFT;
    288     if ((addr & (IMSIC_MMIO_PAGE_SZ - 1)) == IMSIC_MMIO_PAGE_LE) {
    289         if (value && (value < imsic->num_irqs)) {
    290             imsic->eistate[(page * imsic->num_irqs) + value] |=
    291                                                     IMSIC_EISTATE_PENDING;
    292         }
    293     }
    294 
    295     /* Update CPU external interrupt status */
    296     riscv_imsic_update(imsic, page);
    297 
    298     return;
    299 
    300 err:
    301     qemu_log_mask(LOG_GUEST_ERROR,
    302                   "%s: Invalid register write 0x%" HWADDR_PRIx "\n",
    303                   __func__, addr);
    304 }
    305 
    306 static const MemoryRegionOps riscv_imsic_ops = {
    307     .read = riscv_imsic_read,
    308     .write = riscv_imsic_write,
    309     .endianness = DEVICE_LITTLE_ENDIAN,
    310     .valid = {
    311         .min_access_size = 4,
    312         .max_access_size = 4
    313     }
    314 };
    315 
    316 static void riscv_imsic_realize(DeviceState *dev, Error **errp)
    317 {
    318     RISCVIMSICState *imsic = RISCV_IMSIC(dev);
    319     RISCVCPU *rcpu = RISCV_CPU(qemu_get_cpu(imsic->hartid));
    320     CPUState *cpu = qemu_get_cpu(imsic->hartid);
    321     CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
    322 
    323     imsic->num_eistate = imsic->num_pages * imsic->num_irqs;
    324     imsic->eidelivery = g_new0(uint32_t, imsic->num_pages);
    325     imsic->eithreshold = g_new0(uint32_t, imsic->num_pages);
    326     imsic->eistate = g_new0(uint32_t, imsic->num_eistate);
    327 
    328     memory_region_init_io(&imsic->mmio, OBJECT(dev), &riscv_imsic_ops,
    329                           imsic, TYPE_RISCV_IMSIC,
    330                           IMSIC_MMIO_SIZE(imsic->num_pages));
    331     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &imsic->mmio);
    332 
    333     /* Claim the CPU interrupt to be triggered by this IMSIC */
    334     if (riscv_cpu_claim_interrupts(rcpu,
    335             (imsic->mmode) ? MIP_MEIP : MIP_SEIP) < 0) {
    336         error_setg(errp, "%s already claimed",
    337                    (imsic->mmode) ? "MEIP" : "SEIP");
    338         return;
    339     }
    340 
    341     /* Create output IRQ lines */
    342     imsic->external_irqs = g_malloc(sizeof(qemu_irq) * imsic->num_pages);
    343     qdev_init_gpio_out(dev, imsic->external_irqs, imsic->num_pages);
    344 
    345     /* Force select AIA feature and setup CSR read-modify-write callback */
    346     if (env) {
    347         if (!imsic->mmode) {
    348             rcpu->cfg.ext_ssaia = true;
    349             riscv_cpu_set_geilen(env, imsic->num_pages - 1);
    350         } else {
    351             rcpu->cfg.ext_smaia = true;
    352         }
    353         riscv_cpu_set_aia_ireg_rmw_fn(env, (imsic->mmode) ? PRV_M : PRV_S,
    354                                       riscv_imsic_rmw, imsic);
    355     }
    356 
    357     msi_nonbroken = true;
    358 }
    359 
    360 static Property riscv_imsic_properties[] = {
    361     DEFINE_PROP_BOOL("mmode", RISCVIMSICState, mmode, 0),
    362     DEFINE_PROP_UINT32("hartid", RISCVIMSICState, hartid, 0),
    363     DEFINE_PROP_UINT32("num-pages", RISCVIMSICState, num_pages, 0),
    364     DEFINE_PROP_UINT32("num-irqs", RISCVIMSICState, num_irqs, 0),
    365     DEFINE_PROP_END_OF_LIST(),
    366 };
    367 
    368 static const VMStateDescription vmstate_riscv_imsic = {
    369     .name = "riscv_imsic",
    370     .version_id = 1,
    371     .minimum_version_id = 1,
    372     .fields = (VMStateField[]) {
    373             VMSTATE_VARRAY_UINT32(eidelivery, RISCVIMSICState,
    374                                   num_pages, 0,
    375                                   vmstate_info_uint32, uint32_t),
    376             VMSTATE_VARRAY_UINT32(eithreshold, RISCVIMSICState,
    377                                   num_pages, 0,
    378                                   vmstate_info_uint32, uint32_t),
    379             VMSTATE_VARRAY_UINT32(eistate, RISCVIMSICState,
    380                                   num_eistate, 0,
    381                                   vmstate_info_uint32, uint32_t),
    382             VMSTATE_END_OF_LIST()
    383         }
    384 };
    385 
    386 static void riscv_imsic_class_init(ObjectClass *klass, void *data)
    387 {
    388     DeviceClass *dc = DEVICE_CLASS(klass);
    389 
    390     device_class_set_props(dc, riscv_imsic_properties);
    391     dc->realize = riscv_imsic_realize;
    392     dc->vmsd = &vmstate_riscv_imsic;
    393 }
    394 
    395 static const TypeInfo riscv_imsic_info = {
    396     .name          = TYPE_RISCV_IMSIC,
    397     .parent        = TYPE_SYS_BUS_DEVICE,
    398     .instance_size = sizeof(RISCVIMSICState),
    399     .class_init    = riscv_imsic_class_init,
    400 };
    401 
    402 static void riscv_imsic_register_types(void)
    403 {
    404     type_register_static(&riscv_imsic_info);
    405 }
    406 
    407 type_init(riscv_imsic_register_types)
    408 
    409 /*
    410  * Create IMSIC device.
    411  */
    412 DeviceState *riscv_imsic_create(hwaddr addr, uint32_t hartid, bool mmode,
    413                                 uint32_t num_pages, uint32_t num_ids)
    414 {
    415     DeviceState *dev = qdev_new(TYPE_RISCV_IMSIC);
    416     CPUState *cpu = qemu_get_cpu(hartid);
    417     uint32_t i;
    418 
    419     assert(!(addr & (IMSIC_MMIO_PAGE_SZ - 1)));
    420     if (mmode) {
    421         assert(num_pages == 1);
    422     } else {
    423         assert(num_pages >= 1 && num_pages <= (IRQ_LOCAL_GUEST_MAX + 1));
    424     }
    425     assert(IMSIC_MIN_ID <= num_ids);
    426     assert(num_ids <= IMSIC_MAX_ID);
    427     assert((num_ids & IMSIC_MIN_ID) == IMSIC_MIN_ID);
    428 
    429     qdev_prop_set_bit(dev, "mmode", mmode);
    430     qdev_prop_set_uint32(dev, "hartid", hartid);
    431     qdev_prop_set_uint32(dev, "num-pages", num_pages);
    432     qdev_prop_set_uint32(dev, "num-irqs", num_ids + 1);
    433 
    434     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
    435     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
    436 
    437     for (i = 0; i < num_pages; i++) {
    438         if (!i) {
    439             qdev_connect_gpio_out_named(dev, NULL, i,
    440                                         qdev_get_gpio_in(DEVICE(cpu),
    441                                             (mmode) ? IRQ_M_EXT : IRQ_S_EXT));
    442         } else {
    443             qdev_connect_gpio_out_named(dev, NULL, i,
    444                                         qdev_get_gpio_in(DEVICE(cpu),
    445                                             IRQ_LOCAL_MAX + i - 1));
    446         }
    447     }
    448 
    449     return dev;
    450 }