riscv_aclint.c (15790B)
1/* 2 * RISC-V ACLINT (Advanced Core Local Interruptor) 3 * URL: https://github.com/riscv/riscv-aclint 4 * 5 * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu 6 * Copyright (c) 2017 SiFive, Inc. 7 * Copyright (c) 2021 Western Digital Corporation or its affiliates. 8 * 9 * This provides real-time clock, timer and interprocessor interrupts. 10 * 11 * This program is free software; you can redistribute it and/or modify it 12 * under the terms and conditions of the GNU General Public License, 13 * version 2 or later, as published by the Free Software Foundation. 14 * 15 * This program is distributed in the hope it will be useful, but WITHOUT 16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 * more details. 19 * 20 * You should have received a copy of the GNU General Public License along with 21 * this program. If not, see <http://www.gnu.org/licenses/>. 22 */ 23 24#include "qemu/osdep.h" 25#include "qapi/error.h" 26#include "qemu/error-report.h" 27#include "qemu/log.h" 28#include "qemu/module.h" 29#include "hw/sysbus.h" 30#include "target/riscv/cpu.h" 31#include "hw/qdev-properties.h" 32#include "hw/intc/riscv_aclint.h" 33#include "qemu/timer.h" 34#include "hw/irq.h" 35 36typedef struct riscv_aclint_mtimer_callback { 37 RISCVAclintMTimerState *s; 38 int num; 39} riscv_aclint_mtimer_callback; 40 41static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq) 42{ 43 return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 44 timebase_freq, NANOSECONDS_PER_SECOND); 45} 46 47/* 48 * Called when timecmp is written to update the QEMU timer or immediately 49 * trigger timer interrupt if mtimecmp <= current timer value. 50 */ 51static void riscv_aclint_mtimer_write_timecmp(RISCVAclintMTimerState *mtimer, 52 RISCVCPU *cpu, 53 int hartid, 54 uint64_t value, 55 uint32_t timebase_freq) 56{ 57 uint64_t next; 58 uint64_t diff; 59 60 uint64_t rtc_r = cpu_riscv_read_rtc(timebase_freq); 61 62 cpu->env.timecmp = value; 63 if (cpu->env.timecmp <= rtc_r) { 64 /* 65 * If we're setting an MTIMECMP value in the "past", 66 * immediately raise the timer interrupt 67 */ 68 qemu_irq_raise(mtimer->timer_irqs[hartid - mtimer->hartid_base]); 69 return; 70 } 71 72 /* otherwise, set up the future timer interrupt */ 73 qemu_irq_lower(mtimer->timer_irqs[hartid - mtimer->hartid_base]); 74 diff = cpu->env.timecmp - rtc_r; 75 /* back to ns (note args switched in muldiv64) */ 76 uint64_t ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq); 77 78 /* 79 * check if ns_diff overflowed and check if the addition would potentially 80 * overflow 81 */ 82 if ((NANOSECONDS_PER_SECOND > timebase_freq && ns_diff < diff) || 83 ns_diff > INT64_MAX) { 84 next = INT64_MAX; 85 } else { 86 /* 87 * as it is very unlikely qemu_clock_get_ns will return a value 88 * greater than INT64_MAX, no additional check is needed for an 89 * unsigned integer overflow. 90 */ 91 next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns_diff; 92 /* 93 * if ns_diff is INT64_MAX next may still be outside the range 94 * of a signed integer. 95 */ 96 next = MIN(next, INT64_MAX); 97 } 98 99 timer_mod(cpu->env.timer, next); 100} 101 102/* 103 * Callback used when the timer set using timer_mod expires. 104 * Should raise the timer interrupt line 105 */ 106static void riscv_aclint_mtimer_cb(void *opaque) 107{ 108 riscv_aclint_mtimer_callback *state = opaque; 109 110 qemu_irq_raise(state->s->timer_irqs[state->num]); 111} 112 113/* CPU read MTIMER register */ 114static uint64_t riscv_aclint_mtimer_read(void *opaque, hwaddr addr, 115 unsigned size) 116{ 117 RISCVAclintMTimerState *mtimer = opaque; 118 119 if (addr >= mtimer->timecmp_base && 120 addr < (mtimer->timecmp_base + (mtimer->num_harts << 3))) { 121 size_t hartid = mtimer->hartid_base + 122 ((addr - mtimer->timecmp_base) >> 3); 123 CPUState *cpu = qemu_get_cpu(hartid); 124 CPURISCVState *env = cpu ? cpu->env_ptr : NULL; 125 if (!env) { 126 qemu_log_mask(LOG_GUEST_ERROR, 127 "aclint-mtimer: invalid hartid: %zu", hartid); 128 } else if ((addr & 0x7) == 0) { 129 /* timecmp_lo */ 130 uint64_t timecmp = env->timecmp; 131 return timecmp & 0xFFFFFFFF; 132 } else if ((addr & 0x7) == 4) { 133 /* timecmp_hi */ 134 uint64_t timecmp = env->timecmp; 135 return (timecmp >> 32) & 0xFFFFFFFF; 136 } else { 137 qemu_log_mask(LOG_UNIMP, 138 "aclint-mtimer: invalid read: %08x", (uint32_t)addr); 139 return 0; 140 } 141 } else if (addr == mtimer->time_base) { 142 /* time_lo */ 143 return cpu_riscv_read_rtc(mtimer->timebase_freq) & 0xFFFFFFFF; 144 } else if (addr == mtimer->time_base + 4) { 145 /* time_hi */ 146 return (cpu_riscv_read_rtc(mtimer->timebase_freq) >> 32) & 0xFFFFFFFF; 147 } 148 149 qemu_log_mask(LOG_UNIMP, 150 "aclint-mtimer: invalid read: %08x", (uint32_t)addr); 151 return 0; 152} 153 154/* CPU write MTIMER register */ 155static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr, 156 uint64_t value, unsigned size) 157{ 158 RISCVAclintMTimerState *mtimer = opaque; 159 160 if (addr >= mtimer->timecmp_base && 161 addr < (mtimer->timecmp_base + (mtimer->num_harts << 3))) { 162 size_t hartid = mtimer->hartid_base + 163 ((addr - mtimer->timecmp_base) >> 3); 164 CPUState *cpu = qemu_get_cpu(hartid); 165 CPURISCVState *env = cpu ? cpu->env_ptr : NULL; 166 if (!env) { 167 qemu_log_mask(LOG_GUEST_ERROR, 168 "aclint-mtimer: invalid hartid: %zu", hartid); 169 } else if ((addr & 0x7) == 0) { 170 /* timecmp_lo */ 171 uint64_t timecmp_hi = env->timecmp >> 32; 172 riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid, 173 timecmp_hi << 32 | (value & 0xFFFFFFFF), 174 mtimer->timebase_freq); 175 return; 176 } else if ((addr & 0x7) == 4) { 177 /* timecmp_hi */ 178 uint64_t timecmp_lo = env->timecmp; 179 riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid, 180 value << 32 | (timecmp_lo & 0xFFFFFFFF), 181 mtimer->timebase_freq); 182 } else { 183 qemu_log_mask(LOG_UNIMP, 184 "aclint-mtimer: invalid timecmp write: %08x", 185 (uint32_t)addr); 186 } 187 return; 188 } else if (addr == mtimer->time_base) { 189 /* time_lo */ 190 qemu_log_mask(LOG_UNIMP, 191 "aclint-mtimer: time_lo write not implemented"); 192 return; 193 } else if (addr == mtimer->time_base + 4) { 194 /* time_hi */ 195 qemu_log_mask(LOG_UNIMP, 196 "aclint-mtimer: time_hi write not implemented"); 197 return; 198 } 199 200 qemu_log_mask(LOG_UNIMP, 201 "aclint-mtimer: invalid write: %08x", (uint32_t)addr); 202} 203 204static const MemoryRegionOps riscv_aclint_mtimer_ops = { 205 .read = riscv_aclint_mtimer_read, 206 .write = riscv_aclint_mtimer_write, 207 .endianness = DEVICE_LITTLE_ENDIAN, 208 .valid = { 209 .min_access_size = 4, 210 .max_access_size = 8 211 } 212}; 213 214static Property riscv_aclint_mtimer_properties[] = { 215 DEFINE_PROP_UINT32("hartid-base", RISCVAclintMTimerState, 216 hartid_base, 0), 217 DEFINE_PROP_UINT32("num-harts", RISCVAclintMTimerState, num_harts, 1), 218 DEFINE_PROP_UINT32("timecmp-base", RISCVAclintMTimerState, 219 timecmp_base, RISCV_ACLINT_DEFAULT_MTIMECMP), 220 DEFINE_PROP_UINT32("time-base", RISCVAclintMTimerState, 221 time_base, RISCV_ACLINT_DEFAULT_MTIME), 222 DEFINE_PROP_UINT32("aperture-size", RISCVAclintMTimerState, 223 aperture_size, RISCV_ACLINT_DEFAULT_MTIMER_SIZE), 224 DEFINE_PROP_UINT32("timebase-freq", RISCVAclintMTimerState, 225 timebase_freq, 0), 226 DEFINE_PROP_END_OF_LIST(), 227}; 228 229static void riscv_aclint_mtimer_realize(DeviceState *dev, Error **errp) 230{ 231 RISCVAclintMTimerState *s = RISCV_ACLINT_MTIMER(dev); 232 int i; 233 234 memory_region_init_io(&s->mmio, OBJECT(dev), &riscv_aclint_mtimer_ops, 235 s, TYPE_RISCV_ACLINT_MTIMER, s->aperture_size); 236 sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); 237 238 s->timer_irqs = g_malloc(sizeof(qemu_irq) * s->num_harts); 239 qdev_init_gpio_out(dev, s->timer_irqs, s->num_harts); 240 241 /* Claim timer interrupt bits */ 242 for (i = 0; i < s->num_harts; i++) { 243 RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(s->hartid_base + i)); 244 if (riscv_cpu_claim_interrupts(cpu, MIP_MTIP) < 0) { 245 error_report("MTIP already claimed"); 246 exit(1); 247 } 248 } 249} 250 251static void riscv_aclint_mtimer_class_init(ObjectClass *klass, void *data) 252{ 253 DeviceClass *dc = DEVICE_CLASS(klass); 254 dc->realize = riscv_aclint_mtimer_realize; 255 device_class_set_props(dc, riscv_aclint_mtimer_properties); 256} 257 258static const TypeInfo riscv_aclint_mtimer_info = { 259 .name = TYPE_RISCV_ACLINT_MTIMER, 260 .parent = TYPE_SYS_BUS_DEVICE, 261 .instance_size = sizeof(RISCVAclintMTimerState), 262 .class_init = riscv_aclint_mtimer_class_init, 263}; 264 265/* 266 * Create ACLINT MTIMER device. 267 */ 268DeviceState *riscv_aclint_mtimer_create(hwaddr addr, hwaddr size, 269 uint32_t hartid_base, uint32_t num_harts, 270 uint32_t timecmp_base, uint32_t time_base, uint32_t timebase_freq, 271 bool provide_rdtime) 272{ 273 int i; 274 DeviceState *dev = qdev_new(TYPE_RISCV_ACLINT_MTIMER); 275 276 assert(num_harts <= RISCV_ACLINT_MAX_HARTS); 277 assert(!(addr & 0x7)); 278 assert(!(timecmp_base & 0x7)); 279 assert(!(time_base & 0x7)); 280 281 qdev_prop_set_uint32(dev, "hartid-base", hartid_base); 282 qdev_prop_set_uint32(dev, "num-harts", num_harts); 283 qdev_prop_set_uint32(dev, "timecmp-base", timecmp_base); 284 qdev_prop_set_uint32(dev, "time-base", time_base); 285 qdev_prop_set_uint32(dev, "aperture-size", size); 286 qdev_prop_set_uint32(dev, "timebase-freq", timebase_freq); 287 sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); 288 sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); 289 290 for (i = 0; i < num_harts; i++) { 291 CPUState *cpu = qemu_get_cpu(hartid_base + i); 292 RISCVCPU *rvcpu = RISCV_CPU(cpu); 293 CPURISCVState *env = cpu ? cpu->env_ptr : NULL; 294 riscv_aclint_mtimer_callback *cb = 295 g_malloc0(sizeof(riscv_aclint_mtimer_callback)); 296 297 if (!env) { 298 g_free(cb); 299 continue; 300 } 301 if (provide_rdtime) { 302 riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc, timebase_freq); 303 } 304 305 cb->s = RISCV_ACLINT_MTIMER(dev); 306 cb->num = i; 307 env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, 308 &riscv_aclint_mtimer_cb, cb); 309 env->timecmp = 0; 310 311 qdev_connect_gpio_out(dev, i, 312 qdev_get_gpio_in(DEVICE(rvcpu), IRQ_M_TIMER)); 313 } 314 315 return dev; 316} 317 318/* CPU read [M|S]SWI register */ 319static uint64_t riscv_aclint_swi_read(void *opaque, hwaddr addr, 320 unsigned size) 321{ 322 RISCVAclintSwiState *swi = opaque; 323 324 if (addr < (swi->num_harts << 2)) { 325 size_t hartid = swi->hartid_base + (addr >> 2); 326 CPUState *cpu = qemu_get_cpu(hartid); 327 CPURISCVState *env = cpu ? cpu->env_ptr : NULL; 328 if (!env) { 329 qemu_log_mask(LOG_GUEST_ERROR, 330 "aclint-swi: invalid hartid: %zu", hartid); 331 } else if ((addr & 0x3) == 0) { 332 return (swi->sswi) ? 0 : ((env->mip & MIP_MSIP) > 0); 333 } 334 } 335 336 qemu_log_mask(LOG_UNIMP, 337 "aclint-swi: invalid read: %08x", (uint32_t)addr); 338 return 0; 339} 340 341/* CPU write [M|S]SWI register */ 342static void riscv_aclint_swi_write(void *opaque, hwaddr addr, uint64_t value, 343 unsigned size) 344{ 345 RISCVAclintSwiState *swi = opaque; 346 347 if (addr < (swi->num_harts << 2)) { 348 size_t hartid = swi->hartid_base + (addr >> 2); 349 CPUState *cpu = qemu_get_cpu(hartid); 350 CPURISCVState *env = cpu ? cpu->env_ptr : NULL; 351 if (!env) { 352 qemu_log_mask(LOG_GUEST_ERROR, 353 "aclint-swi: invalid hartid: %zu", hartid); 354 } else if ((addr & 0x3) == 0) { 355 if (value & 0x1) { 356 qemu_irq_raise(swi->soft_irqs[hartid - swi->hartid_base]); 357 } else { 358 if (!swi->sswi) { 359 qemu_irq_lower(swi->soft_irqs[hartid - swi->hartid_base]); 360 } 361 } 362 return; 363 } 364 } 365 366 qemu_log_mask(LOG_UNIMP, 367 "aclint-swi: invalid write: %08x", (uint32_t)addr); 368} 369 370static const MemoryRegionOps riscv_aclint_swi_ops = { 371 .read = riscv_aclint_swi_read, 372 .write = riscv_aclint_swi_write, 373 .endianness = DEVICE_LITTLE_ENDIAN, 374 .valid = { 375 .min_access_size = 4, 376 .max_access_size = 4 377 } 378}; 379 380static Property riscv_aclint_swi_properties[] = { 381 DEFINE_PROP_UINT32("hartid-base", RISCVAclintSwiState, hartid_base, 0), 382 DEFINE_PROP_UINT32("num-harts", RISCVAclintSwiState, num_harts, 1), 383 DEFINE_PROP_UINT32("sswi", RISCVAclintSwiState, sswi, false), 384 DEFINE_PROP_END_OF_LIST(), 385}; 386 387static void riscv_aclint_swi_realize(DeviceState *dev, Error **errp) 388{ 389 RISCVAclintSwiState *swi = RISCV_ACLINT_SWI(dev); 390 int i; 391 392 memory_region_init_io(&swi->mmio, OBJECT(dev), &riscv_aclint_swi_ops, swi, 393 TYPE_RISCV_ACLINT_SWI, RISCV_ACLINT_SWI_SIZE); 394 sysbus_init_mmio(SYS_BUS_DEVICE(dev), &swi->mmio); 395 396 swi->soft_irqs = g_malloc(sizeof(qemu_irq) * swi->num_harts); 397 qdev_init_gpio_out(dev, swi->soft_irqs, swi->num_harts); 398 399 /* Claim software interrupt bits */ 400 for (i = 0; i < swi->num_harts; i++) { 401 RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(swi->hartid_base + i)); 402 /* We don't claim mip.SSIP because it is writeable by software */ 403 if (riscv_cpu_claim_interrupts(cpu, swi->sswi ? 0 : MIP_MSIP) < 0) { 404 error_report("MSIP already claimed"); 405 exit(1); 406 } 407 } 408} 409 410static void riscv_aclint_swi_class_init(ObjectClass *klass, void *data) 411{ 412 DeviceClass *dc = DEVICE_CLASS(klass); 413 dc->realize = riscv_aclint_swi_realize; 414 device_class_set_props(dc, riscv_aclint_swi_properties); 415} 416 417static const TypeInfo riscv_aclint_swi_info = { 418 .name = TYPE_RISCV_ACLINT_SWI, 419 .parent = TYPE_SYS_BUS_DEVICE, 420 .instance_size = sizeof(RISCVAclintSwiState), 421 .class_init = riscv_aclint_swi_class_init, 422}; 423 424/* 425 * Create ACLINT [M|S]SWI device. 426 */ 427DeviceState *riscv_aclint_swi_create(hwaddr addr, uint32_t hartid_base, 428 uint32_t num_harts, bool sswi) 429{ 430 int i; 431 DeviceState *dev = qdev_new(TYPE_RISCV_ACLINT_SWI); 432 433 assert(num_harts <= RISCV_ACLINT_MAX_HARTS); 434 assert(!(addr & 0x3)); 435 436 qdev_prop_set_uint32(dev, "hartid-base", hartid_base); 437 qdev_prop_set_uint32(dev, "num-harts", num_harts); 438 qdev_prop_set_uint32(dev, "sswi", sswi ? true : false); 439 sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); 440 sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); 441 442 for (i = 0; i < num_harts; i++) { 443 CPUState *cpu = qemu_get_cpu(hartid_base + i); 444 RISCVCPU *rvcpu = RISCV_CPU(cpu); 445 446 qdev_connect_gpio_out(dev, i, 447 qdev_get_gpio_in(DEVICE(rvcpu), 448 (sswi) ? IRQ_S_SOFT : IRQ_M_SOFT)); 449 } 450 451 return dev; 452} 453 454static void riscv_aclint_register_types(void) 455{ 456 type_register_static(&riscv_aclint_mtimer_info); 457 type_register_static(&riscv_aclint_swi_info); 458} 459 460type_init(riscv_aclint_register_types)