uml: style fixes pass 3
[pandora-kernel.git] / arch / um / kernel / tlb.c
1 /*
2  * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
3  * Licensed under the GPL
4  */
5
6 #include "linux/mm.h"
7 #include "asm/pgtable.h"
8 #include "asm/tlbflush.h"
9 #include "as-layout.h"
10 #include "mem_user.h"
11 #include "os.h"
12 #include "skas.h"
13 #include "tlb.h"
14
15 static int add_mmap(unsigned long virt, unsigned long phys, unsigned long len,
16                     unsigned int prot, struct host_vm_op *ops, int *index,
17                     int last_filled, union mm_context *mmu, void **flush,
18                     int (*do_ops)(union mm_context *, struct host_vm_op *,
19                                   int, int, void **))
20 {
21         __u64 offset;
22         struct host_vm_op *last;
23         int fd, ret = 0;
24
25         fd = phys_mapping(phys, &offset);
26         if (*index != -1) {
27                 last = &ops[*index];
28                 if ((last->type == MMAP) &&
29                    (last->u.mmap.addr + last->u.mmap.len == virt) &&
30                    (last->u.mmap.prot == prot) && (last->u.mmap.fd == fd) &&
31                    (last->u.mmap.offset + last->u.mmap.len == offset)) {
32                         last->u.mmap.len += len;
33                         return 0;
34                 }
35         }
36
37         if (*index == last_filled) {
38                 ret = (*do_ops)(mmu, ops, last_filled, 0, flush);
39                 *index = -1;
40         }
41
42         ops[++*index] = ((struct host_vm_op) { .type    = MMAP,
43                                                 .u = { .mmap = {
44                                                        .addr    = virt,
45                                                        .len     = len,
46                                                        .prot    = prot,
47                                                        .fd      = fd,
48                                                        .offset  = offset }
49                            } });
50         return ret;
51 }
52
53 static int add_munmap(unsigned long addr, unsigned long len,
54                       struct host_vm_op *ops, int *index, int last_filled,
55                       union mm_context *mmu, void **flush,
56                       int (*do_ops)(union mm_context *, struct host_vm_op *,
57                                     int, int, void **))
58 {
59         struct host_vm_op *last;
60         int ret = 0;
61
62         if (*index != -1) {
63                 last = &ops[*index];
64                 if ((last->type == MUNMAP) &&
65                    (last->u.munmap.addr + last->u.mmap.len == addr)) {
66                         last->u.munmap.len += len;
67                         return 0;
68                 }
69         }
70
71         if (*index == last_filled) {
72                 ret = (*do_ops)(mmu, ops, last_filled, 0, flush);
73                 *index = -1;
74         }
75
76         ops[++*index] = ((struct host_vm_op) { .type    = MUNMAP,
77                                                .u = { .munmap = {
78                                                         .addr   = addr,
79                                                         .len    = len } } });
80         return ret;
81 }
82
83 static int add_mprotect(unsigned long addr, unsigned long len,
84                         unsigned int prot, struct host_vm_op *ops, int *index,
85                         int last_filled, union mm_context *mmu, void **flush,
86                         int (*do_ops)(union mm_context *, struct host_vm_op *,
87                                       int, int, void **))
88 {
89         struct host_vm_op *last;
90         int ret = 0;
91
92         if (*index != -1) {
93                 last = &ops[*index];
94                 if ((last->type == MPROTECT) &&
95                    (last->u.mprotect.addr + last->u.mprotect.len == addr) &&
96                    (last->u.mprotect.prot == prot)) {
97                         last->u.mprotect.len += len;
98                         return 0;
99                 }
100         }
101
102         if (*index == last_filled) {
103                 ret = (*do_ops)(mmu, ops, last_filled, 0, flush);
104                 *index = -1;
105         }
106
107         ops[++*index] = ((struct host_vm_op) { .type    = MPROTECT,
108                                                .u = { .mprotect = {
109                                                        .addr    = addr,
110                                                        .len     = len,
111                                                        .prot    = prot } } });
112         return ret;
113 }
114
115 #define ADD_ROUND(n, inc) (((n) + (inc)) & ~((inc) - 1))
116
117 static inline int update_pte_range(pmd_t *pmd, unsigned long addr,
118                                    unsigned long end, struct host_vm_op *ops,
119                                    int last_op, int *op_index, int force,
120                                    union mm_context *mmu, void **flush,
121                                    int (*do_ops)(union mm_context *,
122                                                  struct host_vm_op *, int, int,
123                                                  void **))
124 {
125         pte_t *pte;
126         int r, w, x, prot, ret = 0;
127
128         pte = pte_offset_kernel(pmd, addr);
129         do {
130                 r = pte_read(*pte);
131                 w = pte_write(*pte);
132                 x = pte_exec(*pte);
133                 if (!pte_young(*pte)) {
134                         r = 0;
135                         w = 0;
136                 } else if (!pte_dirty(*pte)) {
137                         w = 0;
138                 }
139                 prot = ((r ? UM_PROT_READ : 0) | (w ? UM_PROT_WRITE : 0) |
140                         (x ? UM_PROT_EXEC : 0));
141                 if (force || pte_newpage(*pte)) {
142                         if (pte_present(*pte))
143                                 ret = add_mmap(addr, pte_val(*pte) & PAGE_MASK,
144                                                PAGE_SIZE, prot, ops, op_index,
145                                                last_op, mmu, flush, do_ops);
146                         else ret = add_munmap(addr, PAGE_SIZE, ops, op_index,
147                                               last_op, mmu, flush, do_ops);
148                 }
149                 else if (pte_newprot(*pte))
150                         ret = add_mprotect(addr, PAGE_SIZE, prot, ops, op_index,
151                                            last_op, mmu, flush, do_ops);
152                 *pte = pte_mkuptodate(*pte);
153         } while (pte++, addr += PAGE_SIZE, ((addr != end) && !ret));
154         return ret;
155 }
156
157 static inline int update_pmd_range(pud_t *pud, unsigned long addr,
158                                    unsigned long end, struct host_vm_op *ops,
159                                    int last_op, int *op_index, int force,
160                                    union mm_context *mmu, void **flush,
161                                    int (*do_ops)(union mm_context *,
162                                                  struct host_vm_op *, int, int,
163                                                  void **))
164 {
165         pmd_t *pmd;
166         unsigned long next;
167         int ret = 0;
168
169         pmd = pmd_offset(pud, addr);
170         do {
171                 next = pmd_addr_end(addr, end);
172                 if (!pmd_present(*pmd)) {
173                         if (force || pmd_newpage(*pmd)) {
174                                 ret = add_munmap(addr, next - addr, ops,
175                                                  op_index, last_op, mmu,
176                                                  flush, do_ops);
177                                 pmd_mkuptodate(*pmd);
178                         }
179                 }
180                 else ret = update_pte_range(pmd, addr, next, ops, last_op,
181                                             op_index, force, mmu, flush,
182                                             do_ops);
183         } while (pmd++, addr = next, ((addr != end) && !ret));
184         return ret;
185 }
186
187 static inline int update_pud_range(pgd_t *pgd, unsigned long addr,
188                                    unsigned long end, struct host_vm_op *ops,
189                                    int last_op, int *op_index, int force,
190                                    union mm_context *mmu, void **flush,
191                                    int (*do_ops)(union mm_context *,
192                                                  struct host_vm_op *, int, int,
193                                                  void **))
194 {
195         pud_t *pud;
196         unsigned long next;
197         int ret = 0;
198
199         pud = pud_offset(pgd, addr);
200         do {
201                 next = pud_addr_end(addr, end);
202                 if (!pud_present(*pud)) {
203                         if (force || pud_newpage(*pud)) {
204                                 ret = add_munmap(addr, next - addr, ops,
205                                                  op_index, last_op, mmu,
206                                                  flush, do_ops);
207                                 pud_mkuptodate(*pud);
208                         }
209                 }
210                 else ret = update_pmd_range(pud, addr, next, ops, last_op,
211                                             op_index, force, mmu, flush,
212                                             do_ops);
213         } while (pud++, addr = next, ((addr != end) && !ret));
214         return ret;
215 }
216
217 void fix_range_common(struct mm_struct *mm, unsigned long start_addr,
218                       unsigned long end_addr, int force,
219                       int (*do_ops)(union mm_context *, struct host_vm_op *,
220                                     int, int, void **))
221 {
222         pgd_t *pgd;
223         union mm_context *mmu = &mm->context;
224         struct host_vm_op ops[1];
225         unsigned long addr = start_addr, next;
226         int ret = 0, last_op = ARRAY_SIZE(ops) - 1, op_index = -1;
227         void *flush = NULL;
228
229         ops[0].type = NONE;
230         pgd = pgd_offset(mm, addr);
231         do {
232                 next = pgd_addr_end(addr, end_addr);
233                 if (!pgd_present(*pgd)) {
234                         if (force || pgd_newpage(*pgd)) {
235                                 ret = add_munmap(addr, next - addr, ops,
236                                                  &op_index, last_op, mmu,
237                                                  &flush, do_ops);
238                                 pgd_mkuptodate(*pgd);
239                         }
240                 }
241                 else ret = update_pud_range(pgd, addr, next, ops, last_op,
242                                             &op_index, force, mmu, &flush,
243                                             do_ops);
244         } while (pgd++, addr = next, ((addr != end_addr) && !ret));
245
246         if (!ret)
247                 ret = (*do_ops)(mmu, ops, op_index, 1, &flush);
248
249         /* This is not an else because ret is modified above */
250         if (ret) {
251                 printk(KERN_ERR "fix_range_common: failed, killing current "
252                        "process\n");
253                 force_sig(SIGKILL, current);
254         }
255 }
256
257 int flush_tlb_kernel_range_common(unsigned long start, unsigned long end)
258 {
259         struct mm_struct *mm;
260         pgd_t *pgd;
261         pud_t *pud;
262         pmd_t *pmd;
263         pte_t *pte;
264         unsigned long addr, last;
265         int updated = 0, err;
266
267         mm = &init_mm;
268         for (addr = start; addr < end;) {
269                 pgd = pgd_offset(mm, addr);
270                 if (!pgd_present(*pgd)) {
271                         last = ADD_ROUND(addr, PGDIR_SIZE);
272                         if (last > end)
273                                 last = end;
274                         if (pgd_newpage(*pgd)) {
275                                 updated = 1;
276                                 err = os_unmap_memory((void *) addr,
277                                                       last - addr);
278                                 if (err < 0)
279                                         panic("munmap failed, errno = %d\n",
280                                               -err);
281                         }
282                         addr = last;
283                         continue;
284                 }
285
286                 pud = pud_offset(pgd, addr);
287                 if (!pud_present(*pud)) {
288                         last = ADD_ROUND(addr, PUD_SIZE);
289                         if (last > end)
290                                 last = end;
291                         if (pud_newpage(*pud)) {
292                                 updated = 1;
293                                 err = os_unmap_memory((void *) addr,
294                                                       last - addr);
295                                 if (err < 0)
296                                         panic("munmap failed, errno = %d\n",
297                                               -err);
298                         }
299                         addr = last;
300                         continue;
301                 }
302
303                 pmd = pmd_offset(pud, addr);
304                 if (!pmd_present(*pmd)) {
305                         last = ADD_ROUND(addr, PMD_SIZE);
306                         if (last > end)
307                                 last = end;
308                         if (pmd_newpage(*pmd)) {
309                                 updated = 1;
310                                 err = os_unmap_memory((void *) addr,
311                                                       last - addr);
312                                 if (err < 0)
313                                         panic("munmap failed, errno = %d\n",
314                                               -err);
315                         }
316                         addr = last;
317                         continue;
318                 }
319
320                 pte = pte_offset_kernel(pmd, addr);
321                 if (!pte_present(*pte) || pte_newpage(*pte)) {
322                         updated = 1;
323                         err = os_unmap_memory((void *) addr,
324                                               PAGE_SIZE);
325                         if (err < 0)
326                                 panic("munmap failed, errno = %d\n",
327                                       -err);
328                         if (pte_present(*pte))
329                                 map_memory(addr,
330                                            pte_val(*pte) & PAGE_MASK,
331                                            PAGE_SIZE, 1, 1, 1);
332                 }
333                 else if (pte_newprot(*pte)) {
334                         updated = 1;
335                         os_protect_memory((void *) addr, PAGE_SIZE, 1, 1, 1);
336                 }
337                 addr += PAGE_SIZE;
338         }
339         return updated;
340 }
341
342 void flush_tlb_page(struct vm_area_struct *vma, unsigned long address)
343 {
344         pgd_t *pgd;
345         pud_t *pud;
346         pmd_t *pmd;
347         pte_t *pte;
348         struct mm_struct *mm = vma->vm_mm;
349         void *flush = NULL;
350         int r, w, x, prot, err = 0;
351         struct mm_id *mm_id;
352
353         address &= PAGE_MASK;
354         pgd = pgd_offset(mm, address);
355         if (!pgd_present(*pgd))
356                 goto kill;
357
358         pud = pud_offset(pgd, address);
359         if (!pud_present(*pud))
360                 goto kill;
361
362         pmd = pmd_offset(pud, address);
363         if (!pmd_present(*pmd))
364                 goto kill;
365
366         pte = pte_offset_kernel(pmd, address);
367
368         r = pte_read(*pte);
369         w = pte_write(*pte);
370         x = pte_exec(*pte);
371         if (!pte_young(*pte)) {
372                 r = 0;
373                 w = 0;
374         } else if (!pte_dirty(*pte)) {
375                 w = 0;
376         }
377
378         mm_id = &mm->context.skas.id;
379         prot = ((r ? UM_PROT_READ : 0) | (w ? UM_PROT_WRITE : 0) |
380                 (x ? UM_PROT_EXEC : 0));
381         if (pte_newpage(*pte)) {
382                 if (pte_present(*pte)) {
383                         unsigned long long offset;
384                         int fd;
385
386                         fd = phys_mapping(pte_val(*pte) & PAGE_MASK, &offset);
387                         err = map(mm_id, address, PAGE_SIZE, prot, fd, offset,
388                                   1, &flush);
389                 }
390                 else err = unmap(mm_id, address, PAGE_SIZE, 1, &flush);
391         }
392         else if (pte_newprot(*pte))
393                 err = protect(mm_id, address, PAGE_SIZE, prot, 1, &flush);
394
395         if (err)
396                 goto kill;
397
398         *pte = pte_mkuptodate(*pte);
399
400         return;
401
402 kill:
403         printk(KERN_ERR "Failed to flush page for address 0x%lx\n", address);
404         force_sig(SIGKILL, current);
405 }
406
407 pgd_t *pgd_offset_proc(struct mm_struct *mm, unsigned long address)
408 {
409         return pgd_offset(mm, address);
410 }
411
412 pud_t *pud_offset_proc(pgd_t *pgd, unsigned long address)
413 {
414         return pud_offset(pgd, address);
415 }
416
417 pmd_t *pmd_offset_proc(pud_t *pud, unsigned long address)
418 {
419         return pmd_offset(pud, address);
420 }
421
422 pte_t *pte_offset_proc(pmd_t *pmd, unsigned long address)
423 {
424         return pte_offset_kernel(pmd, address);
425 }
426
427 pte_t *addr_pte(struct task_struct *task, unsigned long addr)
428 {
429         pgd_t *pgd = pgd_offset(task->mm, addr);
430         pud_t *pud = pud_offset(pgd, addr);
431         pmd_t *pmd = pmd_offset(pud, addr);
432
433         return pte_offset_map(pmd, addr);
434 }
435
436 void flush_tlb_all(void)
437 {
438         flush_tlb_mm(current->mm);
439 }
440
441 void flush_tlb_kernel_range(unsigned long start, unsigned long end)
442 {
443         flush_tlb_kernel_range_common(start, end);
444 }
445
446 void flush_tlb_kernel_vm(void)
447 {
448         flush_tlb_kernel_range_common(start_vm, end_vm);
449 }
450
451 void __flush_tlb_one(unsigned long addr)
452 {
453         flush_tlb_kernel_range_common(addr, addr + PAGE_SIZE);
454 }
455
456 static int do_ops(union mm_context *mmu, struct host_vm_op *ops, int last,
457                   int finished, void **flush)
458 {
459         struct host_vm_op *op;
460         int i, ret = 0;
461
462         for (i = 0; i <= last && !ret; i++) {
463         op = &ops[i];
464                 switch(op->type) {
465                 case MMAP:
466                         ret = map(&mmu->skas.id, op->u.mmap.addr,
467                                   op->u.mmap.len, op->u.mmap.prot,
468                                   op->u.mmap.fd, op->u.mmap.offset, finished,
469                                   flush);
470                         break;
471                 case MUNMAP:
472                         ret = unmap(&mmu->skas.id, op->u.munmap.addr,
473                                     op->u.munmap.len, finished, flush);
474                         break;
475                 case MPROTECT:
476                         ret = protect(&mmu->skas.id, op->u.mprotect.addr,
477                                       op->u.mprotect.len, op->u.mprotect.prot,
478                                       finished, flush);
479                         break;
480                 default:
481                         printk(KERN_ERR "Unknown op type %d in do_ops\n",
482                                op->type);
483                         break;
484                 }
485         }
486
487         return ret;
488 }
489
490 static void fix_range(struct mm_struct *mm, unsigned long start_addr,
491                       unsigned long end_addr, int force)
492 {
493         if (!proc_mm && (end_addr > CONFIG_STUB_START))
494                 end_addr = CONFIG_STUB_START;
495
496         fix_range_common(mm, start_addr, end_addr, force, do_ops);
497 }
498
499 void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
500                      unsigned long end)
501 {
502         if (vma->vm_mm == NULL)
503                 flush_tlb_kernel_range_common(start, end);
504         else fix_range(vma->vm_mm, start, end, 0);
505 }
506
507 void flush_tlb_mm(struct mm_struct *mm)
508 {
509         unsigned long end;
510
511         /*
512          * Don't bother flushing if this address space is about to be
513          * destroyed.
514          */
515         if (atomic_read(&mm->mm_users) == 0)
516                 return;
517
518         end = proc_mm ? task_size : CONFIG_STUB_START;
519         fix_range(mm, 0, end, 0);
520 }
521
522 void force_flush_all(void)
523 {
524         struct mm_struct *mm = current->mm;
525         struct vm_area_struct *vma = mm->mmap;
526
527         while (vma != NULL) {
528                 fix_range(mm, vma->vm_start, vma->vm_end, 1);
529                 vma = vma->vm_next;
530         }
531 }