diff options
Diffstat (limited to 'arch/x86/kernel/aperture_64.c')
-rw-r--r-- | arch/x86/kernel/aperture_64.c | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/arch/x86/kernel/aperture_64.c b/arch/x86/kernel/aperture_64.c new file mode 100644 index 000000000000..8f681cae7bf7 --- /dev/null +++ b/arch/x86/kernel/aperture_64.c | |||
@@ -0,0 +1,298 @@ | |||
1 | /* | ||
2 | * Firmware replacement code. | ||
3 | * | ||
4 | * Work around broken BIOSes that don't set an aperture or only set the | ||
5 | * aperture in the AGP bridge. | ||
6 | * If all fails map the aperture over some low memory. This is cheaper than | ||
7 | * doing bounce buffering. The memory is lost. This is done at early boot | ||
8 | * because only the bootmem allocator can allocate 32+MB. | ||
9 | * | ||
10 | * Copyright 2002 Andi Kleen, SuSE Labs. | ||
11 | */ | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/bootmem.h> | ||
16 | #include <linux/mmzone.h> | ||
17 | #include <linux/pci_ids.h> | ||
18 | #include <linux/pci.h> | ||
19 | #include <linux/bitops.h> | ||
20 | #include <linux/ioport.h> | ||
21 | #include <asm/e820.h> | ||
22 | #include <asm/io.h> | ||
23 | #include <asm/iommu.h> | ||
24 | #include <asm/pci-direct.h> | ||
25 | #include <asm/dma.h> | ||
26 | #include <asm/k8.h> | ||
27 | |||
28 | int iommu_aperture; | ||
29 | int iommu_aperture_disabled __initdata = 0; | ||
30 | int iommu_aperture_allowed __initdata = 0; | ||
31 | |||
32 | int fallback_aper_order __initdata = 1; /* 64MB */ | ||
33 | int fallback_aper_force __initdata = 0; | ||
34 | |||
35 | int fix_aperture __initdata = 1; | ||
36 | |||
37 | static struct resource gart_resource = { | ||
38 | .name = "GART", | ||
39 | .flags = IORESOURCE_MEM, | ||
40 | }; | ||
41 | |||
42 | static void __init insert_aperture_resource(u32 aper_base, u32 aper_size) | ||
43 | { | ||
44 | gart_resource.start = aper_base; | ||
45 | gart_resource.end = aper_base + aper_size - 1; | ||
46 | insert_resource(&iomem_resource, &gart_resource); | ||
47 | } | ||
48 | |||
49 | /* This code runs before the PCI subsystem is initialized, so just | ||
50 | access the northbridge directly. */ | ||
51 | |||
52 | static u32 __init allocate_aperture(void) | ||
53 | { | ||
54 | u32 aper_size; | ||
55 | void *p; | ||
56 | |||
57 | if (fallback_aper_order > 7) | ||
58 | fallback_aper_order = 7; | ||
59 | aper_size = (32 * 1024 * 1024) << fallback_aper_order; | ||
60 | |||
61 | /* | ||
62 | * Aperture has to be naturally aligned. This means an 2GB aperture won't | ||
63 | * have much chance of finding a place in the lower 4GB of memory. | ||
64 | * Unfortunately we cannot move it up because that would make the | ||
65 | * IOMMU useless. | ||
66 | */ | ||
67 | p = __alloc_bootmem_nopanic(aper_size, aper_size, 0); | ||
68 | if (!p || __pa(p)+aper_size > 0xffffffff) { | ||
69 | printk("Cannot allocate aperture memory hole (%p,%uK)\n", | ||
70 | p, aper_size>>10); | ||
71 | if (p) | ||
72 | free_bootmem(__pa(p), aper_size); | ||
73 | return 0; | ||
74 | } | ||
75 | printk("Mapping aperture over %d KB of RAM @ %lx\n", | ||
76 | aper_size >> 10, __pa(p)); | ||
77 | insert_aperture_resource((u32)__pa(p), aper_size); | ||
78 | return (u32)__pa(p); | ||
79 | } | ||
80 | |||
81 | static int __init aperture_valid(u64 aper_base, u32 aper_size) | ||
82 | { | ||
83 | if (!aper_base) | ||
84 | return 0; | ||
85 | if (aper_size < 64*1024*1024) { | ||
86 | printk("Aperture too small (%d MB)\n", aper_size>>20); | ||
87 | return 0; | ||
88 | } | ||
89 | if (aper_base + aper_size > 0x100000000UL) { | ||
90 | printk("Aperture beyond 4GB. Ignoring.\n"); | ||
91 | return 0; | ||
92 | } | ||
93 | if (e820_any_mapped(aper_base, aper_base + aper_size, E820_RAM)) { | ||
94 | printk("Aperture pointing to e820 RAM. Ignoring.\n"); | ||
95 | return 0; | ||
96 | } | ||
97 | return 1; | ||
98 | } | ||
99 | |||
100 | /* Find a PCI capability */ | ||
101 | static __u32 __init find_cap(int num, int slot, int func, int cap) | ||
102 | { | ||
103 | u8 pos; | ||
104 | int bytes; | ||
105 | if (!(read_pci_config_16(num,slot,func,PCI_STATUS) & PCI_STATUS_CAP_LIST)) | ||
106 | return 0; | ||
107 | pos = read_pci_config_byte(num,slot,func,PCI_CAPABILITY_LIST); | ||
108 | for (bytes = 0; bytes < 48 && pos >= 0x40; bytes++) { | ||
109 | u8 id; | ||
110 | pos &= ~3; | ||
111 | id = read_pci_config_byte(num,slot,func,pos+PCI_CAP_LIST_ID); | ||
112 | if (id == 0xff) | ||
113 | break; | ||
114 | if (id == cap) | ||
115 | return pos; | ||
116 | pos = read_pci_config_byte(num,slot,func,pos+PCI_CAP_LIST_NEXT); | ||
117 | } | ||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | /* Read a standard AGPv3 bridge header */ | ||
122 | static __u32 __init read_agp(int num, int slot, int func, int cap, u32 *order) | ||
123 | { | ||
124 | u32 apsize; | ||
125 | u32 apsizereg; | ||
126 | int nbits; | ||
127 | u32 aper_low, aper_hi; | ||
128 | u64 aper; | ||
129 | |||
130 | printk("AGP bridge at %02x:%02x:%02x\n", num, slot, func); | ||
131 | apsizereg = read_pci_config_16(num,slot,func, cap + 0x14); | ||
132 | if (apsizereg == 0xffffffff) { | ||
133 | printk("APSIZE in AGP bridge unreadable\n"); | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | apsize = apsizereg & 0xfff; | ||
138 | /* Some BIOS use weird encodings not in the AGPv3 table. */ | ||
139 | if (apsize & 0xff) | ||
140 | apsize |= 0xf00; | ||
141 | nbits = hweight16(apsize); | ||
142 | *order = 7 - nbits; | ||
143 | if ((int)*order < 0) /* < 32MB */ | ||
144 | *order = 0; | ||
145 | |||
146 | aper_low = read_pci_config(num,slot,func, 0x10); | ||
147 | aper_hi = read_pci_config(num,slot,func,0x14); | ||
148 | aper = (aper_low & ~((1<<22)-1)) | ((u64)aper_hi << 32); | ||
149 | |||
150 | printk("Aperture from AGP @ %Lx size %u MB (APSIZE %x)\n", | ||
151 | aper, 32 << *order, apsizereg); | ||
152 | |||
153 | if (!aperture_valid(aper, (32*1024*1024) << *order)) | ||
154 | return 0; | ||
155 | return (u32)aper; | ||
156 | } | ||
157 | |||
158 | /* Look for an AGP bridge. Windows only expects the aperture in the | ||
159 | AGP bridge and some BIOS forget to initialize the Northbridge too. | ||
160 | Work around this here. | ||
161 | |||
162 | Do an PCI bus scan by hand because we're running before the PCI | ||
163 | subsystem. | ||
164 | |||
165 | All K8 AGP bridges are AGPv3 compliant, so we can do this scan | ||
166 | generically. It's probably overkill to always scan all slots because | ||
167 | the AGP bridges should be always an own bus on the HT hierarchy, | ||
168 | but do it here for future safety. */ | ||
169 | static __u32 __init search_agp_bridge(u32 *order, int *valid_agp) | ||
170 | { | ||
171 | int num, slot, func; | ||
172 | |||
173 | /* Poor man's PCI discovery */ | ||
174 | for (num = 0; num < 256; num++) { | ||
175 | for (slot = 0; slot < 32; slot++) { | ||
176 | for (func = 0; func < 8; func++) { | ||
177 | u32 class, cap; | ||
178 | u8 type; | ||
179 | class = read_pci_config(num,slot,func, | ||
180 | PCI_CLASS_REVISION); | ||
181 | if (class == 0xffffffff) | ||
182 | break; | ||
183 | |||
184 | switch (class >> 16) { | ||
185 | case PCI_CLASS_BRIDGE_HOST: | ||
186 | case PCI_CLASS_BRIDGE_OTHER: /* needed? */ | ||
187 | /* AGP bridge? */ | ||
188 | cap = find_cap(num,slot,func,PCI_CAP_ID_AGP); | ||
189 | if (!cap) | ||
190 | break; | ||
191 | *valid_agp = 1; | ||
192 | return read_agp(num,slot,func,cap,order); | ||
193 | } | ||
194 | |||
195 | /* No multi-function device? */ | ||
196 | type = read_pci_config_byte(num,slot,func, | ||
197 | PCI_HEADER_TYPE); | ||
198 | if (!(type & 0x80)) | ||
199 | break; | ||
200 | } | ||
201 | } | ||
202 | } | ||
203 | printk("No AGP bridge found\n"); | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | void __init iommu_hole_init(void) | ||
208 | { | ||
209 | int fix, num; | ||
210 | u32 aper_size, aper_alloc = 0, aper_order = 0, last_aper_order = 0; | ||
211 | u64 aper_base, last_aper_base = 0; | ||
212 | int valid_agp = 0; | ||
213 | |||
214 | if (iommu_aperture_disabled || !fix_aperture || !early_pci_allowed()) | ||
215 | return; | ||
216 | |||
217 | printk(KERN_INFO "Checking aperture...\n"); | ||
218 | |||
219 | fix = 0; | ||
220 | for (num = 24; num < 32; num++) { | ||
221 | if (!early_is_k8_nb(read_pci_config(0, num, 3, 0x00))) | ||
222 | continue; | ||
223 | |||
224 | iommu_detected = 1; | ||
225 | iommu_aperture = 1; | ||
226 | |||
227 | aper_order = (read_pci_config(0, num, 3, 0x90) >> 1) & 7; | ||
228 | aper_size = (32 * 1024 * 1024) << aper_order; | ||
229 | aper_base = read_pci_config(0, num, 3, 0x94) & 0x7fff; | ||
230 | aper_base <<= 25; | ||
231 | |||
232 | printk("CPU %d: aperture @ %Lx size %u MB\n", num-24, | ||
233 | aper_base, aper_size>>20); | ||
234 | |||
235 | if (!aperture_valid(aper_base, aper_size)) { | ||
236 | fix = 1; | ||
237 | break; | ||
238 | } | ||
239 | |||
240 | if ((last_aper_order && aper_order != last_aper_order) || | ||
241 | (last_aper_base && aper_base != last_aper_base)) { | ||
242 | fix = 1; | ||
243 | break; | ||
244 | } | ||
245 | last_aper_order = aper_order; | ||
246 | last_aper_base = aper_base; | ||
247 | } | ||
248 | |||
249 | if (!fix && !fallback_aper_force) { | ||
250 | if (last_aper_base) { | ||
251 | unsigned long n = (32 * 1024 * 1024) << last_aper_order; | ||
252 | insert_aperture_resource((u32)last_aper_base, n); | ||
253 | } | ||
254 | return; | ||
255 | } | ||
256 | |||
257 | if (!fallback_aper_force) | ||
258 | aper_alloc = search_agp_bridge(&aper_order, &valid_agp); | ||
259 | |||
260 | if (aper_alloc) { | ||
261 | /* Got the aperture from the AGP bridge */ | ||
262 | } else if (swiotlb && !valid_agp) { | ||
263 | /* Do nothing */ | ||
264 | } else if ((!no_iommu && end_pfn > MAX_DMA32_PFN) || | ||
265 | force_iommu || | ||
266 | valid_agp || | ||
267 | fallback_aper_force) { | ||
268 | printk("Your BIOS doesn't leave a aperture memory hole\n"); | ||
269 | printk("Please enable the IOMMU option in the BIOS setup\n"); | ||
270 | printk("This costs you %d MB of RAM\n", | ||
271 | 32 << fallback_aper_order); | ||
272 | |||
273 | aper_order = fallback_aper_order; | ||
274 | aper_alloc = allocate_aperture(); | ||
275 | if (!aper_alloc) { | ||
276 | /* Could disable AGP and IOMMU here, but it's probably | ||
277 | not worth it. But the later users cannot deal with | ||
278 | bad apertures and turning on the aperture over memory | ||
279 | causes very strange problems, so it's better to | ||
280 | panic early. */ | ||
281 | panic("Not enough memory for aperture"); | ||
282 | } | ||
283 | } else { | ||
284 | return; | ||
285 | } | ||
286 | |||
287 | /* Fix up the north bridges */ | ||
288 | for (num = 24; num < 32; num++) { | ||
289 | if (!early_is_k8_nb(read_pci_config(0, num, 3, 0x00))) | ||
290 | continue; | ||
291 | |||
292 | /* Don't enable translation yet. That is done later. | ||
293 | Assume this BIOS didn't initialise the GART so | ||
294 | just overwrite all previous bits */ | ||
295 | write_pci_config(0, num, 3, 0x90, aper_order<<1); | ||
296 | write_pci_config(0, num, 3, 0x94, aper_alloc>>25); | ||
297 | } | ||
298 | } | ||