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 "qemu/log.h" 23 #include "qemu/error-report.h" 24 #include "hw/sysbus.h" 25 #include "target/riscv/cpu.h" 26 #include "hw/riscv/sifive_plic.h" 27 28 #define RISCV_DEBUG_PLIC 0 29 30 static PLICMode char_to_mode(char c) 31 { 32 switch (c) { 33 case 'U': return PLICMode_U; 34 case 'S': return PLICMode_S; 35 case 'H': return PLICMode_H; 36 case 'M': return PLICMode_M; 37 default: 38 error_report("plic: invalid mode '%c'", c); 39 exit(1); 40 } 41 } 42 43 static char mode_to_char(PLICMode m) 44 { 45 switch (m) { 46 case PLICMode_U: return 'U'; 47 case PLICMode_S: return 'S'; 48 case PLICMode_H: return 'H'; 49 case PLICMode_M: return 'M'; 50 default: return '?'; 51 } 52 } 53 54 static void sifive_plic_print_state(SiFivePLICState *plic) 55 { 56 int i; 57 int addrid; 58 59 /* pending */ 60 qemu_log("pending : "); 61 for (i = plic->bitfield_words - 1; i >= 0; i--) { 62 qemu_log("%08x", plic->pending[i]); 63 } 64 qemu_log("\n"); 65 66 /* pending */ 67 qemu_log("claimed : "); 68 for (i = plic->bitfield_words - 1; i >= 0; i--) { 69 qemu_log("%08x", plic->claimed[i]); 70 } 71 qemu_log("\n"); 72 73 for (addrid = 0; addrid < plic->num_addrs; addrid++) { 74 qemu_log("hart%d-%c enable: ", 75 plic->addr_config[addrid].hartid, 76 mode_to_char(plic->addr_config[addrid].mode)); 77 for (i = plic->bitfield_words - 1; i >= 0; i--) { 78 qemu_log("%08x", plic->enable[addrid * plic->bitfield_words + i]); 79 } 80 qemu_log("\n"); 81 } 82 } 83 84 static uint32_t atomic_set_masked(uint32_t *a, uint32_t mask, uint32_t value) 85 { 86 uint32_t old, new, cmp = atomic_read(a); 87 88 do { 89 old = cmp; 90 new = (old & ~mask) | (value & mask); 91 cmp = atomic_cmpxchg(a, old, new); 92 } while (old != cmp); 93 94 return old; 95 } 96 97 static void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool level) 98 { 99 atomic_set_masked(&plic->pending[irq >> 5], 1 << (irq & 31), -!!level); 100 } 101 102 static void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool level) 103 { 104 atomic_set_masked(&plic->claimed[irq >> 5], 1 << (irq & 31), -!!level); 105 } 106 107 static int sifive_plic_irqs_pending(SiFivePLICState *plic, uint32_t addrid) 108 { 109 int i, j; 110 for (i = 0; i < plic->bitfield_words; i++) { 111 uint32_t pending_enabled_not_claimed = 112 (plic->pending[i] & ~plic->claimed[i]) & 113 plic->enable[addrid * plic->bitfield_words + i]; 114 if (!pending_enabled_not_claimed) { 115 continue; 116 } 117 for (j = 0; j < 32; j++) { 118 int irq = (i << 5) + j; 119 uint32_t prio = plic->source_priority[irq]; 120 int enabled = pending_enabled_not_claimed & (1 << j); 121 if (enabled && prio > plic->target_priority[addrid]) { 122 return 1; 123 } 124 } 125 } 126 return 0; 127 } 128 129 static void sifive_plic_update(SiFivePLICState *plic) 130 { 131 int addrid; 132 133 /* raise irq on harts where this irq is enabled */ 134 for (addrid = 0; addrid < plic->num_addrs; addrid++) { 135 uint32_t hartid = plic->addr_config[addrid].hartid; 136 PLICMode mode = plic->addr_config[addrid].mode; 137 CPUState *cpu = qemu_get_cpu(hartid); 138 CPURISCVState *env = cpu ? cpu->env_ptr : NULL; 139 if (!env) { 140 continue; 141 } 142 int level = sifive_plic_irqs_pending(plic, addrid); 143 switch (mode) { 144 case PLICMode_M: 145 riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MEIP, level); 146 break; 147 case PLICMode_S: 148 riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_SEIP, level); 149 break; 150 default: 151 break; 152 } 153 } 154 155 if (RISCV_DEBUG_PLIC) { 156 sifive_plic_print_state(plic); 157 } 158 } 159 160 void sifive_plic_raise_irq(SiFivePLICState *plic, uint32_t irq) 161 { 162 sifive_plic_set_pending(plic, irq, true); 163 sifive_plic_update(plic); 164 } 165 166 void sifive_plic_lower_irq(SiFivePLICState *plic, uint32_t irq) 167 { 168 sifive_plic_set_pending(plic, irq, false); 169 sifive_plic_update(plic); 170 } 171 172 static uint32_t sifive_plic_claim(SiFivePLICState *plic, uint32_t addrid) 173 { 174 int i, j; 175 for (i = 0; i < plic->bitfield_words; i++) { 176 uint32_t pending_enabled_not_claimed = 177 (plic->pending[i] & ~plic->claimed[i]) & 178 plic->enable[addrid * plic->bitfield_words + i]; 179 if (!pending_enabled_not_claimed) { 180 continue; 181 } 182 for (j = 0; j < 32; j++) { 183 int irq = (i << 5) + j; 184 uint32_t prio = plic->source_priority[irq]; 185 int enabled = pending_enabled_not_claimed & (1 << j); 186 if (enabled && prio > plic->target_priority[addrid]) { 187 sifive_plic_set_pending(plic, irq, false); 188 sifive_plic_set_claimed(plic, irq, true); 189 return irq; 190 } 191 } 192 } 193 return 0; 194 } 195 196 static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size) 197 { 198 SiFivePLICState *plic = opaque; 199 200 /* writes must be 4 byte words */ 201 if ((addr & 0x3) != 0) { 202 goto err; 203 } 204 205 if (addr >= plic->priority_base && /* 4 bytes per source */ 206 addr < plic->priority_base + (plic->num_sources << 2)) 207 { 208 uint32_t irq = (addr - plic->priority_base) >> 2; 209 if (RISCV_DEBUG_PLIC) { 210 qemu_log("plic: read priority: irq=%d priority=%d\n", 211 irq, plic->source_priority[irq]); 212 } 213 return plic->source_priority[irq]; 214 } else if (addr >= plic->pending_base && /* 1 bit per source */ 215 addr < plic->pending_base + (plic->num_sources >> 3)) 216 { 217 uint32_t word = (addr - plic->priority_base) >> 2; 218 if (RISCV_DEBUG_PLIC) { 219 qemu_log("plic: read pending: word=%d value=%d\n", 220 word, plic->pending[word]); 221 } 222 return plic->pending[word]; 223 } else if (addr >= plic->enable_base && /* 1 bit per source */ 224 addr < plic->enable_base + plic->num_addrs * plic->enable_stride) 225 { 226 uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride; 227 uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2; 228 if (wordid < plic->bitfield_words) { 229 if (RISCV_DEBUG_PLIC) { 230 qemu_log("plic: read enable: hart%d-%c word=%d value=%x\n", 231 plic->addr_config[addrid].hartid, 232 mode_to_char(plic->addr_config[addrid].mode), wordid, 233 plic->enable[addrid * plic->bitfield_words + wordid]); 234 } 235 return plic->enable[addrid * plic->bitfield_words + wordid]; 236 } 237 } else if (addr >= plic->context_base && /* 1 bit per source */ 238 addr < plic->context_base + plic->num_addrs * plic->context_stride) 239 { 240 uint32_t addrid = (addr - plic->context_base) / plic->context_stride; 241 uint32_t contextid = (addr & (plic->context_stride - 1)); 242 if (contextid == 0) { 243 if (RISCV_DEBUG_PLIC) { 244 qemu_log("plic: read priority: hart%d-%c priority=%x\n", 245 plic->addr_config[addrid].hartid, 246 mode_to_char(plic->addr_config[addrid].mode), 247 plic->target_priority[addrid]); 248 } 249 return plic->target_priority[addrid]; 250 } else if (contextid == 4) { 251 uint32_t value = sifive_plic_claim(plic, addrid); 252 if (RISCV_DEBUG_PLIC) { 253 qemu_log("plic: read claim: hart%d-%c irq=%x\n", 254 plic->addr_config[addrid].hartid, 255 mode_to_char(plic->addr_config[addrid].mode), 256 value); 257 sifive_plic_print_state(plic); 258 } 259 return value; 260 } 261 } 262 263 err: 264 error_report("plic: invalid register read: %08x", (uint32_t)addr); 265 return 0; 266 } 267 268 static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value, 269 unsigned size) 270 { 271 SiFivePLICState *plic = opaque; 272 273 /* writes must be 4 byte words */ 274 if ((addr & 0x3) != 0) { 275 goto err; 276 } 277 278 if (addr >= plic->priority_base && /* 4 bytes per source */ 279 addr < plic->priority_base + (plic->num_sources << 2)) 280 { 281 uint32_t irq = (addr - plic->priority_base) >> 2; 282 plic->source_priority[irq] = value & 7; 283 if (RISCV_DEBUG_PLIC) { 284 qemu_log("plic: write priority: irq=%d priority=%d\n", 285 irq, plic->source_priority[irq]); 286 } 287 return; 288 } else if (addr >= plic->pending_base && /* 1 bit per source */ 289 addr < plic->pending_base + (plic->num_sources >> 3)) 290 { 291 error_report("plic: invalid pending write: %08x", (uint32_t)addr); 292 return; 293 } else if (addr >= plic->enable_base && /* 1 bit per source */ 294 addr < plic->enable_base + plic->num_addrs * plic->enable_stride) 295 { 296 uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride; 297 uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2; 298 if (wordid < plic->bitfield_words) { 299 plic->enable[addrid * plic->bitfield_words + wordid] = value; 300 if (RISCV_DEBUG_PLIC) { 301 qemu_log("plic: write enable: hart%d-%c word=%d value=%x\n", 302 plic->addr_config[addrid].hartid, 303 mode_to_char(plic->addr_config[addrid].mode), wordid, 304 plic->enable[addrid * plic->bitfield_words + wordid]); 305 } 306 return; 307 } 308 } else if (addr >= plic->context_base && /* 4 bytes per reg */ 309 addr < plic->context_base + plic->num_addrs * plic->context_stride) 310 { 311 uint32_t addrid = (addr - plic->context_base) / plic->context_stride; 312 uint32_t contextid = (addr & (plic->context_stride - 1)); 313 if (contextid == 0) { 314 if (RISCV_DEBUG_PLIC) { 315 qemu_log("plic: write priority: hart%d-%c priority=%x\n", 316 plic->addr_config[addrid].hartid, 317 mode_to_char(plic->addr_config[addrid].mode), 318 plic->target_priority[addrid]); 319 } 320 if (value <= plic->num_priorities) { 321 plic->target_priority[addrid] = value; 322 sifive_plic_update(plic); 323 } 324 return; 325 } else if (contextid == 4) { 326 if (RISCV_DEBUG_PLIC) { 327 qemu_log("plic: write claim: hart%d-%c irq=%x\n", 328 plic->addr_config[addrid].hartid, 329 mode_to_char(plic->addr_config[addrid].mode), 330 (uint32_t)value); 331 } 332 if (value < plic->num_sources) { 333 sifive_plic_set_claimed(plic, value, false); 334 sifive_plic_update(plic); 335 } 336 return; 337 } 338 } 339 340 err: 341 error_report("plic: invalid register write: %08x", (uint32_t)addr); 342 } 343 344 static const MemoryRegionOps sifive_plic_ops = { 345 .read = sifive_plic_read, 346 .write = sifive_plic_write, 347 .endianness = DEVICE_LITTLE_ENDIAN, 348 .valid = { 349 .min_access_size = 4, 350 .max_access_size = 4 351 } 352 }; 353 354 static Property sifive_plic_properties[] = { 355 DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config), 356 DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0), 357 DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0), 358 DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0), 359 DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0), 360 DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0), 361 DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0), 362 DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0), 363 DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0), 364 DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0), 365 DEFINE_PROP_END_OF_LIST(), 366 }; 367 368 /* 369 * parse PLIC hart/mode address offset config 370 * 371 * "M" 1 hart with M mode 372 * "MS,MS" 2 harts, 0-1 with M and S mode 373 * "M,MS,MS,MS,MS" 5 harts, 0 with M mode, 1-5 with M and S mode 374 */ 375 static void parse_hart_config(SiFivePLICState *plic) 376 { 377 int addrid, hartid, modes; 378 const char *p; 379 char c; 380 381 /* count and validate hart/mode combinations */ 382 addrid = 0, hartid = 0, modes = 0; 383 p = plic->hart_config; 384 while ((c = *p++)) { 385 if (c == ',') { 386 addrid += __builtin_popcount(modes); 387 modes = 0; 388 hartid++; 389 } else { 390 int m = 1 << char_to_mode(c); 391 if (modes == (modes | m)) { 392 error_report("plic: duplicate mode '%c' in config: %s", 393 c, plic->hart_config); 394 exit(1); 395 } 396 modes |= m; 397 } 398 } 399 if (modes) { 400 addrid += __builtin_popcount(modes); 401 } 402 hartid++; 403 404 /* store hart/mode combinations */ 405 plic->num_addrs = addrid; 406 plic->addr_config = g_new(PLICAddr, plic->num_addrs); 407 addrid = 0, hartid = 0; 408 p = plic->hart_config; 409 while ((c = *p++)) { 410 if (c == ',') { 411 hartid++; 412 } else { 413 plic->addr_config[addrid].addrid = addrid; 414 plic->addr_config[addrid].hartid = hartid; 415 plic->addr_config[addrid].mode = char_to_mode(c); 416 addrid++; 417 } 418 } 419 } 420 421 static void sifive_plic_irq_request(void *opaque, int irq, int level) 422 { 423 SiFivePLICState *plic = opaque; 424 if (RISCV_DEBUG_PLIC) { 425 qemu_log("sifive_plic_irq_request: irq=%d level=%d\n", irq, level); 426 } 427 sifive_plic_set_pending(plic, irq, level > 0); 428 sifive_plic_update(plic); 429 } 430 431 static void sifive_plic_realize(DeviceState *dev, Error **errp) 432 { 433 SiFivePLICState *plic = SIFIVE_PLIC(dev); 434 435 memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic, 436 TYPE_SIFIVE_PLIC, plic->aperture_size); 437 parse_hart_config(plic); 438 plic->bitfield_words = (plic->num_sources + 31) >> 5; 439 plic->source_priority = g_new0(uint32_t, plic->num_sources); 440 plic->target_priority = g_new(uint32_t, plic->num_addrs); 441 plic->pending = g_new0(uint32_t, plic->bitfield_words); 442 plic->claimed = g_new0(uint32_t, plic->bitfield_words); 443 plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs); 444 sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio); 445 qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources); 446 } 447 448 static void sifive_plic_class_init(ObjectClass *klass, void *data) 449 { 450 DeviceClass *dc = DEVICE_CLASS(klass); 451 452 dc->props = sifive_plic_properties; 453 dc->realize = sifive_plic_realize; 454 } 455 456 static const TypeInfo sifive_plic_info = { 457 .name = TYPE_SIFIVE_PLIC, 458 .parent = TYPE_SYS_BUS_DEVICE, 459 .instance_size = sizeof(SiFivePLICState), 460 .class_init = sifive_plic_class_init, 461 }; 462 463 static void sifive_plic_register_types(void) 464 { 465 type_register_static(&sifive_plic_info); 466 } 467 468 type_init(sifive_plic_register_types) 469 470 /* 471 * Create PLIC device. 472 */ 473 DeviceState *sifive_plic_create(hwaddr addr, char *hart_config, 474 uint32_t num_sources, uint32_t num_priorities, 475 uint32_t priority_base, uint32_t pending_base, 476 uint32_t enable_base, uint32_t enable_stride, 477 uint32_t context_base, uint32_t context_stride, 478 uint32_t aperture_size) 479 { 480 DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PLIC); 481 assert(enable_stride == (enable_stride & -enable_stride)); 482 assert(context_stride == (context_stride & -context_stride)); 483 qdev_prop_set_string(dev, "hart-config", hart_config); 484 qdev_prop_set_uint32(dev, "num-sources", num_sources); 485 qdev_prop_set_uint32(dev, "num-priorities", num_priorities); 486 qdev_prop_set_uint32(dev, "priority-base", priority_base); 487 qdev_prop_set_uint32(dev, "pending-base", pending_base); 488 qdev_prop_set_uint32(dev, "enable-base", enable_base); 489 qdev_prop_set_uint32(dev, "enable-stride", enable_stride); 490 qdev_prop_set_uint32(dev, "context-base", context_base); 491 qdev_prop_set_uint32(dev, "context-stride", context_stride); 492 qdev_prop_set_uint32(dev, "aperture-size", aperture_size); 493 qdev_init_nofail(dev); 494 sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); 495 return dev; 496 } 497