X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/6ee093c90761eabfc6255624000d3d8248802209..7e0380b9bbf7c40361e06e6e0d8675a55bd0dea0:/hw/i8259.c diff --git a/hw/i8259.c b/hw/i8259.c index 9b2896c3fc..ab519de5d8 100644 --- a/hw/i8259.c +++ b/hw/i8259.c @@ -30,10 +30,18 @@ /* debug PIC */ //#define DEBUG_PIC +#ifdef DEBUG_PIC +#define DPRINTF(fmt, ...) \ + do { printf("pic: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) +#endif + //#define DEBUG_IRQ_LATENCY //#define DEBUG_IRQ_COUNT -typedef struct PicState { +struct PicState { + ISADevice dev; uint8_t last_irr; /* edge detection */ uint8_t irr; /* interrupt request register */ uint8_t imr; /* interrupt mask register */ @@ -51,60 +59,39 @@ typedef struct PicState { uint8_t single_mode; /* true if slave pic is not initialized */ uint8_t elcr; /* PIIX edge/trigger selection*/ uint8_t elcr_mask; - PicState2 *pics_state; -} PicState; - -struct PicState2 { - /* 0 is master pic, 1 is slave pic */ - /* XXX: better separation between the two pics */ - PicState pics[2]; - qemu_irq parent_irq; - void *irq_request_opaque; + qemu_irq int_out[1]; + uint32_t master; /* reflects /SP input pin */ + uint32_t iobase; + uint32_t elcr_addr; + MemoryRegion base_io; + MemoryRegion elcr_io; }; -#if defined(DEBUG_PIC) || defined (DEBUG_IRQ_COUNT) +#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) static int irq_level[16]; #endif #ifdef DEBUG_IRQ_COUNT static uint64_t irq_count[16]; #endif - -/* set irq level. If an edge is detected, then the IRR is set to 1 */ -static inline void pic_set_irq1(PicState *s, int irq, int level) -{ - int mask; - mask = 1 << irq; - if (s->elcr & mask) { - /* level triggered */ - if (level) { - s->irr |= mask; - s->last_irr |= mask; - } else { - s->irr &= ~mask; - s->last_irr &= ~mask; - } - } else { - /* edge triggered */ - if (level) { - if ((s->last_irr & mask) == 0) - s->irr |= mask; - s->last_irr |= mask; - } else { - s->last_irr &= ~mask; - } - } -} +#ifdef DEBUG_IRQ_LATENCY +static int64_t irq_time[16]; +#endif +PicState *isa_pic; +static PicState *slave_pic; /* return the highest priority found in mask (highest = smallest number). Return 8 if no irq */ -static inline int get_priority(PicState *s, int mask) +static int get_priority(PicState *s, int mask) { int priority; - if (mask == 0) + + if (mask == 0) { return 8; + } priority = 0; - while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) + while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) { priority++; + } return priority; } @@ -115,16 +102,19 @@ static int pic_get_irq(PicState *s) mask = s->irr & ~s->imr; priority = get_priority(s, mask); - if (priority == 8) + if (priority == 8) { return -1; + } /* compute current priority. If special fully nested mode on the master, the IRQ coming from the slave is not taken into account for the priority computation. */ mask = s->isr; - if (s->special_mask) + if (s->special_mask) { mask &= ~s->imr; - if (s->special_fully_nested_mode && s == &s->pics_state->pics[0]) + } + if (s->special_fully_nested_mode && s->master) { mask &= ~(1 << 2); + } cur_priority = get_priority(s, mask); if (priority < cur_priority) { /* higher priority found: an irq should be generated */ @@ -134,132 +124,130 @@ static int pic_get_irq(PicState *s) } } -/* raise irq to CPU if necessary. must be called every time the active - irq may change */ -/* XXX: should not export it, but it is needed for an APIC kludge */ -void pic_update_irq(PicState2 *s) +/* Update INT output. Must be called every time the output may have changed. */ +static void pic_update_irq(PicState *s) { - int irq2, irq; - - /* first look at slave pic */ - irq2 = pic_get_irq(&s->pics[1]); - if (irq2 >= 0) { - /* if irq request by slave pic, signal master PIC */ - pic_set_irq1(&s->pics[0], 2, 1); - pic_set_irq1(&s->pics[0], 2, 0); - } - /* look at requested irq */ - irq = pic_get_irq(&s->pics[0]); - if (irq >= 0) { -#if defined(DEBUG_PIC) - { - int i; - for(i = 0; i < 2; i++) { - printf("pic%d: imr=%x irr=%x padd=%d\n", - i, s->pics[i].imr, s->pics[i].irr, - s->pics[i].priority_add); + int irq; - } - } - printf("pic: cpu_interrupt\n"); -#endif - qemu_irq_raise(s->parent_irq); - } - -/* all targets should do this rather than acking the IRQ in the cpu */ -#if defined(TARGET_MIPS) || defined(TARGET_PPC) || defined(TARGET_ALPHA) - else { - qemu_irq_lower(s->parent_irq); + irq = pic_get_irq(s); + if (irq >= 0) { + DPRINTF("pic%d: imr=%x irr=%x padd=%d\n", + s->master ? 0 : 1, s->imr, s->irr, s->priority_add); + qemu_irq_raise(s->int_out[0]); + } else { + qemu_irq_lower(s->int_out[0]); } -#endif } -#ifdef DEBUG_IRQ_LATENCY -int64_t irq_time[16]; -#endif - -static void i8259_set_irq(void *opaque, int irq, int level) +/* set irq level. If an edge is detected, then the IRR is set to 1 */ +static void pic_set_irq(void *opaque, int irq, int level) { - PicState2 *s = opaque; + PicState *s = opaque; + int mask = 1 << irq; -#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) - if (level != irq_level[irq]) { -#if defined(DEBUG_PIC) - printf("i8259_set_irq: irq=%d level=%d\n", irq, level); +#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) || \ + defined(DEBUG_IRQ_LATENCY) + int irq_index = s->master ? irq : irq + 8; #endif - irq_level[irq] = level; +#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) + if (level != irq_level[irq_index]) { + DPRINTF("pic_set_irq: irq=%d level=%d\n", irq_index, level); + irq_level[irq_index] = level; #ifdef DEBUG_IRQ_COUNT - if (level == 1) - irq_count[irq]++; + if (level == 1) { + irq_count[irq_index]++; + } #endif } #endif #ifdef DEBUG_IRQ_LATENCY if (level) { - irq_time[irq] = qemu_get_clock(vm_clock); + irq_time[irq_index] = qemu_get_clock_ns(vm_clock); } #endif - pic_set_irq1(&s->pics[irq >> 3], irq & 7, level); + + if (s->elcr & mask) { + /* level triggered */ + if (level) { + s->irr |= mask; + s->last_irr |= mask; + } else { + s->irr &= ~mask; + s->last_irr &= ~mask; + } + } else { + /* edge triggered */ + if (level) { + if ((s->last_irr & mask) == 0) { + s->irr |= mask; + } + s->last_irr |= mask; + } else { + s->last_irr &= ~mask; + } + } pic_update_irq(s); } /* acknowledge interrupt 'irq' */ -static inline void pic_intack(PicState *s, int irq) +static void pic_intack(PicState *s, int irq) { if (s->auto_eoi) { - if (s->rotate_on_auto_eoi) + if (s->rotate_on_auto_eoi) { s->priority_add = (irq + 1) & 7; + } } else { s->isr |= (1 << irq); } /* We don't clear a level sensitive interrupt here */ - if (!(s->elcr & (1 << irq))) + if (!(s->elcr & (1 << irq))) { s->irr &= ~(1 << irq); + } + pic_update_irq(s); } -int pic_read_irq(PicState2 *s) +int pic_read_irq(PicState *s) { int irq, irq2, intno; - irq = pic_get_irq(&s->pics[0]); + irq = pic_get_irq(s); if (irq >= 0) { - pic_intack(&s->pics[0], irq); if (irq == 2) { - irq2 = pic_get_irq(&s->pics[1]); + irq2 = pic_get_irq(slave_pic); if (irq2 >= 0) { - pic_intack(&s->pics[1], irq2); + pic_intack(slave_pic, irq2); } else { /* spurious IRQ on slave controller */ irq2 = 7; } - intno = s->pics[1].irq_base + irq2; - irq = irq2 + 8; + intno = slave_pic->irq_base + irq2; } else { - intno = s->pics[0].irq_base + irq; + intno = s->irq_base + irq; } + pic_intack(s, irq); } else { /* spurious IRQ on host controller */ irq = 7; - intno = s->pics[0].irq_base + irq; + intno = s->irq_base + irq; } - pic_update_irq(s); +#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_LATENCY) + if (irq == 2) { + irq = irq2 + 8; + } +#endif #ifdef DEBUG_IRQ_LATENCY printf("IRQ%d latency=%0.3fus\n", irq, - (double)(qemu_get_clock(vm_clock) - + (double)(qemu_get_clock_ns(vm_clock) - irq_time[irq]) * 1000000.0 / get_ticks_per_sec()); #endif -#if defined(DEBUG_PIC) - printf("pic_interrupt: irq=%d\n", irq); -#endif + DPRINTF("pic_interrupt: irq=%d\n", irq); return intno; } -static void pic_reset(void *opaque) +static void pic_init_reset(PicState *s) { - PicState *s = opaque; - s->last_irr = 0; s->irr = 0; s->imr = 0; @@ -276,38 +264,48 @@ static void pic_reset(void *opaque) s->init4 = 0; s->single_mode = 0; /* Note: ELCR is not reset */ + pic_update_irq(s); } -static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val) +static void pic_reset(DeviceState *dev) +{ + PicState *s = container_of(dev, PicState, dev.qdev); + + pic_init_reset(s); + s->elcr = 0; +} + +static void pic_ioport_write(void *opaque, target_phys_addr_t addr64, + uint64_t val64, unsigned size) { PicState *s = opaque; + uint32_t addr = addr64; + uint32_t val = val64; int priority, cmd, irq; -#ifdef DEBUG_PIC - printf("pic_write: addr=0x%02x val=0x%02x\n", addr, val); -#endif - addr &= 1; + DPRINTF("write: addr=0x%02x val=0x%02x\n", addr, val); if (addr == 0) { if (val & 0x10) { - /* init */ - pic_reset(s); - /* deassert a pending interrupt */ - qemu_irq_lower(s->pics_state->parent_irq); + pic_init_reset(s); s->init_state = 1; s->init4 = val & 1; s->single_mode = val & 2; - if (val & 0x08) + if (val & 0x08) { hw_error("level sensitive irq not supported"); + } } else if (val & 0x08) { - if (val & 0x04) + if (val & 0x04) { s->poll = 1; - if (val & 0x02) + } + if (val & 0x02) { s->read_reg_select = val & 1; - if (val & 0x40) + } + if (val & 0x40) { s->special_mask = (val >> 5) & 1; + } } else { cmd = val >> 5; - switch(cmd) { + switch (cmd) { case 0: case 4: s->rotate_on_auto_eoi = cmd >> 2; @@ -318,25 +316,26 @@ static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val) if (priority != 8) { irq = (priority + s->priority_add) & 7; s->isr &= ~(1 << irq); - if (cmd == 5) + if (cmd == 5) { s->priority_add = (irq + 1) & 7; - pic_update_irq(s->pics_state); + } + pic_update_irq(s); } break; case 3: irq = val & 7; s->isr &= ~(1 << irq); - pic_update_irq(s->pics_state); + pic_update_irq(s); break; case 6: s->priority_add = (val + 1) & 7; - pic_update_irq(s->pics_state); + pic_update_irq(s); break; case 7: irq = val & 7; s->isr &= ~(1 << irq); s->priority_add = (irq + 1) & 7; - pic_update_irq(s->pics_state); + pic_update_irq(s); break; default: /* no operation */ @@ -344,11 +343,11 @@ static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val) } } } else { - switch(s->init_state) { + switch (s->init_state) { case 0: /* normal mode */ s->imr = val; - pic_update_irq(s->pics_state); + pic_update_irq(s); break; case 1: s->irq_base = val & 0xf8; @@ -370,141 +369,117 @@ static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val) } } -static uint32_t pic_poll_read (PicState *s, uint32_t addr1) -{ - int ret; - - ret = pic_get_irq(s); - if (ret >= 0) { - if (addr1 >> 7) { - s->pics_state->pics[0].isr &= ~(1 << 2); - s->pics_state->pics[0].irr &= ~(1 << 2); - } - s->irr &= ~(1 << ret); - s->isr &= ~(1 << ret); - if (addr1 >> 7 || ret != 2) - pic_update_irq(s->pics_state); - } else { - ret = 0x07; - pic_update_irq(s->pics_state); - } - - return ret; -} - -static uint32_t pic_ioport_read(void *opaque, uint32_t addr1) +static uint64_t pic_ioport_read(void *opaque, target_phys_addr_t addr, + unsigned size) { PicState *s = opaque; - unsigned int addr; int ret; - addr = addr1; - addr &= 1; if (s->poll) { - ret = pic_poll_read(s, addr1); + ret = pic_get_irq(s); + if (ret >= 0) { + pic_intack(s, ret); + ret |= 0x80; + } else { + ret = 0; + } s->poll = 0; } else { if (addr == 0) { - if (s->read_reg_select) + if (s->read_reg_select) { ret = s->isr; - else + } else { ret = s->irr; + } } else { ret = s->imr; } } -#ifdef DEBUG_PIC - printf("pic_read: addr=0x%02x val=0x%02x\n", addr1, ret); -#endif + DPRINTF("read: addr=0x%02x val=0x%02x\n", addr, ret); return ret; } -/* memory mapped interrupt status */ -/* XXX: may be the same than pic_read_irq() */ -uint32_t pic_intack_read(PicState2 *s) +int pic_get_output(PicState *s) { - int ret; - - ret = pic_poll_read(&s->pics[0], 0x00); - if (ret == 2) - ret = pic_poll_read(&s->pics[1], 0x80) + 8; - /* Prepare for ISR read */ - s->pics[0].read_reg_select = 1; - - return ret; + return (pic_get_irq(s) >= 0); } -static void elcr_ioport_write(void *opaque, uint32_t addr, uint32_t val) +static void elcr_ioport_write(void *opaque, target_phys_addr_t addr, + uint64_t val, unsigned size) { PicState *s = opaque; s->elcr = val & s->elcr_mask; } -static uint32_t elcr_ioport_read(void *opaque, uint32_t addr1) +static uint64_t elcr_ioport_read(void *opaque, target_phys_addr_t addr, + unsigned size) { PicState *s = opaque; return s->elcr; } -static void pic_save(QEMUFile *f, void *opaque) -{ - PicState *s = opaque; +static const VMStateDescription vmstate_pic = { + .name = "i8259", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(last_irr, PicState), + VMSTATE_UINT8(irr, PicState), + VMSTATE_UINT8(imr, PicState), + VMSTATE_UINT8(isr, PicState), + VMSTATE_UINT8(priority_add, PicState), + VMSTATE_UINT8(irq_base, PicState), + VMSTATE_UINT8(read_reg_select, PicState), + VMSTATE_UINT8(poll, PicState), + VMSTATE_UINT8(special_mask, PicState), + VMSTATE_UINT8(init_state, PicState), + VMSTATE_UINT8(auto_eoi, PicState), + VMSTATE_UINT8(rotate_on_auto_eoi, PicState), + VMSTATE_UINT8(special_fully_nested_mode, PicState), + VMSTATE_UINT8(init4, PicState), + VMSTATE_UINT8(single_mode, PicState), + VMSTATE_UINT8(elcr, PicState), + VMSTATE_END_OF_LIST() + } +}; - qemu_put_8s(f, &s->last_irr); - qemu_put_8s(f, &s->irr); - qemu_put_8s(f, &s->imr); - qemu_put_8s(f, &s->isr); - qemu_put_8s(f, &s->priority_add); - qemu_put_8s(f, &s->irq_base); - qemu_put_8s(f, &s->read_reg_select); - qemu_put_8s(f, &s->poll); - qemu_put_8s(f, &s->special_mask); - qemu_put_8s(f, &s->init_state); - qemu_put_8s(f, &s->auto_eoi); - qemu_put_8s(f, &s->rotate_on_auto_eoi); - qemu_put_8s(f, &s->special_fully_nested_mode); - qemu_put_8s(f, &s->init4); - qemu_put_8s(f, &s->single_mode); - qemu_put_8s(f, &s->elcr); -} +static const MemoryRegionOps pic_base_ioport_ops = { + .read = pic_ioport_read, + .write = pic_ioport_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static const MemoryRegionOps pic_elcr_ioport_ops = { + .read = elcr_ioport_read, + .write = elcr_ioport_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; -static int pic_load(QEMUFile *f, void *opaque, int version_id) +static int pic_initfn(ISADevice *dev) { - PicState *s = opaque; + PicState *s = DO_UPCAST(PicState, dev, dev); - if (version_id != 1) - return -EINVAL; - - qemu_get_8s(f, &s->last_irr); - qemu_get_8s(f, &s->irr); - qemu_get_8s(f, &s->imr); - qemu_get_8s(f, &s->isr); - qemu_get_8s(f, &s->priority_add); - qemu_get_8s(f, &s->irq_base); - qemu_get_8s(f, &s->read_reg_select); - qemu_get_8s(f, &s->poll); - qemu_get_8s(f, &s->special_mask); - qemu_get_8s(f, &s->init_state); - qemu_get_8s(f, &s->auto_eoi); - qemu_get_8s(f, &s->rotate_on_auto_eoi); - qemu_get_8s(f, &s->special_fully_nested_mode); - qemu_get_8s(f, &s->init4); - qemu_get_8s(f, &s->single_mode); - qemu_get_8s(f, &s->elcr); - return 0; -} + memory_region_init_io(&s->base_io, &pic_base_ioport_ops, s, "pic", 2); + memory_region_init_io(&s->elcr_io, &pic_elcr_ioport_ops, s, "elcr", 1); -/* XXX: add generic master/slave system */ -static void pic_init1(int io_addr, int elcr_addr, PicState *s) -{ - register_ioport_write(io_addr, 2, 1, pic_ioport_write, s); - register_ioport_read(io_addr, 2, 1, pic_ioport_read, s); - if (elcr_addr >= 0) { - register_ioport_write(elcr_addr, 1, 1, elcr_ioport_write, s); - register_ioport_read(elcr_addr, 1, 1, elcr_ioport_read, s); + isa_register_ioport(NULL, &s->base_io, s->iobase); + if (s->elcr_addr != -1) { + isa_register_ioport(NULL, &s->elcr_io, s->elcr_addr); } - register_savevm("i8259", io_addr, 1, pic_save, pic_load, s); - qemu_register_reset(pic_reset, s); + + qdev_init_gpio_out(&dev->qdev, s->int_out, ARRAY_SIZE(s->int_out)); + qdev_init_gpio_in(&dev->qdev, pic_set_irq, 8); + + qdev_set_legacy_instance_id(&dev->qdev, s->iobase, 1); + + return 0; } void pic_info(Monitor *mon) @@ -512,11 +487,11 @@ void pic_info(Monitor *mon) int i; PicState *s; - if (!isa_pic) + if (!isa_pic) { return; - - for(i=0;i<2;i++) { - s = &isa_pic->pics[i]; + } + for (i = 0; i < 2; i++) { + s = i == 0 ? isa_pic : slave_pic; monitor_printf(mon, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d " "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n", i, s->irr, s->imr, s->isr, s->priority_add, @@ -536,24 +511,69 @@ void irq_info(Monitor *mon) monitor_printf(mon, "IRQ statistics:\n"); for (i = 0; i < 16; i++) { count = irq_count[i]; - if (count > 0) + if (count > 0) { monitor_printf(mon, "%2d: %" PRId64 "\n", i, count); + } } #endif } qemu_irq *i8259_init(qemu_irq parent_irq) { - PicState2 *s; - - s = qemu_mallocz(sizeof(PicState2)); - pic_init1(0x20, 0x4d0, &s->pics[0]); - pic_init1(0xa0, 0x4d1, &s->pics[1]); - s->pics[0].elcr_mask = 0xf8; - s->pics[1].elcr_mask = 0xde; - s->parent_irq = parent_irq; - s->pics[0].pics_state = s; - s->pics[1].pics_state = s; - isa_pic = s; - return qemu_allocate_irqs(i8259_set_irq, s, 16); + qemu_irq *irq_set; + ISADevice *dev; + int i; + + irq_set = g_malloc(ISA_NUM_IRQS * sizeof(qemu_irq)); + + dev = isa_create("isa-i8259"); + qdev_prop_set_uint32(&dev->qdev, "iobase", 0x20); + qdev_prop_set_uint32(&dev->qdev, "elcr_addr", 0x4d0); + qdev_prop_set_uint8(&dev->qdev, "elcr_mask", 0xf8); + qdev_prop_set_bit(&dev->qdev, "master", true); + qdev_init_nofail(&dev->qdev); + + qdev_connect_gpio_out(&dev->qdev, 0, parent_irq); + for (i = 0 ; i < 8; i++) { + irq_set[i] = qdev_get_gpio_in(&dev->qdev, i); + } + + isa_pic = DO_UPCAST(PicState, dev, dev); + + dev = isa_create("isa-i8259"); + qdev_prop_set_uint32(&dev->qdev, "iobase", 0xa0); + qdev_prop_set_uint32(&dev->qdev, "elcr_addr", 0x4d1); + qdev_prop_set_uint8(&dev->qdev, "elcr_mask", 0xde); + qdev_init_nofail(&dev->qdev); + + qdev_connect_gpio_out(&dev->qdev, 0, irq_set[2]); + for (i = 0 ; i < 8; i++) { + irq_set[i + 8] = qdev_get_gpio_in(&dev->qdev, i); + } + + slave_pic = DO_UPCAST(PicState, dev, dev); + + return irq_set; +} + +static ISADeviceInfo i8259_info = { + .qdev.name = "isa-i8259", + .qdev.size = sizeof(PicState), + .qdev.vmsd = &vmstate_pic, + .qdev.reset = pic_reset, + .qdev.no_user = 1, + .init = pic_initfn, + .qdev.props = (Property[]) { + DEFINE_PROP_HEX32("iobase", PicState, iobase, -1), + DEFINE_PROP_HEX32("elcr_addr", PicState, elcr_addr, -1), + DEFINE_PROP_HEX8("elcr_mask", PicState, elcr_mask, -1), + DEFINE_PROP_BIT("master", PicState, master, 0, false), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void pic_register(void) +{ + isa_qdev_register(&i8259_info); } +device_init(pic_register)