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