hw: move target-independent files to subdirectories

This patch tackles all files that are compiled once, moving
them to subdirectories of hw/.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini 2013-03-01 13:59:19 +01:00
parent ce3b494cb5
commit 49ab747f66
227 changed files with 205 additions and 208 deletions

View file

@ -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

102
hw/isa/apm.c Normal file
View file

@ -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);
}

277
hw/isa/i82378.c Normal file
View file

@ -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)

282
hw/isa/isa-bus.c Normal file
View file

@ -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)

81
hw/isa/isa_mmio.c Normal file
View file

@ -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);
}

402
hw/isa/pc87312.c Normal file
View file

@ -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)

132
hw/isa/piix4.c Normal file
View file

@ -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)