aboutsummaryrefslogtreecommitdiffstats
path: root/arch/um/kernel/trap_kern.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/um/kernel/trap_kern.c')
-rw-r--r--arch/um/kernel/trap_kern.c63
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
109LIST_HEAD(physmem_remappers); 110/*
110 111 * We give a *copy* of the faultinfo in the regs to segv.
111void 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 116unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, void *sc)
116static 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
136unsigned 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
190void bad_segv(unsigned long address, unsigned long ip, int is_write) 169void 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