xref: /openbmc/qemu/hw/acpi/cxl.c (revision aadfe320)
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 static int cxl_foreach_pxb_hb(Object *obj, void *opaque)
64 {
65     Aml *cedt = opaque;
66 
67     if (object_dynamic_cast(obj, TYPE_PXB_CXL_DEVICE)) {
68         cedt_build_chbs(cedt->buf, PXB_CXL_DEV(obj));
69     }
70 
71     return 0;
72 }
73 
74 void cxl_build_cedt(MachineState *ms, GArray *table_offsets, GArray *table_data,
75                     BIOSLinker *linker, const char *oem_id,
76                     const char *oem_table_id)
77 {
78     Aml *cedt;
79     AcpiTable table = { .sig = "CEDT", .rev = 1, .oem_id = oem_id,
80                         .oem_table_id = oem_table_id };
81 
82     acpi_add_table(table_offsets, table_data);
83     acpi_table_begin(&table, table_data);
84     cedt = init_aml_allocator();
85 
86     /* reserve space for CEDT header */
87 
88     object_child_foreach_recursive(object_get_root(), cxl_foreach_pxb_hb, cedt);
89 
90     /* copy AML table into ACPI tables blob and patch header there */
91     g_array_append_vals(table_data, cedt->buf->data, cedt->buf->len);
92     free_aml_allocator();
93 
94     acpi_table_end(linker, &table);
95 }
96 
97 static Aml *__build_cxl_osc_method(void)
98 {
99     Aml *method, *if_uuid, *else_uuid, *if_arg1_not_1, *if_cxl, *if_caps_masked;
100     Aml *a_ctrl = aml_local(0);
101     Aml *a_cdw1 = aml_name("CDW1");
102 
103     method = aml_method("_OSC", 4, AML_NOTSERIALIZED);
104     /* CDW1 is used for the return value so is present whether or not a match occurs */
105     aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1"));
106 
107     /*
108      * Generate shared section between:
109      * CXL 2.0 - 9.14.2.1.4 and
110      * PCI Firmware Specification 3.0
111      * 4.5.1. _OSC Interface for PCI Host Bridge Devices
112      * The _OSC interface for a PCI/PCI-X/PCI Express hierarchy is
113      * identified by the Universal Unique IDentifier (UUID)
114      * 33DB4D5B-1FF7-401C-9657-7441C03DD766
115      * The _OSC interface for a CXL Host bridge is
116      * identified by the UUID 68F2D50B-C469-4D8A-BD3D-941A103FD3FC
117      * A CXL Host bridge is compatible with a PCI host bridge so
118      * for the shared section match both.
119      */
120     if_uuid = aml_if(
121         aml_lor(aml_equal(aml_arg(0),
122                           aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766")),
123                 aml_equal(aml_arg(0),
124                           aml_touuid("68F2D50B-C469-4D8A-BD3D-941A103FD3FC"))));
125     aml_append(if_uuid, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2"));
126     aml_append(if_uuid, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3"));
127 
128     aml_append(if_uuid, aml_store(aml_name("CDW3"), a_ctrl));
129 
130     /*
131      *
132      * Allows OS control for all 5 features:
133      * PCIeHotplug SHPCHotplug PME AER PCIeCapability
134      */
135     aml_append(if_uuid, aml_and(a_ctrl, aml_int(0x1F), a_ctrl));
136 
137     /*
138      * Check _OSC revision.
139      * PCI Firmware specification 3.3 and CXL 2.0 both use revision 1
140      * Unknown Revision is CDW1 - BIT (3)
141      */
142     if_arg1_not_1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1))));
143     aml_append(if_arg1_not_1, aml_or(a_cdw1, aml_int(0x08), a_cdw1));
144     aml_append(if_uuid, if_arg1_not_1);
145 
146     if_caps_masked = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl)));
147 
148     /* Capability bits were masked */
149     aml_append(if_caps_masked, aml_or(a_cdw1, aml_int(0x10), a_cdw1));
150     aml_append(if_uuid, if_caps_masked);
151 
152     aml_append(if_uuid, aml_store(aml_name("CDW2"), aml_name("SUPP")));
153     aml_append(if_uuid, aml_store(aml_name("CDW3"), aml_name("CTRL")));
154 
155     /* Update DWORD3 (the return value) */
156     aml_append(if_uuid, aml_store(a_ctrl, aml_name("CDW3")));
157 
158     /* CXL only section as per CXL 2.0 - 9.14.2.1.4 */
159     if_cxl = aml_if(aml_equal(
160         aml_arg(0), aml_touuid("68F2D50B-C469-4D8A-BD3D-941A103FD3FC")));
161     /* CXL support field */
162     aml_append(if_cxl, aml_create_dword_field(aml_arg(3), aml_int(12), "CDW4"));
163     /* CXL capabilities */
164     aml_append(if_cxl, aml_create_dword_field(aml_arg(3), aml_int(16), "CDW5"));
165     aml_append(if_cxl, aml_store(aml_name("CDW4"), aml_name("SUPC")));
166     aml_append(if_cxl, aml_store(aml_name("CDW5"), aml_name("CTRC")));
167 
168     /* CXL 2.0 Port/Device Register access */
169     aml_append(if_cxl,
170                aml_or(aml_name("CDW5"), aml_int(0x1), aml_name("CDW5")));
171     aml_append(if_uuid, if_cxl);
172 
173     aml_append(if_uuid, aml_return(aml_arg(3)));
174     aml_append(method, if_uuid);
175 
176     /*
177      * If no UUID matched, return Unrecognized UUID via Arg3 DWord 1
178      * ACPI 6.4 - 6.2.11
179      * Unrecognised UUID - BIT(2)
180      */
181     else_uuid = aml_else();
182 
183     aml_append(else_uuid,
184                aml_or(aml_name("CDW1"), aml_int(0x4), aml_name("CDW1")));
185     aml_append(else_uuid, aml_return(aml_arg(3)));
186     aml_append(method, else_uuid);
187 
188     return method;
189 }
190 
191 void build_cxl_osc_method(Aml *dev)
192 {
193     aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
194     aml_append(dev, aml_name_decl("CTRL", aml_int(0)));
195     aml_append(dev, aml_name_decl("SUPC", aml_int(0)));
196     aml_append(dev, aml_name_decl("CTRC", aml_int(0)));
197     aml_append(dev, __build_cxl_osc_method());
198 }
199