2010-12-25 08:01:41 +03:00
|
|
|
/*
|
|
|
|
* 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
|
2020-10-23 15:19:33 +03:00
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2010-12-25 08:01:41 +03:00
|
|
|
*
|
|
|
|
* 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/>.
|
|
|
|
*/
|
|
|
|
|
2016-01-26 21:17:30 +03:00
|
|
|
#include "qemu/osdep.h"
|
2022-12-22 13:03:28 +03:00
|
|
|
#include "hw/pci/pci_device.h"
|
2019-08-12 08:23:42 +03:00
|
|
|
#include "hw/irq.h"
|
2019-12-12 19:15:43 +03:00
|
|
|
#include "hw/intc/i8259.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "hw/timer/i8254.h"
|
2019-08-12 08:23:45 +03:00
|
|
|
#include "migration/vmstate.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "hw/audio/pcspk.h"
|
2020-09-03 23:43:22 +03:00
|
|
|
#include "qom/object.h"
|
2010-12-25 08:01:41 +03:00
|
|
|
|
2013-07-24 01:16:46 +04:00
|
|
|
#define TYPE_I82378 "i82378"
|
2020-09-16 21:25:19 +03:00
|
|
|
OBJECT_DECLARE_SIMPLE_TYPE(I82378State, I82378)
|
2010-12-25 08:01:41 +03:00
|
|
|
|
2020-09-03 23:43:22 +03:00
|
|
|
struct I82378State {
|
2013-07-24 01:16:46 +04:00
|
|
|
PCIDevice parent_obj;
|
|
|
|
|
2023-02-09 15:15:16 +03:00
|
|
|
qemu_irq cpu_intr;
|
2023-02-10 18:05:34 +03:00
|
|
|
qemu_irq *isa_irqs_in;
|
2020-09-03 23:43:22 +03:00
|
|
|
};
|
2010-12-25 08:01:41 +03:00
|
|
|
|
2013-07-24 01:16:46 +04:00
|
|
|
static const VMStateDescription vmstate_i82378 = {
|
2010-12-25 08:01:41 +03:00
|
|
|
.name = "pci-i82378",
|
|
|
|
.version_id = 0,
|
|
|
|
.minimum_version_id = 0,
|
2023-12-21 06:16:18 +03:00
|
|
|
.fields = (const VMStateField[]) {
|
2013-07-24 01:16:46 +04:00
|
|
|
VMSTATE_PCI_DEVICE(parent_obj, I82378State),
|
2010-12-25 08:01:41 +03:00
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
Revert "hw/isa/i82378: Remove intermediate IRQ forwarder"
To be 'usable', QDev objects (which are QOM objects) must be
1/ initialized (at this point their properties can be modified), then
2/ realized (properties are consumed).
Some devices (objects) might depend on other devices. When creating
the 'QOM composition tree', parent objects can't be 'realized' until
all their children are. We might also have circular dependencies.
A common circular dependency occurs with IRQs. Device (A) has an
output IRQ wired to device (B), and device (B) has one to device (A).
When (A) is realized and connects its IRQ to an unrealized (B), the
IRQ handler on (B) is not yet created. QEMU pass IRQ between objects
as pointer. When (A) poll (B)'s IRQ, it is NULL. Later (B) is realized
and its IRQ pointers are populated, but (A) keeps a reference to a
NULL pointer.
A common pattern to bypass this circular limitation is to use 'proxy'
objects. Proxy (P) is created (and realized) before (A) and (B). Then
(A) and (B) can be created in different order, it doesn't matter: (P)
pointers are already populated.
Commit cef2e7148e ("hw/isa/i82378: Remove intermediate IRQ forwarder")
neglected the QOM/QDev circular dependency issue, and removed the
'proxy' between the southbridge, its PCI functions and the interrupt
controller, resulting in PCI functions wiring output IRQs to
'NULL', leading to guest failures (IRQ never delivered) [1] [2].
Since we are entering feature freeze, it is safer to revert the
offending patch until we figure a way to strengthen our APIs.
[1] https://lore.kernel.org/qemu-devel/928a8552-ab62-9e6c-a492-d6453e338b9d@redhat.com/
[2] https://lore.kernel.org/qemu-devel/cover.1677628524.git.balaton@eik.bme.hu/
This reverts commit cef2e7148e32d61338de0220619d308bf42af770.
Reported-by: Thomas Huth <thuth@redhat.com>
Inspired-by: Bernhard Beschow <shentey@gmail.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
2023-03-07 14:10:08 +03:00
|
|
|
static void i82378_request_out0_irq(void *opaque, int irq, int level)
|
|
|
|
{
|
|
|
|
I82378State *s = opaque;
|
|
|
|
qemu_set_irq(s->cpu_intr, level);
|
|
|
|
}
|
|
|
|
|
2010-12-25 08:01:41 +03:00
|
|
|
static void i82378_request_pic_irq(void *opaque, int irq, int level)
|
|
|
|
{
|
|
|
|
DeviceState *dev = opaque;
|
2013-07-24 01:16:46 +04:00
|
|
|
I82378State *s = I82378(dev);
|
2010-12-25 08:01:41 +03:00
|
|
|
|
2023-02-10 18:05:34 +03:00
|
|
|
qemu_set_irq(s->isa_irqs_in[irq], level);
|
2010-12-25 08:01:41 +03:00
|
|
|
}
|
|
|
|
|
2015-01-19 17:52:30 +03:00
|
|
|
static void i82378_realize(PCIDevice *pci, Error **errp)
|
2010-12-25 08:01:41 +03:00
|
|
|
{
|
2013-07-24 01:16:46 +04:00
|
|
|
DeviceState *dev = DEVICE(pci);
|
|
|
|
I82378State *s = I82378(dev);
|
|
|
|
uint8_t *pci_conf;
|
|
|
|
ISABus *isabus;
|
2020-04-22 16:31:49 +03:00
|
|
|
ISADevice *pit;
|
2023-10-16 13:12:39 +03:00
|
|
|
ISADevice *pcspk;
|
2010-12-25 08:01:41 +03:00
|
|
|
|
2013-07-24 01:16:46 +04:00
|
|
|
pci_conf = pci->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_config_set_interrupt_pin(pci_conf, 1); /* interrupt pin 0 */
|
|
|
|
|
2015-02-01 11:12:50 +03:00
|
|
|
isabus = isa_bus_new(dev, get_system_memory(),
|
isa: Clean up error handling around isa_bus_new()
We can have at most one ISA bus. If you try to create another one,
isa_bus_new() complains to stderr and returns null.
isa_bus_new() is called in two contexts, machine's init() and device's
realize() methods. Since complaining to stderr is not proper in the
latter context, convert isa_bus_new() to Error.
Machine's init():
* mips_jazz_init(), called from the init() methods of machines
"magnum" and "pica"
* mips_r4k_init(), the init() method of machine "mips"
* pc_init1() called from the init() methods of non-q35 PC machines
* typhoon_init(), called from clipper_init(), the init() method of
machine "clipper"
These callers always create the first ISA bus, hence isa_bus_new()
can't fail. Simply pass &error_abort.
Device's realize():
* i82378_realize(), of PCI device "i82378"
* ich9_lpc_realize(), of PCI device "ICH9-LPC"
* pci_ebus_realize(), of PCI device "ebus"
* piix3_realize(), of PCI device "pci-piix3", abstract parent of
"PIIX3" and "PIIX3-xen"
* piix4_realize(), of PCI device "PIIX4"
* vt82c686b_realize(), of PCI device "VT82C686B"
Propagate the error. Note that these devices are typically created
only by machine init() methods with qdev_init_nofail() or similar. If
we screwed up and created an ISA bus before that call, we now give up
right away. Before, we'd hobble on, and typically die in
isa_bus_irqs(). Similar if someone finds a way to hot-plug one of
these critters.
Cc: Richard Henderson <rth@twiddle.net>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: "Hervé Poussineau" <hpoussin@reactos.org>
Cc: Aurelien Jarno <aurelien@aurel32.net>
Cc: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
Reviewed-by: Marcel Apfelbaum <marcel@redhat.com>
Reviewed-by: Hervé Poussineau <hpoussin@reactos.org>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Message-Id: <1450370121-5768-11-git-send-email-armbru@redhat.com>
2015-12-17 19:35:18 +03:00
|
|
|
pci_address_space_io(pci), errp);
|
|
|
|
if (!isabus) {
|
|
|
|
return;
|
|
|
|
}
|
2013-07-24 01:16:46 +04:00
|
|
|
|
2010-12-25 08:01:41 +03:00
|
|
|
/* 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
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* 2 82C59 (irq) */
|
Revert "hw/isa/i82378: Remove intermediate IRQ forwarder"
To be 'usable', QDev objects (which are QOM objects) must be
1/ initialized (at this point their properties can be modified), then
2/ realized (properties are consumed).
Some devices (objects) might depend on other devices. When creating
the 'QOM composition tree', parent objects can't be 'realized' until
all their children are. We might also have circular dependencies.
A common circular dependency occurs with IRQs. Device (A) has an
output IRQ wired to device (B), and device (B) has one to device (A).
When (A) is realized and connects its IRQ to an unrealized (B), the
IRQ handler on (B) is not yet created. QEMU pass IRQ between objects
as pointer. When (A) poll (B)'s IRQ, it is NULL. Later (B) is realized
and its IRQ pointers are populated, but (A) keeps a reference to a
NULL pointer.
A common pattern to bypass this circular limitation is to use 'proxy'
objects. Proxy (P) is created (and realized) before (A) and (B). Then
(A) and (B) can be created in different order, it doesn't matter: (P)
pointers are already populated.
Commit cef2e7148e ("hw/isa/i82378: Remove intermediate IRQ forwarder")
neglected the QOM/QDev circular dependency issue, and removed the
'proxy' between the southbridge, its PCI functions and the interrupt
controller, resulting in PCI functions wiring output IRQs to
'NULL', leading to guest failures (IRQ never delivered) [1] [2].
Since we are entering feature freeze, it is safer to revert the
offending patch until we figure a way to strengthen our APIs.
[1] https://lore.kernel.org/qemu-devel/928a8552-ab62-9e6c-a492-d6453e338b9d@redhat.com/
[2] https://lore.kernel.org/qemu-devel/cover.1677628524.git.balaton@eik.bme.hu/
This reverts commit cef2e7148e32d61338de0220619d308bf42af770.
Reported-by: Thomas Huth <thuth@redhat.com>
Inspired-by: Bernhard Beschow <shentey@gmail.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
2023-03-07 14:10:08 +03:00
|
|
|
s->isa_irqs_in = i8259_init(isabus,
|
|
|
|
qemu_allocate_irq(i82378_request_out0_irq,
|
|
|
|
s, 0));
|
2023-02-09 15:32:18 +03:00
|
|
|
isa_bus_register_input_irqs(isabus, s->isa_irqs_in);
|
2010-12-25 08:01:41 +03:00
|
|
|
|
|
|
|
/* 1 82C54 (pit) */
|
2020-04-22 16:31:49 +03:00
|
|
|
pit = i8254_pit_init(isabus, 0x40, 0, NULL);
|
2010-12-25 08:01:41 +03:00
|
|
|
|
|
|
|
/* speaker */
|
2023-10-16 13:12:39 +03:00
|
|
|
pcspk = isa_new(TYPE_PC_SPEAKER);
|
|
|
|
object_property_set_link(OBJECT(pcspk), "pit", OBJECT(pit), &error_fatal);
|
2023-10-20 13:08:21 +03:00
|
|
|
if (!isa_realize_and_unref(pcspk, isabus, errp)) {
|
|
|
|
return;
|
|
|
|
}
|
2010-12-25 08:01:41 +03:00
|
|
|
|
|
|
|
/* 2 82C37 (dma) */
|
2020-04-22 16:31:49 +03:00
|
|
|
isa_create_simple(isabus, "i82374");
|
2010-12-25 08:01:41 +03:00
|
|
|
}
|
|
|
|
|
2013-07-24 01:16:46 +04:00
|
|
|
static void i82378_init(Object *obj)
|
2010-12-25 08:01:41 +03:00
|
|
|
{
|
2013-07-24 01:16:46 +04:00
|
|
|
DeviceState *dev = DEVICE(obj);
|
|
|
|
I82378State *s = I82378(obj);
|
2010-12-25 08:01:41 +03:00
|
|
|
|
2023-02-09 15:15:16 +03:00
|
|
|
qdev_init_gpio_out(dev, &s->cpu_intr, 1);
|
2013-07-24 01:16:46 +04:00
|
|
|
qdev_init_gpio_in(dev, i82378_request_pic_irq, 16);
|
2010-12-25 08:01:41 +03:00
|
|
|
}
|
|
|
|
|
2013-07-24 01:16:46 +04:00
|
|
|
static void i82378_class_init(ObjectClass *klass, void *data)
|
2011-12-04 22:22:06 +04:00
|
|
|
{
|
|
|
|
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
2011-12-08 07:34:16 +04:00
|
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
2011-12-04 22:22:06 +04:00
|
|
|
|
2015-01-19 17:52:30 +03:00
|
|
|
k->realize = i82378_realize;
|
2011-12-04 22:22:06 +04:00
|
|
|
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;
|
2013-07-24 01:16:46 +04:00
|
|
|
dc->vmsd = &vmstate_i82378;
|
2013-07-29 18:17:45 +04:00
|
|
|
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
2011-12-04 22:22:06 +04:00
|
|
|
}
|
|
|
|
|
2013-07-24 01:16:46 +04:00
|
|
|
static const TypeInfo i82378_type_info = {
|
|
|
|
.name = TYPE_I82378,
|
2011-12-08 07:34:16 +04:00
|
|
|
.parent = TYPE_PCI_DEVICE,
|
2013-07-24 01:16:46 +04:00
|
|
|
.instance_size = sizeof(I82378State),
|
|
|
|
.instance_init = i82378_init,
|
|
|
|
.class_init = i82378_class_init,
|
2017-09-27 22:56:34 +03:00
|
|
|
.interfaces = (InterfaceInfo[]) {
|
|
|
|
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
|
|
|
{ },
|
|
|
|
},
|
2010-12-25 08:01:41 +03:00
|
|
|
};
|
|
|
|
|
2012-02-09 18:20:55 +04:00
|
|
|
static void i82378_register_types(void)
|
2010-12-25 08:01:41 +03:00
|
|
|
{
|
2013-07-24 01:16:46 +04:00
|
|
|
type_register_static(&i82378_type_info);
|
2010-12-25 08:01:41 +03:00
|
|
|
}
|
|
|
|
|
2012-02-09 18:20:55 +04:00
|
|
|
type_init(i82378_register_types)
|