diff options
Diffstat (limited to 'hw/isa')
-rw-r--r-- | hw/isa/Makefile.objs | 7 | ||||
-rw-r--r-- | hw/isa/apm.c | 102 | ||||
-rw-r--r-- | hw/isa/i82378.c | 277 | ||||
-rw-r--r-- | hw/isa/isa-bus.c | 282 | ||||
-rw-r--r-- | hw/isa/isa_mmio.c | 81 | ||||
-rw-r--r-- | hw/isa/pc87312.c | 402 | ||||
-rw-r--r-- | hw/isa/piix4.c | 132 |
7 files changed, 1283 insertions, 0 deletions
diff --git a/hw/isa/Makefile.objs b/hw/isa/Makefile.objs index e69de29bb2..ad3643bd42 100644 --- a/hw/isa/Makefile.objs +++ b/hw/isa/Makefile.objs @@ -0,0 +1,7 @@ +common-obj-y += isa-bus.o +common-obj-$(CONFIG_APM) += apm.o +common-obj-$(CONFIG_I82378) += i82378.o +common-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o +common-obj-$(CONFIG_PC87312) += pc87312.o +common-obj-$(CONFIG_PIIX4) += piix4.o + diff --git a/hw/isa/apm.c b/hw/isa/apm.c new file mode 100644 index 0000000000..5f21d21473 --- /dev/null +++ b/hw/isa/apm.c @@ -0,0 +1,102 @@ +/* + * QEMU PC APM controller Emulation + * This is split out from acpi.c + * + * Copyright (c) 2006 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/> + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "hw/isa/apm.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" + +//#define DEBUG + +#ifdef DEBUG +# define APM_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) +#else +# define APM_DPRINTF(format, ...) do { } while (0) +#endif + +/* fixed I/O location */ +#define APM_CNT_IOPORT 0xb2 +#define APM_STS_IOPORT 0xb3 + +static void apm_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + APMState *apm = opaque; + addr &= 1; + APM_DPRINTF("apm_ioport_writeb addr=0x%x val=0x%02x\n", addr, val); + if (addr == 0) { + apm->apmc = val; + + if (apm->callback) { + (apm->callback)(val, apm->arg); + } + } else { + apm->apms = val; + } +} + +static uint64_t apm_ioport_readb(void *opaque, hwaddr addr, unsigned size) +{ + APMState *apm = opaque; + uint32_t val; + + addr &= 1; + if (addr == 0) { + val = apm->apmc; + } else { + val = apm->apms; + } + APM_DPRINTF("apm_ioport_readb addr=0x%x val=0x%02x\n", addr, val); + return val; +} + +const VMStateDescription vmstate_apm = { + .name = "APM State", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(apmc, APMState), + VMSTATE_UINT8(apms, APMState), + VMSTATE_END_OF_LIST() + } +}; + +static const MemoryRegionOps apm_ops = { + .read = apm_ioport_readb, + .write = apm_ioport_writeb, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +void apm_init(PCIDevice *dev, APMState *apm, apm_ctrl_changed_t callback, + void *arg) +{ + apm->callback = callback; + apm->arg = arg; + + /* ioport 0xb2, 0xb3 */ + memory_region_init_io(&apm->io, &apm_ops, apm, "apm-io", 2); + memory_region_add_subregion(pci_address_space_io(dev), APM_CNT_IOPORT, + &apm->io); +} diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c new file mode 100644 index 0000000000..cced9aff26 --- /dev/null +++ b/hw/isa/i82378.c @@ -0,0 +1,277 @@ +/* + * QEMU Intel i82378 emulation (PCI to ISA bridge) + * + * Copyright (c) 2010-2011 Hervé Poussineau + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw/pci/pci.h" +#include "hw/i386/pc.h" +#include "hw/timer/i8254.h" +#include "hw/audio/pcspk.h" + +//#define DEBUG_I82378 + +#ifdef DEBUG_I82378 +#define DPRINTF(fmt, ...) \ +do { fprintf(stderr, "i82378: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ +do {} while (0) +#endif + +#define BADF(fmt, ...) \ +do { fprintf(stderr, "i82378 ERROR: " fmt , ## __VA_ARGS__); } while (0) + +typedef struct I82378State { + qemu_irq out[2]; + qemu_irq *i8259; + MemoryRegion io; + MemoryRegion mem; +} I82378State; + +typedef struct PCIi82378State { + PCIDevice pci_dev; + uint32_t isa_io_base; + uint32_t isa_mem_base; + I82378State state; +} PCIi82378State; + +static const VMStateDescription vmstate_pci_i82378 = { + .name = "pci-i82378", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(pci_dev, PCIi82378State), + VMSTATE_END_OF_LIST() + }, +}; + +static void i82378_io_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + switch (size) { + case 1: + DPRINTF("%s: " TARGET_FMT_plx "=%02" PRIx64 "\n", __func__, + addr, value); + cpu_outb(addr, value); + break; + case 2: + DPRINTF("%s: " TARGET_FMT_plx "=%04" PRIx64 "\n", __func__, + addr, value); + cpu_outw(addr, value); + break; + case 4: + DPRINTF("%s: " TARGET_FMT_plx "=%08" PRIx64 "\n", __func__, + addr, value); + cpu_outl(addr, value); + break; + default: + abort(); + } +} + +static uint64_t i82378_io_read(void *opaque, hwaddr addr, + unsigned int size) +{ + DPRINTF("%s: " TARGET_FMT_plx "\n", __func__, addr); + switch (size) { + case 1: + return cpu_inb(addr); + case 2: + return cpu_inw(addr); + case 4: + return cpu_inl(addr); + default: + abort(); + } +} + +static const MemoryRegionOps i82378_io_ops = { + .read = i82378_io_read, + .write = i82378_io_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void i82378_mem_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + switch (size) { + case 1: + DPRINTF("%s: " TARGET_FMT_plx "=%02" PRIx64 "\n", __func__, + addr, value); + cpu_outb(addr, value); + break; + case 2: + DPRINTF("%s: " TARGET_FMT_plx "=%04" PRIx64 "\n", __func__, + addr, value); + cpu_outw(addr, value); + break; + case 4: + DPRINTF("%s: " TARGET_FMT_plx "=%08" PRIx64 "\n", __func__, + addr, value); + cpu_outl(addr, value); + break; + default: + abort(); + } +} + +static uint64_t i82378_mem_read(void *opaque, hwaddr addr, + unsigned int size) +{ + DPRINTF("%s: " TARGET_FMT_plx "\n", __func__, addr); + switch (size) { + case 1: + return cpu_inb(addr); + case 2: + return cpu_inw(addr); + case 4: + return cpu_inl(addr); + default: + abort(); + } +} + +static const MemoryRegionOps i82378_mem_ops = { + .read = i82378_mem_read, + .write = i82378_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void i82378_request_out0_irq(void *opaque, int irq, int level) +{ + I82378State *s = opaque; + qemu_set_irq(s->out[0], level); +} + +static void i82378_request_pic_irq(void *opaque, int irq, int level) +{ + DeviceState *dev = opaque; + PCIDevice *pci = DO_UPCAST(PCIDevice, qdev, dev); + PCIi82378State *s = DO_UPCAST(PCIi82378State, pci_dev, pci); + + qemu_set_irq(s->state.i8259[irq], level); +} + +static void i82378_init(DeviceState *dev, I82378State *s) +{ + ISABus *isabus = DO_UPCAST(ISABus, qbus, qdev_get_child_bus(dev, "isa.0")); + ISADevice *pit; + ISADevice *isa; + qemu_irq *out0_irq; + + /* This device has: + 2 82C59 (irq) + 1 82C54 (pit) + 2 82C37 (dma) + NMI + Utility Bus Support Registers + + All devices accept byte access only, except timer + */ + + qdev_init_gpio_out(dev, s->out, 2); + qdev_init_gpio_in(dev, i82378_request_pic_irq, 16); + + /* Workaround the fact that i8259 is not qdev'ified... */ + out0_irq = qemu_allocate_irqs(i82378_request_out0_irq, s, 1); + + /* 2 82C59 (irq) */ + s->i8259 = i8259_init(isabus, *out0_irq); + isa_bus_irqs(isabus, s->i8259); + + /* 1 82C54 (pit) */ + pit = pit_init(isabus, 0x40, 0, NULL); + + /* speaker */ + pcspk_init(isabus, pit); + + /* 2 82C37 (dma) */ + isa = isa_create_simple(isabus, "i82374"); + qdev_connect_gpio_out(&isa->qdev, 0, s->out[1]); + + /* timer */ + isa_create_simple(isabus, "mc146818rtc"); +} + +static int pci_i82378_init(PCIDevice *dev) +{ + PCIi82378State *pci = DO_UPCAST(PCIi82378State, pci_dev, dev); + I82378State *s = &pci->state; + uint8_t *pci_conf; + + pci_conf = dev->config; + pci_set_word(pci_conf + PCI_COMMAND, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + pci_set_word(pci_conf + PCI_STATUS, + PCI_STATUS_DEVSEL_MEDIUM); + + pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin 0 */ + + memory_region_init_io(&s->io, &i82378_io_ops, s, "i82378-io", 0x00010000); + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->io); + + memory_region_init_io(&s->mem, &i82378_mem_ops, s, "i82378-mem", 0x01000000); + pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); + + /* Make I/O address read only */ + pci_set_word(dev->wmask + PCI_COMMAND, PCI_COMMAND_SPECIAL); + pci_set_long(dev->wmask + PCI_BASE_ADDRESS_0, 0); + pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, pci->isa_io_base); + + isa_mem_base = pci->isa_mem_base; + isa_bus_new(&dev->qdev, pci_address_space_io(dev)); + + i82378_init(&dev->qdev, s); + + return 0; +} + +static Property i82378_properties[] = { + DEFINE_PROP_HEX32("iobase", PCIi82378State, isa_io_base, 0x80000000), + DEFINE_PROP_HEX32("membase", PCIi82378State, isa_mem_base, 0xc0000000), + DEFINE_PROP_END_OF_LIST() +}; + +static void pci_i82378_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + k->init = pci_i82378_init; + k->vendor_id = PCI_VENDOR_ID_INTEL; + k->device_id = PCI_DEVICE_ID_INTEL_82378; + k->revision = 0x03; + k->class_id = PCI_CLASS_BRIDGE_ISA; + k->subsystem_vendor_id = 0x0; + k->subsystem_id = 0x0; + dc->vmsd = &vmstate_pci_i82378; + dc->props = i82378_properties; +} + +static const TypeInfo pci_i82378_info = { + .name = "i82378", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIi82378State), + .class_init = pci_i82378_class_init, +}; + +static void i82378_register_types(void) +{ + type_register_static(&pci_i82378_info); +} + +type_init(i82378_register_types) diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c new file mode 100644 index 0000000000..7860b17d66 --- /dev/null +++ b/hw/isa/isa-bus.c @@ -0,0 +1,282 @@ +/* + * isa bus support for qdev. + * + * Copyright (c) 2009 Gerd Hoffmann <kraxel@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#include "hw/hw.h" +#include "monitor/monitor.h" +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "hw/isa/isa.h" +#include "exec/address-spaces.h" + +static ISABus *isabus; +hwaddr isa_mem_base = 0; + +static void isabus_dev_print(Monitor *mon, DeviceState *dev, int indent); +static char *isabus_get_fw_dev_path(DeviceState *dev); + +static void isa_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + k->print_dev = isabus_dev_print; + k->get_fw_dev_path = isabus_get_fw_dev_path; +} + +static const TypeInfo isa_bus_info = { + .name = TYPE_ISA_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(ISABus), + .class_init = isa_bus_class_init, +}; + +ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space_io) +{ + if (isabus) { + fprintf(stderr, "Can't create a second ISA bus\n"); + return NULL; + } + if (NULL == dev) { + dev = qdev_create(NULL, "isabus-bridge"); + qdev_init_nofail(dev); + } + + isabus = FROM_QBUS(ISABus, qbus_create(TYPE_ISA_BUS, dev, NULL)); + isabus->address_space_io = address_space_io; + return isabus; +} + +void isa_bus_irqs(ISABus *bus, qemu_irq *irqs) +{ + if (!bus) { + hw_error("Can't set isa irqs with no isa bus present."); + } + bus->irqs = irqs; +} + +/* + * isa_get_irq() returns the corresponding qemu_irq entry for the i8259. + * + * This function is only for special cases such as the 'ferr', and + * temporary use for normal devices until they are converted to qdev. + */ +qemu_irq isa_get_irq(ISADevice *dev, int isairq) +{ + assert(!dev || DO_UPCAST(ISABus, qbus, dev->qdev.parent_bus) == isabus); + if (isairq < 0 || isairq > 15) { + hw_error("isa irq %d invalid", isairq); + } + return isabus->irqs[isairq]; +} + +void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq) +{ + assert(dev->nirqs < ARRAY_SIZE(dev->isairq)); + dev->isairq[dev->nirqs] = isairq; + *p = isa_get_irq(dev, isairq); + dev->nirqs++; +} + +static inline void isa_init_ioport(ISADevice *dev, uint16_t ioport) +{ + if (dev && (dev->ioport_id == 0 || ioport < dev->ioport_id)) { + dev->ioport_id = ioport; + } +} + +void isa_register_ioport(ISADevice *dev, MemoryRegion *io, uint16_t start) +{ + memory_region_add_subregion(isabus->address_space_io, start, io); + isa_init_ioport(dev, start); +} + +void isa_register_portio_list(ISADevice *dev, uint16_t start, + const MemoryRegionPortio *pio_start, + void *opaque, const char *name) +{ + PortioList *piolist = g_new(PortioList, 1); + + /* START is how we should treat DEV, regardless of the actual + contents of the portio array. This is how the old code + actually handled e.g. the FDC device. */ + isa_init_ioport(dev, start); + + portio_list_init(piolist, pio_start, opaque, name); + portio_list_add(piolist, isabus->address_space_io, start); +} + +static int isa_qdev_init(DeviceState *qdev) +{ + ISADevice *dev = ISA_DEVICE(qdev); + ISADeviceClass *klass = ISA_DEVICE_GET_CLASS(dev); + + if (klass->init) { + return klass->init(dev); + } + + return 0; +} + +static void isa_device_init(Object *obj) +{ + ISADevice *dev = ISA_DEVICE(obj); + + dev->isairq[0] = -1; + dev->isairq[1] = -1; +} + +ISADevice *isa_create(ISABus *bus, const char *name) +{ + DeviceState *dev; + + if (!bus) { + hw_error("Tried to create isa device %s with no isa bus present.", + name); + } + dev = qdev_create(&bus->qbus, name); + return ISA_DEVICE(dev); +} + +ISADevice *isa_try_create(ISABus *bus, const char *name) +{ + DeviceState *dev; + + if (!bus) { + hw_error("Tried to create isa device %s with no isa bus present.", + name); + } + dev = qdev_try_create(&bus->qbus, name); + return ISA_DEVICE(dev); +} + +ISADevice *isa_create_simple(ISABus *bus, const char *name) +{ + ISADevice *dev; + + dev = isa_create(bus, name); + qdev_init_nofail(&dev->qdev); + return dev; +} + +ISADevice *isa_vga_init(ISABus *bus) +{ + switch (vga_interface_type) { + case VGA_CIRRUS: + return isa_create_simple(bus, "isa-cirrus-vga"); + case VGA_QXL: + fprintf(stderr, "%s: qxl: no PCI bus\n", __func__); + return NULL; + case VGA_STD: + return isa_create_simple(bus, "isa-vga"); + case VGA_VMWARE: + fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __func__); + return NULL; + case VGA_NONE: + default: + return NULL; + } +} + +static void isabus_dev_print(Monitor *mon, DeviceState *dev, int indent) +{ + ISADevice *d = ISA_DEVICE(dev); + + if (d->isairq[1] != -1) { + monitor_printf(mon, "%*sisa irqs %d,%d\n", indent, "", + d->isairq[0], d->isairq[1]); + } else if (d->isairq[0] != -1) { + monitor_printf(mon, "%*sisa irq %d\n", indent, "", + d->isairq[0]); + } +} + +static int isabus_bridge_init(SysBusDevice *dev) +{ + /* nothing */ + return 0; +} + +static void isabus_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = isabus_bridge_init; + dc->fw_name = "isa"; + dc->no_user = 1; +} + +static const TypeInfo isabus_bridge_info = { + .name = "isabus-bridge", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SysBusDevice), + .class_init = isabus_bridge_class_init, +}; + +static void isa_device_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *k = DEVICE_CLASS(klass); + k->init = isa_qdev_init; + k->bus_type = TYPE_ISA_BUS; +} + +static const TypeInfo isa_device_type_info = { + .name = TYPE_ISA_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(ISADevice), + .instance_init = isa_device_init, + .abstract = true, + .class_size = sizeof(ISADeviceClass), + .class_init = isa_device_class_init, +}; + +static void isabus_register_types(void) +{ + type_register_static(&isa_bus_info); + type_register_static(&isabus_bridge_info); + type_register_static(&isa_device_type_info); +} + +static char *isabus_get_fw_dev_path(DeviceState *dev) +{ + ISADevice *d = (ISADevice*)dev; + char path[40]; + int off; + + off = snprintf(path, sizeof(path), "%s", qdev_fw_name(dev)); + if (d->ioport_id) { + snprintf(path + off, sizeof(path) - off, "@%04x", d->ioport_id); + } + + return g_strdup(path); +} + +MemoryRegion *isa_address_space(ISADevice *dev) +{ + return get_system_memory(); +} + +MemoryRegion *isa_address_space_io(ISADevice *dev) +{ + if (dev) { + return isa_bus_from_device(dev)->address_space_io; + } + + return isabus->address_space_io; +} + +type_init(isabus_register_types) diff --git a/hw/isa/isa_mmio.c b/hw/isa/isa_mmio.c new file mode 100644 index 0000000000..d4dbf13831 --- /dev/null +++ b/hw/isa/isa_mmio.c @@ -0,0 +1,81 @@ +/* + * Memory mapped access to ISA IO space. + * + * 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/isa/isa.h" +#include "exec/address-spaces.h" + +static void isa_mmio_writeb (void *opaque, hwaddr addr, + uint32_t val) +{ + cpu_outb(addr & IOPORTS_MASK, val); +} + +static void isa_mmio_writew(void *opaque, hwaddr addr, + uint32_t val) +{ + cpu_outw(addr & IOPORTS_MASK, val); +} + +static void isa_mmio_writel(void *opaque, hwaddr addr, + uint32_t val) +{ + cpu_outl(addr & IOPORTS_MASK, val); +} + +static uint32_t isa_mmio_readb (void *opaque, hwaddr addr) +{ + return cpu_inb(addr & IOPORTS_MASK); +} + +static uint32_t isa_mmio_readw(void *opaque, hwaddr addr) +{ + return cpu_inw(addr & IOPORTS_MASK); +} + +static uint32_t isa_mmio_readl(void *opaque, hwaddr addr) +{ + return cpu_inl(addr & IOPORTS_MASK); +} + +static const MemoryRegionOps isa_mmio_ops = { + .old_mmio = { + .write = { isa_mmio_writeb, isa_mmio_writew, isa_mmio_writel }, + .read = { isa_mmio_readb, isa_mmio_readw, isa_mmio_readl, }, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +void isa_mmio_setup(MemoryRegion *mr, hwaddr size) +{ + memory_region_init_io(mr, &isa_mmio_ops, NULL, "isa-mmio", size); +} + +void isa_mmio_init(hwaddr base, hwaddr size) +{ + MemoryRegion *mr = g_malloc(sizeof(*mr)); + + isa_mmio_setup(mr, size); + memory_region_add_subregion(get_system_memory(), base, mr); +} diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c new file mode 100644 index 0000000000..9f5e185685 --- /dev/null +++ b/hw/isa/pc87312.c @@ -0,0 +1,402 @@ +/* + * QEMU National Semiconductor PC87312 (Super I/O) + * + * Copyright (c) 2010-2012 Herve Poussineau + * Copyright (c) 2011-2012 Andreas Färber + * + * 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/isa/pc87312.h" +#include "qemu/error-report.h" +#include "sysemu/blockdev.h" +#include "sysemu/sysemu.h" +#include "char/char.h" +#include "trace.h" + + +#define REG_FER 0 +#define REG_FAR 1 +#define REG_PTR 2 + +#define FER_PARALLEL_EN 0x01 +#define FER_UART1_EN 0x02 +#define FER_UART2_EN 0x04 +#define FER_FDC_EN 0x08 +#define FER_FDC_4 0x10 +#define FER_FDC_ADDR 0x20 +#define FER_IDE_EN 0x40 +#define FER_IDE_ADDR 0x80 + +#define FAR_PARALLEL_ADDR 0x03 +#define FAR_UART1_ADDR 0x0C +#define FAR_UART2_ADDR 0x30 +#define FAR_UART_3_4 0xC0 + +#define PTR_POWER_DOWN 0x01 +#define PTR_CLOCK_DOWN 0x02 +#define PTR_PWDN 0x04 +#define PTR_IRQ_5_7 0x08 +#define PTR_UART1_TEST 0x10 +#define PTR_UART2_TEST 0x20 +#define PTR_LOCK_CONF 0x40 +#define PTR_EPP_MODE 0x80 + + +/* Parallel port */ + +static inline bool is_parallel_enabled(PC87312State *s) +{ + return s->regs[REG_FER] & FER_PARALLEL_EN; +} + +static const uint32_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 }; + +static inline uint32_t get_parallel_iobase(PC87312State *s) +{ + return parallel_base[s->regs[REG_FAR] & FAR_PARALLEL_ADDR]; +} + +static const uint32_t parallel_irq[] = { 5, 7, 5, 0 }; + +static inline uint32_t get_parallel_irq(PC87312State *s) +{ + int idx; + idx = (s->regs[REG_FAR] & FAR_PARALLEL_ADDR); + if (idx == 0) { + return (s->regs[REG_PTR] & PTR_IRQ_5_7) ? 7 : 5; + } else { + return parallel_irq[idx]; + } +} + +static inline bool is_parallel_epp(PC87312State *s) +{ + return s->regs[REG_PTR] & PTR_EPP_MODE; +} + + +/* UARTs */ + +static const uint32_t uart_base[2][4] = { + { 0x3e8, 0x338, 0x2e8, 0x220 }, + { 0x2e8, 0x238, 0x2e0, 0x228 } +}; + +static inline uint32_t get_uart_iobase(PC87312State *s, int i) +{ + int idx; + idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3; + if (idx == 0) { + return 0x3f8; + } else if (idx == 1) { + return 0x2f8; + } else { + return uart_base[idx & 1][(s->regs[REG_FAR] & FAR_UART_3_4) >> 6]; + } +} + +static inline uint32_t get_uart_irq(PC87312State *s, int i) +{ + int idx; + idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3; + return (idx & 1) ? 3 : 4; +} + +static inline bool is_uart_enabled(PC87312State *s, int i) +{ + return s->regs[REG_FER] & (FER_UART1_EN << i); +} + + +/* Floppy controller */ + +static inline bool is_fdc_enabled(PC87312State *s) +{ + return s->regs[REG_FER] & FER_FDC_EN; +} + +static inline uint32_t get_fdc_iobase(PC87312State *s) +{ + return (s->regs[REG_FER] & FER_FDC_ADDR) ? 0x370 : 0x3f0; +} + + +/* IDE controller */ + +static inline bool is_ide_enabled(PC87312State *s) +{ + return s->regs[REG_FER] & FER_IDE_EN; +} + +static inline uint32_t get_ide_iobase(PC87312State *s) +{ + return (s->regs[REG_FER] & FER_IDE_ADDR) ? 0x170 : 0x1f0; +} + + +static void reconfigure_devices(PC87312State *s) +{ + error_report("pc87312: unsupported device reconfiguration (%02x %02x %02x)", + s->regs[REG_FER], s->regs[REG_FAR], s->regs[REG_PTR]); +} + +static void pc87312_soft_reset(PC87312State *s) +{ + static const uint8_t fer_init[] = { + 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4b, 0x4b, + 0x4b, 0x4b, 0x4b, 0x4b, 0x0f, 0x0f, 0x0f, 0x0f, + 0x49, 0x49, 0x49, 0x49, 0x07, 0x07, 0x07, 0x07, + 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x08, 0x00, + }; + static const uint8_t far_init[] = { + 0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x00, 0x01, + 0x01, 0x09, 0x08, 0x08, 0x10, 0x11, 0x39, 0x24, + 0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x39, 0x24, + 0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x10, 0x10, + }; + static const uint8_t ptr_init[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + }; + + s->read_id_step = 0; + s->selected_index = REG_FER; + + s->regs[REG_FER] = fer_init[s->config & 0x1f]; + s->regs[REG_FAR] = far_init[s->config & 0x1f]; + s->regs[REG_PTR] = ptr_init[s->config & 0x1f]; +} + +static void pc87312_hard_reset(PC87312State *s) +{ + pc87312_soft_reset(s); +} + +static void pc87312_io_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int size) +{ + PC87312State *s = opaque; + + trace_pc87312_io_write(addr, val); + + if ((addr & 1) == 0) { + /* Index register */ + s->read_id_step = 2; + s->selected_index = val; + } else { + /* Data register */ + if (s->selected_index < 3) { + s->regs[s->selected_index] = val; + reconfigure_devices(s); + } + } +} + +static uint64_t pc87312_io_read(void *opaque, hwaddr addr, unsigned int size) +{ + PC87312State *s = opaque; + uint32_t val; + + if ((addr & 1) == 0) { + /* Index register */ + if (s->read_id_step++ == 0) { + val = 0x88; + } else if (s->read_id_step++ == 1) { + val = 0; + } else { + val = s->selected_index; + } + } else { + /* Data register */ + if (s->selected_index < 3) { + val = s->regs[s->selected_index]; + } else { + /* Invalid selected index */ + val = 0; + } + } + + trace_pc87312_io_read(addr, val); + return val; +} + +static const MemoryRegionOps pc87312_io_ops = { + .read = pc87312_io_read, + .write = pc87312_io_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static int pc87312_post_load(void *opaque, int version_id) +{ + PC87312State *s = opaque; + + reconfigure_devices(s); + return 0; +} + +static void pc87312_reset(DeviceState *d) +{ + PC87312State *s = PC87312(d); + + pc87312_soft_reset(s); +} + +static int pc87312_init(ISADevice *dev) +{ + PC87312State *s; + DeviceState *d; + ISADevice *isa; + ISABus *bus; + CharDriverState *chr; + DriveInfo *drive; + char name[5]; + int i; + + s = PC87312(dev); + bus = isa_bus_from_device(dev); + pc87312_hard_reset(s); + isa_register_ioport(dev, &s->io, s->iobase); + + if (is_parallel_enabled(s)) { + chr = parallel_hds[0]; + if (chr == NULL) { + chr = qemu_chr_new("par0", "null", NULL); + } + isa = isa_create(bus, "isa-parallel"); + d = DEVICE(isa); + qdev_prop_set_uint32(d, "index", 0); + qdev_prop_set_uint32(d, "iobase", get_parallel_iobase(s)); + qdev_prop_set_uint32(d, "irq", get_parallel_irq(s)); + qdev_prop_set_chr(d, "chardev", chr); + qdev_init_nofail(d); + s->parallel.dev = isa; + trace_pc87312_info_parallel(get_parallel_iobase(s), + get_parallel_irq(s)); + } + + for (i = 0; i < 2; i++) { + if (is_uart_enabled(s, i)) { + chr = serial_hds[i]; + if (chr == NULL) { + snprintf(name, sizeof(name), "ser%d", i); + chr = qemu_chr_new(name, "null", NULL); + } + isa = isa_create(bus, "isa-serial"); + d = DEVICE(isa); + qdev_prop_set_uint32(d, "index", i); + qdev_prop_set_uint32(d, "iobase", get_uart_iobase(s, i)); + qdev_prop_set_uint32(d, "irq", get_uart_irq(s, i)); + qdev_prop_set_chr(d, "chardev", chr); + qdev_init_nofail(d); + s->uart[i].dev = isa; + trace_pc87312_info_serial(i, get_uart_iobase(s, i), + get_uart_irq(s, i)); + } + } + + if (is_fdc_enabled(s)) { + isa = isa_create(bus, "isa-fdc"); + d = DEVICE(isa); + qdev_prop_set_uint32(d, "iobase", get_fdc_iobase(s)); + qdev_prop_set_uint32(d, "irq", 6); + drive = drive_get(IF_FLOPPY, 0, 0); + if (drive != NULL) { + qdev_prop_set_drive_nofail(d, "driveA", drive->bdrv); + } + drive = drive_get(IF_FLOPPY, 0, 1); + if (drive != NULL) { + qdev_prop_set_drive_nofail(d, "driveB", drive->bdrv); + } + qdev_init_nofail(d); + s->fdc.dev = isa; + trace_pc87312_info_floppy(get_fdc_iobase(s)); + } + + if (is_ide_enabled(s)) { + isa = isa_create(bus, "isa-ide"); + d = DEVICE(isa); + qdev_prop_set_uint32(d, "iobase", get_ide_iobase(s)); + qdev_prop_set_uint32(d, "iobase2", get_ide_iobase(s) + 0x206); + qdev_prop_set_uint32(d, "irq", 14); + qdev_init_nofail(d); + s->ide.dev = isa; + trace_pc87312_info_ide(get_ide_iobase(s)); + } + + return 0; +} + +static void pc87312_initfn(Object *obj) +{ + PC87312State *s = PC87312(obj); + + memory_region_init_io(&s->io, &pc87312_io_ops, s, "pc87312", 2); +} + +static const VMStateDescription vmstate_pc87312 = { + .name = "pc87312", + .version_id = 1, + .minimum_version_id = 1, + .post_load = pc87312_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8(read_id_step, PC87312State), + VMSTATE_UINT8(selected_index, PC87312State), + VMSTATE_UINT8_ARRAY(regs, PC87312State, 3), + VMSTATE_END_OF_LIST() + } +}; + +static Property pc87312_properties[] = { + DEFINE_PROP_HEX32("iobase", PC87312State, iobase, 0x398), + DEFINE_PROP_UINT8("config", PC87312State, config, 1), + DEFINE_PROP_END_OF_LIST() +}; + +static void pc87312_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ISADeviceClass *ic = ISA_DEVICE_CLASS(klass); + + ic->init = pc87312_init; + dc->reset = pc87312_reset; + dc->vmsd = &vmstate_pc87312; + dc->props = pc87312_properties; +} + +static const TypeInfo pc87312_type_info = { + .name = TYPE_PC87312, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(PC87312State), + .instance_init = pc87312_initfn, + .class_init = pc87312_class_init, +}; + +static void pc87312_register_types(void) +{ + type_register_static(&pc87312_type_info); +} + +type_init(pc87312_register_types) diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c new file mode 100644 index 0000000000..d750413a7e --- /dev/null +++ b/hw/isa/piix4.c @@ -0,0 +1,132 @@ +/* + * QEMU PIIX4 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/isa/isa.h" +#include "hw/sysbus.h" + +PCIDevice *piix4_dev; + +typedef struct PIIX4State { + PCIDevice dev; +} PIIX4State; + +static void piix4_reset(void *opaque) +{ + PIIX4State *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] = 0x0a; // PCI A -> IRQ 10 + pci_conf[0x61] = 0x0a; // PCI B -> IRQ 10 + pci_conf[0x62] = 0x0b; // PCI C -> IRQ 11 + pci_conf[0x63] = 0x0b; // PCI D -> IRQ 11 + 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; +} + +static const VMStateDescription vmstate_piix4 = { + .name = "PIIX4", + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(dev, PIIX4State), + VMSTATE_END_OF_LIST() + } +}; + +static int piix4_initfn(PCIDevice *dev) +{ + PIIX4State *d = DO_UPCAST(PIIX4State, dev, dev); + + isa_bus_new(&d->dev.qdev, pci_address_space_io(dev)); + piix4_dev = &d->dev; + qemu_register_reset(piix4_reset, d); + return 0; +} + +int piix4_init(PCIBus *bus, ISABus **isa_bus, int devfn) +{ + PCIDevice *d; + + d = pci_create_simple_multifunction(bus, devfn, true, "PIIX4"); + *isa_bus = DO_UPCAST(ISABus, qbus, qdev_get_child_bus(&d->qdev, "isa.0")); + return d->devfn; +} + +static void piix4_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->no_hotplug = 1; + k->init = piix4_initfn; + k->vendor_id = PCI_VENDOR_ID_INTEL; + k->device_id = PCI_DEVICE_ID_INTEL_82371AB_0; + k->class_id = PCI_CLASS_BRIDGE_ISA; + dc->desc = "ISA bridge"; + dc->no_user = 1; + dc->vmsd = &vmstate_piix4; +} + +static const TypeInfo piix4_info = { + .name = "PIIX4", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PIIX4State), + .class_init = piix4_class_init, +}; + +static void piix4_register_types(void) +{ + type_register_static(&piix4_info); +} + +type_init(piix4_register_types) |