1 /*
2 * mmconfig-shared.c - Low-level direct PCI config space access via
3 * MMCONFIG - common code between i386 and x86-64.
4 *
5 * This code does:
6 * - known chipset handling
7 * - ACPI decoding and validation
8 *
9 * Per-architecture code takes care of the mappings and accesses
10 * themselves.
11 *
12 * Author: Allen Kay <allen.m.kay@intel.com> - adapted to xen from Linux
13 */
14
15 #include <xen/init.h>
16 #include <xen/mm.h>
17 #include <xen/param.h>
18 #include <xen/acpi.h>
19 #include <xen/xmalloc.h>
20 #include <xen/pci.h>
21 #include <xen/pci_regs.h>
22 #include <xen/pci_ids.h>
23 #include <asm/e820.h>
24 #include <asm/msr.h>
25 #include <asm/msr-index.h>
26 #include <public/physdev.h>
27
28 #include "mmconfig.h"
29
30 unsigned int pci_probe = PCI_PROBE_CONF1 | PCI_PROBE_MMCONF;
31
parse_mmcfg(const char * s)32 static int __init parse_mmcfg(const char *s)
33 {
34 const char *ss;
35 int rc = 0;
36
37 do {
38 ss = strchr(s, ',');
39 if ( !ss )
40 ss = strchr(s, '\0');
41
42 switch ( parse_bool(s, ss) )
43 {
44 case 0:
45 pci_probe &= ~PCI_PROBE_MMCONF;
46 break;
47 case 1:
48 break;
49 default:
50 if ( !cmdline_strcmp(s, "amd_fam10") ||
51 !cmdline_strcmp(s, "amd-fam10") )
52 pci_probe |= PCI_CHECK_ENABLE_AMD_MMCONF;
53 else
54 rc = -EINVAL;
55 break;
56 }
57
58 s = ss + 1;
59 } while ( *ss );
60
61 return rc;
62 }
63 custom_param("mmcfg", parse_mmcfg);
64
pci_mmcfg_e7520(void)65 static const char __init *pci_mmcfg_e7520(void)
66 {
67 u32 win;
68 win = pci_conf_read16(PCI_SBDF(0, 0, 0, 0), 0xce);
69
70 win = win & 0xf000;
71 if(win == 0x0000 || win == 0xf000)
72 pci_mmcfg_config_num = 0;
73 else {
74 pci_mmcfg_config_num = 1;
75 pci_mmcfg_config = xzalloc(struct acpi_mcfg_allocation);
76 if (!pci_mmcfg_config)
77 return NULL;
78 pci_mmcfg_config[0].address = win << 16;
79 pci_mmcfg_config[0].pci_segment = 0;
80 pci_mmcfg_config[0].start_bus_number = 0;
81 pci_mmcfg_config[0].end_bus_number = 255;
82 }
83
84 return "Intel Corporation E7520 Memory Controller Hub";
85 }
86
pci_mmcfg_intel_945(void)87 static const char __init *pci_mmcfg_intel_945(void)
88 {
89 u32 pciexbar, mask = 0, len = 0;
90
91 pci_mmcfg_config_num = 1;
92
93 pciexbar = pci_conf_read32(PCI_SBDF(0, 0, 0, 0), 0x48);
94
95 /* Enable bit */
96 if (!(pciexbar & 1))
97 pci_mmcfg_config_num = 0;
98
99 /* Size bits */
100 switch ((pciexbar >> 1) & 3) {
101 case 0:
102 mask = 0xf0000000U;
103 len = 0x10000000U;
104 break;
105 case 1:
106 mask = 0xf8000000U;
107 len = 0x08000000U;
108 break;
109 case 2:
110 mask = 0xfc000000U;
111 len = 0x04000000U;
112 break;
113 default:
114 pci_mmcfg_config_num = 0;
115 }
116
117 /* Errata #2, things break when not aligned on a 256Mb boundary */
118 /* Can only happen in 64M/128M mode */
119
120 if ((pciexbar & mask) & 0x0fffffffU)
121 pci_mmcfg_config_num = 0;
122
123 /* Don't hit the APIC registers and their friends */
124 if ((pciexbar & mask) >= 0xf0000000U)
125 pci_mmcfg_config_num = 0;
126
127 if (pci_mmcfg_config_num) {
128 pci_mmcfg_config = xzalloc(struct acpi_mcfg_allocation);
129 if (!pci_mmcfg_config)
130 return NULL;
131 pci_mmcfg_config[0].address = pciexbar & mask;
132 pci_mmcfg_config[0].pci_segment = 0;
133 pci_mmcfg_config[0].start_bus_number = 0;
134 pci_mmcfg_config[0].end_bus_number = (len >> 20) - 1;
135 }
136
137 return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub";
138 }
139
pci_mmcfg_amd_fam10h(void)140 static const char __init *pci_mmcfg_amd_fam10h(void)
141 {
142 uint32_t address;
143 uint64_t base, msr_content;
144 int i;
145 unsigned segnbits = 0, busnbits;
146
147 if (!(pci_probe & PCI_CHECK_ENABLE_AMD_MMCONF))
148 return NULL;
149
150 address = MSR_FAM10H_MMIO_CONF_BASE;
151 if (rdmsr_safe(address, msr_content))
152 return NULL;
153
154 /* mmconfig is not enable */
155 if (!(msr_content & FAM10H_MMIO_CONF_ENABLE))
156 return NULL;
157
158 base = msr_content &
159 (FAM10H_MMIO_CONF_BASE_MASK<<FAM10H_MMIO_CONF_BASE_SHIFT);
160
161 busnbits = (msr_content >> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) &
162 FAM10H_MMIO_CONF_BUSRANGE_MASK;
163
164 /*
165 * only handle bus 0 ?
166 * need to skip it
167 */
168 if (!busnbits)
169 return NULL;
170
171 if (busnbits > 8) {
172 segnbits = busnbits - 8;
173 busnbits = 8;
174 }
175
176 pci_mmcfg_config_num = (1 << segnbits);
177 pci_mmcfg_config = xmalloc_array(struct acpi_mcfg_allocation,
178 pci_mmcfg_config_num);
179 if (!pci_mmcfg_config)
180 return NULL;
181
182 for (i = 0; i < (1 << segnbits); i++) {
183 pci_mmcfg_config[i].address = base + ((unsigned long)i << 28);
184 pci_mmcfg_config[i].pci_segment = i;
185 pci_mmcfg_config[i].start_bus_number = 0;
186 pci_mmcfg_config[i].end_bus_number = (1 << busnbits) - 1;
187 pci_add_segment(i);
188 }
189
190 return "AMD Family 10h NB";
191 }
192
pci_mmcfg_nvidia_mcp55(void)193 static const char __init *pci_mmcfg_nvidia_mcp55(void)
194 {
195 static bool_t __initdata mcp55_checked;
196 int bus, i;
197
198 static const u32 extcfg_regnum = 0x90;
199 static const u32 extcfg_enable_mask = 1u << 31;
200 static const u32 extcfg_start_mask = 0xffu << 16;
201 static const int extcfg_start_shift = 16;
202 static const u32 extcfg_size_mask = 3u << 28;
203 static const int extcfg_size_shift = 28;
204 static const int extcfg_sizebus[] = {0xff, 0x7f, 0x3f, 0x1f};
205 static const u32 extcfg_base_mask[] = {0x7ff8, 0x7ffc, 0x7ffe, 0x7fff};
206 static const int extcfg_base_lshift = 25;
207
208 /* check if amd fam10h already took over */
209 if (!acpi_disabled || pci_mmcfg_config_num || mcp55_checked)
210 return NULL;
211
212 mcp55_checked = 1;
213 for (i = bus = 0; bus < 256; bus++) {
214 u32 l, extcfg;
215 u16 vendor, device;
216
217 l = pci_conf_read32(PCI_SBDF(0, bus, 0, 0), 0);
218 vendor = l & 0xffff;
219 device = (l >> 16) & 0xffff;
220
221 if (PCI_VENDOR_ID_NVIDIA != vendor || 0x0369 != device)
222 continue;
223
224 extcfg = pci_conf_read32(PCI_SBDF(0, bus, 0, 0), extcfg_regnum);
225
226 if (extcfg & extcfg_enable_mask)
227 i++;
228 }
229
230 if (!i)
231 return NULL;
232
233 pci_mmcfg_config_num = i;
234 pci_mmcfg_config = xmalloc_array(struct acpi_mcfg_allocation,
235 pci_mmcfg_config_num);
236
237 for (i = bus = 0; bus < 256; bus++) {
238 u64 base;
239 u32 l, extcfg;
240 u16 vendor, device;
241 int size_index;
242
243 l = pci_conf_read32(PCI_SBDF(0, bus, 0, 0), 0);
244 vendor = l & 0xffff;
245 device = (l >> 16) & 0xffff;
246
247 if (PCI_VENDOR_ID_NVIDIA != vendor || 0x0369 != device)
248 continue;
249
250 extcfg = pci_conf_read32(PCI_SBDF(0, bus, 0, 0), extcfg_regnum);
251
252 if (!(extcfg & extcfg_enable_mask))
253 continue;
254
255 if (i >= pci_mmcfg_config_num)
256 break;
257
258 size_index = (extcfg & extcfg_size_mask) >> extcfg_size_shift;
259 base = extcfg & extcfg_base_mask[size_index];
260 /* base could be > 4G */
261 pci_mmcfg_config[i].address = base << extcfg_base_lshift;
262 pci_mmcfg_config[i].pci_segment = 0;
263 pci_mmcfg_config[i].start_bus_number =
264 (extcfg & extcfg_start_mask) >> extcfg_start_shift;
265 pci_mmcfg_config[i].end_bus_number =
266 pci_mmcfg_config[i].start_bus_number + extcfg_sizebus[size_index];
267 i++;
268 }
269
270 if (bus == 256)
271 return "nVidia MCP55";
272
273 pci_mmcfg_config_num = 0;
274 xfree(pci_mmcfg_config);
275 pci_mmcfg_config = NULL;
276
277 return NULL;
278 }
279
280 struct pci_mmcfg_hostbridge_probe {
281 u32 bus;
282 u32 devfn;
283 u32 vendor;
284 u32 device;
285 const char *(*probe)(void);
286 };
287
288 static struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initdata = {
289 { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL,
290 PCI_DEVICE_ID_INTEL_E7520_MCH, pci_mmcfg_e7520 },
291 { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL,
292 PCI_DEVICE_ID_INTEL_82945G_HB, pci_mmcfg_intel_945 },
293 { 0, PCI_DEVFN(0x18, 0), PCI_VENDOR_ID_AMD,
294 0x1200, pci_mmcfg_amd_fam10h },
295 { 0xff, PCI_DEVFN(0, 0), PCI_VENDOR_ID_AMD,
296 0x1200, pci_mmcfg_amd_fam10h },
297 { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_NVIDIA,
298 0x0369, pci_mmcfg_nvidia_mcp55 },
299 };
300
pci_mmcfg_check_hostbridge(void)301 static int __init pci_mmcfg_check_hostbridge(void)
302 {
303 u32 l;
304 u32 bus, devfn;
305 u16 vendor, device;
306 int i;
307 const char *name;
308
309 pci_mmcfg_config_num = 0;
310 pci_mmcfg_config = NULL;
311 name = NULL;
312
313 for (i = 0; !name && i < ARRAY_SIZE(pci_mmcfg_probes); i++) {
314 bus = pci_mmcfg_probes[i].bus;
315 devfn = pci_mmcfg_probes[i].devfn;
316 l = pci_conf_read32(PCI_SBDF3(0, bus, devfn), 0);
317 vendor = l & 0xffff;
318 device = (l >> 16) & 0xffff;
319
320 if (pci_mmcfg_probes[i].vendor == vendor &&
321 pci_mmcfg_probes[i].device == device)
322 name = pci_mmcfg_probes[i].probe();
323 }
324
325 if (name) {
326 printk(KERN_INFO "PCI: Found %s %s MMCONFIG support.\n",
327 name, pci_mmcfg_config_num ? "with" : "without");
328 }
329
330 return name != NULL;
331 }
332
is_mmconf_reserved(u64 addr,u64 size,int i,typeof (pci_mmcfg_config[0])* cfg)333 static int __init is_mmconf_reserved(
334 u64 addr, u64 size, int i,
335 typeof(pci_mmcfg_config[0]) *cfg)
336 {
337 u64 old_size = size;
338 int valid = 0;
339
340 while (!e820_all_mapped(addr, addr + size, E820_RESERVED)) {
341 size >>= 1;
342 if (size < (16UL<<20))
343 break;
344 }
345
346 if (size >= (16UL<<20) || size == old_size) {
347 printk(KERN_NOTICE "PCI: MCFG area at %lx reserved in E820\n", addr);
348 valid = 1;
349
350 if (old_size != size) {
351 /* update end_bus_number */
352 cfg->end_bus_number = cfg->start_bus_number + ((size>>20) - 1);
353 printk(KERN_NOTICE "PCI: updated MCFG configuration %d: base %lx "
354 "segment %hu buses %u - %u\n",
355 i, (unsigned long)cfg->address, cfg->pci_segment,
356 (unsigned int)cfg->start_bus_number,
357 (unsigned int)cfg->end_bus_number);
358 }
359 }
360
361 return valid;
362 }
363
pci_mmcfg_reject_broken(void)364 static bool_t __init pci_mmcfg_reject_broken(void)
365 {
366 typeof(pci_mmcfg_config[0]) *cfg;
367 int i;
368 bool_t valid = 1;
369
370 if ((pci_mmcfg_config_num == 0) ||
371 (pci_mmcfg_config == NULL) ||
372 (pci_mmcfg_config[0].address == 0))
373 return 0;
374
375 for (i = 0; i < pci_mmcfg_config_num; i++) {
376 u64 addr, size;
377
378 cfg = &pci_mmcfg_config[i];
379 addr = cfg->start_bus_number;
380 addr <<= 20;
381 addr += cfg->address;
382 size = cfg->end_bus_number + 1 - cfg->start_bus_number;
383 size <<= 20;
384 printk(KERN_NOTICE "PCI: MCFG configuration %d: base %lx "
385 "segment %04x buses %02x - %02x\n",
386 i, (unsigned long)cfg->address, cfg->pci_segment,
387 (unsigned int)cfg->start_bus_number,
388 (unsigned int)cfg->end_bus_number);
389
390 if (!is_mmconf_reserved(addr, size, i, cfg) ||
391 pci_mmcfg_arch_enable(i)) {
392 pci_mmcfg_arch_disable(i);
393 valid = 0;
394 }
395 }
396
397 return valid;
398 }
399
acpi_mmcfg_init(void)400 void __init acpi_mmcfg_init(void)
401 {
402 bool_t valid = 1;
403
404 pci_segments_init();
405
406 /* MMCONFIG disabled */
407 if ((pci_probe & PCI_PROBE_MMCONF) == 0)
408 return;
409
410 /* MMCONFIG already enabled */
411 if (!(pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF))
412 return;
413
414 if (pci_mmcfg_check_hostbridge()) {
415 unsigned int i;
416
417 pci_mmcfg_arch_init();
418 for (i = 0; i < pci_mmcfg_config_num; ++i)
419 if (pci_mmcfg_arch_enable(i))
420 valid = 0;
421 } else {
422 acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
423 pci_mmcfg_arch_init();
424 valid = pci_mmcfg_reject_broken();
425 }
426
427 if ((pci_mmcfg_config_num == 0) ||
428 (pci_mmcfg_config == NULL) ||
429 (pci_mmcfg_config[0].address == 0))
430 return;
431
432 if (valid)
433 pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
434 }
435
pci_mmcfg_reserved(uint64_t address,unsigned int segment,unsigned int start_bus,unsigned int end_bus,unsigned int flags)436 int pci_mmcfg_reserved(uint64_t address, unsigned int segment,
437 unsigned int start_bus, unsigned int end_bus,
438 unsigned int flags)
439 {
440 unsigned int i;
441
442 if (flags & ~XEN_PCI_MMCFG_RESERVED)
443 return -EINVAL;
444
445 for (i = 0; i < pci_mmcfg_config_num; ++i) {
446 const typeof(pci_mmcfg_config[0]) *cfg = &pci_mmcfg_config[i];
447
448 if (cfg->pci_segment == segment &&
449 cfg->start_bus_number == start_bus &&
450 cfg->end_bus_number == end_bus) {
451 if (cfg->address != address) {
452 printk(KERN_WARNING
453 "Base address presented for segment %04x bus %02x-%02x"
454 " (%08" PRIx64 ") does not match previously obtained"
455 " one (%08" PRIx64 ")\n",
456 segment, start_bus, end_bus, address, cfg->address);
457 return -EIO;
458 }
459 if (flags & XEN_PCI_MMCFG_RESERVED)
460 return pci_mmcfg_arch_enable(i);
461 pci_mmcfg_arch_disable(i);
462 return 0;
463 }
464 }
465
466 return -ENODEV;
467 }
468