diff options
Diffstat (limited to 'arch/s390/mm/fault.c')
-rw-r--r-- | arch/s390/mm/fault.c | 140 |
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 | ||
129 | static 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 | ||
137 | static 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); | ||
183 | out: | ||
184 | pr_cont("\n"); | ||
185 | return; | ||
186 | bad: | ||
187 | pr_cont("BAD\n"); | ||
188 | } | ||
189 | |||
190 | #else /* CONFIG_64BIT */ | ||
191 | |||
192 | static 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); | ||
208 | out: | ||
209 | pr_cont("\n"); | ||
210 | return; | ||
211 | bad: | ||
212 | pr_cont("BAD\n"); | ||
213 | } | ||
214 | |||
215 | #endif /* CONFIG_64BIT */ | ||
216 | |||
217 | static 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 | |||
129 | static inline void report_user_fault(struct pt_regs *regs, long signr) | 256 | static 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 | } |