b69c3c21a5
Devices may have component devices and buses. Device realization may fail. Realization is recursive: a device's realize() method realizes its components, and device_set_realized() realizes its buses (which should in turn realize the devices on that bus, except bus_set_realized() doesn't implement that, yet). When realization of a component or bus fails, we need to roll back: unrealize everything we realized so far. If any of these unrealizes failed, the device would be left in an inconsistent state. Must not happen. device_set_realized() lets it happen: it ignores errors in the roll back code starting at label child_realize_fail. Since realization is recursive, unrealization must be recursive, too. But how could a partly failed unrealize be rolled back? We'd have to re-realize, which can fail. This design is fundamentally broken. device_set_realized() does not roll back at all. Instead, it keeps unrealizing, ignoring further errors. It can screw up even for a device with no buses: if the lone dc->unrealize() fails, it still unregisters vmstate, and calls listeners' unrealize() callback. bus_set_realized() does not roll back either. Instead, it stops unrealizing. Fortunately, no unrealize method can fail, as we'll see below. To fix the design error, drop parameter @errp from all the unrealize methods. Any unrealize method that uses @errp now needs an update. This leads us to unrealize() methods that can fail. Merely passing it to another unrealize method cannot cause failure, though. Here are the ones that do other things with @errp: * virtio_serial_device_unrealize() Fails when qbus_set_hotplug_handler() fails, but still does all the other work. On failure, the device would stay realized with its resources completely gone. Oops. Can't happen, because qbus_set_hotplug_handler() can't actually fail here. Pass &error_abort to qbus_set_hotplug_handler() instead. * hw/ppc/spapr_drc.c's unrealize() Fails when object_property_del() fails, but all the other work is already done. On failure, the device would stay realized with its vmstate registration gone. Oops. Can't happen, because object_property_del() can't actually fail here. Pass &error_abort to object_property_del() instead. * spapr_phb_unrealize() Fails and bails out when remove_drcs() fails, but other work is already done. On failure, the device would stay realized with some of its resources gone. Oops. remove_drcs() fails only when chassis_from_bus()'s object_property_get_uint() fails, and it can't here. Pass &error_abort to remove_drcs() instead. Therefore, no unrealize method can fail before this patch. device_set_realized()'s recursive unrealization via bus uses object_property_set_bool(). Can't drop @errp there, so pass &error_abort. We similarly unrealize with object_property_set_bool() elsewhere, always ignoring errors. Pass &error_abort instead. Several unrealize methods no longer handle errors from other unrealize methods: virtio_9p_device_unrealize(), virtio_input_device_unrealize(), scsi_qdev_unrealize(), ... Much of the deleted error handling looks wrong anyway. One unrealize methods no longer ignore such errors: usb_ehci_pci_exit(). Several realize methods no longer ignore errors when rolling back: v9fs_device_realize_common(), pci_qdev_unrealize(), spapr_phb_realize(), usb_qdev_realize(), vfio_ccw_realize(), virtio_device_realize(). Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> Message-Id: <20200505152926.18877-17-armbru@redhat.com>
496 lines
15 KiB
C
496 lines
15 KiB
C
/*
|
|
* ioapic.c IOAPIC emulation logic
|
|
*
|
|
* Copyright (c) 2004-2005 Fabrice Bellard
|
|
*
|
|
* Split the ioapic logic from apic.c
|
|
* Xiantao Zhang <xiantao.zhang@intel.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 "qemu/osdep.h"
|
|
#include "qapi/error.h"
|
|
#include "monitor/monitor.h"
|
|
#include "hw/i386/apic.h"
|
|
#include "hw/i386/ioapic.h"
|
|
#include "hw/i386/ioapic_internal.h"
|
|
#include "hw/i386/x86.h"
|
|
#include "hw/intc/i8259.h"
|
|
#include "hw/pci/msi.h"
|
|
#include "hw/qdev-properties.h"
|
|
#include "sysemu/kvm.h"
|
|
#include "sysemu/sysemu.h"
|
|
#include "hw/i386/apic-msidef.h"
|
|
#include "hw/i386/x86-iommu.h"
|
|
#include "trace.h"
|
|
|
|
#define APIC_DELIVERY_MODE_SHIFT 8
|
|
#define APIC_POLARITY_SHIFT 14
|
|
#define APIC_TRIG_MODE_SHIFT 15
|
|
|
|
static IOAPICCommonState *ioapics[MAX_IOAPICS];
|
|
|
|
/* global variable from ioapic_common.c */
|
|
extern int ioapic_no;
|
|
|
|
struct ioapic_entry_info {
|
|
/* fields parsed from IOAPIC entries */
|
|
uint8_t masked;
|
|
uint8_t trig_mode;
|
|
uint16_t dest_idx;
|
|
uint8_t dest_mode;
|
|
uint8_t delivery_mode;
|
|
uint8_t vector;
|
|
|
|
/* MSI message generated from above parsed fields */
|
|
uint32_t addr;
|
|
uint32_t data;
|
|
};
|
|
|
|
static void ioapic_entry_parse(uint64_t entry, struct ioapic_entry_info *info)
|
|
{
|
|
memset(info, 0, sizeof(*info));
|
|
info->masked = (entry >> IOAPIC_LVT_MASKED_SHIFT) & 1;
|
|
info->trig_mode = (entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1;
|
|
/*
|
|
* By default, this would be dest_id[8] + reserved[8]. When IR
|
|
* is enabled, this would be interrupt_index[15] +
|
|
* interrupt_format[1]. This field never means anything, but
|
|
* only used to generate corresponding MSI.
|
|
*/
|
|
info->dest_idx = (entry >> IOAPIC_LVT_DEST_IDX_SHIFT) & 0xffff;
|
|
info->dest_mode = (entry >> IOAPIC_LVT_DEST_MODE_SHIFT) & 1;
|
|
info->delivery_mode = (entry >> IOAPIC_LVT_DELIV_MODE_SHIFT) \
|
|
& IOAPIC_DM_MASK;
|
|
if (info->delivery_mode == IOAPIC_DM_EXTINT) {
|
|
info->vector = pic_read_irq(isa_pic);
|
|
} else {
|
|
info->vector = entry & IOAPIC_VECTOR_MASK;
|
|
}
|
|
|
|
info->addr = APIC_DEFAULT_ADDRESS | \
|
|
(info->dest_idx << MSI_ADDR_DEST_IDX_SHIFT) | \
|
|
(info->dest_mode << MSI_ADDR_DEST_MODE_SHIFT);
|
|
info->data = (info->vector << MSI_DATA_VECTOR_SHIFT) | \
|
|
(info->trig_mode << MSI_DATA_TRIGGER_SHIFT) | \
|
|
(info->delivery_mode << MSI_DATA_DELIVERY_MODE_SHIFT);
|
|
}
|
|
|
|
static void ioapic_service(IOAPICCommonState *s)
|
|
{
|
|
AddressSpace *ioapic_as = X86_MACHINE(qdev_get_machine())->ioapic_as;
|
|
struct ioapic_entry_info info;
|
|
uint8_t i;
|
|
uint32_t mask;
|
|
uint64_t entry;
|
|
|
|
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
|
|
mask = 1 << i;
|
|
if (s->irr & mask) {
|
|
int coalesce = 0;
|
|
|
|
entry = s->ioredtbl[i];
|
|
ioapic_entry_parse(entry, &info);
|
|
if (!info.masked) {
|
|
if (info.trig_mode == IOAPIC_TRIGGER_EDGE) {
|
|
s->irr &= ~mask;
|
|
} else {
|
|
coalesce = s->ioredtbl[i] & IOAPIC_LVT_REMOTE_IRR;
|
|
trace_ioapic_set_remote_irr(i);
|
|
s->ioredtbl[i] |= IOAPIC_LVT_REMOTE_IRR;
|
|
}
|
|
|
|
if (coalesce) {
|
|
/* We are level triggered interrupts, and the
|
|
* guest should be still working on previous one,
|
|
* so skip it. */
|
|
continue;
|
|
}
|
|
|
|
#ifdef CONFIG_KVM
|
|
if (kvm_irqchip_is_split()) {
|
|
if (info.trig_mode == IOAPIC_TRIGGER_EDGE) {
|
|
kvm_set_irq(kvm_state, i, 1);
|
|
kvm_set_irq(kvm_state, i, 0);
|
|
} else {
|
|
kvm_set_irq(kvm_state, i, 1);
|
|
}
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
/* No matter whether IR is enabled, we translate
|
|
* the IOAPIC message into a MSI one, and its
|
|
* address space will decide whether we need a
|
|
* translation. */
|
|
stl_le_phys(ioapic_as, info.addr, info.data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#define SUCCESSIVE_IRQ_MAX_COUNT 10000
|
|
|
|
static void delayed_ioapic_service_cb(void *opaque)
|
|
{
|
|
IOAPICCommonState *s = opaque;
|
|
|
|
ioapic_service(s);
|
|
}
|
|
|
|
static void ioapic_set_irq(void *opaque, int vector, int level)
|
|
{
|
|
IOAPICCommonState *s = opaque;
|
|
|
|
/* ISA IRQs map to GSI 1-1 except for IRQ0 which maps
|
|
* to GSI 2. GSI maps to ioapic 1-1. This is not
|
|
* the cleanest way of doing it but it should work. */
|
|
|
|
trace_ioapic_set_irq(vector, level);
|
|
ioapic_stat_update_irq(s, vector, level);
|
|
if (vector == 0) {
|
|
vector = 2;
|
|
}
|
|
if (vector < IOAPIC_NUM_PINS) {
|
|
uint32_t mask = 1 << vector;
|
|
uint64_t entry = s->ioredtbl[vector];
|
|
|
|
if (((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1) ==
|
|
IOAPIC_TRIGGER_LEVEL) {
|
|
/* level triggered */
|
|
if (level) {
|
|
s->irr |= mask;
|
|
if (!(entry & IOAPIC_LVT_REMOTE_IRR)) {
|
|
ioapic_service(s);
|
|
}
|
|
} else {
|
|
s->irr &= ~mask;
|
|
}
|
|
} else {
|
|
/* According to the 82093AA manual, we must ignore edge requests
|
|
* if the input pin is masked. */
|
|
if (level && !(entry & IOAPIC_LVT_MASKED)) {
|
|
s->irr |= mask;
|
|
ioapic_service(s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ioapic_update_kvm_routes(IOAPICCommonState *s)
|
|
{
|
|
#ifdef CONFIG_KVM
|
|
int i;
|
|
|
|
if (kvm_irqchip_is_split()) {
|
|
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
|
|
MSIMessage msg;
|
|
struct ioapic_entry_info info;
|
|
ioapic_entry_parse(s->ioredtbl[i], &info);
|
|
if (!info.masked) {
|
|
msg.address = info.addr;
|
|
msg.data = info.data;
|
|
kvm_irqchip_update_msi_route(kvm_state, i, msg, NULL);
|
|
}
|
|
}
|
|
kvm_irqchip_commit_routes(kvm_state);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef CONFIG_KVM
|
|
static void ioapic_iec_notifier(void *private, bool global,
|
|
uint32_t index, uint32_t mask)
|
|
{
|
|
IOAPICCommonState *s = (IOAPICCommonState *)private;
|
|
/* For simplicity, we just update all the routes */
|
|
ioapic_update_kvm_routes(s);
|
|
}
|
|
#endif
|
|
|
|
void ioapic_eoi_broadcast(int vector)
|
|
{
|
|
IOAPICCommonState *s;
|
|
uint64_t entry;
|
|
int i, n;
|
|
|
|
trace_ioapic_eoi_broadcast(vector);
|
|
|
|
for (i = 0; i < MAX_IOAPICS; i++) {
|
|
s = ioapics[i];
|
|
if (!s) {
|
|
continue;
|
|
}
|
|
for (n = 0; n < IOAPIC_NUM_PINS; n++) {
|
|
entry = s->ioredtbl[n];
|
|
|
|
if ((entry & IOAPIC_VECTOR_MASK) != vector ||
|
|
((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1) != IOAPIC_TRIGGER_LEVEL) {
|
|
continue;
|
|
}
|
|
|
|
if (!(entry & IOAPIC_LVT_REMOTE_IRR)) {
|
|
continue;
|
|
}
|
|
|
|
trace_ioapic_clear_remote_irr(n, vector);
|
|
s->ioredtbl[n] = entry & ~IOAPIC_LVT_REMOTE_IRR;
|
|
|
|
if (!(entry & IOAPIC_LVT_MASKED) && (s->irr & (1 << n))) {
|
|
++s->irq_eoi[n];
|
|
if (s->irq_eoi[n] >= SUCCESSIVE_IRQ_MAX_COUNT) {
|
|
/*
|
|
* Real hardware does not deliver the interrupt immediately
|
|
* during eoi broadcast, and this lets a buggy guest make
|
|
* slow progress even if it does not correctly handle a
|
|
* level-triggered interrupt. Emulate this behavior if we
|
|
* detect an interrupt storm.
|
|
*/
|
|
s->irq_eoi[n] = 0;
|
|
timer_mod_anticipate(s->delayed_ioapic_service_timer,
|
|
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
|
NANOSECONDS_PER_SECOND / 100);
|
|
trace_ioapic_eoi_delayed_reassert(n);
|
|
} else {
|
|
ioapic_service(s);
|
|
}
|
|
} else {
|
|
s->irq_eoi[n] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static uint64_t
|
|
ioapic_mem_read(void *opaque, hwaddr addr, unsigned int size)
|
|
{
|
|
IOAPICCommonState *s = opaque;
|
|
int index;
|
|
uint32_t val = 0;
|
|
|
|
addr &= 0xff;
|
|
|
|
switch (addr) {
|
|
case IOAPIC_IOREGSEL:
|
|
val = s->ioregsel;
|
|
break;
|
|
case IOAPIC_IOWIN:
|
|
if (size != 4) {
|
|
break;
|
|
}
|
|
switch (s->ioregsel) {
|
|
case IOAPIC_REG_ID:
|
|
case IOAPIC_REG_ARB:
|
|
val = s->id << IOAPIC_ID_SHIFT;
|
|
break;
|
|
case IOAPIC_REG_VER:
|
|
val = s->version |
|
|
((IOAPIC_NUM_PINS - 1) << IOAPIC_VER_ENTRIES_SHIFT);
|
|
break;
|
|
default:
|
|
index = (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1;
|
|
if (index >= 0 && index < IOAPIC_NUM_PINS) {
|
|
if (s->ioregsel & 1) {
|
|
val = s->ioredtbl[index] >> 32;
|
|
} else {
|
|
val = s->ioredtbl[index] & 0xffffffff;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
trace_ioapic_mem_read(addr, s->ioregsel, size, val);
|
|
|
|
return val;
|
|
}
|
|
|
|
/*
|
|
* This is to satisfy the hack in Linux kernel. One hack of it is to
|
|
* simulate clearing the Remote IRR bit of IOAPIC entry using the
|
|
* following:
|
|
*
|
|
* "For IO-APIC's with EOI register, we use that to do an explicit EOI.
|
|
* Otherwise, we simulate the EOI message manually by changing the trigger
|
|
* mode to edge and then back to level, with RTE being masked during
|
|
* this."
|
|
*
|
|
* (See linux kernel __eoi_ioapic_pin() comment in commit c0205701)
|
|
*
|
|
* This is based on the assumption that, Remote IRR bit will be
|
|
* cleared by IOAPIC hardware when configured as edge-triggered
|
|
* interrupts.
|
|
*
|
|
* Without this, level-triggered interrupts in IR mode might fail to
|
|
* work correctly.
|
|
*/
|
|
static inline void
|
|
ioapic_fix_edge_remote_irr(uint64_t *entry)
|
|
{
|
|
if (!(*entry & IOAPIC_LVT_TRIGGER_MODE)) {
|
|
/* Edge-triggered interrupts, make sure remote IRR is zero */
|
|
*entry &= ~((uint64_t)IOAPIC_LVT_REMOTE_IRR);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ioapic_mem_write(void *opaque, hwaddr addr, uint64_t val,
|
|
unsigned int size)
|
|
{
|
|
IOAPICCommonState *s = opaque;
|
|
int index;
|
|
|
|
addr &= 0xff;
|
|
trace_ioapic_mem_write(addr, s->ioregsel, size, val);
|
|
|
|
switch (addr) {
|
|
case IOAPIC_IOREGSEL:
|
|
s->ioregsel = val;
|
|
break;
|
|
case IOAPIC_IOWIN:
|
|
if (size != 4) {
|
|
break;
|
|
}
|
|
switch (s->ioregsel) {
|
|
case IOAPIC_REG_ID:
|
|
s->id = (val >> IOAPIC_ID_SHIFT) & IOAPIC_ID_MASK;
|
|
break;
|
|
case IOAPIC_REG_VER:
|
|
case IOAPIC_REG_ARB:
|
|
break;
|
|
default:
|
|
index = (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1;
|
|
if (index >= 0 && index < IOAPIC_NUM_PINS) {
|
|
uint64_t ro_bits = s->ioredtbl[index] & IOAPIC_RO_BITS;
|
|
if (s->ioregsel & 1) {
|
|
s->ioredtbl[index] &= 0xffffffff;
|
|
s->ioredtbl[index] |= (uint64_t)val << 32;
|
|
} else {
|
|
s->ioredtbl[index] &= ~0xffffffffULL;
|
|
s->ioredtbl[index] |= val;
|
|
}
|
|
/* restore RO bits */
|
|
s->ioredtbl[index] &= IOAPIC_RW_BITS;
|
|
s->ioredtbl[index] |= ro_bits;
|
|
s->irq_eoi[index] = 0;
|
|
ioapic_fix_edge_remote_irr(&s->ioredtbl[index]);
|
|
ioapic_service(s);
|
|
}
|
|
}
|
|
break;
|
|
case IOAPIC_EOI:
|
|
/* Explicit EOI is only supported for IOAPIC version 0x20 */
|
|
if (size != 4 || s->version != 0x20) {
|
|
break;
|
|
}
|
|
ioapic_eoi_broadcast(val);
|
|
break;
|
|
}
|
|
|
|
ioapic_update_kvm_routes(s);
|
|
}
|
|
|
|
static const MemoryRegionOps ioapic_io_ops = {
|
|
.read = ioapic_mem_read,
|
|
.write = ioapic_mem_write,
|
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
|
};
|
|
|
|
static void ioapic_machine_done_notify(Notifier *notifier, void *data)
|
|
{
|
|
#ifdef CONFIG_KVM
|
|
IOAPICCommonState *s = container_of(notifier, IOAPICCommonState,
|
|
machine_done);
|
|
|
|
if (kvm_irqchip_is_split()) {
|
|
X86IOMMUState *iommu = x86_iommu_get_default();
|
|
if (iommu) {
|
|
/* Register this IOAPIC with IOMMU IEC notifier, so that
|
|
* when there are IR invalidates, we can be notified to
|
|
* update kernel IR cache. */
|
|
x86_iommu_iec_register_notifier(iommu, ioapic_iec_notifier, s);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#define IOAPIC_VER_DEF 0x20
|
|
|
|
static void ioapic_realize(DeviceState *dev, Error **errp)
|
|
{
|
|
IOAPICCommonState *s = IOAPIC_COMMON(dev);
|
|
|
|
if (s->version != 0x11 && s->version != 0x20) {
|
|
error_setg(errp, "IOAPIC only supports version 0x11 or 0x20 "
|
|
"(default: 0x%x).", IOAPIC_VER_DEF);
|
|
return;
|
|
}
|
|
|
|
memory_region_init_io(&s->io_memory, OBJECT(s), &ioapic_io_ops, s,
|
|
"ioapic", 0x1000);
|
|
|
|
s->delayed_ioapic_service_timer =
|
|
timer_new_ns(QEMU_CLOCK_VIRTUAL, delayed_ioapic_service_cb, s);
|
|
|
|
qdev_init_gpio_in(dev, ioapic_set_irq, IOAPIC_NUM_PINS);
|
|
|
|
ioapics[ioapic_no] = s;
|
|
s->machine_done.notify = ioapic_machine_done_notify;
|
|
qemu_add_machine_init_done_notifier(&s->machine_done);
|
|
}
|
|
|
|
static void ioapic_unrealize(DeviceState *dev)
|
|
{
|
|
IOAPICCommonState *s = IOAPIC_COMMON(dev);
|
|
|
|
timer_del(s->delayed_ioapic_service_timer);
|
|
timer_free(s->delayed_ioapic_service_timer);
|
|
}
|
|
|
|
static Property ioapic_properties[] = {
|
|
DEFINE_PROP_UINT8("version", IOAPICCommonState, version, IOAPIC_VER_DEF),
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
};
|
|
|
|
static void ioapic_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
IOAPICCommonClass *k = IOAPIC_COMMON_CLASS(klass);
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
|
|
k->realize = ioapic_realize;
|
|
k->unrealize = ioapic_unrealize;
|
|
/*
|
|
* If APIC is in kernel, we need to update the kernel cache after
|
|
* migration, otherwise first 24 gsi routes will be invalid.
|
|
*/
|
|
k->post_load = ioapic_update_kvm_routes;
|
|
dc->reset = ioapic_reset_common;
|
|
device_class_set_props(dc, ioapic_properties);
|
|
}
|
|
|
|
static const TypeInfo ioapic_info = {
|
|
.name = TYPE_IOAPIC,
|
|
.parent = TYPE_IOAPIC_COMMON,
|
|
.instance_size = sizeof(IOAPICCommonState),
|
|
.class_init = ioapic_class_init,
|
|
};
|
|
|
|
static void ioapic_register_types(void)
|
|
{
|
|
type_register_static(&ioapic_info);
|
|
}
|
|
|
|
type_init(ioapic_register_types)
|