vserver 2.0 rc7
[linux-2.6.git] / arch / um / kernel / tt / tlb.c
1 /* 
2  * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
3  * Copyright 2003 PathScale, Inc.
4  * Licensed under the GPL
5  */
6
7 #include "linux/stddef.h"
8 #include "linux/kernel.h"
9 #include "linux/sched.h"
10 #include "linux/mm.h"
11 #include "asm/page.h"
12 #include "asm/pgtable.h"
13 #include "asm/uaccess.h"
14 #include "asm/tlbflush.h"
15 #include "user_util.h"
16 #include "mem_user.h"
17 #include "os.h"
18 #include "tlb.h"
19
20 static void do_ops(int unused, struct host_vm_op *ops, int last)
21 {
22         struct host_vm_op *op;
23         int i;
24
25         for(i = 0; i <= last; i++){
26                 op = &ops[i];
27                 switch(op->type){
28                 case MMAP:
29                         os_map_memory((void *) op->u.mmap.addr, op->u.mmap.fd,
30                                       op->u.mmap.offset, op->u.mmap.len,
31                                       op->u.mmap.r, op->u.mmap.w,
32                                       op->u.mmap.x);
33                         break;
34                 case MUNMAP:
35                         os_unmap_memory((void *) op->u.munmap.addr,
36                                         op->u.munmap.len);
37                         break;
38                 case MPROTECT:
39                         protect_memory(op->u.mprotect.addr, op->u.munmap.len,
40                                        op->u.mprotect.r, op->u.mprotect.w,
41                                        op->u.mprotect.x, 1);
42                         break;
43                 default:
44                         printk("Unknown op type %d in do_ops\n", op->type);
45                         break;
46                 }
47         }
48 }
49
50 static void fix_range(struct mm_struct *mm, unsigned long start_addr, 
51                       unsigned long end_addr, int force)
52 {
53         if((current->thread.mode.tt.extern_pid != -1) &&
54            (current->thread.mode.tt.extern_pid != os_getpid()))
55                 panic("fix_range fixing wrong address space, current = 0x%p",
56                       current);
57
58         fix_range_common(mm, start_addr, end_addr, force, 0, do_ops);
59 }
60
61 atomic_t vmchange_seq = ATOMIC_INIT(1);
62
63 void flush_tlb_kernel_range_tt(unsigned long start, unsigned long end)
64 {
65         if(flush_tlb_kernel_range_common(start, end))
66                 atomic_inc(&vmchange_seq);
67 }
68
69 static void protect_vm_page(unsigned long addr, int w, int must_succeed)
70 {
71         int err;
72
73         err = protect_memory(addr, PAGE_SIZE, 1, w, 1, must_succeed);
74         if(err == 0) return;
75         else if((err == -EFAULT) || (err == -ENOMEM)){
76                 flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
77                 protect_vm_page(addr, w, 1);
78         }
79         else panic("protect_vm_page : protect failed, errno = %d\n", err);
80 }
81
82 void mprotect_kernel_vm(int w)
83 {
84         struct mm_struct *mm;
85         pgd_t *pgd;
86         pud_t *pud;
87         pmd_t *pmd;
88         pte_t *pte;
89         unsigned long addr;
90         
91         mm = &init_mm;
92         for(addr = start_vm; addr < end_vm;){
93                 pgd = pgd_offset(mm, addr);
94                 pud = pud_offset(pgd, addr);
95                 pmd = pmd_offset(pud, addr);
96                 if(pmd_present(*pmd)){
97                         pte = pte_offset_kernel(pmd, addr);
98                         if(pte_present(*pte)) protect_vm_page(addr, w, 0);
99                         addr += PAGE_SIZE;
100                 }
101                 else addr += PMD_SIZE;
102         }
103 }
104
105 void flush_tlb_kernel_vm_tt(void)
106 {
107         flush_tlb_kernel_range(start_vm, end_vm);
108 }
109
110 void __flush_tlb_one_tt(unsigned long addr)
111 {
112         flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
113 }
114   
115 void flush_tlb_range_tt(struct vm_area_struct *vma, unsigned long start, 
116                      unsigned long end)
117 {
118         if(vma->vm_mm != current->mm) return;
119
120         /* Assumes that the range start ... end is entirely within
121          * either process memory or kernel vm
122          */
123         if((start >= start_vm) && (start < end_vm)){
124                 if(flush_tlb_kernel_range_common(start, end))
125                         atomic_inc(&vmchange_seq);
126         }
127         else fix_range(vma->vm_mm, start, end, 0);
128 }
129
130 void flush_tlb_mm_tt(struct mm_struct *mm)
131 {
132         unsigned long seq;
133
134         if(mm != current->mm) return;
135
136         fix_range(mm, 0, STACK_TOP, 0);
137
138         seq = atomic_read(&vmchange_seq);
139         if(current->thread.mode.tt.vm_seq == seq)
140                 return;
141         current->thread.mode.tt.vm_seq = seq;
142         flush_tlb_kernel_range_common(start_vm, end_vm);
143 }
144
145 void force_flush_all_tt(void)
146 {
147         fix_range(current->mm, 0, STACK_TOP, 1);
148         flush_tlb_kernel_range_common(start_vm, end_vm);
149 }