qemu/hw/acpi/cxl.c
<<
>>
Prefs
   1/*
   2 * CXL ACPI Implementation
   3 *
   4 * Copyright(C) 2020 Intel Corporation.
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License as published by the Free Software Foundation; either
   9 * version 2 of the License, or (at your option) any later version.
  10 *
  11 * This library is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see <http://www.gnu.org/licenses/>
  18 */
  19
  20#include "qemu/osdep.h"
  21#include "hw/sysbus.h"
  22#include "hw/pci/pci_bridge.h"
  23#include "hw/pci/pci_host.h"
  24#include "hw/cxl/cxl.h"
  25#include "hw/mem/memory-device.h"
  26#include "hw/acpi/acpi.h"
  27#include "hw/acpi/aml-build.h"
  28#include "hw/acpi/bios-linker-loader.h"
  29#include "hw/acpi/cxl.h"
  30#include "qapi/error.h"
  31#include "qemu/uuid.h"
  32
  33static void cedt_build_chbs(GArray *table_data, PXBDev *cxl)
  34{
  35    SysBusDevice *sbd = SYS_BUS_DEVICE(cxl->cxl.cxl_host_bridge);
  36    struct MemoryRegion *mr = sbd->mmio[0].memory;
  37
  38    /* Type */
  39    build_append_int_noprefix(table_data, 0, 1);
  40
  41    /* Reserved */
  42    build_append_int_noprefix(table_data, 0, 1);
  43
  44    /* Record Length */
  45    build_append_int_noprefix(table_data, 32, 2);
  46
  47    /* UID - currently equal to bus number */
  48    build_append_int_noprefix(table_data, cxl->bus_nr, 4);
  49
  50    /* Version */
  51    build_append_int_noprefix(table_data, 1, 4);
  52
  53    /* Reserved */
  54    build_append_int_noprefix(table_data, 0, 4);
  55
  56    /* Base - subregion within a container that is in PA space */
  57    build_append_int_noprefix(table_data, mr->container->addr + mr->addr, 8);
  58
  59    /* Length */
  60    build_append_int_noprefix(table_data, memory_region_size(mr), 8);
  61}
  62
  63/*
  64 * CFMWS entries in CXL 2.0 ECN: CEDT CFMWS & QTG _DSM.
  65 * Interleave ways encoding in CXL 2.0 ECN: 3, 6, 12 and 16-way memory
  66 * interleaving.
  67 */
  68static void cedt_build_cfmws(GArray *table_data, CXLState *cxls)
  69{
  70    GList *it;
  71
  72    for (it = cxls->fixed_windows; it; it = it->next) {
  73        CXLFixedWindow *fw = it->data;
  74        int i;
  75
  76        /* Type */
  77        build_append_int_noprefix(table_data, 1, 1);
  78
  79        /* Reserved */
  80        build_append_int_noprefix(table_data, 0, 1);
  81
  82        /* Record Length */
  83        build_append_int_noprefix(table_data, 36 + 4 * fw->num_targets, 2);
  84
  85        /* Reserved */
  86        build_append_int_noprefix(table_data, 0, 4);
  87
  88        /* Base HPA */
  89        build_append_int_noprefix(table_data, fw->mr.addr, 8);
  90
  91        /* Window Size */
  92        build_append_int_noprefix(table_data, fw->size, 8);
  93
  94        /* Host Bridge Interleave Ways */
  95        build_append_int_noprefix(table_data, fw->enc_int_ways, 1);
  96
  97        /* Host Bridge Interleave Arithmetic */
  98        build_append_int_noprefix(table_data, 0, 1);
  99
 100        /* Reserved */
 101        build_append_int_noprefix(table_data, 0, 2);
 102
 103        /* Host Bridge Interleave Granularity */
 104        build_append_int_noprefix(table_data, fw->enc_int_gran, 4);
 105
 106        /* Window Restrictions */
 107        build_append_int_noprefix(table_data, 0x0f, 2); /* No restrictions */
 108
 109        /* QTG ID */
 110        build_append_int_noprefix(table_data, 0, 2);
 111
 112        /* Host Bridge List (list of UIDs - currently bus_nr) */
 113        for (i = 0; i < fw->num_targets; i++) {
 114            g_assert(fw->target_hbs[i]);
 115            build_append_int_noprefix(table_data, fw->target_hbs[i]->bus_nr, 4);
 116        }
 117    }
 118}
 119
 120static int cxl_foreach_pxb_hb(Object *obj, void *opaque)
 121{
 122    Aml *cedt = opaque;
 123
 124    if (object_dynamic_cast(obj, TYPE_PXB_CXL_DEVICE)) {
 125        cedt_build_chbs(cedt->buf, PXB_CXL_DEV(obj));
 126    }
 127
 128    return 0;
 129}
 130
 131void cxl_build_cedt(GArray *table_offsets, GArray *table_data,
 132                    BIOSLinker *linker, const char *oem_id,
 133                    const char *oem_table_id, CXLState *cxl_state)
 134{
 135    Aml *cedt;
 136    AcpiTable table = { .sig = "CEDT", .rev = 1, .oem_id = oem_id,
 137                        .oem_table_id = oem_table_id };
 138
 139    acpi_add_table(table_offsets, table_data);
 140    acpi_table_begin(&table, table_data);
 141    cedt = init_aml_allocator();
 142
 143    /* reserve space for CEDT header */
 144
 145    object_child_foreach_recursive(object_get_root(), cxl_foreach_pxb_hb, cedt);
 146    cedt_build_cfmws(cedt->buf, cxl_state);
 147
 148    /* copy AML table into ACPI tables blob and patch header there */
 149    g_array_append_vals(table_data, cedt->buf->data, cedt->buf->len);
 150    free_aml_allocator();
 151
 152    acpi_table_end(linker, &table);
 153}
 154
 155static Aml *__build_cxl_osc_method(void)
 156{
 157    Aml *method, *if_uuid, *else_uuid, *if_arg1_not_1, *if_cxl, *if_caps_masked;
 158    Aml *a_ctrl = aml_local(0);
 159    Aml *a_cdw1 = aml_name("CDW1");
 160
 161    method = aml_method("_OSC", 4, AML_NOTSERIALIZED);
 162    /* CDW1 is used for the return value so is present whether or not a match occurs */
 163    aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1"));
 164
 165    /*
 166     * Generate shared section between:
 167     * CXL 2.0 - 9.14.2.1.4 and
 168     * PCI Firmware Specification 3.0
 169     * 4.5.1. _OSC Interface for PCI Host Bridge Devices
 170     * The _OSC interface for a PCI/PCI-X/PCI Express hierarchy is
 171     * identified by the Universal Unique IDentifier (UUID)
 172     * 33DB4D5B-1FF7-401C-9657-7441C03DD766
 173     * The _OSC interface for a CXL Host bridge is
 174     * identified by the UUID 68F2D50B-C469-4D8A-BD3D-941A103FD3FC
 175     * A CXL Host bridge is compatible with a PCI host bridge so
 176     * for the shared section match both.
 177     */
 178    if_uuid = aml_if(
 179        aml_lor(aml_equal(aml_arg(0),
 180                          aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766")),
 181                aml_equal(aml_arg(0),
 182                          aml_touuid("68F2D50B-C469-4D8A-BD3D-941A103FD3FC"))));
 183    aml_append(if_uuid, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2"));
 184    aml_append(if_uuid, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3"));
 185
 186    aml_append(if_uuid, aml_store(aml_name("CDW3"), a_ctrl));
 187
 188    /*
 189     *
 190     * Allows OS control for all 5 features:
 191     * PCIeHotplug SHPCHotplug PME AER PCIeCapability
 192     */
 193    aml_append(if_uuid, aml_and(a_ctrl, aml_int(0x1F), a_ctrl));
 194
 195    /*
 196     * Check _OSC revision.
 197     * PCI Firmware specification 3.3 and CXL 2.0 both use revision 1
 198     * Unknown Revision is CDW1 - BIT (3)
 199     */
 200    if_arg1_not_1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1))));
 201    aml_append(if_arg1_not_1, aml_or(a_cdw1, aml_int(0x08), a_cdw1));
 202    aml_append(if_uuid, if_arg1_not_1);
 203
 204    if_caps_masked = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl)));
 205
 206    /* Capability bits were masked */
 207    aml_append(if_caps_masked, aml_or(a_cdw1, aml_int(0x10), a_cdw1));
 208    aml_append(if_uuid, if_caps_masked);
 209
 210    aml_append(if_uuid, aml_store(aml_name("CDW2"), aml_name("SUPP")));
 211    aml_append(if_uuid, aml_store(aml_name("CDW3"), aml_name("CTRL")));
 212
 213    /* Update DWORD3 (the return value) */
 214    aml_append(if_uuid, aml_store(a_ctrl, aml_name("CDW3")));
 215
 216    /* CXL only section as per CXL 2.0 - 9.14.2.1.4 */
 217    if_cxl = aml_if(aml_equal(
 218        aml_arg(0), aml_touuid("68F2D50B-C469-4D8A-BD3D-941A103FD3FC")));
 219    /* CXL support field */
 220    aml_append(if_cxl, aml_create_dword_field(aml_arg(3), aml_int(12), "CDW4"));
 221    /* CXL capabilities */
 222    aml_append(if_cxl, aml_create_dword_field(aml_arg(3), aml_int(16), "CDW5"));
 223    aml_append(if_cxl, aml_store(aml_name("CDW4"), aml_name("SUPC")));
 224    aml_append(if_cxl, aml_store(aml_name("CDW5"), aml_name("CTRC")));
 225
 226    /* CXL 2.0 Port/Device Register access */
 227    aml_append(if_cxl,
 228               aml_or(aml_name("CDW5"), aml_int(0x1), aml_name("CDW5")));
 229    aml_append(if_uuid, if_cxl);
 230
 231    aml_append(if_uuid, aml_return(aml_arg(3)));
 232    aml_append(method, if_uuid);
 233
 234    /*
 235     * If no UUID matched, return Unrecognized UUID via Arg3 DWord 1
 236     * ACPI 6.4 - 6.2.11
 237     * Unrecognised UUID - BIT(2)
 238     */
 239    else_uuid = aml_else();
 240
 241    aml_append(else_uuid,
 242               aml_or(aml_name("CDW1"), aml_int(0x4), aml_name("CDW1")));
 243    aml_append(else_uuid, aml_return(aml_arg(3)));
 244    aml_append(method, else_uuid);
 245
 246    return method;
 247}
 248
 249void build_cxl_osc_method(Aml *dev)
 250{
 251    aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
 252    aml_append(dev, aml_name_decl("CTRL", aml_int(0)));
 253    aml_append(dev, aml_name_decl("SUPC", aml_int(0)));
 254    aml_append(dev, aml_name_decl("CTRC", aml_int(0)));
 255    aml_append(dev, __build_cxl_osc_method());
 256}
 257