diff options
Diffstat (limited to 'arch/x86/mm/ioremap.c')
-rw-r--r-- | arch/x86/mm/ioremap.c | 152 |
1 files changed, 114 insertions, 38 deletions
diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c index 794895c6dcc9..c590fd200e29 100644 --- a/arch/x86/mm/ioremap.c +++ b/arch/x86/mm/ioremap.c | |||
@@ -19,11 +19,7 @@ | |||
19 | #include <asm/pgtable.h> | 19 | #include <asm/pgtable.h> |
20 | #include <asm/tlbflush.h> | 20 | #include <asm/tlbflush.h> |
21 | #include <asm/pgalloc.h> | 21 | #include <asm/pgalloc.h> |
22 | 22 | #include <asm/pat.h> | |
23 | enum ioremap_mode { | ||
24 | IOR_MODE_UNCACHED, | ||
25 | IOR_MODE_CACHED, | ||
26 | }; | ||
27 | 23 | ||
28 | #ifdef CONFIG_X86_64 | 24 | #ifdef CONFIG_X86_64 |
29 | 25 | ||
@@ -35,11 +31,23 @@ unsigned long __phys_addr(unsigned long x) | |||
35 | } | 31 | } |
36 | EXPORT_SYMBOL(__phys_addr); | 32 | EXPORT_SYMBOL(__phys_addr); |
37 | 33 | ||
34 | static inline int phys_addr_valid(unsigned long addr) | ||
35 | { | ||
36 | return addr < (1UL << boot_cpu_data.x86_phys_bits); | ||
37 | } | ||
38 | |||
39 | #else | ||
40 | |||
41 | static inline int phys_addr_valid(unsigned long addr) | ||
42 | { | ||
43 | return 1; | ||
44 | } | ||
45 | |||
38 | #endif | 46 | #endif |
39 | 47 | ||
40 | int page_is_ram(unsigned long pagenr) | 48 | int page_is_ram(unsigned long pagenr) |
41 | { | 49 | { |
42 | unsigned long addr, end; | 50 | resource_size_t addr, end; |
43 | int i; | 51 | int i; |
44 | 52 | ||
45 | /* | 53 | /* |
@@ -78,19 +86,22 @@ int page_is_ram(unsigned long pagenr) | |||
78 | * Fix up the linear direct mapping of the kernel to avoid cache attribute | 86 | * Fix up the linear direct mapping of the kernel to avoid cache attribute |
79 | * conflicts. | 87 | * conflicts. |
80 | */ | 88 | */ |
81 | static int ioremap_change_attr(unsigned long vaddr, unsigned long size, | 89 | int ioremap_change_attr(unsigned long vaddr, unsigned long size, |
82 | enum ioremap_mode mode) | 90 | unsigned long prot_val) |
83 | { | 91 | { |
84 | unsigned long nrpages = size >> PAGE_SHIFT; | 92 | unsigned long nrpages = size >> PAGE_SHIFT; |
85 | int err; | 93 | int err; |
86 | 94 | ||
87 | switch (mode) { | 95 | switch (prot_val) { |
88 | case IOR_MODE_UNCACHED: | 96 | case _PAGE_CACHE_UC: |
89 | default: | 97 | default: |
90 | err = set_memory_uc(vaddr, nrpages); | 98 | err = _set_memory_uc(vaddr, nrpages); |
99 | break; | ||
100 | case _PAGE_CACHE_WC: | ||
101 | err = _set_memory_wc(vaddr, nrpages); | ||
91 | break; | 102 | break; |
92 | case IOR_MODE_CACHED: | 103 | case _PAGE_CACHE_WB: |
93 | err = set_memory_wb(vaddr, nrpages); | 104 | err = _set_memory_wb(vaddr, nrpages); |
94 | break; | 105 | break; |
95 | } | 106 | } |
96 | 107 | ||
@@ -107,17 +118,27 @@ static int ioremap_change_attr(unsigned long vaddr, unsigned long size, | |||
107 | * caller shouldn't need to know that small detail. | 118 | * caller shouldn't need to know that small detail. |
108 | */ | 119 | */ |
109 | static void __iomem *__ioremap(resource_size_t phys_addr, unsigned long size, | 120 | static void __iomem *__ioremap(resource_size_t phys_addr, unsigned long size, |
110 | enum ioremap_mode mode) | 121 | unsigned long prot_val) |
111 | { | 122 | { |
112 | unsigned long pfn, offset, last_addr, vaddr; | 123 | unsigned long pfn, offset, vaddr; |
124 | resource_size_t last_addr; | ||
113 | struct vm_struct *area; | 125 | struct vm_struct *area; |
126 | unsigned long new_prot_val; | ||
114 | pgprot_t prot; | 127 | pgprot_t prot; |
128 | int retval; | ||
115 | 129 | ||
116 | /* Don't allow wraparound or zero size */ | 130 | /* Don't allow wraparound or zero size */ |
117 | last_addr = phys_addr + size - 1; | 131 | last_addr = phys_addr + size - 1; |
118 | if (!size || last_addr < phys_addr) | 132 | if (!size || last_addr < phys_addr) |
119 | return NULL; | 133 | return NULL; |
120 | 134 | ||
135 | if (!phys_addr_valid(phys_addr)) { | ||
136 | printk(KERN_WARNING "ioremap: invalid physical address %llx\n", | ||
137 | phys_addr); | ||
138 | WARN_ON_ONCE(1); | ||
139 | return NULL; | ||
140 | } | ||
141 | |||
121 | /* | 142 | /* |
122 | * Don't remap the low PCI/ISA area, it's always mapped.. | 143 | * Don't remap the low PCI/ISA area, it's always mapped.. |
123 | */ | 144 | */ |
@@ -127,25 +148,14 @@ static void __iomem *__ioremap(resource_size_t phys_addr, unsigned long size, | |||
127 | /* | 148 | /* |
128 | * Don't allow anybody to remap normal RAM that we're using.. | 149 | * Don't allow anybody to remap normal RAM that we're using.. |
129 | */ | 150 | */ |
130 | for (pfn = phys_addr >> PAGE_SHIFT; pfn < max_pfn_mapped && | 151 | for (pfn = phys_addr >> PAGE_SHIFT; |
131 | (pfn << PAGE_SHIFT) < last_addr; pfn++) { | 152 | (pfn << PAGE_SHIFT) < last_addr; pfn++) { |
132 | if (page_is_ram(pfn) && pfn_valid(pfn) && | ||
133 | !PageReserved(pfn_to_page(pfn))) | ||
134 | return NULL; | ||
135 | } | ||
136 | 153 | ||
137 | switch (mode) { | 154 | int is_ram = page_is_ram(pfn); |
138 | case IOR_MODE_UNCACHED: | 155 | |
139 | default: | 156 | if (is_ram && pfn_valid(pfn) && !PageReserved(pfn_to_page(pfn))) |
140 | /* | 157 | return NULL; |
141 | * FIXME: we will use UC MINUS for now, as video fb drivers | 158 | WARN_ON_ONCE(is_ram); |
142 | * depend on it. Upcoming ioremap_wc() will fix this behavior. | ||
143 | */ | ||
144 | prot = PAGE_KERNEL_UC_MINUS; | ||
145 | break; | ||
146 | case IOR_MODE_CACHED: | ||
147 | prot = PAGE_KERNEL; | ||
148 | break; | ||
149 | } | 159 | } |
150 | 160 | ||
151 | /* | 161 | /* |
@@ -155,6 +165,49 @@ static void __iomem *__ioremap(resource_size_t phys_addr, unsigned long size, | |||
155 | phys_addr &= PAGE_MASK; | 165 | phys_addr &= PAGE_MASK; |
156 | size = PAGE_ALIGN(last_addr+1) - phys_addr; | 166 | size = PAGE_ALIGN(last_addr+1) - phys_addr; |
157 | 167 | ||
168 | retval = reserve_memtype(phys_addr, phys_addr + size, | ||
169 | prot_val, &new_prot_val); | ||
170 | if (retval) { | ||
171 | pr_debug("Warning: reserve_memtype returned %d\n", retval); | ||
172 | return NULL; | ||
173 | } | ||
174 | |||
175 | if (prot_val != new_prot_val) { | ||
176 | /* | ||
177 | * Do not fallback to certain memory types with certain | ||
178 | * requested type: | ||
179 | * - request is uncached, return cannot be write-back | ||
180 | * - request is uncached, return cannot be write-combine | ||
181 | * - request is write-combine, return cannot be write-back | ||
182 | */ | ||
183 | if ((prot_val == _PAGE_CACHE_UC && | ||
184 | (new_prot_val == _PAGE_CACHE_WB || | ||
185 | new_prot_val == _PAGE_CACHE_WC)) || | ||
186 | (prot_val == _PAGE_CACHE_WC && | ||
187 | new_prot_val == _PAGE_CACHE_WB)) { | ||
188 | pr_debug( | ||
189 | "ioremap error for 0x%llx-0x%llx, requested 0x%lx, got 0x%lx\n", | ||
190 | phys_addr, phys_addr + size, | ||
191 | prot_val, new_prot_val); | ||
192 | free_memtype(phys_addr, phys_addr + size); | ||
193 | return NULL; | ||
194 | } | ||
195 | prot_val = new_prot_val; | ||
196 | } | ||
197 | |||
198 | switch (prot_val) { | ||
199 | case _PAGE_CACHE_UC: | ||
200 | default: | ||
201 | prot = PAGE_KERNEL_NOCACHE; | ||
202 | break; | ||
203 | case _PAGE_CACHE_WC: | ||
204 | prot = PAGE_KERNEL_WC; | ||
205 | break; | ||
206 | case _PAGE_CACHE_WB: | ||
207 | prot = PAGE_KERNEL; | ||
208 | break; | ||
209 | } | ||
210 | |||
158 | /* | 211 | /* |
159 | * Ok, go for it.. | 212 | * Ok, go for it.. |
160 | */ | 213 | */ |
@@ -164,11 +217,13 @@ static void __iomem *__ioremap(resource_size_t phys_addr, unsigned long size, | |||
164 | area->phys_addr = phys_addr; | 217 | area->phys_addr = phys_addr; |
165 | vaddr = (unsigned long) area->addr; | 218 | vaddr = (unsigned long) area->addr; |
166 | if (ioremap_page_range(vaddr, vaddr + size, phys_addr, prot)) { | 219 | if (ioremap_page_range(vaddr, vaddr + size, phys_addr, prot)) { |
220 | free_memtype(phys_addr, phys_addr + size); | ||
167 | free_vm_area(area); | 221 | free_vm_area(area); |
168 | return NULL; | 222 | return NULL; |
169 | } | 223 | } |
170 | 224 | ||
171 | if (ioremap_change_attr(vaddr, size, mode) < 0) { | 225 | if (ioremap_change_attr(vaddr, size, prot_val) < 0) { |
226 | free_memtype(phys_addr, phys_addr + size); | ||
172 | vunmap(area->addr); | 227 | vunmap(area->addr); |
173 | return NULL; | 228 | return NULL; |
174 | } | 229 | } |
@@ -199,13 +254,32 @@ static void __iomem *__ioremap(resource_size_t phys_addr, unsigned long size, | |||
199 | */ | 254 | */ |
200 | void __iomem *ioremap_nocache(resource_size_t phys_addr, unsigned long size) | 255 | void __iomem *ioremap_nocache(resource_size_t phys_addr, unsigned long size) |
201 | { | 256 | { |
202 | return __ioremap(phys_addr, size, IOR_MODE_UNCACHED); | 257 | return __ioremap(phys_addr, size, _PAGE_CACHE_UC); |
203 | } | 258 | } |
204 | EXPORT_SYMBOL(ioremap_nocache); | 259 | EXPORT_SYMBOL(ioremap_nocache); |
205 | 260 | ||
261 | /** | ||
262 | * ioremap_wc - map memory into CPU space write combined | ||
263 | * @offset: bus address of the memory | ||
264 | * @size: size of the resource to map | ||
265 | * | ||
266 | * This version of ioremap ensures that the memory is marked write combining. | ||
267 | * Write combining allows faster writes to some hardware devices. | ||
268 | * | ||
269 | * Must be freed with iounmap. | ||
270 | */ | ||
271 | void __iomem *ioremap_wc(unsigned long phys_addr, unsigned long size) | ||
272 | { | ||
273 | if (pat_wc_enabled) | ||
274 | return __ioremap(phys_addr, size, _PAGE_CACHE_WC); | ||
275 | else | ||
276 | return ioremap_nocache(phys_addr, size); | ||
277 | } | ||
278 | EXPORT_SYMBOL(ioremap_wc); | ||
279 | |||
206 | void __iomem *ioremap_cache(resource_size_t phys_addr, unsigned long size) | 280 | void __iomem *ioremap_cache(resource_size_t phys_addr, unsigned long size) |
207 | { | 281 | { |
208 | return __ioremap(phys_addr, size, IOR_MODE_CACHED); | 282 | return __ioremap(phys_addr, size, _PAGE_CACHE_WB); |
209 | } | 283 | } |
210 | EXPORT_SYMBOL(ioremap_cache); | 284 | EXPORT_SYMBOL(ioremap_cache); |
211 | 285 | ||
@@ -252,6 +326,8 @@ void iounmap(volatile void __iomem *addr) | |||
252 | return; | 326 | return; |
253 | } | 327 | } |
254 | 328 | ||
329 | free_memtype(p->phys_addr, p->phys_addr + get_vm_area_size(p)); | ||
330 | |||
255 | /* Finally remove it */ | 331 | /* Finally remove it */ |
256 | o = remove_vm_area((void *)addr); | 332 | o = remove_vm_area((void *)addr); |
257 | BUG_ON(p != o || o == NULL); | 333 | BUG_ON(p != o || o == NULL); |
@@ -272,8 +348,8 @@ static int __init early_ioremap_debug_setup(char *str) | |||
272 | early_param("early_ioremap_debug", early_ioremap_debug_setup); | 348 | early_param("early_ioremap_debug", early_ioremap_debug_setup); |
273 | 349 | ||
274 | static __initdata int after_paging_init; | 350 | static __initdata int after_paging_init; |
275 | static __initdata pte_t bm_pte[PAGE_SIZE/sizeof(pte_t)] | 351 | static pte_t bm_pte[PAGE_SIZE/sizeof(pte_t)] |
276 | __attribute__((aligned(PAGE_SIZE))); | 352 | __section(.bss.page_aligned); |
277 | 353 | ||
278 | static inline pmd_t * __init early_ioremap_pmd(unsigned long addr) | 354 | static inline pmd_t * __init early_ioremap_pmd(unsigned long addr) |
279 | { | 355 | { |