diff options
| author | Heiko Carstens <heiko.carstens@de.ibm.com> | 2014-04-07 04:20:40 -0400 |
|---|---|---|
| committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2014-04-09 04:19:13 -0400 |
| commit | 3b7df3421f8813ce3b0eedf3d1d9c247c38e3c39 (patch) | |
| tree | 6d5e15731f4d74f9066f77be2d296bfcfa3e09b1 | |
| parent | e7c46c66dbd1727f7de52f54c4acefb692b24f37 (diff) | |
s390/mm: print control registers and page table walk on crash
Print extra debugging information to the console if the kernel or a user
space process crashed (with user space debugging enabled):
- contents of control register 7 and 13
- failing address and translation exception identification
- page table walk for the failing address
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
| -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 | } |
