diff options
Diffstat (limited to 'qemu/hw/pci-host/piix.c')
-rw-r--r-- | qemu/hw/pci-host/piix.c | 785 |
1 files changed, 785 insertions, 0 deletions
diff --git a/qemu/hw/pci-host/piix.c b/qemu/hw/pci-host/piix.c new file mode 100644 index 000000000..ad55f9966 --- /dev/null +++ b/qemu/hw/pci-host/piix.c @@ -0,0 +1,785 @@ +/* + * QEMU i440FX/PIIX3 PCI Bridge Emulation + * + * Copyright (c) 2006 Fabrice Bellard + * + * 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 "hw/hw.h" +#include "hw/i386/pc.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/isa/isa.h" +#include "hw/sysbus.h" +#include "qemu/range.h" +#include "hw/xen/xen.h" +#include "hw/pci-host/pam.h" +#include "sysemu/sysemu.h" +#include "hw/i386/ioapic.h" +#include "qapi/visitor.h" + +/* + * I440FX chipset data sheet. + * http://download.intel.com/design/chipsets/datashts/29054901.pdf + */ + +#define TYPE_I440FX_PCI_HOST_BRIDGE "i440FX-pcihost" +#define I440FX_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(I440FXState, (obj), TYPE_I440FX_PCI_HOST_BRIDGE) + +typedef struct I440FXState { + PCIHostState parent_obj; + PcPciInfo pci_info; + uint64_t pci_hole64_size; + uint32_t short_root_bus; +} I440FXState; + +#define PIIX_NUM_PIC_IRQS 16 /* i8259 * 2 */ +#define PIIX_NUM_PIRQS 4ULL /* PIRQ[A-D] */ +#define XEN_PIIX_NUM_PIRQS 128ULL +#define PIIX_PIRQC 0x60 + +/* + * Reset Control Register: PCI-accessible ISA-Compatible Register at address + * 0xcf9, provided by the PCI/ISA bridge (PIIX3 PCI function 0, 8086:7000). + */ +#define RCR_IOPORT 0xcf9 + +typedef struct PIIX3State { + PCIDevice dev; + + /* + * bitmap to track pic levels. + * The pic level is the logical OR of all the PCI irqs mapped to it + * So one PIC level is tracked by PIIX_NUM_PIRQS bits. + * + * PIRQ is mapped to PIC pins, we track it by + * PIIX_NUM_PIRQS * PIIX_NUM_PIC_IRQS = 64 bits with + * pic_irq * PIIX_NUM_PIRQS + pirq + */ +#if PIIX_NUM_PIC_IRQS * PIIX_NUM_PIRQS > 64 +#error "unable to encode pic state in 64bit in pic_levels." +#endif + uint64_t pic_levels; + + qemu_irq *pic; + + /* This member isn't used. Just for save/load compatibility */ + int32_t pci_irq_levels_vmstate[PIIX_NUM_PIRQS]; + + /* Reset Control Register contents */ + uint8_t rcr; + + /* IO memory region for Reset Control Register (RCR_IOPORT) */ + MemoryRegion rcr_mem; +} PIIX3State; + +#define TYPE_PIIX3_PCI_DEVICE "pci-piix3" +#define PIIX3_PCI_DEVICE(obj) \ + OBJECT_CHECK(PIIX3State, (obj), TYPE_PIIX3_PCI_DEVICE) + +#define TYPE_I440FX_PCI_DEVICE "i440FX" +#define I440FX_PCI_DEVICE(obj) \ + OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE) + +struct PCII440FXState { + /*< private >*/ + PCIDevice parent_obj; + /*< public >*/ + + MemoryRegion *system_memory; + MemoryRegion *pci_address_space; + MemoryRegion *ram_memory; + PAMMemoryRegion pam_regions[13]; + MemoryRegion smram_region; + MemoryRegion smram, low_smram; +}; + + +#define I440FX_PAM 0x59 +#define I440FX_PAM_SIZE 7 +#define I440FX_SMRAM 0x72 + +static void piix3_set_irq(void *opaque, int pirq, int level); +static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pci_intx); +static void piix3_write_config_xen(PCIDevice *dev, + uint32_t address, uint32_t val, int len); + +/* return the global irq number corresponding to a given device irq + pin. We could also use the bus number to have a more precise + mapping. */ +static int pci_slot_get_pirq(PCIDevice *pci_dev, int pci_intx) +{ + int slot_addend; + slot_addend = (pci_dev->devfn >> 3) - 1; + return (pci_intx + slot_addend) & 3; +} + +static void i440fx_update_memory_mappings(PCII440FXState *d) +{ + int i; + PCIDevice *pd = PCI_DEVICE(d); + + memory_region_transaction_begin(); + for (i = 0; i < 13; i++) { + pam_update(&d->pam_regions[i], i, + pd->config[I440FX_PAM + ((i + 1) / 2)]); + } + memory_region_set_enabled(&d->smram_region, + !(pd->config[I440FX_SMRAM] & SMRAM_D_OPEN)); + memory_region_set_enabled(&d->smram, + pd->config[I440FX_SMRAM] & SMRAM_G_SMRAME); + memory_region_transaction_commit(); +} + + +static void i440fx_write_config(PCIDevice *dev, + uint32_t address, uint32_t val, int len) +{ + PCII440FXState *d = I440FX_PCI_DEVICE(dev); + + /* XXX: implement SMRAM.D_LOCK */ + pci_default_write_config(dev, address, val, len); + if (ranges_overlap(address, len, I440FX_PAM, I440FX_PAM_SIZE) || + range_covers_byte(address, len, I440FX_SMRAM)) { + i440fx_update_memory_mappings(d); + } +} + +static int i440fx_load_old(QEMUFile* f, void *opaque, int version_id) +{ + PCII440FXState *d = opaque; + PCIDevice *pd = PCI_DEVICE(d); + int ret, i; + uint8_t smm_enabled; + + ret = pci_device_load(pd, f); + if (ret < 0) + return ret; + i440fx_update_memory_mappings(d); + qemu_get_8s(f, &smm_enabled); + + if (version_id == 2) { + for (i = 0; i < PIIX_NUM_PIRQS; i++) { + qemu_get_be32(f); /* dummy load for compatibility */ + } + } + + return 0; +} + +static int i440fx_post_load(void *opaque, int version_id) +{ + PCII440FXState *d = opaque; + + i440fx_update_memory_mappings(d); + return 0; +} + +static const VMStateDescription vmstate_i440fx = { + .name = "I440FX", + .version_id = 3, + .minimum_version_id = 3, + .minimum_version_id_old = 1, + .load_state_old = i440fx_load_old, + .post_load = i440fx_post_load, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, PCII440FXState), + /* Used to be smm_enabled, which was basically always zero because + * SeaBIOS hardly uses SMM. SMRAM is now handled by CPU code. + */ + VMSTATE_UNUSED(1), + VMSTATE_END_OF_LIST() + } +}; + +static void i440fx_pcihost_get_pci_hole_start(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); + uint32_t value = s->pci_info.w32.begin; + + visit_type_uint32(v, &value, name, errp); +} + +static void i440fx_pcihost_get_pci_hole_end(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); + uint32_t value = s->pci_info.w32.end; + + visit_type_uint32(v, &value, name, errp); +} + +static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + PCIHostState *h = PCI_HOST_BRIDGE(obj); + Range w64; + + pci_bus_get_w64_range(h->bus, &w64); + + visit_type_uint64(v, &w64.begin, name, errp); +} + +static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + PCIHostState *h = PCI_HOST_BRIDGE(obj); + Range w64; + + pci_bus_get_w64_range(h->bus, &w64); + + visit_type_uint64(v, &w64.end, name, errp); +} + +static void i440fx_pcihost_initfn(Object *obj) +{ + PCIHostState *s = PCI_HOST_BRIDGE(obj); + I440FXState *d = I440FX_PCI_HOST_BRIDGE(obj); + + memory_region_init_io(&s->conf_mem, obj, &pci_host_conf_le_ops, s, + "pci-conf-idx", 4); + memory_region_init_io(&s->data_mem, obj, &pci_host_data_le_ops, s, + "pci-conf-data", 4); + + object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_START, "int", + i440fx_pcihost_get_pci_hole_start, + NULL, NULL, NULL, NULL); + + object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_END, "int", + i440fx_pcihost_get_pci_hole_end, + NULL, NULL, NULL, NULL); + + object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_START, "int", + i440fx_pcihost_get_pci_hole64_start, + NULL, NULL, NULL, NULL); + + object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_END, "int", + i440fx_pcihost_get_pci_hole64_end, + NULL, NULL, NULL, NULL); + + d->pci_info.w32.end = IO_APIC_DEFAULT_ADDRESS; +} + +static void i440fx_pcihost_realize(DeviceState *dev, Error **errp) +{ + PCIHostState *s = PCI_HOST_BRIDGE(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + sysbus_add_io(sbd, 0xcf8, &s->conf_mem); + sysbus_init_ioports(sbd, 0xcf8, 4); + + sysbus_add_io(sbd, 0xcfc, &s->data_mem); + sysbus_init_ioports(sbd, 0xcfc, 4); +} + +static void i440fx_realize(PCIDevice *dev, Error **errp) +{ + dev->config[I440FX_SMRAM] = 0x02; +} + +PCIBus *i440fx_init(PCII440FXState **pi440fx_state, + int *piix3_devfn, + ISABus **isa_bus, qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + ram_addr_t ram_size, + ram_addr_t below_4g_mem_size, + ram_addr_t above_4g_mem_size, + MemoryRegion *pci_address_space, + MemoryRegion *ram_memory) +{ + DeviceState *dev; + PCIBus *b; + PCIDevice *d; + PCIHostState *s; + PIIX3State *piix3; + PCII440FXState *f; + unsigned i; + I440FXState *i440fx; + + dev = qdev_create(NULL, TYPE_I440FX_PCI_HOST_BRIDGE); + s = PCI_HOST_BRIDGE(dev); + b = pci_bus_new(dev, NULL, pci_address_space, + address_space_io, 0, TYPE_PCI_BUS); + s->bus = b; + object_property_add_child(qdev_get_machine(), "i440fx", OBJECT(dev), NULL); + qdev_init_nofail(dev); + + d = pci_create_simple(b, 0, TYPE_I440FX_PCI_DEVICE); + *pi440fx_state = I440FX_PCI_DEVICE(d); + f = *pi440fx_state; + f->system_memory = address_space_mem; + f->pci_address_space = pci_address_space; + f->ram_memory = ram_memory; + + i440fx = I440FX_PCI_HOST_BRIDGE(dev); + i440fx->pci_info.w32.begin = below_4g_mem_size; + + /* setup pci memory mapping */ + pc_pci_as_mapping_init(OBJECT(f), f->system_memory, + f->pci_address_space); + + /* if *disabled* show SMRAM to all CPUs */ + memory_region_init_alias(&f->smram_region, OBJECT(d), "smram-region", + f->pci_address_space, 0xa0000, 0x20000); + memory_region_add_subregion_overlap(f->system_memory, 0xa0000, + &f->smram_region, 1); + memory_region_set_enabled(&f->smram_region, true); + + /* smram, as seen by SMM CPUs */ + memory_region_init(&f->smram, OBJECT(d), "smram", 1ull << 32); + memory_region_set_enabled(&f->smram, true); + memory_region_init_alias(&f->low_smram, OBJECT(d), "smram-low", + f->ram_memory, 0xa0000, 0x20000); + memory_region_set_enabled(&f->low_smram, true); + memory_region_add_subregion(&f->smram, 0xa0000, &f->low_smram); + object_property_add_const_link(qdev_get_machine(), "smram", + OBJECT(&f->smram), &error_abort); + + init_pam(dev, f->ram_memory, f->system_memory, f->pci_address_space, + &f->pam_regions[0], PAM_BIOS_BASE, PAM_BIOS_SIZE); + for (i = 0; i < 12; ++i) { + init_pam(dev, f->ram_memory, f->system_memory, f->pci_address_space, + &f->pam_regions[i+1], PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, + PAM_EXPAN_SIZE); + } + + /* Xen supports additional interrupt routes from the PCI devices to + * the IOAPIC: the four pins of each PCI device on the bus are also + * connected to the IOAPIC directly. + * These additional routes can be discovered through ACPI. */ + if (xen_enabled()) { + PCIDevice *pci_dev = pci_create_simple_multifunction(b, + -1, true, "PIIX3-xen"); + piix3 = PIIX3_PCI_DEVICE(pci_dev); + pci_bus_irqs(b, xen_piix3_set_irq, xen_pci_slot_get_pirq, + piix3, XEN_PIIX_NUM_PIRQS); + } else { + PCIDevice *pci_dev = pci_create_simple_multifunction(b, + -1, true, "PIIX3"); + piix3 = PIIX3_PCI_DEVICE(pci_dev); + pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3, + PIIX_NUM_PIRQS); + pci_bus_set_route_irq_fn(b, piix3_route_intx_pin_to_irq); + } + piix3->pic = pic; + *isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(piix3), "isa.0")); + + *piix3_devfn = piix3->dev.devfn; + + ram_size = ram_size / 8 / 1024 / 1024; + if (ram_size > 255) { + ram_size = 255; + } + d->config[0x57] = ram_size; + + i440fx_update_memory_mappings(f); + + return b; +} + +PCIBus *find_i440fx(void) +{ + PCIHostState *s = OBJECT_CHECK(PCIHostState, + object_resolve_path("/machine/i440fx", NULL), + TYPE_PCI_HOST_BRIDGE); + return s ? s->bus : NULL; +} + +/* PIIX3 PCI to ISA bridge */ +static void piix3_set_irq_pic(PIIX3State *piix3, int pic_irq) +{ + qemu_set_irq(piix3->pic[pic_irq], + !!(piix3->pic_levels & + (((1ULL << PIIX_NUM_PIRQS) - 1) << + (pic_irq * PIIX_NUM_PIRQS)))); +} + +static void piix3_set_irq_level_internal(PIIX3State *piix3, int pirq, int level) +{ + int pic_irq; + uint64_t mask; + + pic_irq = piix3->dev.config[PIIX_PIRQC + pirq]; + if (pic_irq >= PIIX_NUM_PIC_IRQS) { + return; + } + + mask = 1ULL << ((pic_irq * PIIX_NUM_PIRQS) + pirq); + piix3->pic_levels &= ~mask; + piix3->pic_levels |= mask * !!level; +} + +static void piix3_set_irq_level(PIIX3State *piix3, int pirq, int level) +{ + int pic_irq; + + pic_irq = piix3->dev.config[PIIX_PIRQC + pirq]; + if (pic_irq >= PIIX_NUM_PIC_IRQS) { + return; + } + + piix3_set_irq_level_internal(piix3, pirq, level); + + piix3_set_irq_pic(piix3, pic_irq); +} + +static void piix3_set_irq(void *opaque, int pirq, int level) +{ + PIIX3State *piix3 = opaque; + piix3_set_irq_level(piix3, pirq, level); +} + +static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pin) +{ + PIIX3State *piix3 = opaque; + int irq = piix3->dev.config[PIIX_PIRQC + pin]; + PCIINTxRoute route; + + if (irq < PIIX_NUM_PIC_IRQS) { + route.mode = PCI_INTX_ENABLED; + route.irq = irq; + } else { + route.mode = PCI_INTX_DISABLED; + route.irq = -1; + } + return route; +} + +/* irq routing is changed. so rebuild bitmap */ +static void piix3_update_irq_levels(PIIX3State *piix3) +{ + int pirq; + + piix3->pic_levels = 0; + for (pirq = 0; pirq < PIIX_NUM_PIRQS; pirq++) { + piix3_set_irq_level(piix3, pirq, + pci_bus_get_irq_level(piix3->dev.bus, pirq)); + } +} + +static void piix3_write_config(PCIDevice *dev, + uint32_t address, uint32_t val, int len) +{ + pci_default_write_config(dev, address, val, len); + if (ranges_overlap(address, len, PIIX_PIRQC, 4)) { + PIIX3State *piix3 = PIIX3_PCI_DEVICE(dev); + int pic_irq; + + pci_bus_fire_intx_routing_notifier(piix3->dev.bus); + piix3_update_irq_levels(piix3); + for (pic_irq = 0; pic_irq < PIIX_NUM_PIC_IRQS; pic_irq++) { + piix3_set_irq_pic(piix3, pic_irq); + } + } +} + +static void piix3_write_config_xen(PCIDevice *dev, + uint32_t address, uint32_t val, int len) +{ + xen_piix_pci_write_config_client(address, val, len); + piix3_write_config(dev, address, val, len); +} + +static void piix3_reset(void *opaque) +{ + PIIX3State *d = opaque; + uint8_t *pci_conf = d->dev.config; + + pci_conf[0x04] = 0x07; /* master, memory and I/O */ + pci_conf[0x05] = 0x00; + pci_conf[0x06] = 0x00; + pci_conf[0x07] = 0x02; /* PCI_status_devsel_medium */ + pci_conf[0x4c] = 0x4d; + pci_conf[0x4e] = 0x03; + pci_conf[0x4f] = 0x00; + pci_conf[0x60] = 0x80; + pci_conf[0x61] = 0x80; + pci_conf[0x62] = 0x80; + pci_conf[0x63] = 0x80; + pci_conf[0x69] = 0x02; + pci_conf[0x70] = 0x80; + pci_conf[0x76] = 0x0c; + pci_conf[0x77] = 0x0c; + pci_conf[0x78] = 0x02; + pci_conf[0x79] = 0x00; + pci_conf[0x80] = 0x00; + pci_conf[0x82] = 0x00; + pci_conf[0xa0] = 0x08; + pci_conf[0xa2] = 0x00; + pci_conf[0xa3] = 0x00; + pci_conf[0xa4] = 0x00; + pci_conf[0xa5] = 0x00; + pci_conf[0xa6] = 0x00; + pci_conf[0xa7] = 0x00; + pci_conf[0xa8] = 0x0f; + pci_conf[0xaa] = 0x00; + pci_conf[0xab] = 0x00; + pci_conf[0xac] = 0x00; + pci_conf[0xae] = 0x00; + + d->pic_levels = 0; + d->rcr = 0; +} + +static int piix3_post_load(void *opaque, int version_id) +{ + PIIX3State *piix3 = opaque; + int pirq; + + /* Because the i8259 has not been deserialized yet, qemu_irq_raise + * might bring the system to a different state than the saved one; + * for example, the interrupt could be masked but the i8259 would + * not know that yet and would trigger an interrupt in the CPU. + * + * Here, we update irq levels without raising the interrupt. + * Interrupt state will be deserialized separately through the i8259. + */ + piix3->pic_levels = 0; + for (pirq = 0; pirq < PIIX_NUM_PIRQS; pirq++) { + piix3_set_irq_level_internal(piix3, pirq, + pci_bus_get_irq_level(piix3->dev.bus, pirq)); + } + return 0; +} + +static void piix3_pre_save(void *opaque) +{ + int i; + PIIX3State *piix3 = opaque; + + for (i = 0; i < ARRAY_SIZE(piix3->pci_irq_levels_vmstate); i++) { + piix3->pci_irq_levels_vmstate[i] = + pci_bus_get_irq_level(piix3->dev.bus, i); + } +} + +static bool piix3_rcr_needed(void *opaque) +{ + PIIX3State *piix3 = opaque; + + return (piix3->rcr != 0); +} + +static const VMStateDescription vmstate_piix3_rcr = { + .name = "PIIX3/rcr", + .version_id = 1, + .minimum_version_id = 1, + .needed = piix3_rcr_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT8(rcr, PIIX3State), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_piix3 = { + .name = "PIIX3", + .version_id = 3, + .minimum_version_id = 2, + .post_load = piix3_post_load, + .pre_save = piix3_pre_save, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(dev, PIIX3State), + VMSTATE_INT32_ARRAY_V(pci_irq_levels_vmstate, PIIX3State, + PIIX_NUM_PIRQS, 3), + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription*[]) { + &vmstate_piix3_rcr, + NULL + } +}; + + +static void rcr_write(void *opaque, hwaddr addr, uint64_t val, unsigned len) +{ + PIIX3State *d = opaque; + + if (val & 4) { + qemu_system_reset_request(); + return; + } + d->rcr = val & 2; /* keep System Reset type only */ +} + +static uint64_t rcr_read(void *opaque, hwaddr addr, unsigned len) +{ + PIIX3State *d = opaque; + + return d->rcr; +} + +static const MemoryRegionOps rcr_ops = { + .read = rcr_read, + .write = rcr_write, + .endianness = DEVICE_LITTLE_ENDIAN +}; + +static void piix3_realize(PCIDevice *dev, Error **errp) +{ + PIIX3State *d = PIIX3_PCI_DEVICE(dev); + + isa_bus_new(DEVICE(d), get_system_memory(), + pci_address_space_io(dev)); + + memory_region_init_io(&d->rcr_mem, OBJECT(dev), &rcr_ops, d, + "piix3-reset-control", 1); + memory_region_add_subregion_overlap(pci_address_space_io(dev), RCR_IOPORT, + &d->rcr_mem, 1); + + qemu_register_reset(piix3_reset, d); +} + +static void pci_piix3_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + dc->desc = "ISA bridge"; + dc->vmsd = &vmstate_piix3; + dc->hotpluggable = false; + k->realize = piix3_realize; + k->vendor_id = PCI_VENDOR_ID_INTEL; + /* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */ + k->device_id = PCI_DEVICE_ID_INTEL_82371SB_0; + k->class_id = PCI_CLASS_BRIDGE_ISA; + /* + * Reason: part of PIIX3 southbridge, needs to be wired up by + * pc_piix.c's pc_init1() + */ + dc->cannot_instantiate_with_device_add_yet = true; +} + +static const TypeInfo piix3_pci_type_info = { + .name = TYPE_PIIX3_PCI_DEVICE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PIIX3State), + .abstract = true, + .class_init = pci_piix3_class_init, +}; + +static void piix3_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->config_write = piix3_write_config; +} + +static const TypeInfo piix3_info = { + .name = "PIIX3", + .parent = TYPE_PIIX3_PCI_DEVICE, + .class_init = piix3_class_init, +}; + +static void piix3_xen_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->config_write = piix3_write_config_xen; +}; + +static const TypeInfo piix3_xen_info = { + .name = "PIIX3-xen", + .parent = TYPE_PIIX3_PCI_DEVICE, + .class_init = piix3_xen_class_init, +}; + +static void i440fx_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->realize = i440fx_realize; + k->config_write = i440fx_write_config; + k->vendor_id = PCI_VENDOR_ID_INTEL; + k->device_id = PCI_DEVICE_ID_INTEL_82441; + k->revision = 0x02; + k->class_id = PCI_CLASS_BRIDGE_HOST; + dc->desc = "Host bridge"; + dc->vmsd = &vmstate_i440fx; + /* + * PCI-facing part of the host bridge, not usable without the + * host-facing part, which can't be device_add'ed, yet. + */ + dc->cannot_instantiate_with_device_add_yet = true; + dc->hotpluggable = false; +} + +static const TypeInfo i440fx_info = { + .name = TYPE_I440FX_PCI_DEVICE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCII440FXState), + .class_init = i440fx_class_init, +}; + +static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge, + PCIBus *rootbus) +{ + I440FXState *s = I440FX_PCI_HOST_BRIDGE(host_bridge); + + /* For backwards compat with old device paths */ + if (s->short_root_bus) { + return "0000"; + } + return "0000:00"; +} + +static Property i440fx_props[] = { + DEFINE_PROP_SIZE(PCI_HOST_PROP_PCI_HOLE64_SIZE, I440FXState, + pci_hole64_size, DEFAULT_PCI_HOLE64_SIZE), + DEFINE_PROP_UINT32("short_root_bus", I440FXState, short_root_bus, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void i440fx_pcihost_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); + + hc->root_bus_path = i440fx_pcihost_root_bus_path; + dc->realize = i440fx_pcihost_realize; + dc->fw_name = "pci"; + dc->props = i440fx_props; +} + +static const TypeInfo i440fx_pcihost_info = { + .name = TYPE_I440FX_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(I440FXState), + .instance_init = i440fx_pcihost_initfn, + .class_init = i440fx_pcihost_class_init, +}; + +static void i440fx_register_types(void) +{ + type_register_static(&i440fx_info); + type_register_static(&piix3_pci_type_info); + type_register_static(&piix3_info); + type_register_static(&piix3_xen_info); + type_register_static(&i440fx_pcihost_info); +} + +type_init(i440fx_register_types) |