aboutsummaryrefslogtreecommitdiffstats
path: root/patches/linux-2.6.16.32/pci-mmconfig-fix-from-2.6.17.patch
blob: e4b709362f6f0a672a878ca6295cc5f6dd61f874 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
diff -pruN ../orig-linux-2.6.16.29/arch/i386/pci/mmconfig.c ./arch/i386/pci/mmconfig.c
--- ../orig-linux-2.6.16.29/arch/i386/pci/mmconfig.c	2006-09-12 19:02:10.000000000 +0100
+++ ./arch/i386/pci/mmconfig.c	2006-09-21 09:35:27.000000000 +0100
@@ -12,14 +12,22 @@
 #include <linux/pci.h>
 #include <linux/init.h>
 #include <linux/acpi.h>
+#include <asm/e820.h>
 #include "pci.h"
 
+/* aperture is up to 256MB but BIOS may reserve less */
+#define MMCONFIG_APER_MIN	(2 * 1024*1024)
+#define MMCONFIG_APER_MAX	(256 * 1024*1024)
+
+/* Assume systems with more busses have correct MCFG */
+#define MAX_CHECK_BUS 16
+
 #define mmcfg_virt_addr ((void __iomem *) fix_to_virt(FIX_PCIE_MCFG))
 
 /* The base address of the last MMCONFIG device accessed */
 static u32 mmcfg_last_accessed_device;
 
-static DECLARE_BITMAP(fallback_slots, 32);
+static DECLARE_BITMAP(fallback_slots, MAX_CHECK_BUS*32);
 
 /*
  * Functions for accessing PCI configuration space with MMCONFIG accesses
@@ -29,8 +37,8 @@ static u32 get_base_addr(unsigned int se
 	int cfg_num = -1;
 	struct acpi_table_mcfg_config *cfg;
 
-	if (seg == 0 && bus == 0 &&
-	    test_bit(PCI_SLOT(devfn), fallback_slots))
+	if (seg == 0 && bus < MAX_CHECK_BUS &&
+	    test_bit(PCI_SLOT(devfn) + 32*bus, fallback_slots))
 		return 0;
 
 	while (1) {
@@ -74,8 +82,10 @@ static int pci_mmcfg_read(unsigned int s
 	unsigned long flags;
 	u32 base;
 
-	if (!value || (bus > 255) || (devfn > 255) || (reg > 4095))
+	if ((bus > 255) || (devfn > 255) || (reg > 4095)) {
+		*value = -1;
 		return -EINVAL;
+	}
 
 	base = get_base_addr(seg, bus, devfn);
 	if (!base)
@@ -146,30 +156,66 @@ static struct pci_raw_ops pci_mmcfg = {
    Normally this can be expressed in the MCFG by not listing them
    and assigning suitable _SEGs, but this isn't implemented in some BIOS.
    Instead try to discover all devices on bus 0 that are unreachable using MM
-   and fallback for them.
-   We only do this for bus 0/seg 0 */
+   and fallback for them. */
 static __init void unreachable_devices(void)
 {
-	int i;
+	int i, k;
 	unsigned long flags;
 
-	for (i = 0; i < 32; i++) {
-		u32 val1;
-		u32 addr;
+	for (k = 0; k < MAX_CHECK_BUS; k++) {
+		for (i = 0; i < 32; i++) {
+			u32 val1;
+			u32 addr;
+
+			pci_conf1_read(0, k, PCI_DEVFN(i, 0), 0, 4, &val1);
+			if (val1 == 0xffffffff)
+				continue;
+
+			/* Locking probably not needed, but safer */
+			spin_lock_irqsave(&pci_config_lock, flags);
+			addr = get_base_addr(0, k, PCI_DEVFN(i, 0));
+			if (addr != 0)
+				pci_exp_set_dev_base(addr, k, PCI_DEVFN(i, 0));
+			if (addr == 0 ||
+			    readl((u32 __iomem *)mmcfg_virt_addr) != val1) {
+				set_bit(i, fallback_slots);
+				printk(KERN_NOTICE
+			"PCI: No mmconfig possible on %x:%x\n", k, i);
+			}
+			spin_unlock_irqrestore(&pci_config_lock, flags);
+		}
+	}
+}
 
-		pci_conf1_read(0, 0, PCI_DEVFN(i, 0), 0, 4, &val1);
-		if (val1 == 0xffffffff)
+/* NB. Ripped from arch/i386/kernel/setup.c for this Xen bugfix patch. */
+#ifdef CONFIG_XEN
+extern struct e820map machine_e820;
+#define e820 machine_e820
+#endif
+static int __init
+e820_all_mapped(unsigned long s, unsigned long e, unsigned type)
+{
+	u64 start = s;
+	u64 end = e;
+	int i;
+	for (i = 0; i < e820.nr_map; i++) {
+		struct e820entry *ei = &e820.map[i];
+		if (type && ei->type != type)
 			continue;
-
-		/* Locking probably not needed, but safer */
-		spin_lock_irqsave(&pci_config_lock, flags);
-		addr = get_base_addr(0, 0, PCI_DEVFN(i, 0));
-		if (addr != 0)
-			pci_exp_set_dev_base(addr, 0, PCI_DEVFN(i, 0));
-		if (addr == 0 || readl((u32 __iomem *)mmcfg_virt_addr) != val1)
-			set_bit(i, fallback_slots);
-		spin_unlock_irqrestore(&pci_config_lock, flags);
+		/* is the region (part) in overlap with the current region ?*/
+		if (ei->addr >= end || ei->addr + ei->size <= start)
+			continue;
+		/* if the region is at the beginning of <start,end> we move
+		 * start to the end of the region since it's ok until there
+		 */
+		if (ei->addr <= start)
+			start = ei->addr + ei->size;
+		/* if start is now at or beyond end, we're done, full
+		 * coverage */
+		if (start >= end)
+			return 1; /* we're done */
 	}
+	return 0;
 }
 
 static int __init pci_mmcfg_init(void)
@@ -183,6 +229,15 @@ static int __init pci_mmcfg_init(void)
 	    (pci_mmcfg_config[0].base_address == 0))
 		goto out;
 
+	if (!e820_all_mapped(pci_mmcfg_config[0].base_address,
+			pci_mmcfg_config[0].base_address + MMCONFIG_APER_MIN,
+			E820_RESERVED)) {
+		printk(KERN_ERR "PCI: BIOS Bug: MCFG area at %x is not E820-reserved\n",
+				pci_mmcfg_config[0].base_address);
+		printk(KERN_ERR "PCI: Not using MMCONFIG.\n");
+		goto out;
+	}
+
 	printk(KERN_INFO "PCI: Using MMCONFIG\n");
 	raw_pci_ops = &pci_mmcfg;
 	pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
diff -pruN ../orig-linux-2.6.16.29/arch/x86_64/pci/mmconfig.c ./arch/x86_64/pci/mmconfig.c
--- ../orig-linux-2.6.16.29/arch/x86_64/pci/mmconfig.c	2006-09-12 19:02:10.000000000 +0100
+++ ./arch/x86_64/pci/mmconfig.c	2006-09-21 09:35:40.000000000 +0100
@@ -9,11 +9,19 @@
 #include <linux/init.h>
 #include <linux/acpi.h>
 #include <linux/bitmap.h>
+#include <asm/e820.h>
+
 #include "pci.h"
 
-#define MMCONFIG_APER_SIZE (256*1024*1024)
+/* aperture is up to 256MB but BIOS may reserve less */
+#define MMCONFIG_APER_MIN	(2 * 1024*1024)
+#define MMCONFIG_APER_MAX	(256 * 1024*1024)
+
+/* Verify the first 16 busses. We assume that systems with more busses
+   get MCFG right. */
+#define MAX_CHECK_BUS 16
 
-static DECLARE_BITMAP(fallback_slots, 32);
+static DECLARE_BITMAP(fallback_slots, 32*MAX_CHECK_BUS);
 
 /* Static virtual mapping of the MMCONFIG aperture */
 struct mmcfg_virt {
@@ -55,7 +63,8 @@ static char __iomem *get_virt(unsigned i
 static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
 {
 	char __iomem *addr;
-	if (seg == 0 && bus == 0 && test_bit(PCI_SLOT(devfn), &fallback_slots))
+	if (seg == 0 && bus < MAX_CHECK_BUS &&
+		test_bit(32*bus + PCI_SLOT(devfn), fallback_slots))
 		return NULL;
 	addr = get_virt(seg, bus);
 	if (!addr)
@@ -69,8 +78,10 @@ static int pci_mmcfg_read(unsigned int s
 	char __iomem *addr;
 
 	/* Why do we have this when nobody checks it. How about a BUG()!? -AK */
-	if (unlikely(!value || (bus > 255) || (devfn > 255) || (reg > 4095)))
+	if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
+		*value = -1;
 		return -EINVAL;
+	}
 
 	addr = pci_dev_base(seg, bus, devfn);
 	if (!addr)
@@ -129,23 +140,56 @@ static struct pci_raw_ops pci_mmcfg = {
    Normally this can be expressed in the MCFG by not listing them
    and assigning suitable _SEGs, but this isn't implemented in some BIOS.
    Instead try to discover all devices on bus 0 that are unreachable using MM
-   and fallback for them.
-   We only do this for bus 0/seg 0 */
+   and fallback for them. */
 static __init void unreachable_devices(void)
 {
-	int i;
-	for (i = 0; i < 32; i++) {
-		u32 val1;
-		char __iomem *addr;
+	int i, k;
+	/* Use the max bus number from ACPI here? */
+	for (k = 0; k < MAX_CHECK_BUS; k++) {
+		for (i = 0; i < 32; i++) {
+			u32 val1;
+			char __iomem *addr;
+
+			pci_conf1_read(0, k, PCI_DEVFN(i,0), 0, 4, &val1);
+			if (val1 == 0xffffffff)
+				continue;
+			addr = pci_dev_base(0, k, PCI_DEVFN(i, 0));
+			if (addr == NULL|| readl(addr) != val1) {
+				set_bit(i + 32*k, fallback_slots);
+				printk(KERN_NOTICE
+				"PCI: No mmconfig possible on device %x:%x\n",
+					k, i);
+			}
+		}
+	}
+}
 
-		pci_conf1_read(0, 0, PCI_DEVFN(i,0), 0, 4, &val1);
-		if (val1 == 0xffffffff)
+/* NB. Ripped from arch/x86_64/kernel/e820.c for this Xen bugfix patch. */
+#ifdef CONFIG_XEN
+extern struct e820map machine_e820;
+#define e820 machine_e820
+#endif
+static int __init e820_all_mapped(unsigned long start, unsigned long end, unsigned type)
+{
+	int i;
+	for (i = 0; i < e820.nr_map; i++) {
+		struct e820entry *ei = &e820.map[i];
+		if (type && ei->type != type)
 			continue;
-		addr = pci_dev_base(0, 0, PCI_DEVFN(i, 0));
-		if (addr == NULL|| readl(addr) != val1) {
-			set_bit(i, &fallback_slots);
-		}
+		/* is the region (part) in overlap with the current region ?*/
+		if (ei->addr >= end || ei->addr + ei->size <= start)
+			continue;
+
+		/* if the region is at the beginning of <start,end> we move
+		 * start to the end of the region since it's ok until there
+		 */
+		if (ei->addr <= start)
+			start = ei->addr + ei->size;
+		/* if start is now at or beyond end, we're done, full coverage */
+		if (start >= end)
+			return 1; /* we're done */
 	}
+	return 0;
 }
 
 static int __init pci_mmcfg_init(void)
@@ -161,6 +205,15 @@ static int __init pci_mmcfg_init(void)
 	    (pci_mmcfg_config[0].base_address == 0))
 		return 0;
 
+	if (!e820_all_mapped(pci_mmcfg_config[0].base_address,
+			pci_mmcfg_config[0].base_address + MMCONFIG_APER_MIN,
+			E820_RESERVED)) {
+		printk(KERN_ERR "PCI: BIOS Bug: MCFG area at %x is not E820-reserved\n",
+				pci_mmcfg_config[0].base_address);
+		printk(KERN_ERR "PCI: Not using MMCONFIG.\n");
+		return 0;
+	}
+
 	/* RED-PEN i386 doesn't do _nocache right now */
 	pci_mmcfg_virt = kmalloc(sizeof(*pci_mmcfg_virt) * pci_mmcfg_config_num, GFP_KERNEL);
 	if (pci_mmcfg_virt == NULL) {
@@ -169,7 +222,8 @@ static int __init pci_mmcfg_init(void)
 	}
 	for (i = 0; i < pci_mmcfg_config_num; ++i) {
 		pci_mmcfg_virt[i].cfg = &pci_mmcfg_config[i];
-		pci_mmcfg_virt[i].virt = ioremap_nocache(pci_mmcfg_config[i].base_address, MMCONFIG_APER_SIZE);
+		pci_mmcfg_virt[i].virt = ioremap_nocache(pci_mmcfg_config[i].base_address,
+							 MMCONFIG_APER_MAX);
 		if (!pci_mmcfg_virt[i].virt) {
 			printk("PCI: Cannot map mmconfig aperture for segment %d\n",
 			       pci_mmcfg_config[i].pci_segment_group_number);