1 /* 2 * Support for generating PCI related ACPI tables and passing them to Guests 3 * 4 * Copyright (C) 2006 Fabrice Bellard 5 * Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net> 6 * Copyright (C) 2013-2019 Red Hat Inc 7 * Copyright (C) 2019 Intel Corporation 8 * 9 * Author: Wei Yang <richardw.yang@linux.intel.com> 10 * Author: Michael S. Tsirkin <mst@redhat.com> 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License as published by 14 * the Free Software Foundation; either version 2 of the License, or 15 * (at your option) any later version. 16 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 22 * You should have received a copy of the GNU General Public License along 23 * with this program; if not, see <http://www.gnu.org/licenses/>. 24 */ 25 26 #include "qemu/osdep.h" 27 #include "qemu/error-report.h" 28 #include "qom/object_interfaces.h" 29 #include "qapi/error.h" 30 #include "hw/boards.h" 31 #include "hw/acpi/aml-build.h" 32 #include "hw/acpi/pci.h" 33 #include "hw/pci/pci_bridge.h" 34 #include "hw/pci/pci_device.h" 35 #include "hw/pci/pcie_host.h" 36 37 /* 38 * PCI Firmware Specification, Revision 3.0 39 * 4.1.2 MCFG Table Description. 40 */ 41 void build_mcfg(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info, 42 const char *oem_id, const char *oem_table_id) 43 { 44 AcpiTable table = { .sig = "MCFG", .rev = 1, 45 .oem_id = oem_id, .oem_table_id = oem_table_id }; 46 47 acpi_table_begin(&table, table_data); 48 49 /* Reserved */ 50 build_append_int_noprefix(table_data, 0, 8); 51 /* 52 * Memory Mapped Enhanced Configuration Space Base Address Allocation 53 * Structure 54 */ 55 /* Base address, processor-relative */ 56 build_append_int_noprefix(table_data, info->base, 8); 57 /* PCI segment group number */ 58 build_append_int_noprefix(table_data, 0, 2); 59 /* Starting PCI Bus number */ 60 build_append_int_noprefix(table_data, 0, 1); 61 /* Final PCI Bus number */ 62 build_append_int_noprefix(table_data, PCIE_MMCFG_BUS(info->size - 1), 1); 63 /* Reserved */ 64 build_append_int_noprefix(table_data, 0, 4); 65 66 acpi_table_end(linker, &table); 67 } 68 69 typedef struct AcpiGenericInitiator { 70 /* private */ 71 Object parent; 72 73 /* public */ 74 char *pci_dev; 75 uint32_t node; 76 } AcpiGenericInitiator; 77 78 typedef struct AcpiGenericInitiatorClass { 79 ObjectClass parent_class; 80 } AcpiGenericInitiatorClass; 81 82 #define TYPE_ACPI_GENERIC_INITIATOR "acpi-generic-initiator" 83 84 OBJECT_DEFINE_TYPE_WITH_INTERFACES(AcpiGenericInitiator, acpi_generic_initiator, 85 ACPI_GENERIC_INITIATOR, OBJECT, 86 { TYPE_USER_CREATABLE }, 87 { NULL }) 88 89 OBJECT_DECLARE_SIMPLE_TYPE(AcpiGenericInitiator, ACPI_GENERIC_INITIATOR) 90 91 static void acpi_generic_initiator_init(Object *obj) 92 { 93 AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj); 94 95 gi->node = MAX_NODES; 96 gi->pci_dev = NULL; 97 } 98 99 static void acpi_generic_initiator_finalize(Object *obj) 100 { 101 AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj); 102 103 g_free(gi->pci_dev); 104 } 105 106 static void acpi_generic_initiator_set_pci_device(Object *obj, const char *val, 107 Error **errp) 108 { 109 AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj); 110 111 gi->pci_dev = g_strdup(val); 112 } 113 114 static void acpi_generic_initiator_set_node(Object *obj, Visitor *v, 115 const char *name, void *opaque, 116 Error **errp) 117 { 118 AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj); 119 MachineState *ms = MACHINE(qdev_get_machine()); 120 uint32_t value; 121 122 if (!visit_type_uint32(v, name, &value, errp)) { 123 return; 124 } 125 126 if (value >= MAX_NODES) { 127 error_printf("%s: Invalid NUMA node specified\n", 128 TYPE_ACPI_GENERIC_INITIATOR); 129 exit(1); 130 } 131 132 gi->node = value; 133 ms->numa_state->nodes[gi->node].has_gi = true; 134 } 135 136 static void acpi_generic_initiator_class_init(ObjectClass *oc, void *data) 137 { 138 object_class_property_add_str(oc, "pci-dev", NULL, 139 acpi_generic_initiator_set_pci_device); 140 object_class_property_set_description(oc, "pci-dev", 141 "PCI device to associate with the node"); 142 object_class_property_add(oc, "node", "int", NULL, 143 acpi_generic_initiator_set_node, NULL, NULL); 144 object_class_property_set_description(oc, "node", 145 "NUMA node associated with the PCI device"); 146 } 147 148 static int build_acpi_generic_initiator(Object *obj, void *opaque) 149 { 150 MachineState *ms = MACHINE(qdev_get_machine()); 151 AcpiGenericInitiator *gi; 152 GArray *table_data = opaque; 153 int32_t devfn; 154 uint8_t bus; 155 Object *o; 156 157 if (!object_dynamic_cast(obj, TYPE_ACPI_GENERIC_INITIATOR)) { 158 return 0; 159 } 160 161 gi = ACPI_GENERIC_INITIATOR(obj); 162 if (gi->node >= ms->numa_state->num_nodes) { 163 error_printf("%s: Specified node %d is invalid.\n", 164 TYPE_ACPI_GENERIC_INITIATOR, gi->node); 165 exit(1); 166 } 167 168 o = object_resolve_path_type(gi->pci_dev, TYPE_PCI_DEVICE, NULL); 169 if (!o) { 170 error_printf("%s: Specified device must be a PCI device.\n", 171 TYPE_ACPI_GENERIC_INITIATOR); 172 exit(1); 173 } 174 175 bus = object_property_get_uint(o, "busnr", &error_fatal); 176 devfn = object_property_get_uint(o, "addr", &error_fatal); 177 /* devfn is constrained in PCI to be 8 bit but storage is an int32_t */ 178 assert(devfn >= 0 && devfn < PCI_DEVFN_MAX); 179 180 build_srat_pci_generic_initiator(table_data, gi->node, 0, bus, devfn); 181 182 return 0; 183 } 184 185 typedef struct AcpiGenericPort { 186 /* private */ 187 Object parent; 188 189 /* public */ 190 char *pci_bus; 191 uint32_t node; 192 } AcpiGenericPort; 193 194 typedef struct AcpiGenericPortClass { 195 ObjectClass parent_class; 196 } AcpiGenericPortClass; 197 198 #define TYPE_ACPI_GENERIC_PORT "acpi-generic-port" 199 200 OBJECT_DEFINE_TYPE_WITH_INTERFACES(AcpiGenericPort, acpi_generic_port, 201 ACPI_GENERIC_PORT, OBJECT, 202 { TYPE_USER_CREATABLE }, 203 { NULL }) 204 205 OBJECT_DECLARE_SIMPLE_TYPE(AcpiGenericPort, ACPI_GENERIC_PORT) 206 207 static void acpi_generic_port_init(Object *obj) 208 { 209 AcpiGenericPort *gp = ACPI_GENERIC_PORT(obj); 210 211 gp->node = MAX_NODES; 212 gp->pci_bus = NULL; 213 } 214 215 static void acpi_generic_port_finalize(Object *obj) 216 { 217 AcpiGenericPort *gp = ACPI_GENERIC_PORT(obj); 218 219 g_free(gp->pci_bus); 220 } 221 222 static void acpi_generic_port_set_pci_bus(Object *obj, const char *val, 223 Error **errp) 224 { 225 AcpiGenericPort *gp = ACPI_GENERIC_PORT(obj); 226 227 gp->pci_bus = g_strdup(val); 228 } 229 230 static void acpi_generic_port_set_node(Object *obj, Visitor *v, 231 const char *name, void *opaque, 232 Error **errp) 233 { 234 AcpiGenericPort *gp = ACPI_GENERIC_PORT(obj); 235 uint32_t value; 236 237 if (!visit_type_uint32(v, name, &value, errp)) { 238 return; 239 } 240 241 if (value >= MAX_NODES) { 242 error_printf("%s: Invalid NUMA node specified\n", 243 TYPE_ACPI_GENERIC_INITIATOR); 244 exit(1); 245 } 246 247 gp->node = value; 248 } 249 250 static void acpi_generic_port_class_init(ObjectClass *oc, void *data) 251 { 252 object_class_property_add_str(oc, "pci-bus", NULL, 253 acpi_generic_port_set_pci_bus); 254 object_class_property_set_description(oc, "pci-bus", 255 "PCI Bus of the host bridge associated with this GP affinity structure"); 256 object_class_property_add(oc, "node", "int", NULL, 257 acpi_generic_port_set_node, NULL, NULL); 258 object_class_property_set_description(oc, "node", 259 "The NUMA node like ID to index HMAT/SLIT NUMA properties involving GP"); 260 } 261 262 static int build_acpi_generic_port(Object *obj, void *opaque) 263 { 264 MachineState *ms = MACHINE(qdev_get_machine()); 265 const char *hid = "ACPI0016"; 266 GArray *table_data = opaque; 267 AcpiGenericPort *gp; 268 uint32_t uid; 269 Object *o; 270 271 if (!object_dynamic_cast(obj, TYPE_ACPI_GENERIC_PORT)) { 272 return 0; 273 } 274 275 gp = ACPI_GENERIC_PORT(obj); 276 277 if (gp->node >= ms->numa_state->num_nodes) { 278 error_printf("%s: node %d is invalid.\n", 279 TYPE_ACPI_GENERIC_PORT, gp->node); 280 exit(1); 281 } 282 283 o = object_resolve_path_type(gp->pci_bus, TYPE_PXB_CXL_BUS, NULL); 284 if (!o) { 285 error_printf("%s: device must be a CXL host bridge.\n", 286 TYPE_ACPI_GENERIC_PORT); 287 exit(1); 288 } 289 290 uid = object_property_get_uint(o, "acpi_uid", &error_fatal); 291 build_srat_acpi_generic_port(table_data, gp->node, hid, uid); 292 293 return 0; 294 } 295 296 void build_srat_generic_affinity_structures(GArray *table_data) 297 { 298 object_child_foreach_recursive(object_get_root(), 299 build_acpi_generic_initiator, 300 table_data); 301 object_child_foreach_recursive(object_get_root(), build_acpi_generic_port, 302 table_data); 303 } 304