mips_itu.c (14510B)
1/* 2 * Inter-Thread Communication Unit emulation. 3 * 4 * Copyright (c) 2016 Imagination Technologies 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 20#include "qemu/osdep.h" 21#include "qemu/units.h" 22#include "qemu/log.h" 23#include "qemu/module.h" 24#include "qapi/error.h" 25#include "exec/exec-all.h" 26#include "hw/misc/mips_itu.h" 27#include "hw/qdev-properties.h" 28 29#define ITC_TAG_ADDRSPACE_SZ (ITC_ADDRESSMAP_NUM * 8) 30/* Initialize as 4kB area to fit all 32 cells with default 128B grain. 31 Storage may be resized by the software. */ 32#define ITC_STORAGE_ADDRSPACE_SZ 0x1000 33 34#define ITC_FIFO_NUM_MAX 16 35#define ITC_SEMAPH_NUM_MAX 16 36#define ITC_AM1_NUMENTRIES_OFS 20 37 38#define ITC_CELL_PV_MAX_VAL 0xFFFF 39 40#define ITC_CELL_TAG_FIFO_DEPTH 28 41#define ITC_CELL_TAG_FIFO_PTR 18 42#define ITC_CELL_TAG_FIFO 17 43#define ITC_CELL_TAG_T 16 44#define ITC_CELL_TAG_F 1 45#define ITC_CELL_TAG_E 0 46 47#define ITC_AM0_BASE_ADDRESS_MASK 0xFFFFFC00ULL 48#define ITC_AM0_EN_MASK 0x1 49 50#define ITC_AM1_ADDR_MASK_MASK 0x1FC00 51#define ITC_AM1_ENTRY_GRAIN_MASK 0x7 52 53typedef enum ITCView { 54 ITCVIEW_BYPASS = 0, 55 ITCVIEW_CONTROL = 1, 56 ITCVIEW_EF_SYNC = 2, 57 ITCVIEW_EF_TRY = 3, 58 ITCVIEW_PV_SYNC = 4, 59 ITCVIEW_PV_TRY = 5, 60 ITCVIEW_PV_ICR0 = 15, 61} ITCView; 62 63#define ITC_ICR0_CELL_NUM 16 64#define ITC_ICR0_BLK_GRAIN 8 65#define ITC_ICR0_BLK_GRAIN_MASK 0x7 66#define ITC_ICR0_ERR_AXI 2 67#define ITC_ICR0_ERR_PARITY 1 68#define ITC_ICR0_ERR_EXEC 0 69 70MemoryRegion *mips_itu_get_tag_region(MIPSITUState *itu) 71{ 72 return &itu->tag_io; 73} 74 75static uint64_t itc_tag_read(void *opaque, hwaddr addr, unsigned size) 76{ 77 MIPSITUState *tag = (MIPSITUState *)opaque; 78 uint64_t index = addr >> 3; 79 80 if (index >= ITC_ADDRESSMAP_NUM) { 81 qemu_log_mask(LOG_GUEST_ERROR, "Read 0x%" PRIx64 "\n", addr); 82 return 0; 83 } 84 85 return tag->ITCAddressMap[index]; 86} 87 88void itc_reconfigure(MIPSITUState *tag) 89{ 90 uint64_t *am = &tag->ITCAddressMap[0]; 91 MemoryRegion *mr = &tag->storage_io; 92 hwaddr address = am[0] & ITC_AM0_BASE_ADDRESS_MASK; 93 uint64_t size = (1 * KiB) + (am[1] & ITC_AM1_ADDR_MASK_MASK); 94 bool is_enabled = (am[0] & ITC_AM0_EN_MASK) != 0; 95 96 if (tag->saar_present) { 97 address = ((*(uint64_t *) tag->saar) & 0xFFFFFFFFE000ULL) << 4; 98 size = 1ULL << ((*(uint64_t *) tag->saar >> 1) & 0x1f); 99 is_enabled = *(uint64_t *) tag->saar & 1; 100 } 101 102 memory_region_transaction_begin(); 103 if (!(size & (size - 1))) { 104 memory_region_set_size(mr, size); 105 } 106 memory_region_set_address(mr, address); 107 memory_region_set_enabled(mr, is_enabled); 108 memory_region_transaction_commit(); 109} 110 111static void itc_tag_write(void *opaque, hwaddr addr, 112 uint64_t data, unsigned size) 113{ 114 MIPSITUState *tag = (MIPSITUState *)opaque; 115 uint64_t *am = &tag->ITCAddressMap[0]; 116 uint64_t am_old, mask; 117 uint64_t index = addr >> 3; 118 119 switch (index) { 120 case 0: 121 mask = ITC_AM0_BASE_ADDRESS_MASK | ITC_AM0_EN_MASK; 122 break; 123 case 1: 124 mask = ITC_AM1_ADDR_MASK_MASK | ITC_AM1_ENTRY_GRAIN_MASK; 125 break; 126 default: 127 qemu_log_mask(LOG_GUEST_ERROR, "Bad write 0x%" PRIx64 "\n", addr); 128 return; 129 } 130 131 am_old = am[index]; 132 am[index] = (data & mask) | (am_old & ~mask); 133 if (am_old != am[index]) { 134 itc_reconfigure(tag); 135 } 136} 137 138static const MemoryRegionOps itc_tag_ops = { 139 .read = itc_tag_read, 140 .write = itc_tag_write, 141 .impl = { 142 .max_access_size = 8, 143 }, 144 .endianness = DEVICE_NATIVE_ENDIAN, 145}; 146 147static inline uint32_t get_num_cells(MIPSITUState *s) 148{ 149 return s->num_fifo + s->num_semaphores; 150} 151 152static inline ITCView get_itc_view(hwaddr addr) 153{ 154 return (addr >> 3) & 0xf; 155} 156 157static inline int get_cell_stride_shift(const MIPSITUState *s) 158{ 159 /* Minimum interval (for EntryGain = 0) is 128 B */ 160 if (s->saar_present) { 161 return 7 + ((s->icr0 >> ITC_ICR0_BLK_GRAIN) & 162 ITC_ICR0_BLK_GRAIN_MASK); 163 } else { 164 return 7 + (s->ITCAddressMap[1] & ITC_AM1_ENTRY_GRAIN_MASK); 165 } 166} 167 168static inline ITCStorageCell *get_cell(MIPSITUState *s, 169 hwaddr addr) 170{ 171 uint32_t cell_idx = addr >> get_cell_stride_shift(s); 172 uint32_t num_cells = get_num_cells(s); 173 174 if (cell_idx >= num_cells) { 175 cell_idx = num_cells - 1; 176 } 177 178 return &s->cell[cell_idx]; 179} 180 181static void wake_blocked_threads(ITCStorageCell *c) 182{ 183 CPUState *cs; 184 CPU_FOREACH(cs) { 185 if (cs->halted && (c->blocked_threads & (1ULL << cs->cpu_index))) { 186 cpu_interrupt(cs, CPU_INTERRUPT_WAKE); 187 } 188 } 189 c->blocked_threads = 0; 190} 191 192static void QEMU_NORETURN block_thread_and_exit(ITCStorageCell *c) 193{ 194 c->blocked_threads |= 1ULL << current_cpu->cpu_index; 195 current_cpu->halted = 1; 196 current_cpu->exception_index = EXCP_HLT; 197 cpu_loop_exit_restore(current_cpu, current_cpu->mem_io_pc); 198} 199 200/* ITC Bypass View */ 201 202static inline uint64_t view_bypass_read(ITCStorageCell *c) 203{ 204 if (c->tag.FIFO) { 205 return c->data[c->fifo_out]; 206 } else { 207 return c->data[0]; 208 } 209} 210 211static inline void view_bypass_write(ITCStorageCell *c, uint64_t val) 212{ 213 if (c->tag.FIFO && (c->tag.FIFOPtr > 0)) { 214 int idx = (c->fifo_out + c->tag.FIFOPtr - 1) % ITC_CELL_DEPTH; 215 c->data[idx] = val; 216 } 217 218 /* ignore a write to the semaphore cell */ 219} 220 221/* ITC Control View */ 222 223static inline uint64_t view_control_read(ITCStorageCell *c) 224{ 225 return ((uint64_t)c->tag.FIFODepth << ITC_CELL_TAG_FIFO_DEPTH) | 226 (c->tag.FIFOPtr << ITC_CELL_TAG_FIFO_PTR) | 227 (c->tag.FIFO << ITC_CELL_TAG_FIFO) | 228 (c->tag.T << ITC_CELL_TAG_T) | 229 (c->tag.E << ITC_CELL_TAG_E) | 230 (c->tag.F << ITC_CELL_TAG_F); 231} 232 233static inline void view_control_write(ITCStorageCell *c, uint64_t val) 234{ 235 c->tag.T = (val >> ITC_CELL_TAG_T) & 1; 236 c->tag.E = (val >> ITC_CELL_TAG_E) & 1; 237 c->tag.F = (val >> ITC_CELL_TAG_F) & 1; 238 239 if (c->tag.E) { 240 c->tag.FIFOPtr = 0; 241 } 242} 243 244/* ITC Empty/Full View */ 245 246static uint64_t view_ef_common_read(ITCStorageCell *c, bool blocking) 247{ 248 uint64_t ret = 0; 249 250 if (!c->tag.FIFO) { 251 return 0; 252 } 253 254 c->tag.F = 0; 255 256 if (blocking && c->tag.E) { 257 block_thread_and_exit(c); 258 } 259 260 if (c->blocked_threads) { 261 wake_blocked_threads(c); 262 } 263 264 if (c->tag.FIFOPtr > 0) { 265 ret = c->data[c->fifo_out]; 266 c->fifo_out = (c->fifo_out + 1) % ITC_CELL_DEPTH; 267 c->tag.FIFOPtr--; 268 } 269 270 if (c->tag.FIFOPtr == 0) { 271 c->tag.E = 1; 272 } 273 274 return ret; 275} 276 277static uint64_t view_ef_sync_read(ITCStorageCell *c) 278{ 279 return view_ef_common_read(c, true); 280} 281 282static uint64_t view_ef_try_read(ITCStorageCell *c) 283{ 284 return view_ef_common_read(c, false); 285} 286 287static inline void view_ef_common_write(ITCStorageCell *c, uint64_t val, 288 bool blocking) 289{ 290 if (!c->tag.FIFO) { 291 return; 292 } 293 294 c->tag.E = 0; 295 296 if (blocking && c->tag.F) { 297 block_thread_and_exit(c); 298 } 299 300 if (c->blocked_threads) { 301 wake_blocked_threads(c); 302 } 303 304 if (c->tag.FIFOPtr < ITC_CELL_DEPTH) { 305 int idx = (c->fifo_out + c->tag.FIFOPtr) % ITC_CELL_DEPTH; 306 c->data[idx] = val; 307 c->tag.FIFOPtr++; 308 } 309 310 if (c->tag.FIFOPtr == ITC_CELL_DEPTH) { 311 c->tag.F = 1; 312 } 313} 314 315static void view_ef_sync_write(ITCStorageCell *c, uint64_t val) 316{ 317 view_ef_common_write(c, val, true); 318} 319 320static void view_ef_try_write(ITCStorageCell *c, uint64_t val) 321{ 322 view_ef_common_write(c, val, false); 323} 324 325/* ITC P/V View */ 326 327static uint64_t view_pv_common_read(ITCStorageCell *c, bool blocking) 328{ 329 uint64_t ret = c->data[0]; 330 331 if (c->tag.FIFO) { 332 return 0; 333 } 334 335 if (c->data[0] > 0) { 336 c->data[0]--; 337 } else if (blocking) { 338 block_thread_and_exit(c); 339 } 340 341 return ret; 342} 343 344static uint64_t view_pv_sync_read(ITCStorageCell *c) 345{ 346 return view_pv_common_read(c, true); 347} 348 349static uint64_t view_pv_try_read(ITCStorageCell *c) 350{ 351 return view_pv_common_read(c, false); 352} 353 354static inline void view_pv_common_write(ITCStorageCell *c) 355{ 356 if (c->tag.FIFO) { 357 return; 358 } 359 360 if (c->data[0] < ITC_CELL_PV_MAX_VAL) { 361 c->data[0]++; 362 } 363 364 if (c->blocked_threads) { 365 wake_blocked_threads(c); 366 } 367} 368 369static void view_pv_sync_write(ITCStorageCell *c) 370{ 371 view_pv_common_write(c); 372} 373 374static void view_pv_try_write(ITCStorageCell *c) 375{ 376 view_pv_common_write(c); 377} 378 379static void raise_exception(int excp) 380{ 381 current_cpu->exception_index = excp; 382 cpu_loop_exit(current_cpu); 383} 384 385static uint64_t itc_storage_read(void *opaque, hwaddr addr, unsigned size) 386{ 387 MIPSITUState *s = (MIPSITUState *)opaque; 388 ITCStorageCell *cell = get_cell(s, addr); 389 ITCView view = get_itc_view(addr); 390 uint64_t ret = -1; 391 392 switch (size) { 393 case 1: 394 case 2: 395 s->icr0 |= 1 << ITC_ICR0_ERR_AXI; 396 raise_exception(EXCP_DBE); 397 return 0; 398 } 399 400 switch (view) { 401 case ITCVIEW_BYPASS: 402 ret = view_bypass_read(cell); 403 break; 404 case ITCVIEW_CONTROL: 405 ret = view_control_read(cell); 406 break; 407 case ITCVIEW_EF_SYNC: 408 ret = view_ef_sync_read(cell); 409 break; 410 case ITCVIEW_EF_TRY: 411 ret = view_ef_try_read(cell); 412 break; 413 case ITCVIEW_PV_SYNC: 414 ret = view_pv_sync_read(cell); 415 break; 416 case ITCVIEW_PV_TRY: 417 ret = view_pv_try_read(cell); 418 break; 419 case ITCVIEW_PV_ICR0: 420 ret = s->icr0; 421 break; 422 default: 423 qemu_log_mask(LOG_GUEST_ERROR, 424 "itc_storage_read: Bad ITC View %d\n", (int)view); 425 break; 426 } 427 428 return ret; 429} 430 431static void itc_storage_write(void *opaque, hwaddr addr, uint64_t data, 432 unsigned size) 433{ 434 MIPSITUState *s = (MIPSITUState *)opaque; 435 ITCStorageCell *cell = get_cell(s, addr); 436 ITCView view = get_itc_view(addr); 437 438 switch (size) { 439 case 1: 440 case 2: 441 s->icr0 |= 1 << ITC_ICR0_ERR_AXI; 442 raise_exception(EXCP_DBE); 443 return; 444 } 445 446 switch (view) { 447 case ITCVIEW_BYPASS: 448 view_bypass_write(cell, data); 449 break; 450 case ITCVIEW_CONTROL: 451 view_control_write(cell, data); 452 break; 453 case ITCVIEW_EF_SYNC: 454 view_ef_sync_write(cell, data); 455 break; 456 case ITCVIEW_EF_TRY: 457 view_ef_try_write(cell, data); 458 break; 459 case ITCVIEW_PV_SYNC: 460 view_pv_sync_write(cell); 461 break; 462 case ITCVIEW_PV_TRY: 463 view_pv_try_write(cell); 464 break; 465 case ITCVIEW_PV_ICR0: 466 if (data & 0x7) { 467 /* clear ERROR bits */ 468 s->icr0 &= ~(data & 0x7); 469 } 470 /* set BLK_GRAIN */ 471 s->icr0 &= ~0x700; 472 s->icr0 |= data & 0x700; 473 break; 474 default: 475 qemu_log_mask(LOG_GUEST_ERROR, 476 "itc_storage_write: Bad ITC View %d\n", (int)view); 477 break; 478 } 479 480} 481 482static const MemoryRegionOps itc_storage_ops = { 483 .read = itc_storage_read, 484 .write = itc_storage_write, 485 .endianness = DEVICE_NATIVE_ENDIAN, 486}; 487 488static void itc_reset_cells(MIPSITUState *s) 489{ 490 int i; 491 492 memset(s->cell, 0, get_num_cells(s) * sizeof(s->cell[0])); 493 494 for (i = 0; i < s->num_fifo; i++) { 495 s->cell[i].tag.E = 1; 496 s->cell[i].tag.FIFO = 1; 497 s->cell[i].tag.FIFODepth = ITC_CELL_DEPTH_SHIFT; 498 } 499} 500 501static void mips_itu_init(Object *obj) 502{ 503 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 504 MIPSITUState *s = MIPS_ITU(obj); 505 506 memory_region_init_io(&s->storage_io, OBJECT(s), &itc_storage_ops, s, 507 "mips-itc-storage", ITC_STORAGE_ADDRSPACE_SZ); 508 sysbus_init_mmio(sbd, &s->storage_io); 509 510 memory_region_init_io(&s->tag_io, OBJECT(s), &itc_tag_ops, s, 511 "mips-itc-tag", ITC_TAG_ADDRSPACE_SZ); 512} 513 514static void mips_itu_realize(DeviceState *dev, Error **errp) 515{ 516 MIPSITUState *s = MIPS_ITU(dev); 517 518 if (s->num_fifo > ITC_FIFO_NUM_MAX) { 519 error_setg(errp, "Exceed maximum number of FIFO cells: %d", 520 s->num_fifo); 521 return; 522 } 523 if (s->num_semaphores > ITC_SEMAPH_NUM_MAX) { 524 error_setg(errp, "Exceed maximum number of Semaphore cells: %d", 525 s->num_semaphores); 526 return; 527 } 528 529 s->cell = g_new(ITCStorageCell, get_num_cells(s)); 530} 531 532static void mips_itu_reset(DeviceState *dev) 533{ 534 MIPSITUState *s = MIPS_ITU(dev); 535 536 if (s->saar_present) { 537 *(uint64_t *) s->saar = 0x11 << 1; 538 s->icr0 = get_num_cells(s) << ITC_ICR0_CELL_NUM; 539 } else { 540 s->ITCAddressMap[0] = 0; 541 s->ITCAddressMap[1] = 542 ((ITC_STORAGE_ADDRSPACE_SZ - 1) & ITC_AM1_ADDR_MASK_MASK) | 543 (get_num_cells(s) << ITC_AM1_NUMENTRIES_OFS); 544 } 545 itc_reconfigure(s); 546 547 itc_reset_cells(s); 548} 549 550static Property mips_itu_properties[] = { 551 DEFINE_PROP_INT32("num-fifo", MIPSITUState, num_fifo, 552 ITC_FIFO_NUM_MAX), 553 DEFINE_PROP_INT32("num-semaphores", MIPSITUState, num_semaphores, 554 ITC_SEMAPH_NUM_MAX), 555 DEFINE_PROP_BOOL("saar-present", MIPSITUState, saar_present, false), 556 DEFINE_PROP_END_OF_LIST(), 557}; 558 559static void mips_itu_class_init(ObjectClass *klass, void *data) 560{ 561 DeviceClass *dc = DEVICE_CLASS(klass); 562 563 device_class_set_props(dc, mips_itu_properties); 564 dc->realize = mips_itu_realize; 565 dc->reset = mips_itu_reset; 566} 567 568static const TypeInfo mips_itu_info = { 569 .name = TYPE_MIPS_ITU, 570 .parent = TYPE_SYS_BUS_DEVICE, 571 .instance_size = sizeof(MIPSITUState), 572 .instance_init = mips_itu_init, 573 .class_init = mips_itu_class_init, 574}; 575 576static void mips_itu_register_types(void) 577{ 578 type_register_static(&mips_itu_info); 579} 580 581type_init(mips_itu_register_types)