qemu

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

pnv_phb3_msi.c (9127B)


      1 /*
      2  * QEMU PowerPC PowerNV (POWER8) PHB3 model
      3  *
      4  * Copyright (c) 2014-2020, IBM Corporation.
      5  *
      6  * This code is licensed under the GPL version 2 or later. See the
      7  * COPYING file in the top-level directory.
      8  */
      9 #include "qemu/osdep.h"
     10 #include "qemu/log.h"
     11 #include "qapi/error.h"
     12 #include "hw/pci-host/pnv_phb3_regs.h"
     13 #include "hw/pci-host/pnv_phb3.h"
     14 #include "hw/ppc/pnv.h"
     15 #include "hw/pci/msi.h"
     16 #include "monitor/monitor.h"
     17 #include "hw/irq.h"
     18 #include "hw/qdev-properties.h"
     19 #include "sysemu/reset.h"
     20 
     21 static uint64_t phb3_msi_ive_addr(PnvPHB3 *phb, int srcno)
     22 {
     23     uint64_t ivtbar = phb->regs[PHB_IVT_BAR >> 3];
     24     uint64_t phbctl = phb->regs[PHB_CONTROL >> 3];
     25 
     26     if (!(ivtbar & PHB_IVT_BAR_ENABLE)) {
     27         qemu_log_mask(LOG_GUEST_ERROR, "Failed access to disable IVT BAR !");
     28         return 0;
     29     }
     30 
     31     if (srcno >= (ivtbar & PHB_IVT_LENGTH_MASK)) {
     32         qemu_log_mask(LOG_GUEST_ERROR, "MSI out of bounds (%d vs  0x%"PRIx64")",
     33                       srcno, (uint64_t) (ivtbar & PHB_IVT_LENGTH_MASK));
     34         return 0;
     35     }
     36 
     37     ivtbar &= PHB_IVT_BASE_ADDRESS_MASK;
     38 
     39     if (phbctl & PHB_CTRL_IVE_128_BYTES) {
     40         return ivtbar + 128 * srcno;
     41     } else {
     42         return ivtbar + 16 * srcno;
     43     }
     44 }
     45 
     46 static bool phb3_msi_read_ive(PnvPHB3 *phb, int srcno, uint64_t *out_ive)
     47 {
     48     uint64_t ive_addr, ive;
     49 
     50     ive_addr = phb3_msi_ive_addr(phb, srcno);
     51     if (!ive_addr) {
     52         return false;
     53     }
     54 
     55     if (dma_memory_read(&address_space_memory, ive_addr,
     56                         &ive, sizeof(ive), MEMTXATTRS_UNSPECIFIED)) {
     57         qemu_log_mask(LOG_GUEST_ERROR, "Failed to read IVE at 0x%" PRIx64,
     58                       ive_addr);
     59         return false;
     60     }
     61     *out_ive = be64_to_cpu(ive);
     62 
     63     return true;
     64 }
     65 
     66 static void phb3_msi_set_p(Phb3MsiState *msi, int srcno, uint8_t gen)
     67 {
     68     uint64_t ive_addr;
     69     uint8_t p = 0x01 | (gen << 1);
     70 
     71     ive_addr = phb3_msi_ive_addr(msi->phb, srcno);
     72     if (!ive_addr) {
     73         return;
     74     }
     75 
     76     if (dma_memory_write(&address_space_memory, ive_addr + 4,
     77                          &p, 1, MEMTXATTRS_UNSPECIFIED)) {
     78         qemu_log_mask(LOG_GUEST_ERROR,
     79                       "Failed to write IVE (set P) at 0x%" PRIx64, ive_addr);
     80     }
     81 }
     82 
     83 static void phb3_msi_set_q(Phb3MsiState *msi, int srcno)
     84 {
     85     uint64_t ive_addr;
     86     uint8_t q = 0x01;
     87 
     88     ive_addr = phb3_msi_ive_addr(msi->phb, srcno);
     89     if (!ive_addr) {
     90         return;
     91     }
     92 
     93     if (dma_memory_write(&address_space_memory, ive_addr + 5,
     94                          &q, 1, MEMTXATTRS_UNSPECIFIED)) {
     95         qemu_log_mask(LOG_GUEST_ERROR,
     96                       "Failed to write IVE (set Q) at 0x%" PRIx64, ive_addr);
     97     }
     98 }
     99 
    100 static void phb3_msi_try_send(Phb3MsiState *msi, int srcno, bool force)
    101 {
    102     ICSState *ics = ICS(msi);
    103     uint64_t ive;
    104     uint64_t server, prio, pq, gen;
    105 
    106     if (!phb3_msi_read_ive(msi->phb, srcno, &ive)) {
    107         return;
    108     }
    109 
    110     server = GETFIELD(IODA2_IVT_SERVER, ive);
    111     prio = GETFIELD(IODA2_IVT_PRIORITY, ive);
    112     if (!force) {
    113         pq = GETFIELD(IODA2_IVT_Q, ive) | (GETFIELD(IODA2_IVT_P, ive) << 1);
    114     } else {
    115         pq = 0;
    116     }
    117     gen = GETFIELD(IODA2_IVT_GEN, ive);
    118 
    119     /*
    120      * The low order 2 bits are the link pointer (Type II interrupts).
    121      * Shift back to get a valid IRQ server.
    122      */
    123     server >>= 2;
    124 
    125     switch (pq) {
    126     case 0: /* 00 */
    127         if (prio == 0xff) {
    128             /* Masked, set Q */
    129             phb3_msi_set_q(msi, srcno);
    130         } else {
    131             /* Enabled, set P and send */
    132             phb3_msi_set_p(msi, srcno, gen);
    133             icp_irq(ics, server, srcno + ics->offset, prio);
    134         }
    135         break;
    136     case 2: /* 10 */
    137         /* Already pending, set Q */
    138         phb3_msi_set_q(msi, srcno);
    139         break;
    140     case 1: /* 01 */
    141     case 3: /* 11 */
    142     default:
    143         /* Just drop stuff if Q already set */
    144         break;
    145     }
    146 }
    147 
    148 static void phb3_msi_set_irq(void *opaque, int srcno, int val)
    149 {
    150     Phb3MsiState *msi = PHB3_MSI(opaque);
    151 
    152     if (val) {
    153         phb3_msi_try_send(msi, srcno, false);
    154     }
    155 }
    156 
    157 
    158 void pnv_phb3_msi_send(Phb3MsiState *msi, uint64_t addr, uint16_t data,
    159                        int32_t dev_pe)
    160 {
    161     ICSState *ics = ICS(msi);
    162     uint64_t ive;
    163     uint16_t pe;
    164     uint32_t src = ((addr >> 4) & 0xffff) | (data & 0x1f);
    165 
    166     if (src >= ics->nr_irqs) {
    167         qemu_log_mask(LOG_GUEST_ERROR, "MSI %d out of bounds", src);
    168         return;
    169     }
    170     if (dev_pe >= 0) {
    171         if (!phb3_msi_read_ive(msi->phb, src, &ive)) {
    172             return;
    173         }
    174         pe = GETFIELD(IODA2_IVT_PE, ive);
    175         if (pe != dev_pe) {
    176             qemu_log_mask(LOG_GUEST_ERROR,
    177                           "MSI %d send by PE#%d but assigned to PE#%d",
    178                           src, dev_pe, pe);
    179             return;
    180         }
    181     }
    182     qemu_irq_pulse(msi->qirqs[src]);
    183 }
    184 
    185 void pnv_phb3_msi_ffi(Phb3MsiState *msi, uint64_t val)
    186 {
    187     /* Emit interrupt */
    188     pnv_phb3_msi_send(msi, val, 0, -1);
    189 
    190     /* Clear FFI lock */
    191     msi->phb->regs[PHB_FFI_LOCK >> 3] = 0;
    192 }
    193 
    194 static void phb3_msi_reject(ICSState *ics, uint32_t nr)
    195 {
    196     Phb3MsiState *msi = PHB3_MSI(ics);
    197     unsigned int srcno = nr - ics->offset;
    198     unsigned int idx = srcno >> 6;
    199     unsigned int bit = 1ull << (srcno & 0x3f);
    200 
    201     assert(srcno < PHB3_MAX_MSI);
    202 
    203     msi->rba[idx] |= bit;
    204     msi->rba_sum |= (1u << idx);
    205 }
    206 
    207 static void phb3_msi_resend(ICSState *ics)
    208 {
    209     Phb3MsiState *msi = PHB3_MSI(ics);
    210     unsigned int i, j;
    211 
    212     if (msi->rba_sum == 0) {
    213         return;
    214     }
    215 
    216     for (i = 0; i < 32; i++) {
    217         if ((msi->rba_sum & (1u << i)) == 0) {
    218             continue;
    219         }
    220         msi->rba_sum &= ~(1u << i);
    221         for (j = 0; j < 64; j++) {
    222             if ((msi->rba[i] & (1ull << j)) == 0) {
    223                 continue;
    224             }
    225             msi->rba[i] &= ~(1ull << j);
    226             phb3_msi_try_send(msi, i * 64 + j, true);
    227         }
    228     }
    229 }
    230 
    231 static void phb3_msi_reset(DeviceState *dev)
    232 {
    233     Phb3MsiState *msi = PHB3_MSI(dev);
    234     ICSStateClass *icsc = ICS_GET_CLASS(dev);
    235 
    236     icsc->parent_reset(dev);
    237 
    238     memset(msi->rba, 0, sizeof(msi->rba));
    239     msi->rba_sum = 0;
    240 }
    241 
    242 static void phb3_msi_reset_handler(void *dev)
    243 {
    244     phb3_msi_reset(dev);
    245 }
    246 
    247 void pnv_phb3_msi_update_config(Phb3MsiState *msi, uint32_t base,
    248                                 uint32_t count)
    249 {
    250     ICSState *ics = ICS(msi);
    251 
    252     if (count > PHB3_MAX_MSI) {
    253         count = PHB3_MAX_MSI;
    254     }
    255     ics->nr_irqs = count;
    256     ics->offset = base;
    257 }
    258 
    259 static void phb3_msi_realize(DeviceState *dev, Error **errp)
    260 {
    261     Phb3MsiState *msi = PHB3_MSI(dev);
    262     ICSState *ics = ICS(msi);
    263     ICSStateClass *icsc = ICS_GET_CLASS(ics);
    264     Error *local_err = NULL;
    265 
    266     assert(msi->phb);
    267 
    268     icsc->parent_realize(dev, &local_err);
    269     if (local_err) {
    270         error_propagate(errp, local_err);
    271         return;
    272     }
    273 
    274     msi->qirqs = qemu_allocate_irqs(phb3_msi_set_irq, msi, ics->nr_irqs);
    275 
    276     qemu_register_reset(phb3_msi_reset_handler, dev);
    277 }
    278 
    279 static void phb3_msi_instance_init(Object *obj)
    280 {
    281     Phb3MsiState *msi = PHB3_MSI(obj);
    282     ICSState *ics = ICS(obj);
    283 
    284     object_property_add_link(obj, "phb", TYPE_PNV_PHB3,
    285                              (Object **)&msi->phb,
    286                              object_property_allow_set_link,
    287                              OBJ_PROP_LINK_STRONG);
    288 
    289     /* Will be overriden later */
    290     ics->offset = 0;
    291 }
    292 
    293 static void phb3_msi_class_init(ObjectClass *klass, void *data)
    294 {
    295     DeviceClass *dc = DEVICE_CLASS(klass);
    296     ICSStateClass *isc = ICS_CLASS(klass);
    297 
    298     device_class_set_parent_realize(dc, phb3_msi_realize,
    299                                     &isc->parent_realize);
    300     device_class_set_parent_reset(dc, phb3_msi_reset,
    301                                   &isc->parent_reset);
    302 
    303     isc->reject = phb3_msi_reject;
    304     isc->resend = phb3_msi_resend;
    305 }
    306 
    307 static const TypeInfo phb3_msi_info = {
    308     .name = TYPE_PHB3_MSI,
    309     .parent = TYPE_ICS,
    310     .instance_size = sizeof(Phb3MsiState),
    311     .class_init = phb3_msi_class_init,
    312     .class_size = sizeof(ICSStateClass),
    313     .instance_init = phb3_msi_instance_init,
    314 };
    315 
    316 static void pnv_phb3_msi_register_types(void)
    317 {
    318     type_register_static(&phb3_msi_info);
    319 }
    320 
    321 type_init(pnv_phb3_msi_register_types);
    322 
    323 void pnv_phb3_msi_pic_print_info(Phb3MsiState *msi, Monitor *mon)
    324 {
    325     ICSState *ics = ICS(msi);
    326     int i;
    327 
    328     monitor_printf(mon, "ICS %4x..%4x %p\n",
    329                    ics->offset, ics->offset + ics->nr_irqs - 1, ics);
    330 
    331     for (i = 0; i < ics->nr_irqs; i++) {
    332         uint64_t ive;
    333 
    334         if (!phb3_msi_read_ive(msi->phb, i, &ive)) {
    335             return;
    336         }
    337 
    338         if (GETFIELD(IODA2_IVT_PRIORITY, ive) == 0xff) {
    339             continue;
    340         }
    341 
    342         monitor_printf(mon, "  %4x %c%c server=%04x prio=%02x gen=%d\n",
    343                        ics->offset + i,
    344                        GETFIELD(IODA2_IVT_P, ive) ? 'P' : '-',
    345                        GETFIELD(IODA2_IVT_Q, ive) ? 'Q' : '-',
    346                        (uint32_t) GETFIELD(IODA2_IVT_SERVER, ive) >> 2,
    347                        (uint32_t) GETFIELD(IODA2_IVT_PRIORITY, ive),
    348                        (uint32_t) GETFIELD(IODA2_IVT_GEN, ive));
    349     }
    350 }