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