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