qemu/hw/intc/i8259.c
<<
>>
Prefs
   1/*
   2 * QEMU 8259 interrupt controller emulation
   3 *
   4 * Copyright (c) 2003-2004 Fabrice Bellard
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24#include "qemu/osdep.h"
  25#include "hw/hw.h"
  26#include "hw/i386/pc.h"
  27#include "hw/isa/isa.h"
  28#include "monitor/monitor.h"
  29#include "qemu/timer.h"
  30#include "qemu/log.h"
  31#include "hw/isa/i8259_internal.h"
  32#include "hw/intc/intc.h"
  33
  34/* debug PIC */
  35//#define DEBUG_PIC
  36
  37#ifdef DEBUG_PIC
  38#define DPRINTF(fmt, ...)                                       \
  39    do { printf("pic: " fmt , ## __VA_ARGS__); } while (0)
  40#else
  41#define DPRINTF(fmt, ...)
  42#endif
  43
  44//#define DEBUG_IRQ_LATENCY
  45//#define DEBUG_IRQ_COUNT
  46
  47#define TYPE_I8259 "isa-i8259"
  48#define PIC_CLASS(class) OBJECT_CLASS_CHECK(PICClass, (class), TYPE_I8259)
  49#define PIC_GET_CLASS(obj) OBJECT_GET_CLASS(PICClass, (obj), TYPE_I8259)
  50
  51/**
  52 * PICClass:
  53 * @parent_realize: The parent's realizefn.
  54 */
  55typedef struct PICClass {
  56    PICCommonClass parent_class;
  57
  58    DeviceRealize parent_realize;
  59} PICClass;
  60
  61#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
  62static int irq_level[16];
  63#endif
  64#ifdef DEBUG_IRQ_COUNT
  65static uint64_t irq_count[16];
  66#endif
  67#ifdef DEBUG_IRQ_LATENCY
  68static int64_t irq_time[16];
  69#endif
  70DeviceState *isa_pic;
  71static PICCommonState *slave_pic;
  72
  73/* return the highest priority found in mask (highest = smallest
  74   number). Return 8 if no irq */
  75static int get_priority(PICCommonState *s, int mask)
  76{
  77    int priority;
  78
  79    if (mask == 0) {
  80        return 8;
  81    }
  82    priority = 0;
  83    while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) {
  84        priority++;
  85    }
  86    return priority;
  87}
  88
  89/* return the pic wanted interrupt. return -1 if none */
  90static int pic_get_irq(PICCommonState *s)
  91{
  92    int mask, cur_priority, priority;
  93
  94    mask = s->irr & ~s->imr;
  95    priority = get_priority(s, mask);
  96    if (priority == 8) {
  97        return -1;
  98    }
  99    /* compute current priority. If special fully nested mode on the
 100       master, the IRQ coming from the slave is not taken into account
 101       for the priority computation. */
 102    mask = s->isr;
 103    if (s->special_mask) {
 104        mask &= ~s->imr;
 105    }
 106    if (s->special_fully_nested_mode && s->master) {
 107        mask &= ~(1 << 2);
 108    }
 109    cur_priority = get_priority(s, mask);
 110    if (priority < cur_priority) {
 111        /* higher priority found: an irq should be generated */
 112        return (priority + s->priority_add) & 7;
 113    } else {
 114        return -1;
 115    }
 116}
 117
 118/* Update INT output. Must be called every time the output may have changed. */
 119static void pic_update_irq(PICCommonState *s)
 120{
 121    int irq;
 122
 123    irq = pic_get_irq(s);
 124    if (irq >= 0) {
 125        DPRINTF("pic%d: imr=%x irr=%x padd=%d\n",
 126                s->master ? 0 : 1, s->imr, s->irr, s->priority_add);
 127        qemu_irq_raise(s->int_out[0]);
 128    } else {
 129        qemu_irq_lower(s->int_out[0]);
 130    }
 131}
 132
 133/* set irq level. If an edge is detected, then the IRR is set to 1 */
 134static void pic_set_irq(void *opaque, int irq, int level)
 135{
 136    PICCommonState *s = opaque;
 137    int mask = 1 << irq;
 138
 139#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) || \
 140    defined(DEBUG_IRQ_LATENCY)
 141    int irq_index = s->master ? irq : irq + 8;
 142#endif
 143#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
 144    if (level != irq_level[irq_index]) {
 145        DPRINTF("pic_set_irq: irq=%d level=%d\n", irq_index, level);
 146        irq_level[irq_index] = level;
 147#ifdef DEBUG_IRQ_COUNT
 148        if (level == 1) {
 149            irq_count[irq_index]++;
 150        }
 151#endif
 152    }
 153#endif
 154#ifdef DEBUG_IRQ_LATENCY
 155    if (level) {
 156        irq_time[irq_index] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
 157    }
 158#endif
 159
 160    if (s->elcr & mask) {
 161        /* level triggered */
 162        if (level) {
 163            s->irr |= mask;
 164            s->last_irr |= mask;
 165        } else {
 166            s->irr &= ~mask;
 167            s->last_irr &= ~mask;
 168        }
 169    } else {
 170        /* edge triggered */
 171        if (level) {
 172            if ((s->last_irr & mask) == 0) {
 173                s->irr |= mask;
 174            }
 175            s->last_irr |= mask;
 176        } else {
 177            s->last_irr &= ~mask;
 178        }
 179    }
 180    pic_update_irq(s);
 181}
 182
 183/* acknowledge interrupt 'irq' */
 184static void pic_intack(PICCommonState *s, int irq)
 185{
 186    if (s->auto_eoi) {
 187        if (s->rotate_on_auto_eoi) {
 188            s->priority_add = (irq + 1) & 7;
 189        }
 190    } else {
 191        s->isr |= (1 << irq);
 192    }
 193    /* We don't clear a level sensitive interrupt here */
 194    if (!(s->elcr & (1 << irq))) {
 195        s->irr &= ~(1 << irq);
 196    }
 197    pic_update_irq(s);
 198}
 199
 200int pic_read_irq(DeviceState *d)
 201{
 202    PICCommonState *s = PIC_COMMON(d);
 203    int irq, irq2, intno;
 204
 205    irq = pic_get_irq(s);
 206    if (irq >= 0) {
 207        if (irq == 2) {
 208            irq2 = pic_get_irq(slave_pic);
 209            if (irq2 >= 0) {
 210                pic_intack(slave_pic, irq2);
 211            } else {
 212                /* spurious IRQ on slave controller */
 213                irq2 = 7;
 214            }
 215            intno = slave_pic->irq_base + irq2;
 216        } else {
 217            intno = s->irq_base + irq;
 218        }
 219        pic_intack(s, irq);
 220    } else {
 221        /* spurious IRQ on host controller */
 222        irq = 7;
 223        intno = s->irq_base + irq;
 224    }
 225
 226#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_LATENCY)
 227    if (irq == 2) {
 228        irq = irq2 + 8;
 229    }
 230#endif
 231#ifdef DEBUG_IRQ_LATENCY
 232    printf("IRQ%d latency=%0.3fus\n",
 233           irq,
 234           (double)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
 235                    irq_time[irq]) * 1000000.0 / NANOSECONDS_PER_SECOND);
 236#endif
 237    DPRINTF("pic_interrupt: irq=%d\n", irq);
 238    return intno;
 239}
 240
 241static void pic_init_reset(PICCommonState *s)
 242{
 243    pic_reset_common(s);
 244    pic_update_irq(s);
 245}
 246
 247static void pic_reset(DeviceState *dev)
 248{
 249    PICCommonState *s = PIC_COMMON(dev);
 250
 251    s->elcr = 0;
 252    pic_init_reset(s);
 253}
 254
 255static bool pic_get_statistics(InterruptStatsProvider *obj,
 256                               uint64_t **irq_counts, unsigned int *nb_irqs)
 257{
 258    PICCommonState *s = PIC_COMMON(obj);
 259
 260    if (s->master) {
 261#ifdef DEBUG_IRQ_COUNT
 262        *irq_counts = irq_count;
 263        *nb_irqs = ARRAY_SIZE(irq_count);
 264#else
 265        return false;
 266#endif
 267    } else {
 268        *irq_counts = NULL;
 269        *nb_irqs = 0;
 270    }
 271    return true;
 272}
 273
 274static void pic_print_info(InterruptStatsProvider *obj, Monitor *mon)
 275{
 276    PICCommonState *s = PIC_COMMON(obj);
 277    monitor_printf(mon, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d "
 278                   "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n",
 279                   s->master ? 0 : 1, s->irr, s->imr, s->isr, s->priority_add,
 280                   s->irq_base, s->read_reg_select, s->elcr,
 281                   s->special_fully_nested_mode);
 282}
 283
 284static void pic_ioport_write(void *opaque, hwaddr addr64,
 285                             uint64_t val64, unsigned size)
 286{
 287    PICCommonState *s = opaque;
 288    uint32_t addr = addr64;
 289    uint32_t val = val64;
 290    int priority, cmd, irq;
 291
 292    DPRINTF("write: addr=0x%02x val=0x%02x\n", addr, val);
 293    if (addr == 0) {
 294        if (val & 0x10) {
 295            pic_init_reset(s);
 296            s->init_state = 1;
 297            s->init4 = val & 1;
 298            s->single_mode = val & 2;
 299            if (val & 0x08) {
 300                qemu_log_mask(LOG_UNIMP,
 301                              "i8259: level sensitive irq not supported\n");
 302            }
 303        } else if (val & 0x08) {
 304            if (val & 0x04) {
 305                s->poll = 1;
 306            }
 307            if (val & 0x02) {
 308                s->read_reg_select = val & 1;
 309            }
 310            if (val & 0x40) {
 311                s->special_mask = (val >> 5) & 1;
 312            }
 313        } else {
 314            cmd = val >> 5;
 315            switch (cmd) {
 316            case 0:
 317            case 4:
 318                s->rotate_on_auto_eoi = cmd >> 2;
 319                break;
 320            case 1: /* end of interrupt */
 321            case 5:
 322                priority = get_priority(s, s->isr);
 323                if (priority != 8) {
 324                    irq = (priority + s->priority_add) & 7;
 325                    s->isr &= ~(1 << irq);
 326                    if (cmd == 5) {
 327                        s->priority_add = (irq + 1) & 7;
 328                    }
 329                    pic_update_irq(s);
 330                }
 331                break;
 332            case 3:
 333                irq = val & 7;
 334                s->isr &= ~(1 << irq);
 335                pic_update_irq(s);
 336                break;
 337            case 6:
 338                s->priority_add = (val + 1) & 7;
 339                pic_update_irq(s);
 340                break;
 341            case 7:
 342                irq = val & 7;
 343                s->isr &= ~(1 << irq);
 344                s->priority_add = (irq + 1) & 7;
 345                pic_update_irq(s);
 346                break;
 347            default:
 348                /* no operation */
 349                break;
 350            }
 351        }
 352    } else {
 353        switch (s->init_state) {
 354        case 0:
 355            /* normal mode */
 356            s->imr = val;
 357            pic_update_irq(s);
 358            break;
 359        case 1:
 360            s->irq_base = val & 0xf8;
 361            s->init_state = s->single_mode ? (s->init4 ? 3 : 0) : 2;
 362            break;
 363        case 2:
 364            if (s->init4) {
 365                s->init_state = 3;
 366            } else {
 367                s->init_state = 0;
 368            }
 369            break;
 370        case 3:
 371            s->special_fully_nested_mode = (val >> 4) & 1;
 372            s->auto_eoi = (val >> 1) & 1;
 373            s->init_state = 0;
 374            break;
 375        }
 376    }
 377}
 378
 379static uint64_t pic_ioport_read(void *opaque, hwaddr addr,
 380                                unsigned size)
 381{
 382    PICCommonState *s = opaque;
 383    int ret;
 384
 385    if (s->poll) {
 386        ret = pic_get_irq(s);
 387        if (ret >= 0) {
 388            pic_intack(s, ret);
 389            ret |= 0x80;
 390        } else {
 391            ret = 0;
 392        }
 393        s->poll = 0;
 394    } else {
 395        if (addr == 0) {
 396            if (s->read_reg_select) {
 397                ret = s->isr;
 398            } else {
 399                ret = s->irr;
 400            }
 401        } else {
 402            ret = s->imr;
 403        }
 404    }
 405    DPRINTF("read: addr=0x%02" HWADDR_PRIx " val=0x%02x\n", addr, ret);
 406    return ret;
 407}
 408
 409int pic_get_output(DeviceState *d)
 410{
 411    PICCommonState *s = PIC_COMMON(d);
 412
 413    return (pic_get_irq(s) >= 0);
 414}
 415
 416static void elcr_ioport_write(void *opaque, hwaddr addr,
 417                              uint64_t val, unsigned size)
 418{
 419    PICCommonState *s = opaque;
 420    s->elcr = val & s->elcr_mask;
 421}
 422
 423static uint64_t elcr_ioport_read(void *opaque, hwaddr addr,
 424                                 unsigned size)
 425{
 426    PICCommonState *s = opaque;
 427    return s->elcr;
 428}
 429
 430static const MemoryRegionOps pic_base_ioport_ops = {
 431    .read = pic_ioport_read,
 432    .write = pic_ioport_write,
 433    .impl = {
 434        .min_access_size = 1,
 435        .max_access_size = 1,
 436    },
 437};
 438
 439static const MemoryRegionOps pic_elcr_ioport_ops = {
 440    .read = elcr_ioport_read,
 441    .write = elcr_ioport_write,
 442    .impl = {
 443        .min_access_size = 1,
 444        .max_access_size = 1,
 445    },
 446};
 447
 448static void pic_realize(DeviceState *dev, Error **errp)
 449{
 450    PICCommonState *s = PIC_COMMON(dev);
 451    PICClass *pc = PIC_GET_CLASS(dev);
 452
 453    memory_region_init_io(&s->base_io, OBJECT(s), &pic_base_ioport_ops, s,
 454                          "pic", 2);
 455    memory_region_init_io(&s->elcr_io, OBJECT(s), &pic_elcr_ioport_ops, s,
 456                          "elcr", 1);
 457
 458    qdev_init_gpio_out(dev, s->int_out, ARRAY_SIZE(s->int_out));
 459    qdev_init_gpio_in(dev, pic_set_irq, 8);
 460
 461    pc->parent_realize(dev, errp);
 462}
 463
 464qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq)
 465{
 466    qemu_irq *irq_set;
 467    DeviceState *dev;
 468    ISADevice *isadev;
 469    int i;
 470
 471    irq_set = g_new0(qemu_irq, ISA_NUM_IRQS);
 472
 473    isadev = i8259_init_chip(TYPE_I8259, bus, true);
 474    dev = DEVICE(isadev);
 475
 476    qdev_connect_gpio_out(dev, 0, parent_irq);
 477    for (i = 0 ; i < 8; i++) {
 478        irq_set[i] = qdev_get_gpio_in(dev, i);
 479    }
 480
 481    isa_pic = dev;
 482
 483    isadev = i8259_init_chip(TYPE_I8259, bus, false);
 484    dev = DEVICE(isadev);
 485
 486    qdev_connect_gpio_out(dev, 0, irq_set[2]);
 487    for (i = 0 ; i < 8; i++) {
 488        irq_set[i + 8] = qdev_get_gpio_in(dev, i);
 489    }
 490
 491    slave_pic = PIC_COMMON(dev);
 492
 493    return irq_set;
 494}
 495
 496static void i8259_class_init(ObjectClass *klass, void *data)
 497{
 498    PICClass *k = PIC_CLASS(klass);
 499    DeviceClass *dc = DEVICE_CLASS(klass);
 500    InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(klass);
 501
 502    k->parent_realize = dc->realize;
 503    dc->realize = pic_realize;
 504    dc->reset = pic_reset;
 505    ic->get_statistics = pic_get_statistics;
 506    ic->print_info = pic_print_info;
 507}
 508
 509static const TypeInfo i8259_info = {
 510    .name       = TYPE_I8259,
 511    .instance_size = sizeof(PICCommonState),
 512    .parent     = TYPE_PIC_COMMON,
 513    .class_init = i8259_class_init,
 514    .class_size = sizeof(PICClass),
 515    .interfaces = (InterfaceInfo[]) {
 516        { TYPE_INTERRUPT_STATS_PROVIDER },
 517        { }
 518    },
 519};
 520
 521static void pic_register_types(void)
 522{
 523    type_register_static(&i8259_info);
 524}
 525
 526type_init(pic_register_types)
 527