diff options
Diffstat (limited to 'arch/um/kernel/trap_kern.c')
-rw-r--r-- | arch/um/kernel/trap_kern.c | 63 |
1 files changed, 21 insertions, 42 deletions
diff --git a/arch/um/kernel/trap_kern.c b/arch/um/kernel/trap_kern.c index 47e766e6ba10..1de22d8a313a 100644 --- a/arch/um/kernel/trap_kern.c +++ b/arch/um/kernel/trap_kern.c | |||
@@ -48,7 +48,7 @@ int handle_page_fault(unsigned long address, unsigned long ip, | |||
48 | goto good_area; | 48 | goto good_area; |
49 | else if(!(vma->vm_flags & VM_GROWSDOWN)) | 49 | else if(!(vma->vm_flags & VM_GROWSDOWN)) |
50 | goto out; | 50 | goto out; |
51 | else if(!ARCH_IS_STACKGROW(address)) | 51 | else if(is_user && !ARCH_IS_STACKGROW(address)) |
52 | goto out; | 52 | goto out; |
53 | else if(expand_stack(vma, address)) | 53 | else if(expand_stack(vma, address)) |
54 | goto out; | 54 | goto out; |
@@ -57,10 +57,11 @@ int handle_page_fault(unsigned long address, unsigned long ip, | |||
57 | *code_out = SEGV_ACCERR; | 57 | *code_out = SEGV_ACCERR; |
58 | if(is_write && !(vma->vm_flags & VM_WRITE)) | 58 | if(is_write && !(vma->vm_flags & VM_WRITE)) |
59 | goto out; | 59 | goto out; |
60 | |||
61 | if(!(vma->vm_flags & (VM_READ | VM_EXEC))) | ||
62 | goto out; | ||
63 | |||
60 | page = address & PAGE_MASK; | 64 | page = address & PAGE_MASK; |
61 | pgd = pgd_offset(mm, page); | ||
62 | pud = pud_offset(pgd, page); | ||
63 | pmd = pmd_offset(pud, page); | ||
64 | do { | 65 | do { |
65 | survive: | 66 | survive: |
66 | switch (handle_mm_fault(mm, vma, address, is_write)){ | 67 | switch (handle_mm_fault(mm, vma, address, is_write)){ |
@@ -106,46 +107,24 @@ out_of_memory: | |||
106 | goto out; | 107 | goto out; |
107 | } | 108 | } |
108 | 109 | ||
109 | LIST_HEAD(physmem_remappers); | 110 | /* |
110 | 111 | * We give a *copy* of the faultinfo in the regs to segv. | |
111 | void register_remapper(struct remapper *info) | 112 | * This must be done, since nesting SEGVs could overwrite |
112 | { | 113 | * the info in the regs. A pointer to the info then would |
113 | list_add(&info->list, &physmem_remappers); | 114 | * give us bad data! |
114 | } | 115 | */ |
115 | 116 | unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, void *sc) | |
116 | static int check_remapped_addr(unsigned long address, int is_write) | ||
117 | { | ||
118 | struct remapper *remapper; | ||
119 | struct list_head *ele; | ||
120 | __u64 offset; | ||
121 | int fd; | ||
122 | |||
123 | fd = phys_mapping(__pa(address), &offset); | ||
124 | if(fd == -1) | ||
125 | return(0); | ||
126 | |||
127 | list_for_each(ele, &physmem_remappers){ | ||
128 | remapper = list_entry(ele, struct remapper, list); | ||
129 | if((*remapper->proc)(fd, address, is_write, offset)) | ||
130 | return(1); | ||
131 | } | ||
132 | |||
133 | return(0); | ||
134 | } | ||
135 | |||
136 | unsigned long segv(unsigned long address, unsigned long ip, int is_write, | ||
137 | int is_user, void *sc) | ||
138 | { | 117 | { |
139 | struct siginfo si; | 118 | struct siginfo si; |
140 | void *catcher; | 119 | void *catcher; |
141 | int err; | 120 | int err; |
121 | int is_write = FAULT_WRITE(fi); | ||
122 | unsigned long address = FAULT_ADDRESS(fi); | ||
142 | 123 | ||
143 | if(!is_user && (address >= start_vm) && (address < end_vm)){ | 124 | if(!is_user && (address >= start_vm) && (address < end_vm)){ |
144 | flush_tlb_kernel_vm(); | 125 | flush_tlb_kernel_vm(); |
145 | return(0); | 126 | return(0); |
146 | } | 127 | } |
147 | else if(check_remapped_addr(address & PAGE_MASK, is_write)) | ||
148 | return(0); | ||
149 | else if(current->mm == NULL) | 128 | else if(current->mm == NULL) |
150 | panic("Segfault with no mm"); | 129 | panic("Segfault with no mm"); |
151 | err = handle_page_fault(address, ip, is_write, is_user, &si.si_code); | 130 | err = handle_page_fault(address, ip, is_write, is_user, &si.si_code); |
@@ -159,7 +138,7 @@ unsigned long segv(unsigned long address, unsigned long ip, int is_write, | |||
159 | } | 138 | } |
160 | else if(current->thread.fault_addr != NULL) | 139 | else if(current->thread.fault_addr != NULL) |
161 | panic("fault_addr set but no fault catcher"); | 140 | panic("fault_addr set but no fault catcher"); |
162 | else if(arch_fixup(ip, sc)) | 141 | else if(!is_user && arch_fixup(ip, sc)) |
163 | return(0); | 142 | return(0); |
164 | 143 | ||
165 | if(!is_user) | 144 | if(!is_user) |
@@ -171,6 +150,7 @@ unsigned long segv(unsigned long address, unsigned long ip, int is_write, | |||
171 | si.si_errno = 0; | 150 | si.si_errno = 0; |
172 | si.si_code = BUS_ADRERR; | 151 | si.si_code = BUS_ADRERR; |
173 | si.si_addr = (void *)address; | 152 | si.si_addr = (void *)address; |
153 | current->thread.arch.faultinfo = fi; | ||
174 | force_sig_info(SIGBUS, &si, current); | 154 | force_sig_info(SIGBUS, &si, current); |
175 | } | 155 | } |
176 | else if(err == -ENOMEM){ | 156 | else if(err == -ENOMEM){ |
@@ -180,22 +160,20 @@ unsigned long segv(unsigned long address, unsigned long ip, int is_write, | |||
180 | else { | 160 | else { |
181 | si.si_signo = SIGSEGV; | 161 | si.si_signo = SIGSEGV; |
182 | si.si_addr = (void *) address; | 162 | si.si_addr = (void *) address; |
183 | current->thread.cr2 = address; | 163 | current->thread.arch.faultinfo = fi; |
184 | current->thread.err = is_write; | ||
185 | force_sig_info(SIGSEGV, &si, current); | 164 | force_sig_info(SIGSEGV, &si, current); |
186 | } | 165 | } |
187 | return(0); | 166 | return(0); |
188 | } | 167 | } |
189 | 168 | ||
190 | void bad_segv(unsigned long address, unsigned long ip, int is_write) | 169 | void bad_segv(struct faultinfo fi, unsigned long ip) |
191 | { | 170 | { |
192 | struct siginfo si; | 171 | struct siginfo si; |
193 | 172 | ||
194 | si.si_signo = SIGSEGV; | 173 | si.si_signo = SIGSEGV; |
195 | si.si_code = SEGV_ACCERR; | 174 | si.si_code = SEGV_ACCERR; |
196 | si.si_addr = (void *) address; | 175 | si.si_addr = (void *) FAULT_ADDRESS(fi); |
197 | current->thread.cr2 = address; | 176 | current->thread.arch.faultinfo = fi; |
198 | current->thread.err = is_write; | ||
199 | force_sig_info(SIGSEGV, &si, current); | 177 | force_sig_info(SIGSEGV, &si, current); |
200 | } | 178 | } |
201 | 179 | ||
@@ -204,6 +182,7 @@ void relay_signal(int sig, union uml_pt_regs *regs) | |||
204 | if(arch_handle_signal(sig, regs)) return; | 182 | if(arch_handle_signal(sig, regs)) return; |
205 | if(!UPT_IS_USER(regs)) | 183 | if(!UPT_IS_USER(regs)) |
206 | panic("Kernel mode signal %d", sig); | 184 | panic("Kernel mode signal %d", sig); |
185 | current->thread.arch.faultinfo = *UPT_FAULTINFO(regs); | ||
207 | force_sig(sig, current); | 186 | force_sig(sig, current); |
208 | } | 187 | } |
209 | 188 | ||