blob: 6fa42e9c4e6fa283e8ad2a8a426629fa8a81d0de [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Olivier Galibertb7867392007-02-13 13:26:20 +01002/*
3 * mmconfig-shared.c - Low-level direct PCI config space access via
4 * MMCONFIG - common code between i386 and x86-64.
5 *
6 * This code does:
Olivier Galibert9358c692007-02-13 13:26:20 +01007 * - known chipset handling
Olivier Galibertb7867392007-02-13 13:26:20 +01008 * - ACPI decoding and validation
9 *
10 * Per-architecture code takes care of the mappings and accesses
11 * themselves.
12 */
13
14#include <linux/pci.h>
15#include <linux/init.h>
Feng Tang5f0db7a2009-08-14 15:37:50 -040016#include <linux/sfi_acpi.h>
Olivier Galibertb7867392007-02-13 13:26:20 +010017#include <linux/bitmap.h>
Bjorn Helgaas9a08f7d2009-10-23 15:20:33 -060018#include <linux/dmi.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090019#include <linux/slab.h>
Jiang Liu376f70a2012-06-22 14:55:12 +080020#include <linux/mutex.h>
21#include <linux/rculist.h>
Ingo Molnar66441bd2017-01-27 10:27:10 +010022#include <asm/e820/api.h>
Jaswinder Singh Rajput82487712008-12-27 18:32:28 +053023#include <asm/pci_x86.h>
Feng Tang5f0db7a2009-08-14 15:37:50 -040024#include <asm/acpi.h>
Olivier Galibertb7867392007-02-13 13:26:20 +010025
Len Brownf4a2d582009-07-28 16:48:02 -040026#define PREFIX "PCI: "
Len Browna192a952009-07-28 16:45:54 -040027
Aaron Durbina5ba7972007-07-21 17:10:34 +020028/* Indicate if the mmcfg resources have been placed into the resource table. */
Jiang Liu95c5e922012-06-22 14:55:14 +080029static bool pci_mmcfg_running_state;
Jiang Liu9c951112012-06-22 14:55:15 +080030static bool pci_mmcfg_arch_init_failed;
Jiang Liu376f70a2012-06-22 14:55:12 +080031static DEFINE_MUTEX(pci_mmcfg_lock);
Joel Fernandes (Google)842a56c2019-07-16 18:12:27 -040032#define pci_mmcfg_lock_held() lock_is_held(&(pci_mmcfg_lock).dep_map)
Aaron Durbina5ba7972007-07-21 17:10:34 +020033
Bjorn Helgaasff097dd2009-11-13 17:34:49 -070034LIST_HEAD(pci_mmcfg_list);
35
Mathias Krause64474b52014-08-25 23:26:36 +020036static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
Bjorn Helgaasba2afba2009-11-13 17:34:54 -070037{
38 if (cfg->res.parent)
39 release_resource(&cfg->res);
40 list_del(&cfg->list);
41 kfree(cfg);
42}
43
Mathias Krause64474b52014-08-25 23:26:36 +020044static void __init free_all_mmcfg(void)
Bjorn Helgaas7da7d362009-11-13 17:33:53 -070045{
Bjorn Helgaasff097dd2009-11-13 17:34:49 -070046 struct pci_mmcfg_region *cfg, *tmp;
Bjorn Helgaas56ddf4d2009-11-13 17:34:29 -070047
Bjorn Helgaas7da7d362009-11-13 17:33:53 -070048 pci_mmcfg_arch_free();
Bjorn Helgaasba2afba2009-11-13 17:34:54 -070049 list_for_each_entry_safe(cfg, tmp, &pci_mmcfg_list, list)
50 pci_mmconfig_remove(cfg);
Bjorn Helgaasff097dd2009-11-13 17:34:49 -070051}
52
Greg Kroah-Hartmana18e3692012-12-21 14:02:53 -080053static void list_add_sorted(struct pci_mmcfg_region *new)
Bjorn Helgaasff097dd2009-11-13 17:34:49 -070054{
55 struct pci_mmcfg_region *cfg;
56
57 /* keep list sorted by segment and starting bus number */
Joel Fernandes (Google)842a56c2019-07-16 18:12:27 -040058 list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list, pci_mmcfg_lock_held()) {
Bjorn Helgaasff097dd2009-11-13 17:34:49 -070059 if (cfg->segment > new->segment ||
60 (cfg->segment == new->segment &&
61 cfg->start_bus >= new->start_bus)) {
Jiang Liu376f70a2012-06-22 14:55:12 +080062 list_add_tail_rcu(&new->list, &cfg->list);
Bjorn Helgaasff097dd2009-11-13 17:34:49 -070063 return;
64 }
65 }
Jiang Liu376f70a2012-06-22 14:55:12 +080066 list_add_tail_rcu(&new->list, &pci_mmcfg_list);
Bjorn Helgaas7da7d362009-11-13 17:33:53 -070067}
68
Greg Kroah-Hartmana18e3692012-12-21 14:02:53 -080069static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
70 int end, u64 addr)
Yinghai Lu068258b2009-03-19 20:55:35 -070071{
Bjorn Helgaasd215a9c2009-11-13 17:34:13 -070072 struct pci_mmcfg_region *new;
Bjorn Helgaas56ddf4d2009-11-13 17:34:29 -070073 struct resource *res;
Yinghai Lu068258b2009-03-19 20:55:35 -070074
Bjorn Helgaasf7ca6982009-11-13 17:34:03 -070075 if (addr == 0)
76 return NULL;
77
Bjorn Helgaasff097dd2009-11-13 17:34:49 -070078 new = kzalloc(sizeof(*new), GFP_KERNEL);
Yinghai Lu068258b2009-03-19 20:55:35 -070079 if (!new)
Bjorn Helgaas7da7d362009-11-13 17:33:53 -070080 return NULL;
Yinghai Lu068258b2009-03-19 20:55:35 -070081
Bjorn Helgaas95cf1cf2009-11-13 17:34:24 -070082 new->address = addr;
83 new->segment = segment;
84 new->start_bus = start;
85 new->end_bus = end;
Bjorn Helgaas7da7d362009-11-13 17:33:53 -070086
Bjorn Helgaas56ddf4d2009-11-13 17:34:29 -070087 res = &new->res;
88 res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
Bjorn Helgaas1ca98fa2010-10-04 12:49:24 -060089 res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
Bjorn Helgaas56ddf4d2009-11-13 17:34:29 -070090 res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
91 snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
92 "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
93 res->name = new->name;
94
Bjorn Helgaasff097dd2009-11-13 17:34:49 -070095 return new;
Yinghai Lu068258b2009-03-19 20:55:35 -070096}
97
Otavio Pontes6fa4a942018-03-07 08:39:14 +010098struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start,
99 int end, u64 addr)
Jiang Liu846e4022012-06-22 14:55:11 +0800100{
101 struct pci_mmcfg_region *new;
102
103 new = pci_mmconfig_alloc(segment, start, end, addr);
Jiang Liu376f70a2012-06-22 14:55:12 +0800104 if (new) {
105 mutex_lock(&pci_mmcfg_lock);
Jiang Liu846e4022012-06-22 14:55:11 +0800106 list_add_sorted(new);
Jiang Liu376f70a2012-06-22 14:55:12 +0800107 mutex_unlock(&pci_mmcfg_lock);
Jiang Liu9c951112012-06-22 14:55:15 +0800108
Jiang Liu24c97f02012-06-22 14:55:22 +0800109 pr_info(PREFIX
Jiang Liu9c951112012-06-22 14:55:15 +0800110 "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
111 "(base %#lx)\n",
112 segment, start, end, &new->res, (unsigned long)addr);
Jiang Liu376f70a2012-06-22 14:55:12 +0800113 }
Jiang Liu846e4022012-06-22 14:55:11 +0800114
115 return new;
116}
117
Bjorn Helgaasf6e1d8c2009-11-13 17:35:04 -0700118struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
119{
120 struct pci_mmcfg_region *cfg;
121
Joel Fernandes (Google)842a56c2019-07-16 18:12:27 -0400122 list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list, pci_mmcfg_lock_held())
Bjorn Helgaasf6e1d8c2009-11-13 17:35:04 -0700123 if (cfg->segment == segment &&
124 cfg->start_bus <= bus && bus <= cfg->end_bus)
125 return cfg;
126
127 return NULL;
128}
129
Mathias Krause64474b52014-08-25 23:26:36 +0200130static const char *__init pci_mmcfg_e7520(void)
Olivier Galibert9358c692007-02-13 13:26:20 +0100131{
132 u32 win;
Yinghai Lubb63b422008-02-28 23:56:50 -0800133 raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0xce, 2, &win);
Olivier Galibert9358c692007-02-13 13:26:20 +0100134
Olivier Galibertb5229db2007-05-02 19:27:22 +0200135 win = win & 0xf000;
Yinghai Lu068258b2009-03-19 20:55:35 -0700136 if (win == 0x0000 || win == 0xf000)
137 return NULL;
138
Bjorn Helgaas7da7d362009-11-13 17:33:53 -0700139 if (pci_mmconfig_add(0, 0, 255, win << 16) == NULL)
Yinghai Lu068258b2009-03-19 20:55:35 -0700140 return NULL;
141
Olivier Galibert9358c692007-02-13 13:26:20 +0100142 return "Intel Corporation E7520 Memory Controller Hub";
143}
144
Mathias Krause64474b52014-08-25 23:26:36 +0200145static const char *__init pci_mmcfg_intel_945(void)
Olivier Galibert9358c692007-02-13 13:26:20 +0100146{
147 u32 pciexbar, mask = 0, len = 0;
148
Yinghai Lubb63b422008-02-28 23:56:50 -0800149 raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0x48, 4, &pciexbar);
Olivier Galibert9358c692007-02-13 13:26:20 +0100150
151 /* Enable bit */
152 if (!(pciexbar & 1))
Yinghai Lu068258b2009-03-19 20:55:35 -0700153 return NULL;
Olivier Galibert9358c692007-02-13 13:26:20 +0100154
155 /* Size bits */
156 switch ((pciexbar >> 1) & 3) {
157 case 0:
158 mask = 0xf0000000U;
159 len = 0x10000000U;
160 break;
161 case 1:
162 mask = 0xf8000000U;
163 len = 0x08000000U;
164 break;
165 case 2:
166 mask = 0xfc000000U;
167 len = 0x04000000U;
168 break;
169 default:
Yinghai Lu068258b2009-03-19 20:55:35 -0700170 return NULL;
Olivier Galibert9358c692007-02-13 13:26:20 +0100171 }
172
173 /* Errata #2, things break when not aligned on a 256Mb boundary */
174 /* Can only happen in 64M/128M mode */
175
176 if ((pciexbar & mask) & 0x0fffffffU)
Yinghai Lu068258b2009-03-19 20:55:35 -0700177 return NULL;
Olivier Galibert9358c692007-02-13 13:26:20 +0100178
Olivier Galibertb5229db2007-05-02 19:27:22 +0200179 /* Don't hit the APIC registers and their friends */
180 if ((pciexbar & mask) >= 0xf0000000U)
Yinghai Lu068258b2009-03-19 20:55:35 -0700181 return NULL;
Olivier Galibertb5229db2007-05-02 19:27:22 +0200182
Bjorn Helgaas7da7d362009-11-13 17:33:53 -0700183 if (pci_mmconfig_add(0, 0, (len >> 20) - 1, pciexbar & mask) == NULL)
Yinghai Lu068258b2009-03-19 20:55:35 -0700184 return NULL;
185
Olivier Galibert9358c692007-02-13 13:26:20 +0100186 return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub";
187}
188
Mathias Krause64474b52014-08-25 23:26:36 +0200189static const char *__init pci_mmcfg_amd_fam10h(void)
Yinghai Lu7fd0da42008-02-19 03:13:02 -0800190{
191 u32 low, high, address;
192 u64 base, msr;
193 int i;
Bjorn Helgaas7da7d362009-11-13 17:33:53 -0700194 unsigned segnbits = 0, busnbits, end_bus;
Yinghai Lu7fd0da42008-02-19 03:13:02 -0800195
Yinghai Lu5f0b2972008-04-14 16:08:25 -0700196 if (!(pci_probe & PCI_CHECK_ENABLE_AMD_MMCONF))
197 return NULL;
198
Yinghai Lu7fd0da42008-02-19 03:13:02 -0800199 address = MSR_FAM10H_MMIO_CONF_BASE;
200 if (rdmsr_safe(address, &low, &high))
201 return NULL;
202
203 msr = high;
204 msr <<= 32;
205 msr |= low;
206
207 /* mmconfig is not enable */
208 if (!(msr & FAM10H_MMIO_CONF_ENABLE))
209 return NULL;
210
211 base = msr & (FAM10H_MMIO_CONF_BASE_MASK<<FAM10H_MMIO_CONF_BASE_SHIFT);
212
213 busnbits = (msr >> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) &
214 FAM10H_MMIO_CONF_BUSRANGE_MASK;
215
216 /*
217 * only handle bus 0 ?
218 * need to skip it
219 */
220 if (!busnbits)
221 return NULL;
222
223 if (busnbits > 8) {
224 segnbits = busnbits - 8;
225 busnbits = 8;
226 }
227
Bjorn Helgaas7da7d362009-11-13 17:33:53 -0700228 end_bus = (1 << busnbits) - 1;
Yinghai Lu068258b2009-03-19 20:55:35 -0700229 for (i = 0; i < (1 << segnbits); i++)
Bjorn Helgaas7da7d362009-11-13 17:33:53 -0700230 if (pci_mmconfig_add(i, 0, end_bus,
231 base + (1<<28) * i) == NULL) {
232 free_all_mmcfg();
233 return NULL;
234 }
Yinghai Lu7fd0da42008-02-19 03:13:02 -0800235
236 return "AMD Family 10h NB";
237}
238
Ed Swierk5546d6f2009-03-19 20:57:56 -0700239static bool __initdata mcp55_checked;
Mathias Krause64474b52014-08-25 23:26:36 +0200240static const char *__init pci_mmcfg_nvidia_mcp55(void)
Ed Swierk5546d6f2009-03-19 20:57:56 -0700241{
242 int bus;
243 int mcp55_mmconf_found = 0;
244
Mathias Krause776f7ad2014-08-25 23:26:37 +0200245 static const u32 extcfg_regnum __initconst = 0x90;
246 static const u32 extcfg_regsize __initconst = 4;
247 static const u32 extcfg_enable_mask __initconst = 1 << 31;
248 static const u32 extcfg_start_mask __initconst = 0xff << 16;
249 static const int extcfg_start_shift __initconst = 16;
250 static const u32 extcfg_size_mask __initconst = 0x3 << 28;
251 static const int extcfg_size_shift __initconst = 28;
252 static const int extcfg_sizebus[] __initconst = {
253 0x100, 0x80, 0x40, 0x20
254 };
255 static const u32 extcfg_base_mask[] __initconst = {
256 0x7ff8, 0x7ffc, 0x7ffe, 0x7fff
257 };
258 static const int extcfg_base_lshift __initconst = 25;
Ed Swierk5546d6f2009-03-19 20:57:56 -0700259
260 /*
261 * do check if amd fam10h already took over
262 */
Bjorn Helgaasff097dd2009-11-13 17:34:49 -0700263 if (!acpi_disabled || !list_empty(&pci_mmcfg_list) || mcp55_checked)
Ed Swierk5546d6f2009-03-19 20:57:56 -0700264 return NULL;
265
266 mcp55_checked = true;
267 for (bus = 0; bus < 256; bus++) {
268 u64 base;
269 u32 l, extcfg;
270 u16 vendor, device;
271 int start, size_index, end;
272
273 raw_pci_ops->read(0, bus, PCI_DEVFN(0, 0), 0, 4, &l);
274 vendor = l & 0xffff;
275 device = (l >> 16) & 0xffff;
276
277 if (PCI_VENDOR_ID_NVIDIA != vendor || 0x0369 != device)
278 continue;
279
280 raw_pci_ops->read(0, bus, PCI_DEVFN(0, 0), extcfg_regnum,
281 extcfg_regsize, &extcfg);
282
283 if (!(extcfg & extcfg_enable_mask))
284 continue;
285
Ed Swierk5546d6f2009-03-19 20:57:56 -0700286 size_index = (extcfg & extcfg_size_mask) >> extcfg_size_shift;
287 base = extcfg & extcfg_base_mask[size_index];
288 /* base could > 4G */
289 base <<= extcfg_base_lshift;
290 start = (extcfg & extcfg_start_mask) >> extcfg_start_shift;
291 end = start + extcfg_sizebus[size_index] - 1;
Bjorn Helgaas7da7d362009-11-13 17:33:53 -0700292 if (pci_mmconfig_add(0, start, end, base) == NULL)
293 continue;
Ed Swierk5546d6f2009-03-19 20:57:56 -0700294 mcp55_mmconf_found++;
295 }
296
297 if (!mcp55_mmconf_found)
298 return NULL;
299
300 return "nVidia MCP55";
301}
302
Olivier Galibert9358c692007-02-13 13:26:20 +0100303struct pci_mmcfg_hostbridge_probe {
Yinghai Lu7fd0da42008-02-19 03:13:02 -0800304 u32 bus;
305 u32 devfn;
Olivier Galibert9358c692007-02-13 13:26:20 +0100306 u32 vendor;
307 u32 device;
308 const char *(*probe)(void);
309};
310
Mathias Krause6af13ba2014-08-25 23:26:38 +0200311static const struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initconst = {
Yinghai Lu7fd0da42008-02-19 03:13:02 -0800312 { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL,
313 PCI_DEVICE_ID_INTEL_E7520_MCH, pci_mmcfg_e7520 },
314 { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL,
315 PCI_DEVICE_ID_INTEL_82945G_HB, pci_mmcfg_intel_945 },
316 { 0, PCI_DEVFN(0x18, 0), PCI_VENDOR_ID_AMD,
317 0x1200, pci_mmcfg_amd_fam10h },
318 { 0xff, PCI_DEVFN(0, 0), PCI_VENDOR_ID_AMD,
319 0x1200, pci_mmcfg_amd_fam10h },
Ed Swierk5546d6f2009-03-19 20:57:56 -0700320 { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_NVIDIA,
321 0x0369, pci_mmcfg_nvidia_mcp55 },
Olivier Galibert9358c692007-02-13 13:26:20 +0100322};
323
Yinghai Lu068258b2009-03-19 20:55:35 -0700324static void __init pci_mmcfg_check_end_bus_number(void)
325{
Bjorn Helgaas987c3672009-11-13 17:34:44 -0700326 struct pci_mmcfg_region *cfg, *cfgx;
Yinghai Lu068258b2009-03-19 20:55:35 -0700327
Thomas Gleixnerbb8d4132010-02-25 16:42:11 +0100328 /* Fixup overlaps */
Bjorn Helgaasff097dd2009-11-13 17:34:49 -0700329 list_for_each_entry(cfg, &pci_mmcfg_list, list) {
Bjorn Helgaasd7e6b662009-11-13 17:34:18 -0700330 if (cfg->end_bus < cfg->start_bus)
331 cfg->end_bus = 255;
Yinghai Lu068258b2009-03-19 20:55:35 -0700332
Thomas Gleixnerbb8d4132010-02-25 16:42:11 +0100333 /* Don't access the list head ! */
334 if (cfg->list.next == &pci_mmcfg_list)
335 break;
336
Bjorn Helgaasff097dd2009-11-13 17:34:49 -0700337 cfgx = list_entry(cfg->list.next, typeof(*cfg), list);
Thomas Gleixnerbb8d4132010-02-25 16:42:11 +0100338 if (cfg->end_bus >= cfgx->start_bus)
Bjorn Helgaasd7e6b662009-11-13 17:34:18 -0700339 cfg->end_bus = cfgx->start_bus - 1;
Yinghai Lu068258b2009-03-19 20:55:35 -0700340 }
341}
342
Olivier Galibert9358c692007-02-13 13:26:20 +0100343static int __init pci_mmcfg_check_hostbridge(void)
344{
345 u32 l;
Yinghai Lu7fd0da42008-02-19 03:13:02 -0800346 u32 bus, devfn;
Olivier Galibert9358c692007-02-13 13:26:20 +0100347 u16 vendor, device;
348 int i;
349 const char *name;
350
Yinghai Lubb63b422008-02-28 23:56:50 -0800351 if (!raw_pci_ops)
352 return 0;
353
Bjorn Helgaas7da7d362009-11-13 17:33:53 -0700354 free_all_mmcfg();
Olivier Galibert9358c692007-02-13 13:26:20 +0100355
Yinghai Lu068258b2009-03-19 20:55:35 -0700356 for (i = 0; i < ARRAY_SIZE(pci_mmcfg_probes); i++) {
Yinghai Lu7fd0da42008-02-19 03:13:02 -0800357 bus = pci_mmcfg_probes[i].bus;
358 devfn = pci_mmcfg_probes[i].devfn;
Yinghai Lubb63b422008-02-28 23:56:50 -0800359 raw_pci_ops->read(0, bus, devfn, 0, 4, &l);
Yinghai Lu7fd0da42008-02-19 03:13:02 -0800360 vendor = l & 0xffff;
361 device = (l >> 16) & 0xffff;
362
Yinghai Lu068258b2009-03-19 20:55:35 -0700363 name = NULL;
OGAWA Hirofumi429d5122007-02-13 13:26:20 +0100364 if (pci_mmcfg_probes[i].vendor == vendor &&
365 pci_mmcfg_probes[i].device == device)
Olivier Galibert9358c692007-02-13 13:26:20 +0100366 name = pci_mmcfg_probes[i].probe();
Yinghai Lu068258b2009-03-19 20:55:35 -0700367
368 if (name)
Jiang Liu24c97f02012-06-22 14:55:22 +0800369 pr_info(PREFIX "%s with MMCONFIG support\n", name);
OGAWA Hirofumi429d5122007-02-13 13:26:20 +0100370 }
Olivier Galibert9358c692007-02-13 13:26:20 +0100371
Yinghai Lu068258b2009-03-19 20:55:35 -0700372 /* some end_bus_number is crazy, fix it */
373 pci_mmcfg_check_end_bus_number();
Olivier Galibert9358c692007-02-13 13:26:20 +0100374
Bjorn Helgaasff097dd2009-11-13 17:34:49 -0700375 return !list_empty(&pci_mmcfg_list);
Olivier Galibert9358c692007-02-13 13:26:20 +0100376}
377
Greg Kroah-Hartmana18e3692012-12-21 14:02:53 -0800378static acpi_status check_mcfg_resource(struct acpi_resource *res, void *data)
Robert Hancock7752d5c2008-02-15 01:27:20 -0800379{
380 struct resource *mcfg_res = data;
381 struct acpi_resource_address64 address;
382 acpi_status status;
383
384 if (res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
385 struct acpi_resource_fixed_memory32 *fixmem32 =
386 &res->data.fixed_memory32;
387 if (!fixmem32)
388 return AE_OK;
389 if ((mcfg_res->start >= fixmem32->address) &&
Yinghai Lu75e613c2009-06-03 00:13:13 -0700390 (mcfg_res->end < (fixmem32->address +
Robert Hancock7752d5c2008-02-15 01:27:20 -0800391 fixmem32->address_length))) {
392 mcfg_res->flags = 1;
393 return AE_CTRL_TERMINATE;
394 }
395 }
396 if ((res->type != ACPI_RESOURCE_TYPE_ADDRESS32) &&
397 (res->type != ACPI_RESOURCE_TYPE_ADDRESS64))
398 return AE_OK;
399
400 status = acpi_resource_to_address64(res, &address);
401 if (ACPI_FAILURE(status) ||
Lv Zhenga45de932015-01-26 16:58:56 +0800402 (address.address.address_length <= 0) ||
Robert Hancock7752d5c2008-02-15 01:27:20 -0800403 (address.resource_type != ACPI_MEMORY_RANGE))
404 return AE_OK;
405
Lv Zhenga45de932015-01-26 16:58:56 +0800406 if ((mcfg_res->start >= address.address.minimum) &&
407 (mcfg_res->end < (address.address.minimum + address.address.address_length))) {
Robert Hancock7752d5c2008-02-15 01:27:20 -0800408 mcfg_res->flags = 1;
409 return AE_CTRL_TERMINATE;
410 }
411 return AE_OK;
412}
413
Greg Kroah-Hartmana18e3692012-12-21 14:02:53 -0800414static acpi_status find_mboard_resource(acpi_handle handle, u32 lvl,
415 void *context, void **rv)
Robert Hancock7752d5c2008-02-15 01:27:20 -0800416{
417 struct resource *mcfg_res = context;
418
419 acpi_walk_resources(handle, METHOD_NAME__CRS,
420 check_mcfg_resource, context);
421
422 if (mcfg_res->flags)
423 return AE_CTRL_TERMINATE;
424
425 return AE_OK;
426}
427
Ingo Molnar81b3e092017-01-28 22:34:55 +0100428static bool is_acpi_reserved(u64 start, u64 end, unsigned not_used)
Robert Hancock7752d5c2008-02-15 01:27:20 -0800429{
430 struct resource mcfg_res;
431
432 mcfg_res.start = start;
Yinghai Lu75e613c2009-06-03 00:13:13 -0700433 mcfg_res.end = end - 1;
Robert Hancock7752d5c2008-02-15 01:27:20 -0800434 mcfg_res.flags = 0;
435
436 acpi_get_devices("PNP0C01", find_mboard_resource, &mcfg_res, NULL);
437
438 if (!mcfg_res.flags)
439 acpi_get_devices("PNP0C02", find_mboard_resource, &mcfg_res,
440 NULL);
441
442 return mcfg_res.flags;
443}
444
Ingo Molnar81b3e092017-01-28 22:34:55 +0100445typedef bool (*check_reserved_t)(u64 start, u64 end, unsigned type);
Yinghai Lua83fe322008-07-18 13:22:36 -0700446
Ingo Molnar81b3e092017-01-28 22:34:55 +0100447static bool __ref is_mmconf_reserved(check_reserved_t is_reserved,
448 struct pci_mmcfg_region *cfg,
449 struct device *dev, int with_e820)
Yinghai Lua83fe322008-07-18 13:22:36 -0700450{
Bjorn Helgaas2f2a8b92009-11-13 17:34:34 -0700451 u64 addr = cfg->res.start;
452 u64 size = resource_size(&cfg->res);
Yinghai Lua83fe322008-07-18 13:22:36 -0700453 u64 old_size = size;
Jiang Liu95c5e922012-06-22 14:55:14 +0800454 int num_buses;
455 char *method = with_e820 ? "E820" : "ACPI motherboard resources";
Yinghai Lua83fe322008-07-18 13:22:36 -0700456
Ingo Molnar09821ff2017-01-28 17:09:33 +0100457 while (!is_reserved(addr, addr + size, E820_TYPE_RESERVED)) {
Yinghai Lua83fe322008-07-18 13:22:36 -0700458 size >>= 1;
459 if (size < (16UL<<20))
460 break;
461 }
462
Jiang Liu95c5e922012-06-22 14:55:14 +0800463 if (size < (16UL<<20) && size != old_size)
464 return 0;
Yinghai Lua83fe322008-07-18 13:22:36 -0700465
Jiang Liu95c5e922012-06-22 14:55:14 +0800466 if (dev)
467 dev_info(dev, "MMCONFIG at %pR reserved in %s\n",
468 &cfg->res, method);
469 else
Jiang Liu24c97f02012-06-22 14:55:22 +0800470 pr_info(PREFIX "MMCONFIG at %pR reserved in %s\n",
Jiang Liu95c5e922012-06-22 14:55:14 +0800471 &cfg->res, method);
472
473 if (old_size != size) {
474 /* update end_bus */
475 cfg->end_bus = cfg->start_bus + ((size>>20) - 1);
476 num_buses = cfg->end_bus - cfg->start_bus + 1;
477 cfg->res.end = cfg->res.start +
478 PCI_MMCFG_BUS_OFFSET(num_buses) - 1;
479 snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN,
480 "PCI MMCONFIG %04x [bus %02x-%02x]",
481 cfg->segment, cfg->start_bus, cfg->end_bus);
482
483 if (dev)
484 dev_info(dev,
485 "MMCONFIG "
486 "at %pR (base %#lx) (size reduced!)\n",
487 &cfg->res, (unsigned long) cfg->address);
488 else
Jiang Liu24c97f02012-06-22 14:55:22 +0800489 pr_info(PREFIX
Jiang Liu95c5e922012-06-22 14:55:14 +0800490 "MMCONFIG for %04x [bus%02x-%02x] "
491 "at %pR (base %#lx) (size reduced!)\n",
492 cfg->segment, cfg->start_bus, cfg->end_bus,
493 &cfg->res, (unsigned long) cfg->address);
Yinghai Lua83fe322008-07-18 13:22:36 -0700494 }
495
Jiang Liu95c5e922012-06-22 14:55:14 +0800496 return 1;
Yinghai Lua83fe322008-07-18 13:22:36 -0700497}
498
Ingo Molnar81b3e092017-01-28 22:34:55 +0100499static bool __ref
500pci_mmcfg_check_reserved(struct device *dev, struct pci_mmcfg_region *cfg, int early)
Jiang Liu2a76c452012-06-22 14:55:10 +0800501{
502 if (!early && !acpi_disabled) {
Jiang Liu95c5e922012-06-22 14:55:14 +0800503 if (is_mmconf_reserved(is_acpi_reserved, cfg, dev, 0))
Jiang Liu2a76c452012-06-22 14:55:10 +0800504 return 1;
Jiang Liu95c5e922012-06-22 14:55:14 +0800505
506 if (dev)
507 dev_info(dev, FW_INFO
508 "MMCONFIG at %pR not reserved in "
509 "ACPI motherboard resources\n",
510 &cfg->res);
Jiang Liu2a76c452012-06-22 14:55:10 +0800511 else
Jiang Liu24c97f02012-06-22 14:55:22 +0800512 pr_info(FW_INFO PREFIX
Jiang Liu2a76c452012-06-22 14:55:10 +0800513 "MMCONFIG at %pR not reserved in "
514 "ACPI motherboard resources\n",
515 &cfg->res);
516 }
517
Jiang Liu95c5e922012-06-22 14:55:14 +0800518 /*
Ingo Molnar3bce64f2017-01-28 14:14:25 +0100519 * e820__mapped_all() is marked as __init.
Jiang Liu95c5e922012-06-22 14:55:14 +0800520 * All entries from ACPI MCFG table have been checked at boot time.
521 * For MCFG information constructed from hotpluggable host bridge's
522 * _CBA method, just assume it's reserved.
523 */
524 if (pci_mmcfg_running_state)
525 return 1;
526
Jiang Liu2a76c452012-06-22 14:55:10 +0800527 /* Don't try to do this check unless configuration
528 type 1 is available. how about type 2 ?*/
529 if (raw_pci_ops)
Ingo Molnar3bce64f2017-01-28 14:14:25 +0100530 return is_mmconf_reserved(e820__mapped_all, cfg, dev, 1);
Jiang Liu2a76c452012-06-22 14:55:10 +0800531
532 return 0;
533}
534
Yinghai Lubb63b422008-02-28 23:56:50 -0800535static void __init pci_mmcfg_reject_broken(int early)
OGAWA Hirofumi44de0202007-02-13 13:26:20 +0100536{
Bjorn Helgaas987c3672009-11-13 17:34:44 -0700537 struct pci_mmcfg_region *cfg;
OGAWA Hirofumi26054ed2007-02-13 13:26:20 +0100538
Bjorn Helgaasff097dd2009-11-13 17:34:49 -0700539 list_for_each_entry(cfg, &pci_mmcfg_list, list) {
Jiang Liu95c5e922012-06-22 14:55:14 +0800540 if (pci_mmcfg_check_reserved(NULL, cfg, early) == 0) {
Jiang Liu24c97f02012-06-22 14:55:22 +0800541 pr_info(PREFIX "not using MMCONFIG\n");
Jiang Liu2a76c452012-06-22 14:55:10 +0800542 free_all_mmcfg();
543 return;
Feng Tanga02ce952010-05-05 17:08:49 +0800544 }
OGAWA Hirofumi26054ed2007-02-13 13:26:20 +0100545 }
OGAWA Hirofumi44de0202007-02-13 13:26:20 +0100546}
547
Bjorn Helgaas9a08f7d2009-10-23 15:20:33 -0600548static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
549 struct acpi_mcfg_allocation *cfg)
Len Brownc4bf2f32009-06-11 23:53:55 -0400550{
Bjorn Helgaas9a08f7d2009-10-23 15:20:33 -0600551 if (cfg->address < 0xFFFFFFFF)
552 return 0;
553
Mike Travis526018b2013-02-11 13:45:10 -0600554 if (!strncmp(mcfg->header.oem_id, "SGI", 3))
Bjorn Helgaas9a08f7d2009-10-23 15:20:33 -0600555 return 0;
556
Andy Shevchenko69c42d42018-02-22 14:59:21 +0200557 if ((mcfg->header.revision >= 1) && (dmi_get_bios_year() >= 2010))
558 return 0;
Bjorn Helgaas9a08f7d2009-10-23 15:20:33 -0600559
Jiang Liu24c97f02012-06-22 14:55:22 +0800560 pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
Bjorn Helgaas9a08f7d2009-10-23 15:20:33 -0600561 "is above 4GB, ignored\n", cfg->pci_segment,
562 cfg->start_bus_number, cfg->end_bus_number, cfg->address);
563 return -EINVAL;
Len Brownc4bf2f32009-06-11 23:53:55 -0400564}
565
566static int __init pci_parse_mcfg(struct acpi_table_header *header)
567{
568 struct acpi_table_mcfg *mcfg;
Bjorn Helgaasd3578ef2009-11-13 17:33:47 -0700569 struct acpi_mcfg_allocation *cfg_table, *cfg;
Len Brownc4bf2f32009-06-11 23:53:55 -0400570 unsigned long i;
Bjorn Helgaas7da7d362009-11-13 17:33:53 -0700571 int entries;
Len Brownc4bf2f32009-06-11 23:53:55 -0400572
573 if (!header)
574 return -EINVAL;
575
576 mcfg = (struct acpi_table_mcfg *)header;
577
578 /* how many config structures do we have */
Bjorn Helgaas7da7d362009-11-13 17:33:53 -0700579 free_all_mmcfg();
Bjorn Helgaase823d6f2009-11-13 17:33:42 -0700580 entries = 0;
Len Brownc4bf2f32009-06-11 23:53:55 -0400581 i = header->length - sizeof(struct acpi_table_mcfg);
582 while (i >= sizeof(struct acpi_mcfg_allocation)) {
Bjorn Helgaase823d6f2009-11-13 17:33:42 -0700583 entries++;
Len Brownc4bf2f32009-06-11 23:53:55 -0400584 i -= sizeof(struct acpi_mcfg_allocation);
Peter Senna Tschudin4b8073e2012-09-18 18:36:14 +0200585 }
Bjorn Helgaase823d6f2009-11-13 17:33:42 -0700586 if (entries == 0) {
Jiang Liu24c97f02012-06-22 14:55:22 +0800587 pr_err(PREFIX "MMCONFIG has no entries\n");
Len Brownc4bf2f32009-06-11 23:53:55 -0400588 return -ENODEV;
589 }
590
Bjorn Helgaasd3578ef2009-11-13 17:33:47 -0700591 cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
Bjorn Helgaase823d6f2009-11-13 17:33:42 -0700592 for (i = 0; i < entries; i++) {
Bjorn Helgaasd3578ef2009-11-13 17:33:47 -0700593 cfg = &cfg_table[i];
594 if (acpi_mcfg_check_entry(mcfg, cfg)) {
Bjorn Helgaas7da7d362009-11-13 17:33:53 -0700595 free_all_mmcfg();
Len Brownc4bf2f32009-06-11 23:53:55 -0400596 return -ENODEV;
597 }
Bjorn Helgaas7da7d362009-11-13 17:33:53 -0700598
599 if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
600 cfg->end_bus_number, cfg->address) == NULL) {
Jiang Liu24c97f02012-06-22 14:55:22 +0800601 pr_warn(PREFIX "no memory for MCFG entries\n");
Bjorn Helgaas7da7d362009-11-13 17:33:53 -0700602 free_all_mmcfg();
603 return -ENOMEM;
604 }
Len Brownc4bf2f32009-06-11 23:53:55 -0400605 }
606
607 return 0;
608}
609
Chen, Gongd91525e2014-12-10 13:53:26 -0800610#ifdef CONFIG_ACPI_APEI
611extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size,
612 void *data), void *data);
613
614static int pci_mmcfg_for_each_region(int (*func)(__u64 start, __u64 size,
615 void *data), void *data)
616{
617 struct pci_mmcfg_region *cfg;
618 int rc;
619
620 if (list_empty(&pci_mmcfg_list))
621 return 0;
622
623 list_for_each_entry(cfg, &pci_mmcfg_list, list) {
624 rc = func(cfg->res.start, resource_size(&cfg->res), data);
625 if (rc)
626 return rc;
627 }
628
629 return 0;
630}
631#define set_apei_filter() (arch_apei_filter_addr = pci_mmcfg_for_each_region)
632#else
633#define set_apei_filter()
634#endif
635
Thomas Gleixner968cbfad2008-05-12 15:43:37 +0200636static void __init __pci_mmcfg_init(int early)
Olivier Galibertb7867392007-02-13 13:26:20 +0100637{
Yinghai Lu068258b2009-03-19 20:55:35 -0700638 pci_mmcfg_reject_broken(early);
Bjorn Helgaasff097dd2009-11-13 17:34:49 -0700639 if (list_empty(&pci_mmcfg_list))
Olivier Galibertb7867392007-02-13 13:26:20 +0100640 return;
641
Jan Beulicha3170c12011-02-23 10:08:10 +0000642 if (pcibios_last_bus < 0) {
643 const struct pci_mmcfg_region *cfg;
644
645 list_for_each_entry(cfg, &pci_mmcfg_list, list) {
646 if (cfg->segment)
647 break;
648 pcibios_last_bus = cfg->end_bus;
649 }
650 }
651
Yinghai Luebd60cd2008-09-04 21:04:32 +0200652 if (pci_mmcfg_arch_init())
Olivier Galibertb7867392007-02-13 13:26:20 +0100653 pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
Yinghai Luebd60cd2008-09-04 21:04:32 +0200654 else {
Jiang Liu66e88502012-06-22 14:55:18 +0800655 free_all_mmcfg();
Jiang Liu9c951112012-06-22 14:55:15 +0800656 pci_mmcfg_arch_init_failed = true;
Olivier Galibertb7867392007-02-13 13:26:20 +0100657 }
658}
Aaron Durbina5ba7972007-07-21 17:10:34 +0200659
Jiang Liu574a5942012-06-22 14:55:20 +0800660static int __initdata known_bridge;
661
Yinghai Lubb63b422008-02-28 23:56:50 -0800662void __init pci_mmcfg_early_init(void)
Yinghai Lu05c58b82008-02-15 01:30:14 -0800663{
Jiang Liu574a5942012-06-22 14:55:20 +0800664 if (pci_probe & PCI_PROBE_MMCONF) {
665 if (pci_mmcfg_check_hostbridge())
666 known_bridge = 1;
667 else
668 acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
669 __pci_mmcfg_init(1);
Chen, Gongd91525e2014-12-10 13:53:26 -0800670
671 set_apei_filter();
Jiang Liu574a5942012-06-22 14:55:20 +0800672 }
Yinghai Lu05c58b82008-02-15 01:30:14 -0800673}
674
675void __init pci_mmcfg_late_init(void)
676{
Jiang Liu574a5942012-06-22 14:55:20 +0800677 /* MMCONFIG disabled */
678 if ((pci_probe & PCI_PROBE_MMCONF) == 0)
679 return;
680
681 if (known_bridge)
682 return;
683
684 /* MMCONFIG hasn't been enabled yet, try again */
685 if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) {
686 acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
687 __pci_mmcfg_init(0);
688 }
Yinghai Lu05c58b82008-02-15 01:30:14 -0800689}
690
Aaron Durbina5ba7972007-07-21 17:10:34 +0200691static int __init pci_mmcfg_late_insert_resources(void)
692{
Jiang Liu66e88502012-06-22 14:55:18 +0800693 struct pci_mmcfg_region *cfg;
694
Jiang Liu95c5e922012-06-22 14:55:14 +0800695 pci_mmcfg_running_state = true;
696
Jiang Liu66e88502012-06-22 14:55:18 +0800697 /* If we are not using MMCONFIG, don't insert the resources. */
698 if ((pci_probe & PCI_PROBE_MMCONF) == 0)
Aaron Durbina5ba7972007-07-21 17:10:34 +0200699 return 1;
700
701 /*
702 * Attempt to insert the mmcfg resources but not with the busy flag
703 * marked so it won't cause request errors when __request_region is
704 * called.
705 */
Jiang Liu66e88502012-06-22 14:55:18 +0800706 list_for_each_entry(cfg, &pci_mmcfg_list, list)
707 if (!cfg->res.parent)
708 insert_resource(&iomem_resource, &cfg->res);
Aaron Durbina5ba7972007-07-21 17:10:34 +0200709
710 return 0;
711}
712
713/*
714 * Perform MMCONFIG resource insertion after PCI initialization to allow for
715 * misprogrammed MCFG tables that state larger sizes but actually conflict
716 * with other system resources.
717 */
718late_initcall(pci_mmcfg_late_insert_resources);
Jiang Liu9c951112012-06-22 14:55:15 +0800719
720/* Add MMCFG information for host bridges */
Greg Kroah-Hartmana18e3692012-12-21 14:02:53 -0800721int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
722 phys_addr_t addr)
Jiang Liu9c951112012-06-22 14:55:15 +0800723{
724 int rc;
725 struct resource *tmp = NULL;
726 struct pci_mmcfg_region *cfg;
727
728 if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed)
729 return -ENODEV;
730
Bjorn Helgaas67d470e2013-10-04 16:14:30 -0600731 if (start > end)
Jiang Liu9c951112012-06-22 14:55:15 +0800732 return -EINVAL;
733
734 mutex_lock(&pci_mmcfg_lock);
735 cfg = pci_mmconfig_lookup(seg, start);
736 if (cfg) {
737 if (cfg->end_bus < end)
738 dev_info(dev, FW_INFO
739 "MMCONFIG for "
740 "domain %04x [bus %02x-%02x] "
741 "only partially covers this bridge\n",
742 cfg->segment, cfg->start_bus, cfg->end_bus);
743 mutex_unlock(&pci_mmcfg_lock);
744 return -EEXIST;
745 }
746
Bjorn Helgaas67d470e2013-10-04 16:14:30 -0600747 if (!addr) {
748 mutex_unlock(&pci_mmcfg_lock);
749 return -EINVAL;
750 }
751
Jiang Liu9c951112012-06-22 14:55:15 +0800752 rc = -EBUSY;
753 cfg = pci_mmconfig_alloc(seg, start, end, addr);
754 if (cfg == NULL) {
755 dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
756 rc = -ENOMEM;
757 } else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
758 dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n",
759 &cfg->res);
760 } else {
761 /* Insert resource if it's not in boot stage */
762 if (pci_mmcfg_running_state)
763 tmp = insert_resource_conflict(&iomem_resource,
764 &cfg->res);
765
766 if (tmp) {
767 dev_warn(dev,
768 "MMCONFIG %pR conflicts with "
769 "%s %pR\n",
770 &cfg->res, tmp->name, tmp);
771 } else if (pci_mmcfg_arch_map(cfg)) {
772 dev_warn(dev, "fail to map MMCONFIG %pR.\n",
773 &cfg->res);
774 } else {
775 list_add_sorted(cfg);
776 dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
777 &cfg->res, (unsigned long)addr);
778 cfg = NULL;
779 rc = 0;
780 }
781 }
782
783 if (cfg) {
784 if (cfg->res.parent)
785 release_resource(&cfg->res);
786 kfree(cfg);
787 }
788
789 mutex_unlock(&pci_mmcfg_lock);
790
791 return rc;
792}
793
794/* Delete MMCFG information for host bridges */
795int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
796{
797 struct pci_mmcfg_region *cfg;
798
799 mutex_lock(&pci_mmcfg_lock);
800 list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
801 if (cfg->segment == seg && cfg->start_bus == start &&
802 cfg->end_bus == end) {
803 list_del_rcu(&cfg->list);
804 synchronize_rcu();
805 pci_mmcfg_arch_unmap(cfg);
806 if (cfg->res.parent)
807 release_resource(&cfg->res);
808 mutex_unlock(&pci_mmcfg_lock);
809 kfree(cfg);
810 return 0;
811 }
812 mutex_unlock(&pci_mmcfg_lock);
813
814 return -ENOENT;
815}