qemu

Форк
0
/
generic_event_device.c 
491 строка · 15.0 Кб
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 "hw/acpi/acpi.h"
15
#include "hw/acpi/generic_event_device.h"
16
#include "hw/irq.h"
17
#include "hw/mem/pc-dimm.h"
18
#include "hw/mem/nvdimm.h"
19
#include "hw/qdev-properties.h"
20
#include "migration/vmstate.h"
21
#include "qemu/error-report.h"
22
#include "sysemu/runstate.h"
23

24
static const uint32_t ged_supported_events[] = {
25
    ACPI_GED_MEM_HOTPLUG_EVT,
26
    ACPI_GED_PWR_DOWN_EVT,
27
    ACPI_GED_NVDIMM_HOTPLUG_EVT,
28
    ACPI_GED_CPU_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
 */
41
void 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_CPU_HOTPLUG_EVT:
112
                aml_append(if_ctx, aml_call0(AML_GED_EVT_CPU_SCAN_METHOD));
113
                break;
114
            case ACPI_GED_PWR_DOWN_EVT:
115
                aml_append(if_ctx,
116
                           aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE),
117
                                      aml_int(0x80)));
118
                break;
119
            case ACPI_GED_NVDIMM_HOTPLUG_EVT:
120
                aml_append(if_ctx,
121
                           aml_notify(aml_name("\\_SB.NVDR"),
122
                                      aml_int(0x80)));
123
                break;
124
            default:
125
                /*
126
                 * Please make sure all the events in ged_supported_events[]
127
                 * are handled above.
128
                 */
129
                g_assert_not_reached();
130
            }
131

132
            aml_append(evt, if_ctx);
133
            ged_events--;
134
        }
135

136
        if (ged_events) {
137
            error_report("Unsupported events specified");
138
            abort();
139
        }
140
    }
141

142
    /* Append _EVT method */
143
    aml_append(dev, evt);
144

145
    aml_append(table, dev);
146
}
147

148
void acpi_dsdt_add_power_button(Aml *scope)
149
{
150
    Aml *dev = aml_device(ACPI_POWER_BUTTON_DEVICE);
151
    aml_append(dev, aml_name_decl("_HID", aml_string("PNP0C0C")));
152
    aml_append(dev, aml_name_decl("_UID", aml_int(0)));
153
    aml_append(scope, dev);
154
}
155

156
/* Memory read by the GED _EVT AML dynamic method */
157
static uint64_t ged_evt_read(void *opaque, hwaddr addr, unsigned size)
158
{
159
    uint64_t val = 0;
160
    GEDState *ged_st = opaque;
161

162
    switch (addr) {
163
    case ACPI_GED_EVT_SEL_OFFSET:
164
        /* Read the selector value and reset it */
165
        val = ged_st->sel;
166
        ged_st->sel = 0;
167
        break;
168
    default:
169
        break;
170
    }
171

172
    return val;
173
}
174

175
/* Nothing is expected to be written to the GED memory region */
176
static void ged_evt_write(void *opaque, hwaddr addr, uint64_t data,
177
                          unsigned int size)
178
{
179
}
180

181
static const MemoryRegionOps ged_evt_ops = {
182
    .read = ged_evt_read,
183
    .write = ged_evt_write,
184
    .endianness = DEVICE_LITTLE_ENDIAN,
185
    .valid = {
186
        .min_access_size = 4,
187
        .max_access_size = 4,
188
    },
189
};
190

191
static uint64_t ged_regs_read(void *opaque, hwaddr addr, unsigned size)
192
{
193
    return 0;
194
}
195

196
static void ged_regs_write(void *opaque, hwaddr addr, uint64_t data,
197
                           unsigned int size)
198
{
199
    bool slp_en;
200
    int slp_typ;
201

202
    switch (addr) {
203
    case ACPI_GED_REG_SLEEP_CTL:
204
        slp_typ = (data >> 2) & 0x07;
205
        slp_en  = (data >> 5) & 0x01;
206
        if (slp_en && slp_typ == 5) {
207
            qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
208
        }
209
        return;
210
    case ACPI_GED_REG_SLEEP_STS:
211
        return;
212
    case ACPI_GED_REG_RESET:
213
        if (data == ACPI_GED_RESET_VALUE) {
214
            qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
215
        }
216
        return;
217
    }
218
}
219

220
static const MemoryRegionOps ged_regs_ops = {
221
    .read = ged_regs_read,
222
    .write = ged_regs_write,
223
    .endianness = DEVICE_LITTLE_ENDIAN,
224
    .valid = {
225
        .min_access_size = 1,
226
        .max_access_size = 1,
227
    },
228
};
229

230
static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
231
                                    DeviceState *dev, Error **errp)
232
{
233
    AcpiGedState *s = ACPI_GED(hotplug_dev);
234

235
    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
236
        if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) {
237
            nvdimm_acpi_plug_cb(hotplug_dev, dev);
238
        } else {
239
            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state, dev, errp);
240
        }
241
    } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
242
        acpi_cpu_plug_cb(hotplug_dev, &s->cpuhp_state, dev, errp);
243
    } else {
244
        error_setg(errp, "virt: device plug request for unsupported device"
245
                   " type: %s", object_get_typename(OBJECT(dev)));
246
    }
247
}
248

249
static void acpi_ged_unplug_request_cb(HotplugHandler *hotplug_dev,
250
                                       DeviceState *dev, Error **errp)
251
{
252
    AcpiGedState *s = ACPI_GED(hotplug_dev);
253

254
    if ((object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) &&
255
                       !(object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)))) {
256
        acpi_memory_unplug_request_cb(hotplug_dev, &s->memhp_state, dev, errp);
257
    } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
258
        acpi_cpu_unplug_request_cb(hotplug_dev, &s->cpuhp_state, dev, errp);
259
    } else {
260
        error_setg(errp, "acpi: device unplug request for unsupported device"
261
                   " type: %s", object_get_typename(OBJECT(dev)));
262
    }
263
}
264

265
static void acpi_ged_unplug_cb(HotplugHandler *hotplug_dev,
266
                               DeviceState *dev, Error **errp)
267
{
268
    AcpiGedState *s = ACPI_GED(hotplug_dev);
269

270
    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
271
        acpi_memory_unplug_cb(&s->memhp_state, dev, errp);
272
    } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
273
        acpi_cpu_unplug_cb(&s->cpuhp_state, dev, errp);
274
    } else {
275
        error_setg(errp, "acpi: device unplug for unsupported device"
276
                   " type: %s", object_get_typename(OBJECT(dev)));
277
    }
278
}
279

280
static void acpi_ged_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
281
{
282
    AcpiGedState *s = ACPI_GED(adev);
283

284
    acpi_memory_ospm_status(&s->memhp_state, list);
285
    acpi_cpu_ospm_status(&s->cpuhp_state, list);
286
}
287

288
static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
289
{
290
    AcpiGedState *s = ACPI_GED(adev);
291
    GEDState *ged_st = &s->ged_state;
292
    uint32_t sel;
293

294
    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
295
        sel = ACPI_GED_MEM_HOTPLUG_EVT;
296
    } else if (ev & ACPI_POWER_DOWN_STATUS) {
297
        sel = ACPI_GED_PWR_DOWN_EVT;
298
    } else if (ev & ACPI_NVDIMM_HOTPLUG_STATUS) {
299
        sel = ACPI_GED_NVDIMM_HOTPLUG_EVT;
300
    } else if (ev & ACPI_CPU_HOTPLUG_STATUS) {
301
        sel = ACPI_GED_CPU_HOTPLUG_EVT;
302
    } else {
303
        /* Unknown event. Return without generating interrupt. */
304
        warn_report("GED: Unsupported event %d. No irq injected", ev);
305
        return;
306
    }
307

308
    /*
309
     * Set the GED selector field to communicate the event type.
310
     * This will be read by GED aml code to select the appropriate
311
     * event method.
312
     */
313
    ged_st->sel |= sel;
314

315
    /* Trigger the event by sending an interrupt to the guest. */
316
    qemu_irq_pulse(s->irq);
317
}
318

319
static Property acpi_ged_properties[] = {
320
    DEFINE_PROP_UINT32("ged-event", AcpiGedState, ged_event_bitmap, 0),
321
    DEFINE_PROP_END_OF_LIST(),
322
};
323

324
static const VMStateDescription vmstate_memhp_state = {
325
    .name = "acpi-ged/memhp",
326
    .version_id = 1,
327
    .minimum_version_id = 1,
328
    .fields = (const VMStateField[]) {
329
        VMSTATE_MEMORY_HOTPLUG(memhp_state, AcpiGedState),
330
        VMSTATE_END_OF_LIST()
331
    }
332
};
333

334
static const VMStateDescription vmstate_ged_state = {
335
    .name = "acpi-ged-state",
336
    .version_id = 1,
337
    .minimum_version_id = 1,
338
    .fields = (const VMStateField[]) {
339
        VMSTATE_UINT32(sel, GEDState),
340
        VMSTATE_END_OF_LIST()
341
    }
342
};
343

344
static const VMStateDescription vmstate_ghes = {
345
    .name = "acpi-ghes",
346
    .version_id = 1,
347
    .minimum_version_id = 1,
348
    .fields = (const VMStateField[]) {
349
        VMSTATE_UINT64(ghes_addr_le, AcpiGhesState),
350
        VMSTATE_END_OF_LIST()
351
    },
352
};
353

354
static bool ghes_needed(void *opaque)
355
{
356
    AcpiGedState *s = opaque;
357
    return s->ghes_state.ghes_addr_le;
358
}
359

360
static const VMStateDescription vmstate_ghes_state = {
361
    .name = "acpi-ged/ghes",
362
    .version_id = 1,
363
    .minimum_version_id = 1,
364
    .needed = ghes_needed,
365
    .fields = (const VMStateField[]) {
366
        VMSTATE_STRUCT(ghes_state, AcpiGedState, 1,
367
                       vmstate_ghes, AcpiGhesState),
368
        VMSTATE_END_OF_LIST()
369
    }
370
};
371

372
static const VMStateDescription vmstate_acpi_ged = {
373
    .name = "acpi-ged",
374
    .version_id = 1,
375
    .minimum_version_id = 1,
376
    .fields = (const VMStateField[]) {
377
        VMSTATE_STRUCT(ged_state, AcpiGedState, 1, vmstate_ged_state, GEDState),
378
        VMSTATE_END_OF_LIST(),
379
    },
380
    .subsections = (const VMStateDescription * const []) {
381
        &vmstate_memhp_state,
382
        &vmstate_ghes_state,
383
        NULL
384
    }
385
};
386

387
static void acpi_ged_realize(DeviceState *dev, Error **errp)
388
{
389
    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
390
    AcpiGedState *s = ACPI_GED(dev);
391
    uint32_t ged_events;
392
    int i;
393

394
    ged_events = ctpop32(s->ged_event_bitmap);
395

396
    for (i = 0; i < ARRAY_SIZE(ged_supported_events) && ged_events; i++) {
397
        uint32_t event = s->ged_event_bitmap & ged_supported_events[i];
398

399
        if (!event) {
400
            continue;
401
        }
402

403
        switch (event) {
404
        case ACPI_GED_CPU_HOTPLUG_EVT:
405
            /* initialize CPU Hotplug related regions */
406
            memory_region_init(&s->container_cpuhp, OBJECT(dev),
407
                                "cpuhp container",
408
                                ACPI_CPU_HOTPLUG_REG_LEN);
409
            sysbus_init_mmio(sbd, &s->container_cpuhp);
410
            cpu_hotplug_hw_init(&s->container_cpuhp, OBJECT(dev),
411
                                &s->cpuhp_state, 0);
412
            break;
413
        }
414
        ged_events--;
415
    }
416

417
    if (ged_events) {
418
        error_report("Unsupported events specified");
419
        abort();
420
    }
421
}
422

423
static void acpi_ged_initfn(Object *obj)
424
{
425
    DeviceState *dev = DEVICE(obj);
426
    AcpiGedState *s = ACPI_GED(dev);
427
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
428
    GEDState *ged_st = &s->ged_state;
429

430
    memory_region_init_io(&ged_st->evt, obj, &ged_evt_ops, ged_st,
431
                          TYPE_ACPI_GED, ACPI_GED_EVT_SEL_LEN);
432
    sysbus_init_mmio(sbd, &ged_st->evt);
433

434
    sysbus_init_irq(sbd, &s->irq);
435

436
    s->memhp_state.is_enabled = true;
437
    /*
438
     * GED handles memory hotplug event and acpi-mem-hotplug
439
     * memory region gets initialized here. Create an exclusive
440
     * container for memory hotplug IO and expose it as GED sysbus
441
     * MMIO so that boards can map it separately.
442
     */
443
     memory_region_init(&s->container_memhp, OBJECT(dev), "memhp container",
444
                        MEMORY_HOTPLUG_IO_LEN);
445
     sysbus_init_mmio(sbd, &s->container_memhp);
446
     acpi_memory_hotplug_init(&s->container_memhp, OBJECT(dev),
447
                              &s->memhp_state, 0);
448

449
    memory_region_init_io(&ged_st->regs, obj, &ged_regs_ops, ged_st,
450
                          TYPE_ACPI_GED "-regs", ACPI_GED_REG_COUNT);
451
    sysbus_init_mmio(sbd, &ged_st->regs);
452
}
453

454
static void acpi_ged_class_init(ObjectClass *class, void *data)
455
{
456
    DeviceClass *dc = DEVICE_CLASS(class);
457
    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
458
    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
459

460
    dc->desc = "ACPI Generic Event Device";
461
    device_class_set_props(dc, acpi_ged_properties);
462
    dc->vmsd = &vmstate_acpi_ged;
463
    dc->realize = acpi_ged_realize;
464

465
    hc->plug = acpi_ged_device_plug_cb;
466
    hc->unplug_request = acpi_ged_unplug_request_cb;
467
    hc->unplug = acpi_ged_unplug_cb;
468

469
    adevc->ospm_status = acpi_ged_ospm_status;
470
    adevc->send_event = acpi_ged_send_event;
471
}
472

473
static const TypeInfo acpi_ged_info = {
474
    .name          = TYPE_ACPI_GED,
475
    .parent        = TYPE_SYS_BUS_DEVICE,
476
    .instance_size = sizeof(AcpiGedState),
477
    .instance_init  = acpi_ged_initfn,
478
    .class_init    = acpi_ged_class_init,
479
    .interfaces = (InterfaceInfo[]) {
480
        { TYPE_HOTPLUG_HANDLER },
481
        { TYPE_ACPI_DEVICE_IF },
482
        { }
483
    }
484
};
485

486
static void acpi_ged_register_types(void)
487
{
488
    type_register_static(&acpi_ged_info);
489
}
490

491
type_init(acpi_ged_register_types)
492

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.