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