qemu/hw/acpi/generic_event_device.c
<<
>>
Prefs
   1/*
   2 *
   3 * Copyright (c) 2018 Intel Corporation
   4 * Copyright (c) 2019 Huawei Technologies R & D (UK) Ltd
   5 * Written by Samuel Ortiz, Shameer Kolothum
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms and conditions of the GNU General Public License,
   9 * version 2 or later, as published by the Free Software Foundation.
  10 */
  11
  12#include "qemu/osdep.h"
  13#include "qapi/error.h"
  14#include "exec/address-spaces.h"
  15#include "hw/acpi/acpi.h"
  16#include "hw/acpi/generic_event_device.h"
  17#include "hw/irq.h"
  18#include "hw/mem/pc-dimm.h"
  19#include "hw/mem/nvdimm.h"
  20#include "hw/qdev-properties.h"
  21#include "migration/vmstate.h"
  22#include "qemu/error-report.h"
  23#include "sysemu/runstate.h"
  24
  25static const uint32_t ged_supported_events[] = {
  26    ACPI_GED_MEM_HOTPLUG_EVT,
  27    ACPI_GED_PWR_DOWN_EVT,
  28    ACPI_GED_NVDIMM_HOTPLUG_EVT,
  29};
  30
  31/*
  32 * The ACPI Generic Event Device (GED) is a hardware-reduced specific
  33 * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
  34 * including the hotplug ones. Platforms need to specify their own
  35 * GED Event bitmap to describe what kind of events they want to support
  36 * through GED. This routine uses a single interrupt for the GED device,
  37 * relying on IO memory region to communicate the type of device
  38 * affected by the interrupt. This way, we can support up to 32 events
  39 * with a unique interrupt.
  40 */
  41void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
  42                   uint32_t ged_irq, AmlRegionSpace rs, hwaddr ged_base)
  43{
  44    AcpiGedState *s = ACPI_GED(hotplug_dev);
  45    Aml *crs = aml_resource_template();
  46    Aml *evt, *field;
  47    Aml *dev = aml_device("%s", name);
  48    Aml *evt_sel = aml_local(0);
  49    Aml *esel = aml_name(AML_GED_EVT_SEL);
  50
  51    /* _CRS interrupt */
  52    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
  53                                  AML_EXCLUSIVE, &ged_irq, 1));
  54
  55    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
  56    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
  57    aml_append(dev, aml_name_decl("_CRS", crs));
  58
  59    /* Append IO region */
  60    aml_append(dev, aml_operation_region(AML_GED_EVT_REG, rs,
  61               aml_int(ged_base + ACPI_GED_EVT_SEL_OFFSET),
  62               ACPI_GED_EVT_SEL_LEN));
  63    field = aml_field(AML_GED_EVT_REG, AML_DWORD_ACC, AML_NOLOCK,
  64                      AML_WRITE_AS_ZEROS);
  65    aml_append(field, aml_named_field(AML_GED_EVT_SEL,
  66                                      ACPI_GED_EVT_SEL_LEN * BITS_PER_BYTE));
  67    aml_append(dev, field);
  68
  69    /*
  70     * For each GED event we:
  71     * - Add a conditional block for each event, inside a loop.
  72     * - Call a method for each supported GED event type.
  73     *
  74     * The resulting ASL code looks like:
  75     *
  76     * Local0 = ESEL
  77     * If ((Local0 & One) == One)
  78     * {
  79     *     MethodEvent0()
  80     * }
  81     *
  82     * If ((Local0 & 0x2) == 0x2)
  83     * {
  84     *     MethodEvent1()
  85     * }
  86     * ...
  87     */
  88    evt = aml_method("_EVT", 1, AML_SERIALIZED);
  89    {
  90        Aml *if_ctx;
  91        uint32_t i;
  92        uint32_t ged_events = ctpop32(s->ged_event_bitmap);
  93
  94        /* Local0 = ESEL */
  95        aml_append(evt, aml_store(esel, evt_sel));
  96
  97        for (i = 0; i < ARRAY_SIZE(ged_supported_events) && ged_events; i++) {
  98            uint32_t event = s->ged_event_bitmap & ged_supported_events[i];
  99
 100            if (!event) {
 101                continue;
 102            }
 103
 104            if_ctx = aml_if(aml_equal(aml_and(evt_sel, aml_int(event), NULL),
 105                                      aml_int(event)));
 106            switch (event) {
 107            case ACPI_GED_MEM_HOTPLUG_EVT:
 108                aml_append(if_ctx, aml_call0(MEMORY_DEVICES_CONTAINER "."
 109                                             MEMORY_SLOT_SCAN_METHOD));
 110                break;
 111            case ACPI_GED_PWR_DOWN_EVT:
 112                aml_append(if_ctx,
 113                           aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE),
 114                                      aml_int(0x80)));
 115                break;
 116            case ACPI_GED_NVDIMM_HOTPLUG_EVT:
 117                aml_append(if_ctx,
 118                           aml_notify(aml_name("\\_SB.NVDR"),
 119                                      aml_int(0x80)));
 120                break;
 121            default:
 122                /*
 123                 * Please make sure all the events in ged_supported_events[]
 124                 * are handled above.
 125                 */
 126                g_assert_not_reached();
 127            }
 128
 129            aml_append(evt, if_ctx);
 130            ged_events--;
 131        }
 132
 133        if (ged_events) {
 134            error_report("Unsupported events specified");
 135            abort();
 136        }
 137    }
 138
 139    /* Append _EVT method */
 140    aml_append(dev, evt);
 141
 142    aml_append(table, dev);
 143}
 144
 145void acpi_dsdt_add_power_button(Aml *scope)
 146{
 147    Aml *dev = aml_device(ACPI_POWER_BUTTON_DEVICE);
 148    aml_append(dev, aml_name_decl("_HID", aml_string("PNP0C0C")));
 149    aml_append(dev, aml_name_decl("_UID", aml_int(0)));
 150    aml_append(scope, dev);
 151}
 152
 153/* Memory read by the GED _EVT AML dynamic method */
 154static uint64_t ged_evt_read(void *opaque, hwaddr addr, unsigned size)
 155{
 156    uint64_t val = 0;
 157    GEDState *ged_st = opaque;
 158
 159    switch (addr) {
 160    case ACPI_GED_EVT_SEL_OFFSET:
 161        /* Read the selector value and reset it */
 162        val = ged_st->sel;
 163        ged_st->sel = 0;
 164        break;
 165    default:
 166        break;
 167    }
 168
 169    return val;
 170}
 171
 172/* Nothing is expected to be written to the GED memory region */
 173static void ged_evt_write(void *opaque, hwaddr addr, uint64_t data,
 174                          unsigned int size)
 175{
 176}
 177
 178static const MemoryRegionOps ged_evt_ops = {
 179    .read = ged_evt_read,
 180    .write = ged_evt_write,
 181    .endianness = DEVICE_LITTLE_ENDIAN,
 182    .valid = {
 183        .min_access_size = 4,
 184        .max_access_size = 4,
 185    },
 186};
 187
 188static uint64_t ged_regs_read(void *opaque, hwaddr addr, unsigned size)
 189{
 190    return 0;
 191}
 192
 193static void ged_regs_write(void *opaque, hwaddr addr, uint64_t data,
 194                           unsigned int size)
 195{
 196    bool slp_en;
 197    int slp_typ;
 198
 199    switch (addr) {
 200    case ACPI_GED_REG_SLEEP_CTL:
 201        slp_typ = (data >> 2) & 0x07;
 202        slp_en  = (data >> 5) & 0x01;
 203        if (slp_en && slp_typ == 5) {
 204            qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
 205        }
 206        return;
 207    case ACPI_GED_REG_SLEEP_STS:
 208        return;
 209    case ACPI_GED_REG_RESET:
 210        if (data == ACPI_GED_RESET_VALUE) {
 211            qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
 212        }
 213        return;
 214    }
 215}
 216
 217static const MemoryRegionOps ged_regs_ops = {
 218    .read = ged_regs_read,
 219    .write = ged_regs_write,
 220    .endianness = DEVICE_LITTLE_ENDIAN,
 221    .valid = {
 222        .min_access_size = 1,
 223        .max_access_size = 1,
 224    },
 225};
 226
 227static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
 228                                    DeviceState *dev, Error **errp)
 229{
 230    AcpiGedState *s = ACPI_GED(hotplug_dev);
 231
 232    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
 233        if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) {
 234            nvdimm_acpi_plug_cb(hotplug_dev, dev);
 235        } else {
 236            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state, dev, errp);
 237        }
 238    } else {
 239        error_setg(errp, "virt: device plug request for unsupported device"
 240                   " type: %s", object_get_typename(OBJECT(dev)));
 241    }
 242}
 243
 244static void acpi_ged_unplug_request_cb(HotplugHandler *hotplug_dev,
 245                                       DeviceState *dev, Error **errp)
 246{
 247    AcpiGedState *s = ACPI_GED(hotplug_dev);
 248
 249    if ((object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) &&
 250                       !(object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)))) {
 251        acpi_memory_unplug_request_cb(hotplug_dev, &s->memhp_state, dev, errp);
 252    } else {
 253        error_setg(errp, "acpi: device unplug request for unsupported device"
 254                   " type: %s", object_get_typename(OBJECT(dev)));
 255    }
 256}
 257
 258static void acpi_ged_unplug_cb(HotplugHandler *hotplug_dev,
 259                               DeviceState *dev, Error **errp)
 260{
 261    AcpiGedState *s = ACPI_GED(hotplug_dev);
 262
 263    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
 264        acpi_memory_unplug_cb(&s->memhp_state, dev, errp);
 265    } else {
 266        error_setg(errp, "acpi: device unplug for unsupported device"
 267                   " type: %s", object_get_typename(OBJECT(dev)));
 268    }
 269}
 270
 271static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
 272{
 273    AcpiGedState *s = ACPI_GED(adev);
 274    GEDState *ged_st = &s->ged_state;
 275    uint32_t sel;
 276
 277    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
 278        sel = ACPI_GED_MEM_HOTPLUG_EVT;
 279    } else if (ev & ACPI_POWER_DOWN_STATUS) {
 280        sel = ACPI_GED_PWR_DOWN_EVT;
 281    } else if (ev & ACPI_NVDIMM_HOTPLUG_STATUS) {
 282        sel = ACPI_GED_NVDIMM_HOTPLUG_EVT;
 283    } else {
 284        /* Unknown event. Return without generating interrupt. */
 285        warn_report("GED: Unsupported event %d. No irq injected", ev);
 286        return;
 287    }
 288
 289    /*
 290     * Set the GED selector field to communicate the event type.
 291     * This will be read by GED aml code to select the appropriate
 292     * event method.
 293     */
 294    ged_st->sel |= sel;
 295
 296    /* Trigger the event by sending an interrupt to the guest. */
 297    qemu_irq_pulse(s->irq);
 298}
 299
 300static Property acpi_ged_properties[] = {
 301    DEFINE_PROP_UINT32("ged-event", AcpiGedState, ged_event_bitmap, 0),
 302    DEFINE_PROP_END_OF_LIST(),
 303};
 304
 305static const VMStateDescription vmstate_memhp_state = {
 306    .name = "acpi-ged/memhp",
 307    .version_id = 1,
 308    .minimum_version_id = 1,
 309    .fields      = (VMStateField[]) {
 310        VMSTATE_MEMORY_HOTPLUG(memhp_state, AcpiGedState),
 311        VMSTATE_END_OF_LIST()
 312    }
 313};
 314
 315static const VMStateDescription vmstate_ged_state = {
 316    .name = "acpi-ged-state",
 317    .version_id = 1,
 318    .minimum_version_id = 1,
 319    .fields      = (VMStateField[]) {
 320        VMSTATE_UINT32(sel, GEDState),
 321        VMSTATE_END_OF_LIST()
 322    }
 323};
 324
 325static const VMStateDescription vmstate_ghes = {
 326    .name = "acpi-ghes",
 327    .version_id = 1,
 328    .minimum_version_id = 1,
 329    .fields     = (VMStateField[]) {
 330        VMSTATE_UINT64(ghes_addr_le, AcpiGhesState),
 331        VMSTATE_END_OF_LIST()
 332    },
 333};
 334
 335static bool ghes_needed(void *opaque)
 336{
 337    AcpiGedState *s = opaque;
 338    return s->ghes_state.ghes_addr_le;
 339}
 340
 341static const VMStateDescription vmstate_ghes_state = {
 342    .name = "acpi-ged/ghes",
 343    .version_id = 1,
 344    .minimum_version_id = 1,
 345    .needed = ghes_needed,
 346    .fields      = (VMStateField[]) {
 347        VMSTATE_STRUCT(ghes_state, AcpiGedState, 1,
 348                       vmstate_ghes, AcpiGhesState),
 349        VMSTATE_END_OF_LIST()
 350    }
 351};
 352
 353static const VMStateDescription vmstate_acpi_ged = {
 354    .name = "acpi-ged",
 355    .version_id = 1,
 356    .minimum_version_id = 1,
 357    .fields = (VMStateField[]) {
 358        VMSTATE_STRUCT(ged_state, AcpiGedState, 1, vmstate_ged_state, GEDState),
 359        VMSTATE_END_OF_LIST(),
 360    },
 361    .subsections = (const VMStateDescription * []) {
 362        &vmstate_memhp_state,
 363        &vmstate_ghes_state,
 364        NULL
 365    }
 366};
 367
 368static void acpi_ged_initfn(Object *obj)
 369{
 370    DeviceState *dev = DEVICE(obj);
 371    AcpiGedState *s = ACPI_GED(dev);
 372    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 373    GEDState *ged_st = &s->ged_state;
 374
 375    memory_region_init_io(&ged_st->evt, obj, &ged_evt_ops, ged_st,
 376                          TYPE_ACPI_GED, ACPI_GED_EVT_SEL_LEN);
 377    sysbus_init_mmio(sbd, &ged_st->evt);
 378
 379    sysbus_init_irq(sbd, &s->irq);
 380
 381    s->memhp_state.is_enabled = true;
 382    /*
 383     * GED handles memory hotplug event and acpi-mem-hotplug
 384     * memory region gets initialized here. Create an exclusive
 385     * container for memory hotplug IO and expose it as GED sysbus
 386     * MMIO so that boards can map it separately.
 387     */
 388     memory_region_init(&s->container_memhp, OBJECT(dev), "memhp container",
 389                        MEMORY_HOTPLUG_IO_LEN);
 390     sysbus_init_mmio(sbd, &s->container_memhp);
 391     acpi_memory_hotplug_init(&s->container_memhp, OBJECT(dev),
 392                              &s->memhp_state, 0);
 393
 394    memory_region_init_io(&ged_st->regs, obj, &ged_regs_ops, ged_st,
 395                          TYPE_ACPI_GED "-regs", ACPI_GED_REG_COUNT);
 396    sysbus_init_mmio(sbd, &ged_st->regs);
 397}
 398
 399static void acpi_ged_class_init(ObjectClass *class, void *data)
 400{
 401    DeviceClass *dc = DEVICE_CLASS(class);
 402    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
 403    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
 404
 405    dc->desc = "ACPI Generic Event Device";
 406    device_class_set_props(dc, acpi_ged_properties);
 407    dc->vmsd = &vmstate_acpi_ged;
 408
 409    hc->plug = acpi_ged_device_plug_cb;
 410    hc->unplug_request = acpi_ged_unplug_request_cb;
 411    hc->unplug = acpi_ged_unplug_cb;
 412
 413    adevc->send_event = acpi_ged_send_event;
 414}
 415
 416static const TypeInfo acpi_ged_info = {
 417    .name          = TYPE_ACPI_GED,
 418    .parent        = TYPE_SYS_BUS_DEVICE,
 419    .instance_size = sizeof(AcpiGedState),
 420    .instance_init  = acpi_ged_initfn,
 421    .class_init    = acpi_ged_class_init,
 422    .interfaces = (InterfaceInfo[]) {
 423        { TYPE_HOTPLUG_HANDLER },
 424        { TYPE_ACPI_DEVICE_IF },
 425        { }
 426    }
 427};
 428
 429static void acpi_ged_register_types(void)
 430{
 431    type_register_static(&acpi_ged_info);
 432}
 433
 434type_init(acpi_ged_register_types)
 435