cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

cpu_hotplug.c (11713B)


      1/*
      2 * QEMU ACPI hotplug utilities
      3 *
      4 * Copyright (C) 2013 Red Hat Inc
      5 *
      6 * Authors:
      7 *   Igor Mammedov <imammedo@redhat.com>
      8 *
      9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
     10 * See the COPYING file in the top-level directory.
     11 */
     12#include "qemu/osdep.h"
     13#include "hw/acpi/cpu_hotplug.h"
     14#include "qapi/error.h"
     15#include "hw/core/cpu.h"
     16#include "hw/i386/pc.h"
     17#include "hw/pci/pci.h"
     18#include "qemu/error-report.h"
     19
     20#define CPU_EJECT_METHOD "CPEJ"
     21#define CPU_MAT_METHOD "CPMA"
     22#define CPU_ON_BITMAP "CPON"
     23#define CPU_STATUS_METHOD "CPST"
     24#define CPU_STATUS_MAP "PRS"
     25#define CPU_SCAN_METHOD "PRSC"
     26
     27static uint64_t cpu_status_read(void *opaque, hwaddr addr, unsigned int size)
     28{
     29    AcpiCpuHotplug *cpus = opaque;
     30    uint64_t val = cpus->sts[addr];
     31
     32    return val;
     33}
     34
     35static void cpu_status_write(void *opaque, hwaddr addr, uint64_t data,
     36                             unsigned int size)
     37{
     38    /* firmware never used to write in CPU present bitmap so use
     39       this fact as means to switch QEMU into modern CPU hotplug
     40       mode by writing 0 at the beginning of legacy CPU bitmap
     41     */
     42    if (addr == 0 && data == 0) {
     43        AcpiCpuHotplug *cpus = opaque;
     44        object_property_set_bool(cpus->device, "cpu-hotplug-legacy", false,
     45                                 &error_abort);
     46    }
     47}
     48
     49static const MemoryRegionOps AcpiCpuHotplug_ops = {
     50    .read = cpu_status_read,
     51    .write = cpu_status_write,
     52    .endianness = DEVICE_LITTLE_ENDIAN,
     53    .valid = {
     54        .min_access_size = 1,
     55        .max_access_size = 1,
     56    },
     57};
     58
     59static void acpi_set_cpu_present_bit(AcpiCpuHotplug *g, CPUState *cpu)
     60{
     61    CPUClass *k = CPU_GET_CLASS(cpu);
     62    int64_t cpu_id;
     63
     64    cpu_id = k->get_arch_id(cpu);
     65    if ((cpu_id / 8) >= ACPI_GPE_PROC_LEN) {
     66        object_property_set_bool(g->device, "cpu-hotplug-legacy", false,
     67                                 &error_abort);
     68        return;
     69    }
     70
     71    g->sts[cpu_id / 8] |= (1 << (cpu_id % 8));
     72}
     73
     74void legacy_acpi_cpu_plug_cb(HotplugHandler *hotplug_dev,
     75                             AcpiCpuHotplug *g, DeviceState *dev, Error **errp)
     76{
     77    acpi_set_cpu_present_bit(g, CPU(dev));
     78    acpi_send_event(DEVICE(hotplug_dev), ACPI_CPU_HOTPLUG_STATUS);
     79}
     80
     81void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner,
     82                                  AcpiCpuHotplug *gpe_cpu, uint16_t base)
     83{
     84    CPUState *cpu;
     85
     86    memory_region_init_io(&gpe_cpu->io, owner, &AcpiCpuHotplug_ops,
     87                          gpe_cpu, "acpi-cpu-hotplug", ACPI_GPE_PROC_LEN);
     88    memory_region_add_subregion(parent, base, &gpe_cpu->io);
     89    gpe_cpu->device = owner;
     90
     91    CPU_FOREACH(cpu) {
     92        acpi_set_cpu_present_bit(gpe_cpu, cpu);
     93    }
     94}
     95
     96void acpi_switch_to_modern_cphp(AcpiCpuHotplug *gpe_cpu,
     97                                CPUHotplugState *cpuhp_state,
     98                                uint16_t io_port)
     99{
    100    MemoryRegion *parent = pci_address_space_io(PCI_DEVICE(gpe_cpu->device));
    101
    102    memory_region_del_subregion(parent, &gpe_cpu->io);
    103    cpu_hotplug_hw_init(parent, gpe_cpu->device, cpuhp_state, io_port);
    104}
    105
    106void build_legacy_cpu_hotplug_aml(Aml *ctx, MachineState *machine,
    107                                  uint16_t io_base)
    108{
    109    Aml *dev;
    110    Aml *crs;
    111    Aml *pkg;
    112    Aml *field;
    113    Aml *method;
    114    Aml *if_ctx;
    115    Aml *else_ctx;
    116    int i, apic_idx;
    117    Aml *sb_scope = aml_scope("_SB");
    118    uint8_t madt_tmpl[8] = {0x00, 0x08, 0x00, 0x00, 0x00, 0, 0, 0};
    119    Aml *cpu_id = aml_arg(1);
    120    Aml *apic_id = aml_arg(0);
    121    Aml *cpu_on = aml_local(0);
    122    Aml *madt = aml_local(1);
    123    Aml *cpus_map = aml_name(CPU_ON_BITMAP);
    124    Aml *zero = aml_int(0);
    125    Aml *one = aml_int(1);
    126    MachineClass *mc = MACHINE_GET_CLASS(machine);
    127    const CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine);
    128    X86MachineState *x86ms = X86_MACHINE(machine);
    129
    130    /*
    131     * _MAT method - creates an madt apic buffer
    132     * apic_id = Arg0 = Local APIC ID
    133     * cpu_id  = Arg1 = Processor ID
    134     * cpu_on = Local0 = CPON flag for this cpu
    135     * madt = Local1 = Buffer (in madt apic form) to return
    136     */
    137    method = aml_method(CPU_MAT_METHOD, 2, AML_NOTSERIALIZED);
    138    aml_append(method,
    139        aml_store(aml_derefof(aml_index(cpus_map, apic_id)), cpu_on));
    140    aml_append(method,
    141        aml_store(aml_buffer(sizeof(madt_tmpl), madt_tmpl), madt));
    142    /* Update the processor id, lapic id, and enable/disable status */
    143    aml_append(method, aml_store(cpu_id, aml_index(madt, aml_int(2))));
    144    aml_append(method, aml_store(apic_id, aml_index(madt, aml_int(3))));
    145    aml_append(method, aml_store(cpu_on, aml_index(madt, aml_int(4))));
    146    aml_append(method, aml_return(madt));
    147    aml_append(sb_scope, method);
    148
    149    /*
    150     * _STA method - return ON status of cpu
    151     * apic_id = Arg0 = Local APIC ID
    152     * cpu_on = Local0 = CPON flag for this cpu
    153     */
    154    method = aml_method(CPU_STATUS_METHOD, 1, AML_NOTSERIALIZED);
    155    aml_append(method,
    156        aml_store(aml_derefof(aml_index(cpus_map, apic_id)), cpu_on));
    157    if_ctx = aml_if(cpu_on);
    158    {
    159        aml_append(if_ctx, aml_return(aml_int(0xF)));
    160    }
    161    aml_append(method, if_ctx);
    162    else_ctx = aml_else();
    163    {
    164        aml_append(else_ctx, aml_return(zero));
    165    }
    166    aml_append(method, else_ctx);
    167    aml_append(sb_scope, method);
    168
    169    method = aml_method(CPU_EJECT_METHOD, 2, AML_NOTSERIALIZED);
    170    aml_append(method, aml_sleep(200));
    171    aml_append(sb_scope, method);
    172
    173    method = aml_method(CPU_SCAN_METHOD, 0, AML_NOTSERIALIZED);
    174    {
    175        Aml *while_ctx, *if_ctx2, *else_ctx2;
    176        Aml *bus_check_evt = aml_int(1);
    177        Aml *remove_evt = aml_int(3);
    178        Aml *status_map = aml_local(5); /* Local5 = active cpu bitmap */
    179        Aml *byte = aml_local(2); /* Local2 = last read byte from bitmap */
    180        Aml *idx = aml_local(0); /* Processor ID / APIC ID iterator */
    181        Aml *is_cpu_on = aml_local(1); /* Local1 = CPON flag for cpu */
    182        Aml *status = aml_local(3); /* Local3 = active state for cpu */
    183
    184        aml_append(method, aml_store(aml_name(CPU_STATUS_MAP), status_map));
    185        aml_append(method, aml_store(zero, byte));
    186        aml_append(method, aml_store(zero, idx));
    187
    188        /* While (idx < SizeOf(CPON)) */
    189        while_ctx = aml_while(aml_lless(idx, aml_sizeof(cpus_map)));
    190        aml_append(while_ctx,
    191            aml_store(aml_derefof(aml_index(cpus_map, idx)), is_cpu_on));
    192
    193        if_ctx = aml_if(aml_and(idx, aml_int(0x07), NULL));
    194        {
    195            /* Shift down previously read bitmap byte */
    196            aml_append(if_ctx, aml_shiftright(byte, one, byte));
    197        }
    198        aml_append(while_ctx, if_ctx);
    199
    200        else_ctx = aml_else();
    201        {
    202            /* Read next byte from cpu bitmap */
    203            aml_append(else_ctx, aml_store(aml_derefof(aml_index(status_map,
    204                       aml_shiftright(idx, aml_int(3), NULL))), byte));
    205        }
    206        aml_append(while_ctx, else_ctx);
    207
    208        aml_append(while_ctx, aml_store(aml_and(byte, one, NULL), status));
    209        if_ctx = aml_if(aml_lnot(aml_equal(is_cpu_on, status)));
    210        {
    211            /* State change - update CPON with new state */
    212            aml_append(if_ctx, aml_store(status, aml_index(cpus_map, idx)));
    213            if_ctx2 = aml_if(aml_equal(status, one));
    214            {
    215                aml_append(if_ctx2,
    216                    aml_call2(AML_NOTIFY_METHOD, idx, bus_check_evt));
    217            }
    218            aml_append(if_ctx, if_ctx2);
    219            else_ctx2 = aml_else();
    220            {
    221                aml_append(else_ctx2,
    222                    aml_call2(AML_NOTIFY_METHOD, idx, remove_evt));
    223            }
    224        }
    225        aml_append(if_ctx, else_ctx2);
    226        aml_append(while_ctx, if_ctx);
    227
    228        aml_append(while_ctx, aml_increment(idx)); /* go to next cpu */
    229        aml_append(method, while_ctx);
    230    }
    231    aml_append(sb_scope, method);
    232
    233    /* The current AML generator can cover the APIC ID range [0..255],
    234     * inclusive, for VCPU hotplug. */
    235    QEMU_BUILD_BUG_ON(ACPI_CPU_HOTPLUG_ID_LIMIT > 256);
    236    if (x86ms->apic_id_limit > ACPI_CPU_HOTPLUG_ID_LIMIT) {
    237        error_report("max_cpus is too large. APIC ID of last CPU is %u",
    238                     x86ms->apic_id_limit - 1);
    239        exit(1);
    240    }
    241
    242    /* create PCI0.PRES device and its _CRS to reserve CPU hotplug MMIO */
    243    dev = aml_device("PCI0." stringify(CPU_HOTPLUG_RESOURCE_DEVICE));
    244    aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A06")));
    245    aml_append(dev,
    246        aml_name_decl("_UID", aml_string("CPU Hotplug resources"))
    247    );
    248    /* device present, functioning, decoding, not shown in UI */
    249    aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
    250    crs = aml_resource_template();
    251    aml_append(crs,
    252        aml_io(AML_DECODE16, io_base, io_base, 1, ACPI_GPE_PROC_LEN)
    253    );
    254    aml_append(dev, aml_name_decl("_CRS", crs));
    255    aml_append(sb_scope, dev);
    256    /* declare CPU hotplug MMIO region and PRS field to access it */
    257    aml_append(sb_scope, aml_operation_region(
    258        "PRST", AML_SYSTEM_IO, aml_int(io_base), ACPI_GPE_PROC_LEN));
    259    field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
    260    aml_append(field, aml_named_field("PRS", 256));
    261    aml_append(sb_scope, field);
    262
    263    /* build Processor object for each processor */
    264    for (i = 0; i < apic_ids->len; i++) {
    265        int apic_id = apic_ids->cpus[i].arch_id;
    266
    267        assert(apic_id < ACPI_CPU_HOTPLUG_ID_LIMIT);
    268
    269        dev = aml_processor(i, 0, 0, "CP%.02X", apic_id);
    270
    271        method = aml_method("_MAT", 0, AML_NOTSERIALIZED);
    272        aml_append(method,
    273            aml_return(aml_call2(CPU_MAT_METHOD, aml_int(apic_id), aml_int(i))
    274        ));
    275        aml_append(dev, method);
    276
    277        method = aml_method("_STA", 0, AML_NOTSERIALIZED);
    278        aml_append(method,
    279            aml_return(aml_call1(CPU_STATUS_METHOD, aml_int(apic_id))));
    280        aml_append(dev, method);
    281
    282        method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
    283        aml_append(method,
    284            aml_return(aml_call2(CPU_EJECT_METHOD, aml_int(apic_id),
    285                aml_arg(0)))
    286        );
    287        aml_append(dev, method);
    288
    289        aml_append(sb_scope, dev);
    290    }
    291
    292    /* build this code:
    293     *   Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...}
    294     */
    295    /* Arg0 = APIC ID */
    296    method = aml_method(AML_NOTIFY_METHOD, 2, AML_NOTSERIALIZED);
    297    for (i = 0; i < apic_ids->len; i++) {
    298        int apic_id = apic_ids->cpus[i].arch_id;
    299
    300        if_ctx = aml_if(aml_equal(aml_arg(0), aml_int(apic_id)));
    301        aml_append(if_ctx,
    302            aml_notify(aml_name("CP%.02X", apic_id), aml_arg(1))
    303        );
    304        aml_append(method, if_ctx);
    305    }
    306    aml_append(sb_scope, method);
    307
    308    /* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })"
    309     *
    310     * Note: The ability to create variable-sized packages was first
    311     * introduced in ACPI 2.0. ACPI 1.0 only allowed fixed-size packages
    312     * ith up to 255 elements. Windows guests up to win2k8 fail when
    313     * VarPackageOp is used.
    314     */
    315    pkg = x86ms->apic_id_limit <= 255 ? aml_package(x86ms->apic_id_limit) :
    316                                        aml_varpackage(x86ms->apic_id_limit);
    317
    318    for (i = 0, apic_idx = 0; i < apic_ids->len; i++) {
    319        int apic_id = apic_ids->cpus[i].arch_id;
    320
    321        for (; apic_idx < apic_id; apic_idx++) {
    322            aml_append(pkg, aml_int(0));
    323        }
    324        aml_append(pkg, aml_int(apic_ids->cpus[i].cpu ? 1 : 0));
    325        apic_idx = apic_id + 1;
    326    }
    327    aml_append(sb_scope, aml_name_decl(CPU_ON_BITMAP, pkg));
    328    aml_append(ctx, sb_scope);
    329
    330    method = aml_method("\\_GPE._E02", 0, AML_NOTSERIALIZED);
    331    aml_append(method, aml_call0("\\_SB." CPU_SCAN_METHOD));
    332    aml_append(ctx, method);
    333}