qemu/hw/acpi/pcihp.c
<<
>>
Prefs
   1/*
   2 * QEMU<->ACPI BIOS PCI hotplug interface
   3 *
   4 * QEMU supports PCI hotplug via ACPI. This module
   5 * implements the interface between QEMU and the ACPI BIOS.
   6 * Interface specification - see docs/specs/acpi_pci_hotplug.txt
   7 *
   8 * Copyright (c) 2013, Red Hat Inc, Michael S. Tsirkin (mst@redhat.com)
   9 * Copyright (c) 2006 Fabrice Bellard
  10 *
  11 * This library is free software; you can redistribute it and/or
  12 * modify it under the terms of the GNU Lesser General Public
  13 * License version 2.1 as published by the Free Software Foundation.
  14 *
  15 * This library is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18 * Lesser General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU Lesser General Public
  21 * License along with this library; if not, see <http://www.gnu.org/licenses/>
  22 *
  23 * Contributions after 2012-01-13 are licensed under the terms of the
  24 * GNU GPL, version 2 or (at your option) any later version.
  25 */
  26
  27#include "qemu/osdep.h"
  28#include "hw/acpi/pcihp.h"
  29
  30#include "hw/pci-host/i440fx.h"
  31#include "hw/pci/pci.h"
  32#include "hw/pci/pci_bridge.h"
  33#include "hw/acpi/acpi.h"
  34#include "exec/address-spaces.h"
  35#include "hw/pci/pci_bus.h"
  36#include "migration/vmstate.h"
  37#include "qapi/error.h"
  38#include "qom/qom-qobject.h"
  39#include "trace.h"
  40
  41#define ACPI_PCIHP_ADDR 0xae00
  42#define ACPI_PCIHP_SIZE 0x0014
  43#define PCI_UP_BASE 0x0000
  44#define PCI_DOWN_BASE 0x0004
  45#define PCI_EJ_BASE 0x0008
  46#define PCI_RMV_BASE 0x000c
  47#define PCI_SEL_BASE 0x0010
  48
  49typedef struct AcpiPciHpFind {
  50    int bsel;
  51    PCIBus *bus;
  52} AcpiPciHpFind;
  53
  54static int acpi_pcihp_get_bsel(PCIBus *bus)
  55{
  56    Error *local_err = NULL;
  57    uint64_t bsel = object_property_get_uint(OBJECT(bus), ACPI_PCIHP_PROP_BSEL,
  58                                             &local_err);
  59
  60    if (local_err || bsel >= ACPI_PCIHP_MAX_HOTPLUG_BUS) {
  61        if (local_err) {
  62            error_free(local_err);
  63        }
  64        return -1;
  65    } else {
  66        return bsel;
  67    }
  68}
  69
  70/* Assign BSEL property to all buses.  In the future, this can be changed
  71 * to only assign to buses that support hotplug.
  72 */
  73static void *acpi_set_bsel(PCIBus *bus, void *opaque)
  74{
  75    unsigned *bsel_alloc = opaque;
  76    unsigned *bus_bsel;
  77
  78    if (qbus_is_hotpluggable(BUS(bus))) {
  79        bus_bsel = g_malloc(sizeof *bus_bsel);
  80
  81        *bus_bsel = (*bsel_alloc)++;
  82        object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL,
  83                                       bus_bsel, OBJ_PROP_FLAG_READ);
  84    }
  85
  86    return bsel_alloc;
  87}
  88
  89static void acpi_set_pci_info(void)
  90{
  91    static bool bsel_is_set;
  92    PCIBus *bus;
  93    unsigned bsel_alloc = ACPI_PCIHP_BSEL_DEFAULT;
  94
  95    if (bsel_is_set) {
  96        return;
  97    }
  98    bsel_is_set = true;
  99
 100    bus = find_i440fx(); /* TODO: Q35 support */
 101    if (bus) {
 102        /* Scan all PCI buses. Set property to enable acpi based hotplug. */
 103        pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &bsel_alloc);
 104    }
 105}
 106
 107static void acpi_pcihp_disable_root_bus(void)
 108{
 109    static bool root_hp_disabled;
 110    PCIBus *bus;
 111
 112    if (root_hp_disabled) {
 113        return;
 114    }
 115
 116    bus = find_i440fx();
 117    if (bus) {
 118        /* setting the hotplug handler to NULL makes the bus non-hotpluggable */
 119        qbus_set_hotplug_handler(BUS(bus), NULL);
 120    }
 121    root_hp_disabled = true;
 122    return;
 123}
 124
 125static void acpi_pcihp_test_hotplug_bus(PCIBus *bus, void *opaque)
 126{
 127    AcpiPciHpFind *find = opaque;
 128    if (find->bsel == acpi_pcihp_get_bsel(bus)) {
 129        find->bus = bus;
 130    }
 131}
 132
 133static PCIBus *acpi_pcihp_find_hotplug_bus(AcpiPciHpState *s, int bsel)
 134{
 135    AcpiPciHpFind find = { .bsel = bsel, .bus = NULL };
 136
 137    if (bsel < 0) {
 138        return NULL;
 139    }
 140
 141    pci_for_each_bus(s->root, acpi_pcihp_test_hotplug_bus, &find);
 142
 143    /* Make bsel 0 eject root bus if bsel property is not set,
 144     * for compatibility with non acpi setups.
 145     * TODO: really needed?
 146     */
 147    if (!bsel && !find.bus) {
 148        find.bus = s->root;
 149    }
 150
 151    /*
 152     * Check if find.bus is actually hotpluggable. If bsel is set to
 153     * NULL for example on the root bus in order to make it
 154     * non-hotpluggable, find.bus will match the root bus when bsel
 155     * is 0. See acpi_pcihp_test_hotplug_bus() above. Since the
 156     * bus is not hotpluggable however, we should not select the bus.
 157     * Instead, we should set find.bus to NULL in that case. In the check
 158     * below, we generalize this case for all buses, not just the root bus.
 159     * The callers of this function check for a null return value and
 160     * handle them appropriately.
 161     */
 162    if (find.bus && !qbus_is_hotpluggable(BUS(find.bus))) {
 163        find.bus = NULL;
 164    }
 165    return find.bus;
 166}
 167
 168static bool acpi_pcihp_pc_no_hotplug(AcpiPciHpState *s, PCIDevice *dev)
 169{
 170    PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
 171    DeviceClass *dc = DEVICE_GET_CLASS(dev);
 172    /*
 173     * ACPI doesn't allow hotplug of bridge devices.  Don't allow
 174     * hot-unplug of bridge devices unless they were added by hotplug
 175     * (and so, not described by acpi).
 176     */
 177    return (pc->is_bridge && !dev->qdev.hotplugged) || !dc->hotpluggable;
 178}
 179
 180static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slots)
 181{
 182    HotplugHandler *hotplug_ctrl;
 183    BusChild *kid, *next;
 184    int slot = ctz32(slots);
 185    PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel);
 186
 187    trace_acpi_pci_eject_slot(bsel, slot);
 188
 189    if (!bus || slot > 31) {
 190        return;
 191    }
 192
 193    /* Mark request as complete */
 194    s->acpi_pcihp_pci_status[bsel].down &= ~(1U << slot);
 195    s->acpi_pcihp_pci_status[bsel].up &= ~(1U << slot);
 196
 197    QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) {
 198        DeviceState *qdev = kid->child;
 199        PCIDevice *dev = PCI_DEVICE(qdev);
 200        if (PCI_SLOT(dev->devfn) == slot) {
 201            if (!acpi_pcihp_pc_no_hotplug(s, dev)) {
 202                hotplug_ctrl = qdev_get_hotplug_handler(qdev);
 203                hotplug_handler_unplug(hotplug_ctrl, qdev, &error_abort);
 204                object_unparent(OBJECT(qdev));
 205            }
 206        }
 207    }
 208}
 209
 210static void acpi_pcihp_update_hotplug_bus(AcpiPciHpState *s, int bsel)
 211{
 212    BusChild *kid, *next;
 213    PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel);
 214
 215    /* Execute any pending removes during reset */
 216    while (s->acpi_pcihp_pci_status[bsel].down) {
 217        acpi_pcihp_eject_slot(s, bsel, s->acpi_pcihp_pci_status[bsel].down);
 218    }
 219
 220    s->acpi_pcihp_pci_status[bsel].hotplug_enable = ~0;
 221
 222    if (!bus) {
 223        return;
 224    }
 225    QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) {
 226        DeviceState *qdev = kid->child;
 227        PCIDevice *pdev = PCI_DEVICE(qdev);
 228        int slot = PCI_SLOT(pdev->devfn);
 229
 230        if (acpi_pcihp_pc_no_hotplug(s, pdev)) {
 231            s->acpi_pcihp_pci_status[bsel].hotplug_enable &= ~(1U << slot);
 232        }
 233    }
 234}
 235
 236static void acpi_pcihp_update(AcpiPciHpState *s)
 237{
 238    int i;
 239
 240    for (i = 0; i < ACPI_PCIHP_MAX_HOTPLUG_BUS; ++i) {
 241        acpi_pcihp_update_hotplug_bus(s, i);
 242    }
 243}
 244
 245void acpi_pcihp_reset(AcpiPciHpState *s, bool acpihp_root_off)
 246{
 247    if (acpihp_root_off) {
 248        acpi_pcihp_disable_root_bus();
 249    }
 250    acpi_set_pci_info();
 251    acpi_pcihp_update(s);
 252}
 253
 254void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev,
 255                                   DeviceState *dev, Error **errp)
 256{
 257    /* Only hotplugged devices need the hotplug capability. */
 258    if (dev->hotplugged &&
 259        acpi_pcihp_get_bsel(pci_get_bus(PCI_DEVICE(dev))) < 0) {
 260        error_setg(errp, "Unsupported bus. Bus doesn't have property '"
 261                   ACPI_PCIHP_PROP_BSEL "' set");
 262        return;
 263    }
 264}
 265
 266void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
 267                               DeviceState *dev, Error **errp)
 268{
 269    PCIDevice *pdev = PCI_DEVICE(dev);
 270    int slot = PCI_SLOT(pdev->devfn);
 271    int bsel;
 272
 273    /* Don't send event when device is enabled during qemu machine creation:
 274     * it is present on boot, no hotplug event is necessary. We do send an
 275     * event when the device is disabled later. */
 276    if (!dev->hotplugged) {
 277        /*
 278         * Overwrite the default hotplug handler with the ACPI PCI one
 279         * for cold plugged bridges only.
 280         */
 281        if (!s->legacy_piix &&
 282            object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) {
 283            PCIBus *sec = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev));
 284
 285            qbus_set_hotplug_handler(BUS(sec), OBJECT(hotplug_dev));
 286            /* We don't have to overwrite any other hotplug handler yet */
 287            assert(QLIST_EMPTY(&sec->child));
 288        }
 289
 290        return;
 291    }
 292
 293    bsel = acpi_pcihp_get_bsel(pci_get_bus(pdev));
 294    g_assert(bsel >= 0);
 295    s->acpi_pcihp_pci_status[bsel].up |= (1U << slot);
 296    acpi_send_event(DEVICE(hotplug_dev), ACPI_PCI_HOTPLUG_STATUS);
 297}
 298
 299void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
 300                                 DeviceState *dev, Error **errp)
 301{
 302    trace_acpi_pci_unplug(PCI_SLOT(PCI_DEVICE(dev)->devfn),
 303                          acpi_pcihp_get_bsel(pci_get_bus(PCI_DEVICE(dev))));
 304    qdev_unrealize(dev);
 305}
 306
 307void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev,
 308                                         AcpiPciHpState *s, DeviceState *dev,
 309                                         Error **errp)
 310{
 311    PCIDevice *pdev = PCI_DEVICE(dev);
 312    int slot = PCI_SLOT(pdev->devfn);
 313    int bsel = acpi_pcihp_get_bsel(pci_get_bus(pdev));
 314
 315    trace_acpi_pci_unplug_request(bsel, slot);
 316
 317    if (bsel < 0) {
 318        error_setg(errp, "Unsupported bus. Bus doesn't have property '"
 319                   ACPI_PCIHP_PROP_BSEL "' set");
 320        return;
 321    }
 322
 323    s->acpi_pcihp_pci_status[bsel].down |= (1U << slot);
 324    acpi_send_event(DEVICE(hotplug_dev), ACPI_PCI_HOTPLUG_STATUS);
 325}
 326
 327static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size)
 328{
 329    AcpiPciHpState *s = opaque;
 330    uint32_t val = 0;
 331    int bsel = s->hotplug_select;
 332
 333    if (bsel < 0 || bsel >= ACPI_PCIHP_MAX_HOTPLUG_BUS) {
 334        return 0;
 335    }
 336
 337    switch (addr) {
 338    case PCI_UP_BASE:
 339        val = s->acpi_pcihp_pci_status[bsel].up;
 340        if (!s->legacy_piix) {
 341            s->acpi_pcihp_pci_status[bsel].up = 0;
 342        }
 343        trace_acpi_pci_up_read(val);
 344        break;
 345    case PCI_DOWN_BASE:
 346        val = s->acpi_pcihp_pci_status[bsel].down;
 347        trace_acpi_pci_down_read(val);
 348        break;
 349    case PCI_EJ_BASE:
 350        /* No feature defined yet */
 351        trace_acpi_pci_features_read(val);
 352        break;
 353    case PCI_RMV_BASE:
 354        val = s->acpi_pcihp_pci_status[bsel].hotplug_enable;
 355        trace_acpi_pci_rmv_read(val);
 356        break;
 357    case PCI_SEL_BASE:
 358        val = s->hotplug_select;
 359        trace_acpi_pci_sel_read(val);
 360    default:
 361        break;
 362    }
 363
 364    return val;
 365}
 366
 367static void pci_write(void *opaque, hwaddr addr, uint64_t data,
 368                      unsigned int size)
 369{
 370    AcpiPciHpState *s = opaque;
 371    switch (addr) {
 372    case PCI_EJ_BASE:
 373        if (s->hotplug_select >= ACPI_PCIHP_MAX_HOTPLUG_BUS) {
 374            break;
 375        }
 376        acpi_pcihp_eject_slot(s, s->hotplug_select, data);
 377        trace_acpi_pci_ej_write(addr, data);
 378        break;
 379    case PCI_SEL_BASE:
 380        s->hotplug_select = s->legacy_piix ? ACPI_PCIHP_BSEL_DEFAULT : data;
 381        trace_acpi_pci_sel_write(addr, data);
 382    default:
 383        break;
 384    }
 385}
 386
 387static const MemoryRegionOps acpi_pcihp_io_ops = {
 388    .read = pci_read,
 389    .write = pci_write,
 390    .endianness = DEVICE_LITTLE_ENDIAN,
 391    .valid = {
 392        .min_access_size = 4,
 393        .max_access_size = 4,
 394    },
 395};
 396
 397void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus,
 398                     MemoryRegion *address_space_io, bool bridges_enabled)
 399{
 400    s->io_len = ACPI_PCIHP_SIZE;
 401    s->io_base = ACPI_PCIHP_ADDR;
 402
 403    s->root = root_bus;
 404    s->legacy_piix = !bridges_enabled;
 405
 406    memory_region_init_io(&s->io, owner, &acpi_pcihp_io_ops, s,
 407                          "acpi-pci-hotplug", s->io_len);
 408    memory_region_add_subregion(address_space_io, s->io_base, &s->io);
 409
 410    object_property_add_uint16_ptr(owner, ACPI_PCIHP_IO_BASE_PROP, &s->io_base,
 411                                   OBJ_PROP_FLAG_READ);
 412    object_property_add_uint16_ptr(owner, ACPI_PCIHP_IO_LEN_PROP, &s->io_len,
 413                                   OBJ_PROP_FLAG_READ);
 414}
 415
 416const VMStateDescription vmstate_acpi_pcihp_pci_status = {
 417    .name = "acpi_pcihp_pci_status",
 418    .version_id = 1,
 419    .minimum_version_id = 1,
 420    .fields = (VMStateField[]) {
 421        VMSTATE_UINT32(up, AcpiPciHpPciStatus),
 422        VMSTATE_UINT32(down, AcpiPciHpPciStatus),
 423        VMSTATE_END_OF_LIST()
 424    }
 425};
 426