qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

cxl.c (8654B)


      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 
     33 static 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  */
     68 static 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 
    120 static 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 
    131 void 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 
    155 static 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 
    249 void 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 }