vserver 2.0 rc7
[linux-2.6.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 }