aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/mm/fault.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390/mm/fault.c')
-rw-r--r--arch/s390/mm/fault.c140
1 files changed, 135 insertions, 5 deletions
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c
index 19f623f1f21c..2f51a998a67e 100644
--- a/arch/s390/mm/fault.c
+++ b/arch/s390/mm/fault.c
@@ -126,6 +126,133 @@ static inline int user_space_fault(struct pt_regs *regs)
126 return 0; 126 return 0;
127} 127}
128 128
129static int bad_address(void *p)
130{
131 unsigned long dummy;
132
133 return probe_kernel_address((unsigned long *)p, dummy);
134}
135
136#ifdef CONFIG_64BIT
137static void dump_pagetable(unsigned long asce, unsigned long address)
138{
139 unsigned long *table = __va(asce & PAGE_MASK);
140
141 pr_alert("AS:%016lx ", asce);
142 switch (asce & _ASCE_TYPE_MASK) {
143 case _ASCE_TYPE_REGION1:
144 table = table + ((address >> 53) & 0x7ff);
145 if (bad_address(table))
146 goto bad;
147 pr_cont("R1:%016lx ", *table);
148 if (*table & _REGION_ENTRY_INVALID)
149 goto out;
150 table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
151 /* fallthrough */
152 case _ASCE_TYPE_REGION2:
153 table = table + ((address >> 42) & 0x7ff);
154 if (bad_address(table))
155 goto bad;
156 pr_cont("R2:%016lx ", *table);
157 if (*table & _REGION_ENTRY_INVALID)
158 goto out;
159 table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
160 /* fallthrough */
161 case _ASCE_TYPE_REGION3:
162 table = table + ((address >> 31) & 0x7ff);
163 if (bad_address(table))
164 goto bad;
165 pr_cont("R3:%016lx ", *table);
166 if (*table & (_REGION_ENTRY_INVALID | _REGION3_ENTRY_LARGE))
167 goto out;
168 table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
169 /* fallthrough */
170 case _ASCE_TYPE_SEGMENT:
171 table = table + ((address >> 20) & 0x7ff);
172 if (bad_address(table))
173 goto bad;
174 pr_cont(KERN_CONT "S:%016lx ", *table);
175 if (*table & (_SEGMENT_ENTRY_INVALID | _SEGMENT_ENTRY_LARGE))
176 goto out;
177 table = (unsigned long *)(*table & _SEGMENT_ENTRY_ORIGIN);
178 }
179 table = table + ((address >> 12) & 0xff);
180 if (bad_address(table))
181 goto bad;
182 pr_cont("P:%016lx ", *table);
183out:
184 pr_cont("\n");
185 return;
186bad:
187 pr_cont("BAD\n");
188}
189
190#else /* CONFIG_64BIT */
191
192static void dump_pagetable(unsigned long asce, unsigned long address)
193{
194 unsigned long *table = __va(asce & PAGE_MASK);
195
196 pr_alert("AS:%08lx ", asce);
197 table = table + ((address >> 20) & 0x7ff);
198 if (bad_address(table))
199 goto bad;
200 pr_cont("S:%08lx ", *table);
201 if (*table & _SEGMENT_ENTRY_INVALID)
202 goto out;
203 table = (unsigned long *)(*table & _SEGMENT_ENTRY_ORIGIN);
204 table = table + ((address >> 12) & 0xff);
205 if (bad_address(table))
206 goto bad;
207 pr_cont("P:%08lx ", *table);
208out:
209 pr_cont("\n");
210 return;
211bad:
212 pr_cont("BAD\n");
213}
214
215#endif /* CONFIG_64BIT */
216
217static void dump_fault_info(struct pt_regs *regs)
218{
219 unsigned long asce;
220
221 pr_alert("Fault in ");
222 switch (regs->int_parm_long & 3) {
223 case 3:
224 pr_cont("home space ");
225 break;
226 case 2:
227 pr_cont("secondary space ");
228 break;
229 case 1:
230 pr_cont("access register ");
231 break;
232 case 0:
233 pr_cont("primary space ");
234 break;
235 }
236 pr_cont("mode while using ");
237 if (!user_space_fault(regs)) {
238 asce = S390_lowcore.kernel_asce;
239 pr_cont("kernel ");
240 }
241#ifdef CONFIG_PGSTE
242 else if ((current->flags & PF_VCPU) && S390_lowcore.gmap) {
243 struct gmap *gmap = (struct gmap *)S390_lowcore.gmap;
244 asce = gmap->asce;
245 pr_cont("gmap ");
246 }
247#endif
248 else {
249 asce = S390_lowcore.user_asce;
250 pr_cont("user ");
251 }
252 pr_cont("ASCE.\n");
253 dump_pagetable(asce, regs->int_parm_long & __FAIL_ADDR_MASK);
254}
255
129static inline void report_user_fault(struct pt_regs *regs, long signr) 256static inline void report_user_fault(struct pt_regs *regs, long signr)
130{ 257{
131 if ((task_pid_nr(current) > 1) && !show_unhandled_signals) 258 if ((task_pid_nr(current) > 1) && !show_unhandled_signals)
@@ -138,8 +265,9 @@ static inline void report_user_fault(struct pt_regs *regs, long signr)
138 regs->int_code); 265 regs->int_code);
139 print_vma_addr(KERN_CONT "in ", regs->psw.addr & PSW_ADDR_INSN); 266 print_vma_addr(KERN_CONT "in ", regs->psw.addr & PSW_ADDR_INSN);
140 printk(KERN_CONT "\n"); 267 printk(KERN_CONT "\n");
141 printk(KERN_ALERT "failing address: %lX\n", 268 printk(KERN_ALERT "failing address: %016lx TEID: %016lx\n",
142 regs->int_parm_long & __FAIL_ADDR_MASK); 269 regs->int_parm_long & __FAIL_ADDR_MASK, regs->int_parm_long);
270 dump_fault_info(regs);
143 show_regs(regs); 271 show_regs(regs);
144} 272}
145 273
@@ -177,11 +305,13 @@ static noinline void do_no_context(struct pt_regs *regs)
177 address = regs->int_parm_long & __FAIL_ADDR_MASK; 305 address = regs->int_parm_long & __FAIL_ADDR_MASK;
178 if (!user_space_fault(regs)) 306 if (!user_space_fault(regs))
179 printk(KERN_ALERT "Unable to handle kernel pointer dereference" 307 printk(KERN_ALERT "Unable to handle kernel pointer dereference"
180 " at virtual kernel address %p\n", (void *)address); 308 " in virtual kernel address space\n");
181 else 309 else
182 printk(KERN_ALERT "Unable to handle kernel paging request" 310 printk(KERN_ALERT "Unable to handle kernel paging request"
183 " at virtual user address %p\n", (void *)address); 311 " in virtual user address space\n");
184 312 printk(KERN_ALERT "failing address: %016lx TEID: %016lx\n",
313 regs->int_parm_long & __FAIL_ADDR_MASK, regs->int_parm_long);
314 dump_fault_info(regs);
185 die(regs, "Oops"); 315 die(regs, "Oops");
186 do_exit(SIGKILL); 316 do_exit(SIGKILL);
187} 317}