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 (value <= plic->num_priorities) { 184 plic->source_priority[irq] = value; 185 sifive_plic_update(plic); 186 } 187 } else if (addr_between(addr, plic->pending_base, 188 plic->num_sources >> 3)) { 189 qemu_log_mask(LOG_GUEST_ERROR, 190 "%s: invalid pending write: 0x%" HWADDR_PRIx "", 191 __func__, addr); 192 } else if (addr_between(addr, plic->enable_base, 193 plic->num_addrs * plic->enable_stride)) { 194 uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride; 195 uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2; 196 197 if (wordid < plic->bitfield_words) { 198 plic->enable[addrid * plic->bitfield_words + wordid] = value; 199 } else { 200 qemu_log_mask(LOG_GUEST_ERROR, 201 "%s: Invalid enable write 0x%" HWADDR_PRIx "\n", 202 __func__, addr); 203 } 204 } else if (addr_between(addr, plic->context_base, 205 plic->num_addrs * plic->context_stride)) { 206 uint32_t addrid = (addr - plic->context_base) / plic->context_stride; 207 uint32_t contextid = (addr & (plic->context_stride - 1)); 208 209 if (contextid == 0) { 210 if (value <= plic->num_priorities) { 211 plic->target_priority[addrid] = value; 212 sifive_plic_update(plic); 213 } 214 } else if (contextid == 4) { 215 if (value < plic->num_sources) { 216 sifive_plic_set_claimed(plic, value, false); 217 sifive_plic_update(plic); 218 } 219 } else { 220 qemu_log_mask(LOG_GUEST_ERROR, 221 "%s: Invalid context write 0x%" HWADDR_PRIx "\n", 222 __func__, addr); 223 } 224 } else { 225 qemu_log_mask(LOG_GUEST_ERROR, 226 "%s: Invalid register write 0x%" HWADDR_PRIx "\n", 227 __func__, addr); 228 } 229 } 230 231 static const MemoryRegionOps sifive_plic_ops = { 232 .read = sifive_plic_read, 233 .write = sifive_plic_write, 234 .endianness = DEVICE_LITTLE_ENDIAN, 235 .valid = { 236 .min_access_size = 4, 237 .max_access_size = 4 238 } 239 }; 240 241 static void sifive_plic_reset(DeviceState *dev) 242 { 243 SiFivePLICState *s = SIFIVE_PLIC(dev); 244 int i; 245 246 memset(s->source_priority, 0, sizeof(uint32_t) * s->num_sources); 247 memset(s->target_priority, 0, sizeof(uint32_t) * s->num_addrs); 248 memset(s->pending, 0, sizeof(uint32_t) * s->bitfield_words); 249 memset(s->claimed, 0, sizeof(uint32_t) * s->bitfield_words); 250 memset(s->enable, 0, sizeof(uint32_t) * s->num_enables); 251 252 for (i = 0; i < s->num_harts; i++) { 253 qemu_set_irq(s->m_external_irqs[i], 0); 254 qemu_set_irq(s->s_external_irqs[i], 0); 255 } 256 } 257 258 /* 259 * parse PLIC hart/mode address offset config 260 * 261 * "M" 1 hart with M mode 262 * "MS,MS" 2 harts, 0-1 with M and S mode 263 * "M,MS,MS,MS,MS" 5 harts, 0 with M mode, 1-5 with M and S mode 264 */ 265 static void parse_hart_config(SiFivePLICState *plic) 266 { 267 int addrid, hartid, modes; 268 const char *p; 269 char c; 270 271 /* count and validate hart/mode combinations */ 272 addrid = 0, hartid = 0, modes = 0; 273 p = plic->hart_config; 274 while ((c = *p++)) { 275 if (c == ',') { 276 addrid += ctpop8(modes); 277 modes = 0; 278 hartid++; 279 } else { 280 int m = 1 << char_to_mode(c); 281 if (modes == (modes | m)) { 282 error_report("plic: duplicate mode '%c' in config: %s", 283 c, plic->hart_config); 284 exit(1); 285 } 286 modes |= m; 287 } 288 } 289 if (modes) { 290 addrid += ctpop8(modes); 291 } 292 hartid++; 293 294 plic->num_addrs = addrid; 295 plic->num_harts = hartid; 296 297 /* store hart/mode combinations */ 298 plic->addr_config = g_new(PLICAddr, plic->num_addrs); 299 addrid = 0, hartid = plic->hartid_base; 300 p = plic->hart_config; 301 while ((c = *p++)) { 302 if (c == ',') { 303 hartid++; 304 } else { 305 plic->addr_config[addrid].addrid = addrid; 306 plic->addr_config[addrid].hartid = hartid; 307 plic->addr_config[addrid].mode = char_to_mode(c); 308 addrid++; 309 } 310 } 311 } 312 313 static void sifive_plic_irq_request(void *opaque, int irq, int level) 314 { 315 SiFivePLICState *s = opaque; 316 317 sifive_plic_set_pending(s, irq, level > 0); 318 sifive_plic_update(s); 319 } 320 321 static void sifive_plic_realize(DeviceState *dev, Error **errp) 322 { 323 SiFivePLICState *s = SIFIVE_PLIC(dev); 324 int i; 325 326 memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_plic_ops, s, 327 TYPE_SIFIVE_PLIC, s->aperture_size); 328 sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); 329 330 parse_hart_config(s); 331 332 s->bitfield_words = (s->num_sources + 31) >> 5; 333 s->num_enables = s->bitfield_words * s->num_addrs; 334 s->source_priority = g_new0(uint32_t, s->num_sources); 335 s->target_priority = g_new(uint32_t, s->num_addrs); 336 s->pending = g_new0(uint32_t, s->bitfield_words); 337 s->claimed = g_new0(uint32_t, s->bitfield_words); 338 s->enable = g_new0(uint32_t, s->num_enables); 339 340 qdev_init_gpio_in(dev, sifive_plic_irq_request, s->num_sources); 341 342 s->s_external_irqs = g_malloc(sizeof(qemu_irq) * s->num_harts); 343 qdev_init_gpio_out(dev, s->s_external_irqs, s->num_harts); 344 345 s->m_external_irqs = g_malloc(sizeof(qemu_irq) * s->num_harts); 346 qdev_init_gpio_out(dev, s->m_external_irqs, s->num_harts); 347 348 /* We can't allow the supervisor to control SEIP as this would allow the 349 * supervisor to clear a pending external interrupt which will result in 350 * lost a interrupt in the case a PLIC is attached. The SEIP bit must be 351 * hardware controlled when a PLIC is attached. 352 */ 353 for (i = 0; i < s->num_harts; i++) { 354 RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(s->hartid_base + i)); 355 if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) { 356 error_report("SEIP already claimed"); 357 exit(1); 358 } 359 } 360 361 msi_nonbroken = true; 362 } 363 364 static const VMStateDescription vmstate_sifive_plic = { 365 .name = "riscv_sifive_plic", 366 .version_id = 1, 367 .minimum_version_id = 1, 368 .fields = (VMStateField[]) { 369 VMSTATE_VARRAY_UINT32(source_priority, SiFivePLICState, 370 num_sources, 0, 371 vmstate_info_uint32, uint32_t), 372 VMSTATE_VARRAY_UINT32(target_priority, SiFivePLICState, 373 num_addrs, 0, 374 vmstate_info_uint32, uint32_t), 375 VMSTATE_VARRAY_UINT32(pending, SiFivePLICState, bitfield_words, 0, 376 vmstate_info_uint32, uint32_t), 377 VMSTATE_VARRAY_UINT32(claimed, SiFivePLICState, bitfield_words, 0, 378 vmstate_info_uint32, uint32_t), 379 VMSTATE_VARRAY_UINT32(enable, SiFivePLICState, num_enables, 0, 380 vmstate_info_uint32, uint32_t), 381 VMSTATE_END_OF_LIST() 382 } 383 }; 384 385 static Property sifive_plic_properties[] = { 386 DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config), 387 DEFINE_PROP_UINT32("hartid-base", SiFivePLICState, hartid_base, 0), 388 DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0), 389 DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0), 390 DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0), 391 DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0), 392 DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0), 393 DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0), 394 DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0), 395 DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0), 396 DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0), 397 DEFINE_PROP_END_OF_LIST(), 398 }; 399 400 static void sifive_plic_class_init(ObjectClass *klass, void *data) 401 { 402 DeviceClass *dc = DEVICE_CLASS(klass); 403 404 dc->reset = sifive_plic_reset; 405 device_class_set_props(dc, sifive_plic_properties); 406 dc->realize = sifive_plic_realize; 407 dc->vmsd = &vmstate_sifive_plic; 408 } 409 410 static const TypeInfo sifive_plic_info = { 411 .name = TYPE_SIFIVE_PLIC, 412 .parent = TYPE_SYS_BUS_DEVICE, 413 .instance_size = sizeof(SiFivePLICState), 414 .class_init = sifive_plic_class_init, 415 }; 416 417 static void sifive_plic_register_types(void) 418 { 419 type_register_static(&sifive_plic_info); 420 } 421 422 type_init(sifive_plic_register_types) 423 424 /* 425 * Create PLIC device. 426 */ 427 DeviceState *sifive_plic_create(hwaddr addr, char *hart_config, 428 uint32_t num_harts, 429 uint32_t hartid_base, uint32_t num_sources, 430 uint32_t num_priorities, uint32_t priority_base, 431 uint32_t pending_base, uint32_t enable_base, 432 uint32_t enable_stride, uint32_t context_base, 433 uint32_t context_stride, uint32_t aperture_size) 434 { 435 DeviceState *dev = qdev_new(TYPE_SIFIVE_PLIC); 436 int i; 437 SiFivePLICState *plic; 438 439 assert(enable_stride == (enable_stride & -enable_stride)); 440 assert(context_stride == (context_stride & -context_stride)); 441 qdev_prop_set_string(dev, "hart-config", hart_config); 442 qdev_prop_set_uint32(dev, "hartid-base", hartid_base); 443 qdev_prop_set_uint32(dev, "num-sources", num_sources); 444 qdev_prop_set_uint32(dev, "num-priorities", num_priorities); 445 qdev_prop_set_uint32(dev, "priority-base", priority_base); 446 qdev_prop_set_uint32(dev, "pending-base", pending_base); 447 qdev_prop_set_uint32(dev, "enable-base", enable_base); 448 qdev_prop_set_uint32(dev, "enable-stride", enable_stride); 449 qdev_prop_set_uint32(dev, "context-base", context_base); 450 qdev_prop_set_uint32(dev, "context-stride", context_stride); 451 qdev_prop_set_uint32(dev, "aperture-size", aperture_size); 452 sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); 453 sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); 454 455 plic = SIFIVE_PLIC(dev); 456 457 for (i = 0; i < plic->num_addrs; i++) { 458 int cpu_num = plic->addr_config[i].hartid; 459 CPUState *cpu = qemu_get_cpu(cpu_num); 460 461 if (plic->addr_config[i].mode == PLICMode_M) { 462 qdev_connect_gpio_out(dev, num_harts - plic->hartid_base + cpu_num, 463 qdev_get_gpio_in(DEVICE(cpu), IRQ_M_EXT)); 464 } 465 if (plic->addr_config[i].mode == PLICMode_S) { 466 qdev_connect_gpio_out(dev, cpu_num, 467 qdev_get_gpio_in(DEVICE(cpu), IRQ_S_EXT)); 468 } 469 } 470 471 return dev; 472 } 473