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