]> git.ipfire.org Git - people/ms/linux.git/blob - arch/um/kernel/tlb.c
Linux-2.6.12-rc2
[people/ms/linux.git] / arch / um / kernel / tlb.c
1 /*
2 * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
3 * Licensed under the GPL
4 */
5
6 #include "linux/mm.h"
7 #include "asm/page.h"
8 #include "asm/pgalloc.h"
9 #include "asm/tlbflush.h"
10 #include "choose-mode.h"
11 #include "mode_kern.h"
12 #include "user_util.h"
13 #include "tlb.h"
14 #include "mem.h"
15 #include "mem_user.h"
16 #include "os.h"
17
18 #define ADD_ROUND(n, inc) (((n) + (inc)) & ~((inc) - 1))
19
20 void fix_range_common(struct mm_struct *mm, unsigned long start_addr,
21 unsigned long end_addr, int force, int data,
22 void (*do_ops)(int, struct host_vm_op *, int))
23 {
24 pgd_t *npgd;
25 pud_t *npud;
26 pmd_t *npmd;
27 pte_t *npte;
28 unsigned long addr, end;
29 int r, w, x;
30 struct host_vm_op ops[16];
31 int op_index = -1, last_op = sizeof(ops) / sizeof(ops[0]) - 1;
32
33 if(mm == NULL) return;
34
35 for(addr = start_addr; addr < end_addr;){
36 npgd = pgd_offset(mm, addr);
37 if(!pgd_present(*npgd)){
38 end = ADD_ROUND(addr, PGDIR_SIZE);
39 if(end > end_addr)
40 end = end_addr;
41 if(force || pgd_newpage(*npgd)){
42 op_index = add_munmap(addr, end - addr, ops,
43 op_index, last_op, data,
44 do_ops);
45 pgd_mkuptodate(*npgd);
46 }
47 addr = end;
48 continue;
49 }
50
51 npud = pud_offset(npgd, addr);
52 if(!pud_present(*npud)){
53 end = ADD_ROUND(addr, PUD_SIZE);
54 if(end > end_addr)
55 end = end_addr;
56 if(force || pud_newpage(*npud)){
57 op_index = add_munmap(addr, end - addr, ops,
58 op_index, last_op, data,
59 do_ops);
60 pud_mkuptodate(*npud);
61 }
62 addr = end;
63 continue;
64 }
65
66 npmd = pmd_offset(npud, addr);
67 if(!pmd_present(*npmd)){
68 end = ADD_ROUND(addr, PMD_SIZE);
69 if(end > end_addr)
70 end = end_addr;
71 if(force || pmd_newpage(*npmd)){
72 op_index = add_munmap(addr, end - addr, ops,
73 op_index, last_op, data,
74 do_ops);
75 pmd_mkuptodate(*npmd);
76 }
77 addr = end;
78 continue;
79 }
80
81 npte = pte_offset_kernel(npmd, addr);
82 r = pte_read(*npte);
83 w = pte_write(*npte);
84 x = pte_exec(*npte);
85 if(!pte_dirty(*npte))
86 w = 0;
87 if(!pte_young(*npte)){
88 r = 0;
89 w = 0;
90 }
91 if(force || pte_newpage(*npte)){
92 if(pte_present(*npte))
93 op_index = add_mmap(addr,
94 pte_val(*npte) & PAGE_MASK,
95 PAGE_SIZE, r, w, x, ops,
96 op_index, last_op, data,
97 do_ops);
98 else op_index = add_munmap(addr, PAGE_SIZE, ops,
99 op_index, last_op, data,
100 do_ops);
101 }
102 else if(pte_newprot(*npte))
103 op_index = add_mprotect(addr, PAGE_SIZE, r, w, x, ops,
104 op_index, last_op, data,
105 do_ops);
106
107 *npte = pte_mkuptodate(*npte);
108 addr += PAGE_SIZE;
109 }
110 (*do_ops)(data, ops, op_index);
111 }
112
113 int flush_tlb_kernel_range_common(unsigned long start, unsigned long end)
114 {
115 struct mm_struct *mm;
116 pgd_t *pgd;
117 pud_t *pud;
118 pmd_t *pmd;
119 pte_t *pte;
120 unsigned long addr, last;
121 int updated = 0, err;
122
123 mm = &init_mm;
124 for(addr = start; addr < end;){
125 pgd = pgd_offset(mm, addr);
126 if(!pgd_present(*pgd)){
127 last = ADD_ROUND(addr, PGDIR_SIZE);
128 if(last > end)
129 last = end;
130 if(pgd_newpage(*pgd)){
131 updated = 1;
132 err = os_unmap_memory((void *) addr,
133 last - addr);
134 if(err < 0)
135 panic("munmap failed, errno = %d\n",
136 -err);
137 }
138 addr = last;
139 continue;
140 }
141
142 pud = pud_offset(pgd, addr);
143 if(!pud_present(*pud)){
144 last = ADD_ROUND(addr, PUD_SIZE);
145 if(last > end)
146 last = end;
147 if(pud_newpage(*pud)){
148 updated = 1;
149 err = os_unmap_memory((void *) addr,
150 last - addr);
151 if(err < 0)
152 panic("munmap failed, errno = %d\n",
153 -err);
154 }
155 addr = last;
156 continue;
157 }
158
159 pmd = pmd_offset(pud, addr);
160 if(!pmd_present(*pmd)){
161 last = ADD_ROUND(addr, PMD_SIZE);
162 if(last > end)
163 last = end;
164 if(pmd_newpage(*pmd)){
165 updated = 1;
166 err = os_unmap_memory((void *) addr,
167 last - addr);
168 if(err < 0)
169 panic("munmap failed, errno = %d\n",
170 -err);
171 }
172 addr = last;
173 continue;
174 }
175
176 pte = pte_offset_kernel(pmd, addr);
177 if(!pte_present(*pte) || pte_newpage(*pte)){
178 updated = 1;
179 err = os_unmap_memory((void *) addr,
180 PAGE_SIZE);
181 if(err < 0)
182 panic("munmap failed, errno = %d\n",
183 -err);
184 if(pte_present(*pte))
185 map_memory(addr,
186 pte_val(*pte) & PAGE_MASK,
187 PAGE_SIZE, 1, 1, 1);
188 }
189 else if(pte_newprot(*pte)){
190 updated = 1;
191 protect_memory(addr, PAGE_SIZE, 1, 1, 1, 1);
192 }
193 addr += PAGE_SIZE;
194 }
195 return(updated);
196 }
197
198 void flush_tlb_page(struct vm_area_struct *vma, unsigned long address)
199 {
200 address &= PAGE_MASK;
201 flush_tlb_range(vma, address, address + PAGE_SIZE);
202 }
203
204 void flush_tlb_all(void)
205 {
206 flush_tlb_mm(current->mm);
207 }
208
209 void flush_tlb_kernel_range(unsigned long start, unsigned long end)
210 {
211 CHOOSE_MODE_PROC(flush_tlb_kernel_range_tt,
212 flush_tlb_kernel_range_common, start, end);
213 }
214
215 void flush_tlb_kernel_vm(void)
216 {
217 CHOOSE_MODE(flush_tlb_kernel_vm_tt(),
218 flush_tlb_kernel_range_common(start_vm, end_vm));
219 }
220
221 void __flush_tlb_one(unsigned long addr)
222 {
223 CHOOSE_MODE_PROC(__flush_tlb_one_tt, __flush_tlb_one_skas, addr);
224 }
225
226 void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
227 unsigned long end)
228 {
229 CHOOSE_MODE_PROC(flush_tlb_range_tt, flush_tlb_range_skas, vma, start,
230 end);
231 }
232
233 void flush_tlb_mm(struct mm_struct *mm)
234 {
235 CHOOSE_MODE_PROC(flush_tlb_mm_tt, flush_tlb_mm_skas, mm);
236 }
237
238 void force_flush_all(void)
239 {
240 CHOOSE_MODE(force_flush_all_tt(), force_flush_all_skas());
241 }
242
243 pgd_t *pgd_offset_proc(struct mm_struct *mm, unsigned long address)
244 {
245 return(pgd_offset(mm, address));
246 }
247
248 pud_t *pud_offset_proc(pgd_t *pgd, unsigned long address)
249 {
250 return(pud_offset(pgd, address));
251 }
252
253 pmd_t *pmd_offset_proc(pud_t *pud, unsigned long address)
254 {
255 return(pmd_offset(pud, address));
256 }
257
258 pte_t *pte_offset_proc(pmd_t *pmd, unsigned long address)
259 {
260 return(pte_offset_kernel(pmd, address));
261 }
262
263 pte_t *addr_pte(struct task_struct *task, unsigned long addr)
264 {
265 pgd_t *pgd = pgd_offset(task->mm, addr);
266 pud_t *pud = pud_offset(pgd, addr);
267 pmd_t *pmd = pmd_offset(pud, addr);
268
269 return(pte_offset_map(pmd, addr));
270 }
271
272 int add_mmap(unsigned long virt, unsigned long phys, unsigned long len,
273 int r, int w, int x, struct host_vm_op *ops, int index,
274 int last_filled, int data,
275 void (*do_ops)(int, struct host_vm_op *, int))
276 {
277 __u64 offset;
278 struct host_vm_op *last;
279 int fd;
280
281 fd = phys_mapping(phys, &offset);
282 if(index != -1){
283 last = &ops[index];
284 if((last->type == MMAP) &&
285 (last->u.mmap.addr + last->u.mmap.len == virt) &&
286 (last->u.mmap.r == r) && (last->u.mmap.w == w) &&
287 (last->u.mmap.x == x) && (last->u.mmap.fd == fd) &&
288 (last->u.mmap.offset + last->u.mmap.len == offset)){
289 last->u.mmap.len += len;
290 return(index);
291 }
292 }
293
294 if(index == last_filled){
295 (*do_ops)(data, ops, last_filled);
296 index = -1;
297 }
298
299 ops[++index] = ((struct host_vm_op) { .type = MMAP,
300 .u = { .mmap = {
301 .addr = virt,
302 .len = len,
303 .r = r,
304 .w = w,
305 .x = x,
306 .fd = fd,
307 .offset = offset }
308 } });
309 return(index);
310 }
311
312 int add_munmap(unsigned long addr, unsigned long len, struct host_vm_op *ops,
313 int index, int last_filled, int data,
314 void (*do_ops)(int, struct host_vm_op *, int))
315 {
316 struct host_vm_op *last;
317
318 if(index != -1){
319 last = &ops[index];
320 if((last->type == MUNMAP) &&
321 (last->u.munmap.addr + last->u.mmap.len == addr)){
322 last->u.munmap.len += len;
323 return(index);
324 }
325 }
326
327 if(index == last_filled){
328 (*do_ops)(data, ops, last_filled);
329 index = -1;
330 }
331
332 ops[++index] = ((struct host_vm_op) { .type = MUNMAP,
333 .u = { .munmap = {
334 .addr = addr,
335 .len = len } } });
336 return(index);
337 }
338
339 int add_mprotect(unsigned long addr, unsigned long len, int r, int w, int x,
340 struct host_vm_op *ops, int index, int last_filled, int data,
341 void (*do_ops)(int, struct host_vm_op *, int))
342 {
343 struct host_vm_op *last;
344
345 if(index != -1){
346 last = &ops[index];
347 if((last->type == MPROTECT) &&
348 (last->u.mprotect.addr + last->u.mprotect.len == addr) &&
349 (last->u.mprotect.r == r) && (last->u.mprotect.w == w) &&
350 (last->u.mprotect.x == x)){
351 last->u.mprotect.len += len;
352 return(index);
353 }
354 }
355
356 if(index == last_filled){
357 (*do_ops)(data, ops, last_filled);
358 index = -1;
359 }
360
361 ops[++index] = ((struct host_vm_op) { .type = MPROTECT,
362 .u = { .mprotect = {
363 .addr = addr,
364 .len = len,
365 .r = r,
366 .w = w,
367 .x = x } } });
368 return(index);
369 }