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