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