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