Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * Re-map IO memory to kernel address space so that we can access it.
4 : : * This is needed for high PCI addresses that aren't mapped in the
5 : : * 640k-1MB IO memory area on PC's
6 : : *
7 : : * (C) Copyright 1995 1996 Linus Torvalds
8 : : */
9 : : #include <linux/vmalloc.h>
10 : : #include <linux/mm.h>
11 : : #include <linux/sched.h>
12 : : #include <linux/io.h>
13 : : #include <linux/export.h>
14 : : #include <asm/cacheflush.h>
15 : : #include <asm/pgtable.h>
16 : :
17 : : #ifdef CONFIG_HAVE_ARCH_HUGE_VMAP
18 : : static int __read_mostly ioremap_p4d_capable;
19 : : static int __read_mostly ioremap_pud_capable;
20 : : static int __read_mostly ioremap_pmd_capable;
21 : : static int __read_mostly ioremap_huge_disabled;
22 : :
23 : 0 : static int __init set_nohugeiomap(char *str)
24 : : {
25 : 0 : ioremap_huge_disabled = 1;
26 : 0 : return 0;
27 : : }
28 : : early_param("nohugeiomap", set_nohugeiomap);
29 : :
30 : 3 : void __init ioremap_huge_init(void)
31 : : {
32 [ + - ]: 3 : if (!ioremap_huge_disabled) {
33 [ - + ]: 3 : if (arch_ioremap_p4d_supported())
34 : 0 : ioremap_p4d_capable = 1;
35 [ - + ]: 3 : if (arch_ioremap_pud_supported())
36 : 0 : ioremap_pud_capable = 1;
37 [ + - ]: 3 : if (arch_ioremap_pmd_supported())
38 : 3 : ioremap_pmd_capable = 1;
39 : : }
40 : 3 : }
41 : :
42 : 24 : static inline int ioremap_p4d_enabled(void)
43 : : {
44 : 24 : return ioremap_p4d_capable;
45 : : }
46 : :
47 : 24 : static inline int ioremap_pud_enabled(void)
48 : : {
49 : 24 : return ioremap_pud_capable;
50 : : }
51 : :
52 : 24 : static inline int ioremap_pmd_enabled(void)
53 : : {
54 : 24 : return ioremap_pmd_capable;
55 : : }
56 : :
57 : : #else /* !CONFIG_HAVE_ARCH_HUGE_VMAP */
58 : : static inline int ioremap_p4d_enabled(void) { return 0; }
59 : : static inline int ioremap_pud_enabled(void) { return 0; }
60 : : static inline int ioremap_pmd_enabled(void) { return 0; }
61 : : #endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
62 : :
63 : 24 : static int ioremap_pte_range(pmd_t *pmd, unsigned long addr,
64 : : unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
65 : : {
66 : 24 : pte_t *pte;
67 : 24 : u64 pfn;
68 : :
69 : 24 : pfn = phys_addr >> PAGE_SHIFT;
70 [ + + + - ]: 24 : pte = pte_alloc_kernel(pmd, addr);
71 [ - + ]: 24 : if (!pte)
72 : 0 : return -ENOMEM;
73 : 72 : do {
74 [ - + ]: 72 : BUG_ON(!pte_none(*pte));
75 [ + - ]: 72 : set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot));
76 : 72 : pfn++;
77 [ + + ]: 72 : } while (pte++, addr += PAGE_SIZE, addr != end);
78 : : return 0;
79 : : }
80 : :
81 : 24 : static int ioremap_try_huge_pmd(pmd_t *pmd, unsigned long addr,
82 : : unsigned long end, phys_addr_t phys_addr,
83 : : pgprot_t prot)
84 : : {
85 [ + - ]: 24 : if (!ioremap_pmd_enabled())
86 : : return 0;
87 : :
88 [ - + ]: 24 : if ((end - addr) != PMD_SIZE)
89 : : return 0;
90 : :
91 [ # # ]: 0 : if (!IS_ALIGNED(addr, PMD_SIZE))
92 : : return 0;
93 : :
94 [ # # ]: 0 : if (!IS_ALIGNED(phys_addr, PMD_SIZE))
95 : : return 0;
96 : :
97 [ # # # # : 0 : if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
# # ]
98 : : return 0;
99 : :
100 : 0 : return pmd_set_huge(pmd, phys_addr, prot);
101 : : }
102 : :
103 : 24 : static inline int ioremap_pmd_range(pud_t *pud, unsigned long addr,
104 : : unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
105 : : {
106 : 24 : pmd_t *pmd;
107 : 24 : unsigned long next;
108 : :
109 : 24 : pmd = pmd_alloc(&init_mm, pud, addr);
110 [ + - ]: 24 : if (!pmd)
111 : : return -ENOMEM;
112 : 24 : do {
113 [ - + ]: 24 : next = pmd_addr_end(addr, end);
114 : :
115 [ - + ]: 24 : if (ioremap_try_huge_pmd(pmd, addr, next, phys_addr, prot))
116 : 0 : continue;
117 : :
118 [ + - ]: 24 : if (ioremap_pte_range(pmd, addr, next, phys_addr, prot))
119 : : return -ENOMEM;
120 [ - + ]: 24 : } while (pmd++, phys_addr += (next - addr), addr = next, addr != end);
121 : : return 0;
122 : : }
123 : :
124 : 24 : static int ioremap_try_huge_pud(pud_t *pud, unsigned long addr,
125 : : unsigned long end, phys_addr_t phys_addr,
126 : : pgprot_t prot)
127 : : {
128 [ - + ]: 24 : if (!ioremap_pud_enabled())
129 : : return 0;
130 : :
131 [ # # ]: 0 : if ((end - addr) != PUD_SIZE)
132 : : return 0;
133 : :
134 [ # # ]: 0 : if (!IS_ALIGNED(addr, PUD_SIZE))
135 : : return 0;
136 : :
137 [ # # ]: 0 : if (!IS_ALIGNED(phys_addr, PUD_SIZE))
138 : : return 0;
139 : :
140 [ # # # # : 0 : if (pud_present(*pud) && !pud_free_pmd_page(pud, addr))
# # ]
141 : : return 0;
142 : :
143 : 0 : return pud_set_huge(pud, phys_addr, prot);
144 : : }
145 : :
146 : 24 : static inline int ioremap_pud_range(p4d_t *p4d, unsigned long addr,
147 : : unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
148 : : {
149 : 24 : pud_t *pud;
150 : 24 : unsigned long next;
151 : :
152 : 24 : pud = pud_alloc(&init_mm, p4d, addr);
153 [ + - ]: 24 : if (!pud)
154 : : return -ENOMEM;
155 : 24 : do {
156 [ - + ]: 24 : next = pud_addr_end(addr, end);
157 : :
158 [ - + ]: 24 : if (ioremap_try_huge_pud(pud, addr, next, phys_addr, prot))
159 : 0 : continue;
160 : :
161 [ + - ]: 24 : if (ioremap_pmd_range(pud, addr, next, phys_addr, prot))
162 : : return -ENOMEM;
163 [ - + ]: 24 : } while (pud++, phys_addr += (next - addr), addr = next, addr != end);
164 : : return 0;
165 : : }
166 : :
167 : 24 : static int ioremap_try_huge_p4d(p4d_t *p4d, unsigned long addr,
168 : : unsigned long end, phys_addr_t phys_addr,
169 : : pgprot_t prot)
170 : : {
171 [ - + ]: 24 : if (!ioremap_p4d_enabled())
172 : : return 0;
173 : :
174 [ # # ]: 0 : if ((end - addr) != P4D_SIZE)
175 : : return 0;
176 : :
177 [ # # ]: 0 : if (!IS_ALIGNED(addr, P4D_SIZE))
178 : : return 0;
179 : :
180 [ # # ]: 0 : if (!IS_ALIGNED(phys_addr, P4D_SIZE))
181 : : return 0;
182 : :
183 [ # # # # ]: 0 : if (p4d_present(*p4d) && !p4d_free_pud_page(p4d, addr))
184 : : return 0;
185 : :
186 : 0 : return p4d_set_huge(p4d, phys_addr, prot);
187 : : }
188 : :
189 : 24 : static inline int ioremap_p4d_range(pgd_t *pgd, unsigned long addr,
190 : : unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
191 : : {
192 : 24 : p4d_t *p4d;
193 : 24 : unsigned long next;
194 : :
195 : 24 : p4d = p4d_alloc(&init_mm, pgd, addr);
196 [ + - ]: 24 : if (!p4d)
197 : : return -ENOMEM;
198 : 24 : do {
199 [ - + ]: 24 : next = p4d_addr_end(addr, end);
200 : :
201 [ - + ]: 24 : if (ioremap_try_huge_p4d(p4d, addr, next, phys_addr, prot))
202 : 0 : continue;
203 : :
204 [ + - ]: 24 : if (ioremap_pud_range(p4d, addr, next, phys_addr, prot))
205 : : return -ENOMEM;
206 [ - + ]: 24 : } while (p4d++, phys_addr += (next - addr), addr = next, addr != end);
207 : : return 0;
208 : : }
209 : :
210 : 24 : int ioremap_page_range(unsigned long addr,
211 : : unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
212 : : {
213 : 24 : pgd_t *pgd;
214 : 24 : unsigned long start;
215 : 24 : unsigned long next;
216 : 24 : int err;
217 : :
218 : 24 : might_sleep();
219 [ - + ]: 24 : BUG_ON(addr >= end);
220 : :
221 : 24 : start = addr;
222 : 24 : pgd = pgd_offset_k(addr);
223 : 24 : do {
224 [ + - ]: 24 : next = pgd_addr_end(addr, end);
225 : 24 : err = ioremap_p4d_range(pgd, addr, next, phys_addr, prot);
226 [ + - ]: 24 : if (err)
227 : : break;
228 [ - + ]: 24 : } while (pgd++, phys_addr += (next - addr), addr = next, addr != end);
229 : :
230 : 24 : flush_cache_vmap(start, end);
231 : :
232 : 24 : return err;
233 : : }
234 : :
235 : : #ifdef CONFIG_GENERIC_IOREMAP
236 : : void __iomem *ioremap_prot(phys_addr_t addr, size_t size, unsigned long prot)
237 : : {
238 : : unsigned long offset, vaddr;
239 : : phys_addr_t last_addr;
240 : : struct vm_struct *area;
241 : :
242 : : /* Disallow wrap-around or zero size */
243 : : last_addr = addr + size - 1;
244 : : if (!size || last_addr < addr)
245 : : return NULL;
246 : :
247 : : /* Page-align mappings */
248 : : offset = addr & (~PAGE_MASK);
249 : : addr -= offset;
250 : : size = PAGE_ALIGN(size + offset);
251 : :
252 : : area = get_vm_area_caller(size, VM_IOREMAP,
253 : : __builtin_return_address(0));
254 : : if (!area)
255 : : return NULL;
256 : : vaddr = (unsigned long)area->addr;
257 : :
258 : : if (ioremap_page_range(vaddr, vaddr + size, addr, __pgprot(prot))) {
259 : : free_vm_area(area);
260 : : return NULL;
261 : : }
262 : :
263 : : return (void __iomem *)(vaddr + offset);
264 : : }
265 : : EXPORT_SYMBOL(ioremap_prot);
266 : :
267 : : void iounmap(volatile void __iomem *addr)
268 : : {
269 : : vunmap((void *)((unsigned long)addr & PAGE_MASK));
270 : : }
271 : : EXPORT_SYMBOL(iounmap);
272 : : #endif /* CONFIG_GENERIC_IOREMAP */
|