cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

sh_intc.c (13687B)


      1/*
      2 * SuperH interrupt controller module
      3 *
      4 * Copyright (c) 2007 Magnus Damm
      5 * Based on sh_timer.c and arm_timer.c by Paul Brook
      6 * Copyright (c) 2005-2006 CodeSourcery.
      7 *
      8 * This code is licensed under the GPL.
      9 */
     10
     11#include "qemu/osdep.h"
     12#include "cpu.h"
     13#include "hw/sh4/sh_intc.h"
     14#include "hw/irq.h"
     15#include "hw/sh4/sh.h"
     16
     17//#define DEBUG_INTC
     18//#define DEBUG_INTC_SOURCES
     19
     20#define INTC_A7(x) ((x) & 0x1fffffff)
     21
     22void sh_intc_toggle_source(struct intc_source *source,
     23			   int enable_adj, int assert_adj)
     24{
     25    int enable_changed = 0;
     26    int pending_changed = 0;
     27    int old_pending;
     28
     29    if ((source->enable_count == source->enable_max) && (enable_adj == -1))
     30        enable_changed = -1;
     31
     32    source->enable_count += enable_adj;
     33
     34    if (source->enable_count == source->enable_max)
     35        enable_changed = 1;
     36
     37    source->asserted += assert_adj;
     38
     39    old_pending = source->pending;
     40    source->pending = source->asserted &&
     41      (source->enable_count == source->enable_max);
     42
     43    if (old_pending != source->pending)
     44        pending_changed = 1;
     45
     46    if (pending_changed) {
     47        if (source->pending) {
     48            source->parent->pending++;
     49            if (source->parent->pending == 1) {
     50                cpu_interrupt(first_cpu, CPU_INTERRUPT_HARD);
     51            }
     52        } else {
     53            source->parent->pending--;
     54            if (source->parent->pending == 0) {
     55                cpu_reset_interrupt(first_cpu, CPU_INTERRUPT_HARD);
     56            }
     57	}
     58    }
     59
     60  if (enable_changed || assert_adj || pending_changed) {
     61#ifdef DEBUG_INTC_SOURCES
     62            printf("sh_intc: (%d/%d/%d/%d) interrupt source 0x%x %s%s%s\n",
     63		   source->parent->pending,
     64		   source->asserted,
     65		   source->enable_count,
     66		   source->enable_max,
     67		   source->vect,
     68		   source->asserted ? "asserted " :
     69		   assert_adj ? "deasserted" : "",
     70		   enable_changed == 1 ? "enabled " :
     71		   enable_changed == -1 ? "disabled " : "",
     72		   source->pending ? "pending" : "");
     73#endif
     74  }
     75}
     76
     77static void sh_intc_set_irq (void *opaque, int n, int level)
     78{
     79  struct intc_desc *desc = opaque;
     80  struct intc_source *source = &(desc->sources[n]);
     81
     82  if (level && !source->asserted)
     83    sh_intc_toggle_source(source, 0, 1);
     84  else if (!level && source->asserted)
     85    sh_intc_toggle_source(source, 0, -1);
     86}
     87
     88int sh_intc_get_pending_vector(struct intc_desc *desc, int imask)
     89{
     90    unsigned int i;
     91
     92    /* slow: use a linked lists of pending sources instead */
     93    /* wrong: take interrupt priority into account (one list per priority) */
     94
     95    if (imask == 0x0f) {
     96        return -1; /* FIXME, update code to include priority per source */
     97    }
     98
     99    for (i = 0; i < desc->nr_sources; i++) {
    100        struct intc_source *source = desc->sources + i;
    101
    102	if (source->pending) {
    103#ifdef DEBUG_INTC_SOURCES
    104            printf("sh_intc: (%d) returning interrupt source 0x%x\n",
    105		   desc->pending, source->vect);
    106#endif
    107            return source->vect;
    108	}
    109    }
    110
    111    abort();
    112}
    113
    114#define INTC_MODE_NONE       0
    115#define INTC_MODE_DUAL_SET   1
    116#define INTC_MODE_DUAL_CLR   2
    117#define INTC_MODE_ENABLE_REG 3
    118#define INTC_MODE_MASK_REG   4
    119#define INTC_MODE_IS_PRIO    8
    120
    121static unsigned int sh_intc_mode(unsigned long address,
    122				 unsigned long set_reg, unsigned long clr_reg)
    123{
    124    if ((address != INTC_A7(set_reg)) &&
    125	(address != INTC_A7(clr_reg)))
    126        return INTC_MODE_NONE;
    127
    128    if (set_reg && clr_reg) {
    129        if (address == INTC_A7(set_reg))
    130            return INTC_MODE_DUAL_SET;
    131	else
    132            return INTC_MODE_DUAL_CLR;
    133    }
    134
    135    if (set_reg)
    136        return INTC_MODE_ENABLE_REG;
    137    else
    138        return INTC_MODE_MASK_REG;
    139}
    140
    141static void sh_intc_locate(struct intc_desc *desc,
    142			   unsigned long address,
    143			   unsigned long **datap,
    144			   intc_enum **enums,
    145			   unsigned int *first,
    146			   unsigned int *width,
    147			   unsigned int *modep)
    148{
    149    unsigned int i, mode;
    150
    151    /* this is slow but works for now */
    152
    153    if (desc->mask_regs) {
    154        for (i = 0; i < desc->nr_mask_regs; i++) {
    155	    struct intc_mask_reg *mr = desc->mask_regs + i;
    156
    157	    mode = sh_intc_mode(address, mr->set_reg, mr->clr_reg);
    158	    if (mode == INTC_MODE_NONE)
    159                continue;
    160
    161	    *modep = mode;
    162	    *datap = &mr->value;
    163	    *enums = mr->enum_ids;
    164	    *first = mr->reg_width - 1;
    165	    *width = 1;
    166	    return;
    167	}
    168    }
    169
    170    if (desc->prio_regs) {
    171        for (i = 0; i < desc->nr_prio_regs; i++) {
    172	    struct intc_prio_reg *pr = desc->prio_regs + i;
    173
    174	    mode = sh_intc_mode(address, pr->set_reg, pr->clr_reg);
    175	    if (mode == INTC_MODE_NONE)
    176                continue;
    177
    178	    *modep = mode | INTC_MODE_IS_PRIO;
    179	    *datap = &pr->value;
    180	    *enums = pr->enum_ids;
    181	    *first = (pr->reg_width / pr->field_width) - 1;
    182	    *width = pr->field_width;
    183	    return;
    184	}
    185    }
    186
    187    abort();
    188}
    189
    190static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id,
    191				int enable, int is_group)
    192{
    193    struct intc_source *source = desc->sources + id;
    194
    195    if (!id)
    196	return;
    197
    198    if (!source->next_enum_id && (!source->enable_max || !source->vect)) {
    199#ifdef DEBUG_INTC_SOURCES
    200        printf("sh_intc: reserved interrupt source %d modified\n", id);
    201#endif
    202	return;
    203    }
    204
    205    if (source->vect)
    206        sh_intc_toggle_source(source, enable ? 1 : -1, 0);
    207
    208#ifdef DEBUG_INTC
    209    else {
    210        printf("setting interrupt group %d to %d\n", id, !!enable);
    211    }
    212#endif
    213
    214    if ((is_group || !source->vect) && source->next_enum_id) {
    215        sh_intc_toggle_mask(desc, source->next_enum_id, enable, 1);
    216    }
    217
    218#ifdef DEBUG_INTC
    219    if (!source->vect) {
    220        printf("setting interrupt group %d to %d - done\n", id, !!enable);
    221    }
    222#endif
    223}
    224
    225static uint64_t sh_intc_read(void *opaque, hwaddr offset,
    226                             unsigned size)
    227{
    228    struct intc_desc *desc = opaque;
    229    intc_enum *enum_ids = NULL;
    230    unsigned int first = 0;
    231    unsigned int width = 0;
    232    unsigned int mode = 0;
    233    unsigned long *valuep;
    234
    235#ifdef DEBUG_INTC
    236    printf("sh_intc_read 0x%lx\n", (unsigned long) offset);
    237#endif
    238
    239    sh_intc_locate(desc, (unsigned long)offset, &valuep, 
    240		   &enum_ids, &first, &width, &mode);
    241    return *valuep;
    242}
    243
    244static void sh_intc_write(void *opaque, hwaddr offset,
    245                          uint64_t value, unsigned size)
    246{
    247    struct intc_desc *desc = opaque;
    248    intc_enum *enum_ids = NULL;
    249    unsigned int first = 0;
    250    unsigned int width = 0;
    251    unsigned int mode = 0;
    252    unsigned int k;
    253    unsigned long *valuep;
    254    unsigned long mask;
    255
    256#ifdef DEBUG_INTC
    257    printf("sh_intc_write 0x%lx 0x%08x\n", (unsigned long) offset, value);
    258#endif
    259
    260    sh_intc_locate(desc, (unsigned long)offset, &valuep, 
    261		   &enum_ids, &first, &width, &mode);
    262
    263    switch (mode) {
    264    case INTC_MODE_ENABLE_REG | INTC_MODE_IS_PRIO: break;
    265    case INTC_MODE_DUAL_SET: value |= *valuep; break;
    266    case INTC_MODE_DUAL_CLR: value = *valuep & ~value; break;
    267    default: abort();
    268    }
    269
    270    for (k = 0; k <= first; k++) {
    271        mask = ((1 << width) - 1) << ((first - k) * width);
    272
    273	if ((*valuep & mask) == (value & mask))
    274            continue;
    275#if 0
    276	printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n", 
    277	       k, first, enum_ids[k], (unsigned int)mask);
    278#endif
    279        sh_intc_toggle_mask(desc, enum_ids[k], value & mask, 0);
    280    }
    281
    282    *valuep = value;
    283
    284#ifdef DEBUG_INTC
    285    printf("sh_intc_write 0x%lx -> 0x%08x\n", (unsigned long) offset, value);
    286#endif
    287}
    288
    289static const MemoryRegionOps sh_intc_ops = {
    290    .read = sh_intc_read,
    291    .write = sh_intc_write,
    292    .endianness = DEVICE_NATIVE_ENDIAN,
    293};
    294
    295struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id)
    296{
    297    if (id)
    298        return desc->sources + id;
    299
    300    return NULL;
    301}
    302
    303static unsigned int sh_intc_register(MemoryRegion *sysmem,
    304                             struct intc_desc *desc,
    305                             const unsigned long address,
    306                             const char *type,
    307                             const char *action,
    308                             const unsigned int index)
    309{
    310    char name[60];
    311    MemoryRegion *iomem, *iomem_p4, *iomem_a7;
    312
    313    if (!address) {
    314        return 0;
    315    }
    316
    317    iomem = &desc->iomem;
    318    iomem_p4 = desc->iomem_aliases + index;
    319    iomem_a7 = iomem_p4 + 1;
    320
    321#define SH_INTC_IOMEM_FORMAT "interrupt-controller-%s-%s-%s"
    322    snprintf(name, sizeof(name), SH_INTC_IOMEM_FORMAT, type, action, "p4");
    323    memory_region_init_alias(iomem_p4, NULL, name, iomem, INTC_A7(address), 4);
    324    memory_region_add_subregion(sysmem, P4ADDR(address), iomem_p4);
    325
    326    snprintf(name, sizeof(name), SH_INTC_IOMEM_FORMAT, type, action, "a7");
    327    memory_region_init_alias(iomem_a7, NULL, name, iomem, INTC_A7(address), 4);
    328    memory_region_add_subregion(sysmem, A7ADDR(address), iomem_a7);
    329#undef SH_INTC_IOMEM_FORMAT
    330
    331    /* used to increment aliases index */
    332    return 2;
    333}
    334
    335static void sh_intc_register_source(struct intc_desc *desc,
    336				    intc_enum source,
    337				    struct intc_group *groups,
    338				    int nr_groups)
    339{
    340    unsigned int i, k;
    341    struct intc_source *s;
    342
    343    if (desc->mask_regs) {
    344        for (i = 0; i < desc->nr_mask_regs; i++) {
    345	    struct intc_mask_reg *mr = desc->mask_regs + i;
    346
    347	    for (k = 0; k < ARRAY_SIZE(mr->enum_ids); k++) {
    348                if (mr->enum_ids[k] != source)
    349                    continue;
    350
    351		s = sh_intc_source(desc, mr->enum_ids[k]);
    352		if (s)
    353                    s->enable_max++;
    354	    }
    355	}
    356    }
    357
    358    if (desc->prio_regs) {
    359        for (i = 0; i < desc->nr_prio_regs; i++) {
    360	    struct intc_prio_reg *pr = desc->prio_regs + i;
    361
    362	    for (k = 0; k < ARRAY_SIZE(pr->enum_ids); k++) {
    363                if (pr->enum_ids[k] != source)
    364                    continue;
    365
    366		s = sh_intc_source(desc, pr->enum_ids[k]);
    367		if (s)
    368                    s->enable_max++;
    369	    }
    370	}
    371    }
    372
    373    if (groups) {
    374        for (i = 0; i < nr_groups; i++) {
    375	    struct intc_group *gr = groups + i;
    376
    377	    for (k = 0; k < ARRAY_SIZE(gr->enum_ids); k++) {
    378                if (gr->enum_ids[k] != source)
    379                    continue;
    380
    381		s = sh_intc_source(desc, gr->enum_ids[k]);
    382		if (s)
    383                    s->enable_max++;
    384	    }
    385	}
    386    }
    387
    388}
    389
    390void sh_intc_register_sources(struct intc_desc *desc,
    391			      struct intc_vect *vectors,
    392			      int nr_vectors,
    393			      struct intc_group *groups,
    394			      int nr_groups)
    395{
    396    unsigned int i, k;
    397    struct intc_source *s;
    398
    399    for (i = 0; i < nr_vectors; i++) {
    400	struct intc_vect *vect = vectors + i;
    401
    402	sh_intc_register_source(desc, vect->enum_id, groups, nr_groups);
    403	s = sh_intc_source(desc, vect->enum_id);
    404        if (s) {
    405            s->vect = vect->vect;
    406
    407#ifdef DEBUG_INTC_SOURCES
    408            printf("sh_intc: registered source %d -> 0x%04x (%d/%d)\n",
    409                   vect->enum_id, s->vect, s->enable_count, s->enable_max);
    410#endif
    411        }
    412    }
    413
    414    if (groups) {
    415        for (i = 0; i < nr_groups; i++) {
    416	    struct intc_group *gr = groups + i;
    417
    418	    s = sh_intc_source(desc, gr->enum_id);
    419	    s->next_enum_id = gr->enum_ids[0];
    420
    421	    for (k = 1; k < ARRAY_SIZE(gr->enum_ids); k++) {
    422                if (!gr->enum_ids[k])
    423                    continue;
    424
    425		s = sh_intc_source(desc, gr->enum_ids[k - 1]);
    426		s->next_enum_id = gr->enum_ids[k];
    427	    }
    428
    429#ifdef DEBUG_INTC_SOURCES
    430	    printf("sh_intc: registered group %d (%d/%d)\n",
    431		   gr->enum_id, s->enable_count, s->enable_max);
    432#endif
    433	}
    434    }
    435}
    436
    437int sh_intc_init(MemoryRegion *sysmem,
    438         struct intc_desc *desc,
    439		 int nr_sources,
    440		 struct intc_mask_reg *mask_regs,
    441		 int nr_mask_regs,
    442		 struct intc_prio_reg *prio_regs,
    443		 int nr_prio_regs)
    444{
    445    unsigned int i, j;
    446
    447    desc->pending = 0;
    448    desc->nr_sources = nr_sources;
    449    desc->mask_regs = mask_regs;
    450    desc->nr_mask_regs = nr_mask_regs;
    451    desc->prio_regs = prio_regs;
    452    desc->nr_prio_regs = nr_prio_regs;
    453    /* Allocate 4 MemoryRegions per register (2 actions * 2 aliases).
    454     **/
    455    desc->iomem_aliases = g_new0(MemoryRegion,
    456                                 (nr_mask_regs + nr_prio_regs) * 4);
    457
    458    j = 0;
    459    i = sizeof(struct intc_source) * nr_sources;
    460    desc->sources = g_malloc0(i);
    461
    462    for (i = 0; i < desc->nr_sources; i++) {
    463        struct intc_source *source = desc->sources + i;
    464
    465        source->parent = desc;
    466    }
    467
    468    desc->irqs = qemu_allocate_irqs(sh_intc_set_irq, desc, nr_sources);
    469 
    470    memory_region_init_io(&desc->iomem, NULL, &sh_intc_ops, desc,
    471                          "interrupt-controller", 0x100000000ULL);
    472
    473#define INT_REG_PARAMS(reg_struct, type, action, j) \
    474        reg_struct->action##_reg, #type, #action, j
    475    if (desc->mask_regs) {
    476        for (i = 0; i < desc->nr_mask_regs; i++) {
    477	    struct intc_mask_reg *mr = desc->mask_regs + i;
    478
    479            j += sh_intc_register(sysmem, desc,
    480                                  INT_REG_PARAMS(mr, mask, set, j));
    481            j += sh_intc_register(sysmem, desc,
    482                                  INT_REG_PARAMS(mr, mask, clr, j));
    483	}
    484    }
    485
    486    if (desc->prio_regs) {
    487        for (i = 0; i < desc->nr_prio_regs; i++) {
    488	    struct intc_prio_reg *pr = desc->prio_regs + i;
    489
    490            j += sh_intc_register(sysmem, desc,
    491                                  INT_REG_PARAMS(pr, prio, set, j));
    492            j += sh_intc_register(sysmem, desc,
    493                                  INT_REG_PARAMS(pr, prio, clr, j));
    494	}
    495    }
    496#undef INT_REG_PARAMS
    497
    498    return 0;
    499}
    500
    501/* Assert level <n> IRL interrupt. 
    502   0:deassert. 1:lowest priority,... 15:highest priority. */
    503void sh_intc_set_irl(void *opaque, int n, int level)
    504{
    505    struct intc_source *s = opaque;
    506    int i, irl = level ^ 15;
    507    for (i = 0; (s = sh_intc_source(s->parent, s->next_enum_id)); i++) {
    508	if (i == irl)
    509	    sh_intc_toggle_source(s, s->enable_count?0:1, s->asserted?0:1);
    510	else
    511	    if (s->asserted)
    512	        sh_intc_toggle_source(s, 0, -1);
    513    }
    514}