| 1 | #include "qemu/osdep.h" |
| 2 | #include "hw/acpi/aml-build.h" |
| 3 | #include "hw/acpi/pci.h" |
| 4 | #include "hw/pci-host/gpex.h" |
| 5 | #include "hw/arm/virt.h" |
| 6 | #include "hw/pci/pci_bus.h" |
| 7 | #include "hw/pci/pci_bridge.h" |
| 8 | #include "hw/pci/pcie_host.h" |
| 9 | #include "hw/acpi/cxl.h" |
| 10 | |
| 11 | static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq, |
| 12 | Aml *scope, uint8_t bus_num) |
| 13 | { |
| 14 | Aml *method, *crs; |
| 15 | int i, slot_no; |
| 16 | |
| 17 | /* Declare the PCI Routing Table. */ |
| 18 | Aml *rt_pkg = aml_varpackage(PCI_SLOT_MAX * PCI_NUM_PINS); |
| 19 | for (slot_no = 0; slot_no < PCI_SLOT_MAX; slot_no++) { |
| 20 | for (i = 0; i < PCI_NUM_PINS; i++) { |
| 21 | int gsi = (i + slot_no) % PCI_NUM_PINS; |
| 22 | Aml *pkg = aml_package(4); |
| 23 | aml_append(pkg, aml_int((slot_no << 16) | 0xFFFF)); |
| 24 | aml_append(pkg, aml_int(i)); |
| 25 | aml_append(pkg, aml_name("L%.02X%X", bus_num, gsi)); |
| 26 | aml_append(pkg, aml_int(0)); |
| 27 | aml_append(rt_pkg, pkg); |
| 28 | } |
| 29 | } |
| 30 | aml_append(dev, aml_name_decl("_PRT", rt_pkg)); |
| 31 | |
| 32 | /* Create GSI link device */ |
| 33 | for (i = 0; i < PCI_NUM_PINS; i++) { |
| 34 | uint32_t irqs = irq + i; |
| 35 | Aml *dev_gsi = aml_device("L%.02X%X", bus_num, i); |
| 36 | aml_append(dev_gsi, aml_name_decl("_HID", aml_string("PNP0C0F"))); |
| 37 | aml_append(dev_gsi, aml_name_decl("_UID", aml_int(i))); |
| 38 | crs = aml_resource_template(); |
| 39 | aml_append(crs, |
| 40 | aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, |
| 41 | AML_EXCLUSIVE, &irqs, 1)); |
| 42 | aml_append(dev_gsi, aml_name_decl("_PRS", crs)); |
| 43 | crs = aml_resource_template(); |
| 44 | aml_append(crs, |
| 45 | aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, |
| 46 | AML_EXCLUSIVE, &irqs, 1)); |
| 47 | aml_append(dev_gsi, aml_name_decl("_CRS", crs)); |
| 48 | method = aml_method("_SRS", 1, AML_NOTSERIALIZED); |
| 49 | aml_append(dev_gsi, method); |
| 50 | aml_append(scope, dev_gsi); |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | static Aml *build_pci_host_bridge_dsm_method(bool preserve_config) |
| 55 | { |
| 56 | Aml *method = aml_method("_DSM", 4, AML_NOTSERIALIZED); |
| 57 | Aml *UUID, *ifctx, *ifctx1, *buf; |
| 58 | uint8_t byte_list[1] = {0}; |
| 59 | |
| 60 | /* PCI Firmware Specification 3.0 |
| 61 | * 4.6.1. _DSM for PCI Express Slot Information |
| 62 | * The UUID in _DSM in this context is |
| 63 | * {E5C937D0-3553-4D7A-9117-EA4D19C3434D} |
| 64 | */ |
| 65 | UUID = aml_touuid("E5C937D0-3553-4D7A-9117-EA4D19C3434D"); |
| 66 | ifctx = aml_if(aml_equal(aml_arg(0), UUID)); |
| 67 | ifctx1 = aml_if(aml_equal(aml_arg(2), aml_int(0))); |
| 68 | if (preserve_config) { |
| 69 | /* support functions other than 0, specifically function 5 */ |
| 70 | byte_list[0] = 0x21; |
| 71 | } |
| 72 | buf = aml_buffer(1, byte_list); |
| 73 | aml_append(ifctx1, aml_return(buf)); |
| 74 | aml_append(ifctx, ifctx1); |
| 75 | if (preserve_config) { |
| 76 | Aml *ifctx2 = aml_if(aml_equal(aml_arg(2), aml_int(5))); |
| 77 | /* |
| 78 | * 0 - The operating system must not ignore the PCI configuration that |
| 79 | * firmware has done at boot time. |
| 80 | */ |
| 81 | aml_append(ifctx2, aml_return(aml_int(0))); |
| 82 | aml_append(ifctx, ifctx2); |
| 83 | } |
| 84 | |
| 85 | aml_append(method, ifctx); |
| 86 | |
| 87 | byte_list[0] = 0; |
| 88 | buf = aml_buffer(1, byte_list); |
| 89 | aml_append(method, aml_return(buf)); |
| 90 | return method; |
| 91 | } |
| 92 | |
| 93 | static void acpi_dsdt_add_host_bridge_methods(Aml *dev, |
| 94 | bool enable_native_pcie_hotplug, |
| 95 | bool preserve_config) |
| 96 | { |
| 97 | /* Declare an _OSC (OS Control Handoff) method */ |
| 98 | aml_append(dev, |
| 99 | build_pci_host_bridge_osc_method(enable_native_pcie_hotplug)); |
| 100 | aml_append(dev, build_pci_host_bridge_dsm_method(preserve_config)); |
| 101 | } |
| 102 | |
| 103 | void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) |
| 104 | { |
| 105 | int nr_pcie_buses = cfg->ecam.size / PCIE_MMCFG_SIZE_MIN; |
| 106 | Aml *method, *crs, *dev, *rbuf; |
| 107 | PCIBus *bus = cfg->bus; |
| 108 | CrsRangeSet crs_range_set; |
| 109 | CrsRangeEntry *entry; |
| 110 | int i; |
| 111 | |
| 112 | /* start to construct the tables for pxb */ |
| 113 | crs_range_set_init(&crs_range_set); |
| 114 | if (bus) { |
| 115 | QLIST_FOREACH(bus, &bus->child, sibling) { |
| 116 | uint8_t bus_num = pci_bus_num(bus); |
| 117 | uint8_t numa_node = pci_bus_numa_node(bus); |
| 118 | uint32_t uid; |
| 119 | bool is_cxl = pci_bus_is_cxl(bus); |
| 120 | |
| 121 | if (!pci_bus_is_root(bus)) { |
| 122 | continue; |
| 123 | } |
| 124 | |
| 125 | /* |
| 126 | * 0 - (nr_pcie_buses - 1) is the bus range for the main |
| 127 | * host-bridge and it equals the MIN of the |
| 128 | * busNr defined for pxb-pcie. |
| 129 | */ |
| 130 | if (bus_num < nr_pcie_buses) { |
| 131 | nr_pcie_buses = bus_num; |
| 132 | } |
| 133 | |
| 134 | uid = object_property_get_uint(OBJECT(bus), "acpi_uid", |
| 135 | &error_fatal); |
| 136 | dev = aml_device("PC%.02X", bus_num); |
| 137 | if (is_cxl) { |
| 138 | struct Aml *pkg = aml_package(2); |
| 139 | aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0016"))); |
| 140 | aml_append(pkg, aml_eisaid("PNP0A08")); |
| 141 | aml_append(pkg, aml_eisaid("PNP0A03")); |
| 142 | aml_append(dev, aml_name_decl("_CID", pkg)); |
| 143 | } else { |
| 144 | aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A08"))); |
| 145 | aml_append(dev, aml_name_decl("_CID", aml_string("PNP0A03"))); |
| 146 | } |
| 147 | aml_append(dev, aml_name_decl("_BBN", aml_int(bus_num))); |
| 148 | aml_append(dev, aml_name_decl("_UID", aml_int(uid))); |
| 149 | aml_append(dev, aml_name_decl("_STR", aml_unicode("pxb Device"))); |
| 150 | aml_append(dev, aml_name_decl("_CCA", aml_int(1))); |
| 151 | if (numa_node != NUMA_NODE_UNASSIGNED) { |
| 152 | aml_append(dev, aml_name_decl("_PXM", aml_int(numa_node))); |
| 153 | } |
| 154 | |
| 155 | acpi_dsdt_add_pci_route_table(dev, cfg->irq, scope, bus_num); |
| 156 | |
| 157 | /* |
| 158 | * Resources defined for PXBs are composed of the following parts: |
| 159 | * 1. The resources the pci-bridge/pcie-root-port need. |
| 160 | * 2. The resources the devices behind pxb need. |
| 161 | */ |
| 162 | crs = build_crs(PCI_HOST_BRIDGE(BUS(bus)->parent), &crs_range_set, |
| 163 | cfg->pio.base, 0, 0, 0); |
| 164 | aml_append(dev, aml_name_decl("_CRS", crs)); |
| 165 | |
| 166 | if (is_cxl) { |
| 167 | build_cxl_osc_method(dev); |
| 168 | } else { |
| 169 | /* pxb bridges do not have ACPI PCI Hot-plug enabled */ |
| 170 | acpi_dsdt_add_host_bridge_methods(dev, true, |
| 171 | cfg->preserve_config); |
| 172 | } |
| 173 | |
| 174 | aml_append(scope, dev); |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | /* tables for the main */ |
| 179 | dev = aml_device("%s", "PCI0"); |
| 180 | aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A08"))); |
| 181 | aml_append(dev, aml_name_decl("_CID", aml_string("PNP0A03"))); |
| 182 | aml_append(dev, aml_name_decl("_SEG", aml_int(0))); |
| 183 | aml_append(dev, aml_name_decl("_BBN", aml_int(0))); |
| 184 | aml_append(dev, aml_name_decl("_UID", aml_int(0))); |
| 185 | aml_append(dev, aml_name_decl("_STR", aml_unicode("PCIe 0 Device"))); |
| 186 | aml_append(dev, aml_name_decl("_CCA", aml_int(1))); |
| 187 | |
| 188 | acpi_dsdt_add_pci_route_table(dev, cfg->irq, scope, 0); |
| 189 | |
| 190 | method = aml_method("_CBA", 0, AML_NOTSERIALIZED); |
| 191 | aml_append(method, aml_return(aml_int(cfg->ecam.base))); |
| 192 | aml_append(dev, method); |
| 193 | |
| 194 | /* |
| 195 | * At this point crs_range_set has all the ranges used by pci |
| 196 | * busses *other* than PCI0. These ranges will be excluded from |
| 197 | * the PCI0._CRS. |
| 198 | */ |
| 199 | rbuf = aml_resource_template(); |
| 200 | aml_append(rbuf, |
| 201 | aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, |
| 202 | 0x0000, 0x0000, nr_pcie_buses - 1, 0x0000, |
| 203 | nr_pcie_buses)); |
| 204 | if (cfg->mmio32.size) { |
| 205 | crs_replace_with_free_ranges(crs_range_set.mem_ranges, |
| 206 | cfg->mmio32.base, |
| 207 | cfg->mmio32.base + cfg->mmio32.size - 1); |
| 208 | for (i = 0; i < crs_range_set.mem_ranges->len; i++) { |
| 209 | entry = g_ptr_array_index(crs_range_set.mem_ranges, i); |
| 210 | aml_append(rbuf, |
| 211 | aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, |
| 212 | AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000, |
| 213 | entry->base, entry->limit, |
| 214 | 0x0000, entry->limit - entry->base + 1)); |
| 215 | } |
| 216 | } |
| 217 | if (cfg->pio.size) { |
| 218 | crs_replace_with_free_ranges(crs_range_set.io_ranges, |
| 219 | 0x0000, |
| 220 | cfg->pio.size - 1); |
| 221 | for (i = 0; i < crs_range_set.io_ranges->len; i++) { |
| 222 | entry = g_ptr_array_index(crs_range_set.io_ranges, i); |
| 223 | aml_append(rbuf, |
| 224 | aml_dword_io(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, |
| 225 | AML_ENTIRE_RANGE, 0x0000, entry->base, |
| 226 | entry->limit, cfg->pio.base, |
| 227 | entry->limit - entry->base + 1)); |
| 228 | } |
| 229 | } |
| 230 | if (cfg->mmio64.size) { |
| 231 | crs_replace_with_free_ranges(crs_range_set.mem_64bit_ranges, |
| 232 | cfg->mmio64.base, |
| 233 | cfg->mmio64.base + cfg->mmio64.size - 1); |
| 234 | for (i = 0; i < crs_range_set.mem_64bit_ranges->len; i++) { |
| 235 | entry = g_ptr_array_index(crs_range_set.mem_64bit_ranges, i); |
| 236 | aml_append(rbuf, |
| 237 | aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, |
| 238 | AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000, |
| 239 | entry->base, |
| 240 | entry->limit, 0x0000, |
| 241 | entry->limit - entry->base + 1)); |
| 242 | } |
| 243 | } |
| 244 | aml_append(dev, aml_name_decl("_CRS", rbuf)); |
| 245 | |
| 246 | acpi_dsdt_add_host_bridge_methods(dev, cfg->pci_native_hotplug, |
| 247 | cfg->preserve_config); |
| 248 | |
| 249 | Aml *dev_res0 = aml_device("%s", "RES0"); |
| 250 | aml_append(dev_res0, aml_name_decl("_HID", aml_string("PNP0C02"))); |
| 251 | crs = aml_resource_template(); |
| 252 | aml_append(crs, |
| 253 | aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, |
| 254 | AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000, |
| 255 | cfg->ecam.base, |
| 256 | cfg->ecam.base + cfg->ecam.size - 1, |
| 257 | 0x0000, |
| 258 | cfg->ecam.size)); |
| 259 | aml_append(dev_res0, aml_name_decl("_CRS", crs)); |
| 260 | aml_append(dev, dev_res0); |
| 261 | aml_append(scope, dev); |
| 262 | |
| 263 | crs_range_set_free(&crs_range_set); |
| 264 | } |
| 265 | |
| 266 | void acpi_dsdt_add_gpex_host(Aml *scope, uint32_t irq) |
| 267 | { |
| 268 | bool ambig; |
| 269 | Object *obj = object_resolve_path_type("", TYPE_GPEX_HOST, &ambig); |
| 270 | |
| 271 | if (!obj || ambig) { |
| 272 | return; |
| 273 | } |
| 274 | |
| 275 | GPEX_HOST(obj)->gpex_cfg.irq = irq; |
| 276 | acpi_dsdt_add_gpex(scope, &GPEX_HOST(obj)->gpex_cfg); |
| 277 | } |