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