From 512709f559dce1690fa89fe2a67a8e1984cd3895 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sun, 16 Oct 2011 14:38:45 +0200 Subject: [PATCH] i8259: Factor out base class for KVM reuse Analogously to the APIC, we will reuse some parts of the user space i8259 model for KVM. The base class provides a common device state, the vmstate, the property list, a reset core and some shared init bits. This also introduces a common helper to instantiate a single i8259 chip from the cascade-creating i8259_init function. Signed-off-by: Jan Kiszka --- Makefile.objs | 2 +- hw/i8259.c | 158 +++++++++----------------------------------- hw/i8259_common.c | 147 +++++++++++++++++++++++++++++++++++++++++ hw/i8259_internal.h | 76 +++++++++++++++++++++ 4 files changed, 254 insertions(+), 129 deletions(-) create mode 100644 hw/i8259_common.c create mode 100644 hw/i8259_internal.h diff --git a/Makefile.objs b/Makefile.objs index 4f6d26c917..6270a95747 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -220,7 +220,7 @@ hw-obj-$(CONFIG_APPLESMC) += applesmc.o hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o hw-obj-$(CONFIG_USB_REDIR) += usb-redir.o -hw-obj-$(CONFIG_I8259) += i8259.o +hw-obj-$(CONFIG_I8259) += i8259_common.o i8259.o # PPC devices hw-obj-$(CONFIG_PREP_PCI) += prep_pci.o diff --git a/hw/i8259.c b/hw/i8259.c index cfaa35c092..3005ce244d 100644 --- a/hw/i8259.c +++ b/hw/i8259.c @@ -26,6 +26,7 @@ #include "isa.h" #include "monitor.h" #include "qemu-timer.h" +#include "i8259_internal.h" /* debug PIC */ //#define DEBUG_PIC @@ -40,35 +41,6 @@ //#define DEBUG_IRQ_LATENCY //#define DEBUG_IRQ_COUNT -typedef struct PicState PicState; - -struct PicState { - ISADevice dev; - uint8_t last_irr; /* edge detection */ - uint8_t irr; /* interrupt request register */ - uint8_t imr; /* interrupt mask register */ - uint8_t isr; /* interrupt service register */ - uint8_t priority_add; /* highest irq priority */ - uint8_t irq_base; - uint8_t read_reg_select; - uint8_t poll; - uint8_t special_mask; - uint8_t init_state; - uint8_t auto_eoi; - uint8_t rotate_on_auto_eoi; - uint8_t special_fully_nested_mode; - uint8_t init4; /* true if 4 byte init */ - uint8_t single_mode; /* true if slave pic is not initialized */ - uint8_t elcr; /* PIIX edge/trigger selection*/ - uint8_t elcr_mask; - 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) static int irq_level[16]; #endif @@ -79,11 +51,11 @@ static uint64_t irq_count[16]; static int64_t irq_time[16]; #endif DeviceState *isa_pic; -static PicState *slave_pic; +static PICCommonState *slave_pic; /* return the highest priority found in mask (highest = smallest number). Return 8 if no irq */ -static int get_priority(PicState *s, int mask) +static int get_priority(PICCommonState *s, int mask) { int priority; @@ -98,7 +70,7 @@ static int get_priority(PicState *s, int mask) } /* return the pic wanted interrupt. return -1 if none */ -static int pic_get_irq(PicState *s) +static int pic_get_irq(PICCommonState *s) { int mask, cur_priority, priority; @@ -127,7 +99,7 @@ static int pic_get_irq(PicState *s) } /* Update INT output. Must be called every time the output may have changed. */ -static void pic_update_irq(PicState *s) +static void pic_update_irq(PICCommonState *s) { int irq; @@ -144,7 +116,7 @@ static void pic_update_irq(PicState *s) /* 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) { - PicState *s = opaque; + PICCommonState *s = opaque; int mask = 1 << irq; #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) || \ @@ -192,7 +164,7 @@ static void pic_set_irq(void *opaque, int irq, int level) } /* acknowledge interrupt 'irq' */ -static void pic_intack(PicState *s, int irq) +static void pic_intack(PICCommonState *s, int irq) { if (s->auto_eoi) { if (s->rotate_on_auto_eoi) { @@ -210,7 +182,7 @@ static void pic_intack(PicState *s, int irq) int pic_read_irq(DeviceState *d) { - PicState *s = DO_UPCAST(PicState, dev.qdev, d); + PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, d); int irq, irq2, intno; irq = pic_get_irq(s); @@ -249,30 +221,15 @@ int pic_read_irq(DeviceState *d) return intno; } -static void pic_init_reset(PicState *s) +static void pic_init_reset(PICCommonState *s) { - s->last_irr = 0; - s->irr = 0; - s->imr = 0; - s->isr = 0; - s->priority_add = 0; - s->irq_base = 0; - s->read_reg_select = 0; - s->poll = 0; - s->special_mask = 0; - s->init_state = 0; - s->auto_eoi = 0; - s->rotate_on_auto_eoi = 0; - s->special_fully_nested_mode = 0; - s->init4 = 0; - s->single_mode = 0; - /* Note: ELCR is not reset */ + pic_reset_common(s); pic_update_irq(s); } static void pic_reset(DeviceState *dev) { - PicState *s = DO_UPCAST(PicState, dev.qdev, dev); + PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, dev); pic_init_reset(s); s->elcr = 0; @@ -281,7 +238,7 @@ static void pic_reset(DeviceState *dev) static void pic_ioport_write(void *opaque, target_phys_addr_t addr64, uint64_t val64, unsigned size) { - PicState *s = opaque; + PICCommonState *s = opaque; uint32_t addr = addr64; uint32_t val = val64; int priority, cmd, irq; @@ -375,7 +332,7 @@ static void pic_ioport_write(void *opaque, target_phys_addr_t addr64, static uint64_t pic_ioport_read(void *opaque, target_phys_addr_t addr, unsigned size) { - PicState *s = opaque; + PICCommonState *s = opaque; int ret; if (s->poll) { @@ -404,7 +361,7 @@ static uint64_t pic_ioport_read(void *opaque, target_phys_addr_t addr, int pic_get_output(DeviceState *d) { - PicState *s = DO_UPCAST(PicState, dev.qdev, d); + PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, d); return (pic_get_irq(s) >= 0); } @@ -412,43 +369,17 @@ int pic_get_output(DeviceState *d) static void elcr_ioport_write(void *opaque, target_phys_addr_t addr, uint64_t val, unsigned size) { - PicState *s = opaque; + PICCommonState *s = opaque; s->elcr = val & s->elcr_mask; } static uint64_t elcr_ioport_read(void *opaque, target_phys_addr_t addr, unsigned size) { - PicState *s = opaque; + PICCommonState *s = opaque; return s->elcr; } -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() - } -}; - static const MemoryRegionOps pic_base_ioport_ops = { .read = pic_ioport_read, .write = pic_ioport_write, @@ -467,36 +398,25 @@ static const MemoryRegionOps pic_elcr_ioport_ops = { }, }; -static int pic_initfn(ISADevice *dev) +static void pic_init(PICCommonState *s) { - PicState *s = DO_UPCAST(PicState, dev, dev); - 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); - isa_register_ioport(dev, &s->base_io, s->iobase); - if (s->elcr_addr != -1) { - isa_register_ioport(dev, &s->elcr_io, s->elcr_addr); - } - - 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; + qdev_init_gpio_out(&s->dev.qdev, s->int_out, ARRAY_SIZE(s->int_out)); + qdev_init_gpio_in(&s->dev.qdev, pic_set_irq, 8); } void pic_info(Monitor *mon) { int i; - PicState *s; + PICCommonState *s; if (!isa_pic) { return; } for (i = 0; i < 2; i++) { - s = i == 0 ? DO_UPCAST(PicState, dev.qdev, isa_pic) : slave_pic; + s = i == 0 ? DO_UPCAST(PICCommonState, dev.qdev, 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, @@ -531,12 +451,7 @@ qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq) irq_set = g_malloc(ISA_NUM_IRQS * sizeof(qemu_irq)); - dev = isa_create(bus, "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); + dev = i8259_init_chip("isa-i8259", bus, true); qdev_connect_gpio_out(&dev->qdev, 0, parent_irq); for (i = 0 ; i < 8; i++) { @@ -545,40 +460,27 @@ qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq) isa_pic = &dev->qdev; - dev = isa_create(bus, "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); + dev = i8259_init_chip("isa-i8259", bus, false); 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); + slave_pic = DO_UPCAST(PICCommonState, 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 PICCommonInfo i8259_info = { + .isadev.qdev.name = "isa-i8259", + .isadev.qdev.reset = pic_reset, + .init = pic_init, }; static void pic_register(void) { - isa_qdev_register(&i8259_info); + pic_qdev_register(&i8259_info); } + device_init(pic_register) diff --git a/hw/i8259_common.c b/hw/i8259_common.c new file mode 100644 index 0000000000..e515876c48 --- /dev/null +++ b/hw/i8259_common.c @@ -0,0 +1,147 @@ +/* + * QEMU 8259 - common bits of emulated and KVM kernel model + * + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2011 Jan Kiszka, Siemens AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "pc.h" +#include "i8259_internal.h" + +void pic_reset_common(PICCommonState *s) +{ + s->last_irr = 0; + s->irr = 0; + s->imr = 0; + s->isr = 0; + s->priority_add = 0; + s->irq_base = 0; + s->read_reg_select = 0; + s->poll = 0; + s->special_mask = 0; + s->init_state = 0; + s->auto_eoi = 0; + s->rotate_on_auto_eoi = 0; + s->special_fully_nested_mode = 0; + s->init4 = 0; + s->single_mode = 0; + /* Note: ELCR is not reset */ +} + +static void pic_dispatch_pre_save(void *opaque) +{ + PICCommonState *s = opaque; + PICCommonInfo *info = + DO_UPCAST(PICCommonInfo, isadev.qdev, s->dev.qdev.info); + + if (info->pre_save) { + info->pre_save(s); + } +} + +static int pic_dispatch_post_load(void *opaque, int version_id) +{ + PICCommonState *s = opaque; + PICCommonInfo *info = + DO_UPCAST(PICCommonInfo, isadev.qdev, s->dev.qdev.info); + + if (info->post_load) { + info->post_load(s); + } + return 0; +} + +static int pic_init_common(ISADevice *dev) +{ + PICCommonState *s = DO_UPCAST(PICCommonState, dev, dev); + PICCommonInfo *info = + DO_UPCAST(PICCommonInfo, isadev.qdev, dev->qdev.info); + + info->init(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); + } + + qdev_set_legacy_instance_id(&s->dev.qdev, s->iobase, 1); + + return 0; +} + +ISADevice *i8259_init_chip(const char *name, ISABus *bus, bool master) +{ + ISADevice *dev; + + dev = isa_create(bus, name); + qdev_prop_set_uint32(&dev->qdev, "iobase", master ? 0x20 : 0xa0); + qdev_prop_set_uint32(&dev->qdev, "elcr_addr", master ? 0x4d0 : 0x4d1); + qdev_prop_set_uint8(&dev->qdev, "elcr_mask", master ? 0xf8 : 0xde); + qdev_prop_set_bit(&dev->qdev, "master", master); + qdev_init_nofail(&dev->qdev); + + return dev; +} + +static const VMStateDescription vmstate_pic_common = { + .name = "i8259", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .pre_save = pic_dispatch_pre_save, + .post_load = pic_dispatch_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8(last_irr, PICCommonState), + VMSTATE_UINT8(irr, PICCommonState), + VMSTATE_UINT8(imr, PICCommonState), + VMSTATE_UINT8(isr, PICCommonState), + VMSTATE_UINT8(priority_add, PICCommonState), + VMSTATE_UINT8(irq_base, PICCommonState), + VMSTATE_UINT8(read_reg_select, PICCommonState), + VMSTATE_UINT8(poll, PICCommonState), + VMSTATE_UINT8(special_mask, PICCommonState), + VMSTATE_UINT8(init_state, PICCommonState), + VMSTATE_UINT8(auto_eoi, PICCommonState), + VMSTATE_UINT8(rotate_on_auto_eoi, PICCommonState), + VMSTATE_UINT8(special_fully_nested_mode, PICCommonState), + VMSTATE_UINT8(init4, PICCommonState), + VMSTATE_UINT8(single_mode, PICCommonState), + VMSTATE_UINT8(elcr, PICCommonState), + VMSTATE_END_OF_LIST() + } +}; + +static Property pic_properties_common[] = { + DEFINE_PROP_HEX32("iobase", PICCommonState, iobase, -1), + DEFINE_PROP_HEX32("elcr_addr", PICCommonState, elcr_addr, -1), + DEFINE_PROP_HEX8("elcr_mask", PICCommonState, elcr_mask, -1), + DEFINE_PROP_BIT("master", PICCommonState, master, 0, false), + DEFINE_PROP_END_OF_LIST(), +}; + +void pic_qdev_register(PICCommonInfo *info) +{ + info->isadev.init = pic_init_common; + info->isadev.qdev.size = sizeof(PICCommonState); + info->isadev.qdev.vmsd = &vmstate_pic_common; + info->isadev.qdev.no_user = 1; + info->isadev.qdev.props = pic_properties_common; + isa_qdev_register(&info->isadev); +} diff --git a/hw/i8259_internal.h b/hw/i8259_internal.h new file mode 100644 index 0000000000..13deb14b63 --- /dev/null +++ b/hw/i8259_internal.h @@ -0,0 +1,76 @@ +/* + * QEMU 8259 - internal interfaces + * + * Copyright (c) 2011 Jan Kiszka, Siemens AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_I8259_INTERNAL_H +#define QEMU_I8259_INTERNAL_H + +#include "hw.h" +#include "pc.h" +#include "isa.h" + +typedef struct PICCommonState PICCommonState; + +struct PICCommonState { + ISADevice dev; + uint8_t last_irr; /* edge detection */ + uint8_t irr; /* interrupt request register */ + uint8_t imr; /* interrupt mask register */ + uint8_t isr; /* interrupt service register */ + uint8_t priority_add; /* highest irq priority */ + uint8_t irq_base; + uint8_t read_reg_select; + uint8_t poll; + uint8_t special_mask; + uint8_t init_state; + uint8_t auto_eoi; + uint8_t rotate_on_auto_eoi; + uint8_t special_fully_nested_mode; + uint8_t init4; /* true if 4 byte init */ + uint8_t single_mode; /* true if slave pic is not initialized */ + uint8_t elcr; /* PIIX edge/trigger selection*/ + uint8_t elcr_mask; + 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; +}; + +typedef struct PICCommonInfo PICCommonInfo; + +struct PICCommonInfo { + ISADeviceInfo isadev; + void (*init)(PICCommonState *s); + void (*pre_save)(PICCommonState *s); + void (*post_load)(PICCommonState *s); +}; + +void pic_reset_common(PICCommonState *s); + +ISADevice *i8259_init_chip(const char *name, ISABus *bus, bool master); + +void pic_qdev_register(PICCommonInfo *info); + +#endif /* !QEMU_I8259_INTERNAL_H */