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