diff options
Diffstat (limited to 'arch/sparc/mm/fault_64.c')
| -rw-r--r-- | arch/sparc/mm/fault_64.c | 98 |
1 files changed, 53 insertions, 45 deletions
diff --git a/arch/sparc/mm/fault_64.c b/arch/sparc/mm/fault_64.c index 69bb818fdd79..4ced3fc66130 100644 --- a/arch/sparc/mm/fault_64.c +++ b/arch/sparc/mm/fault_64.c | |||
| @@ -96,38 +96,51 @@ static unsigned int get_user_insn(unsigned long tpc) | |||
| 96 | pte_t *ptep, pte; | 96 | pte_t *ptep, pte; |
| 97 | unsigned long pa; | 97 | unsigned long pa; |
| 98 | u32 insn = 0; | 98 | u32 insn = 0; |
| 99 | unsigned long pstate; | ||
| 100 | 99 | ||
| 101 | if (pgd_none(*pgdp)) | 100 | if (pgd_none(*pgdp) || unlikely(pgd_bad(*pgdp))) |
| 102 | goto outret; | 101 | goto out; |
| 103 | pudp = pud_offset(pgdp, tpc); | 102 | pudp = pud_offset(pgdp, tpc); |
| 104 | if (pud_none(*pudp)) | 103 | if (pud_none(*pudp) || unlikely(pud_bad(*pudp))) |
| 105 | goto outret; | 104 | goto out; |
| 106 | pmdp = pmd_offset(pudp, tpc); | ||
| 107 | if (pmd_none(*pmdp)) | ||
| 108 | goto outret; | ||
| 109 | 105 | ||
| 110 | /* This disables preemption for us as well. */ | 106 | /* This disables preemption for us as well. */ |
| 111 | __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); | 107 | local_irq_disable(); |
| 112 | __asm__ __volatile__("wrpr %0, %1, %%pstate" | 108 | |
| 113 | : : "r" (pstate), "i" (PSTATE_IE)); | 109 | pmdp = pmd_offset(pudp, tpc); |
| 114 | ptep = pte_offset_map(pmdp, tpc); | 110 | if (pmd_none(*pmdp) || unlikely(pmd_bad(*pmdp))) |
| 115 | pte = *ptep; | 111 | goto out_irq_enable; |
| 116 | if (!pte_present(pte)) | ||
| 117 | goto out; | ||
| 118 | 112 | ||
| 119 | pa = (pte_pfn(pte) << PAGE_SHIFT); | 113 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |
| 120 | pa += (tpc & ~PAGE_MASK); | 114 | if (pmd_trans_huge(*pmdp)) { |
| 115 | if (pmd_trans_splitting(*pmdp)) | ||
| 116 | goto out_irq_enable; | ||
| 121 | 117 | ||
| 122 | /* Use phys bypass so we don't pollute dtlb/dcache. */ | 118 | pa = pmd_pfn(*pmdp) << PAGE_SHIFT; |
| 123 | __asm__ __volatile__("lduwa [%1] %2, %0" | 119 | pa += tpc & ~HPAGE_MASK; |
| 124 | : "=r" (insn) | ||
| 125 | : "r" (pa), "i" (ASI_PHYS_USE_EC)); | ||
| 126 | 120 | ||
| 121 | /* Use phys bypass so we don't pollute dtlb/dcache. */ | ||
| 122 | __asm__ __volatile__("lduwa [%1] %2, %0" | ||
| 123 | : "=r" (insn) | ||
| 124 | : "r" (pa), "i" (ASI_PHYS_USE_EC)); | ||
| 125 | } else | ||
| 126 | #endif | ||
| 127 | { | ||
| 128 | ptep = pte_offset_map(pmdp, tpc); | ||
| 129 | pte = *ptep; | ||
| 130 | if (pte_present(pte)) { | ||
| 131 | pa = (pte_pfn(pte) << PAGE_SHIFT); | ||
| 132 | pa += (tpc & ~PAGE_MASK); | ||
| 133 | |||
| 134 | /* Use phys bypass so we don't pollute dtlb/dcache. */ | ||
| 135 | __asm__ __volatile__("lduwa [%1] %2, %0" | ||
| 136 | : "=r" (insn) | ||
| 137 | : "r" (pa), "i" (ASI_PHYS_USE_EC)); | ||
| 138 | } | ||
| 139 | pte_unmap(ptep); | ||
| 140 | } | ||
| 141 | out_irq_enable: | ||
| 142 | local_irq_enable(); | ||
| 127 | out: | 143 | out: |
| 128 | pte_unmap(ptep); | ||
| 129 | __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate)); | ||
| 130 | outret: | ||
| 131 | return insn; | 144 | return insn; |
| 132 | } | 145 | } |
| 133 | 146 | ||
| @@ -153,7 +166,8 @@ show_signal_msg(struct pt_regs *regs, int sig, int code, | |||
| 153 | } | 166 | } |
| 154 | 167 | ||
| 155 | static void do_fault_siginfo(int code, int sig, struct pt_regs *regs, | 168 | static void do_fault_siginfo(int code, int sig, struct pt_regs *regs, |
| 156 | unsigned int insn, int fault_code) | 169 | unsigned long fault_addr, unsigned int insn, |
| 170 | int fault_code) | ||
| 157 | { | 171 | { |
| 158 | unsigned long addr; | 172 | unsigned long addr; |
| 159 | siginfo_t info; | 173 | siginfo_t info; |
| @@ -161,10 +175,18 @@ static void do_fault_siginfo(int code, int sig, struct pt_regs *regs, | |||
| 161 | info.si_code = code; | 175 | info.si_code = code; |
| 162 | info.si_signo = sig; | 176 | info.si_signo = sig; |
| 163 | info.si_errno = 0; | 177 | info.si_errno = 0; |
| 164 | if (fault_code & FAULT_CODE_ITLB) | 178 | if (fault_code & FAULT_CODE_ITLB) { |
| 165 | addr = regs->tpc; | 179 | addr = regs->tpc; |
| 166 | else | 180 | } else { |
| 167 | addr = compute_effective_address(regs, insn, 0); | 181 | /* If we were able to probe the faulting instruction, use it |
| 182 | * to compute a precise fault address. Otherwise use the fault | ||
| 183 | * time provided address which may only have page granularity. | ||
| 184 | */ | ||
| 185 | if (insn) | ||
| 186 | addr = compute_effective_address(regs, insn, 0); | ||
| 187 | else | ||
| 188 | addr = fault_addr; | ||
| 189 | } | ||
| 168 | info.si_addr = (void __user *) addr; | 190 | info.si_addr = (void __user *) addr; |
| 169 | info.si_trapno = 0; | 191 | info.si_trapno = 0; |
| 170 | 192 | ||
| @@ -239,7 +261,7 @@ static void __kprobes do_kernel_fault(struct pt_regs *regs, int si_code, | |||
| 239 | /* The si_code was set to make clear whether | 261 | /* The si_code was set to make clear whether |
| 240 | * this was a SEGV_MAPERR or SEGV_ACCERR fault. | 262 | * this was a SEGV_MAPERR or SEGV_ACCERR fault. |
| 241 | */ | 263 | */ |
| 242 | do_fault_siginfo(si_code, SIGSEGV, regs, insn, fault_code); | 264 | do_fault_siginfo(si_code, SIGSEGV, regs, address, insn, fault_code); |
| 243 | return; | 265 | return; |
| 244 | } | 266 | } |
| 245 | 267 | ||
| @@ -259,18 +281,6 @@ static void noinline __kprobes bogus_32bit_fault_tpc(struct pt_regs *regs) | |||
| 259 | show_regs(regs); | 281 | show_regs(regs); |
| 260 | } | 282 | } |
| 261 | 283 | ||
| 262 | static void noinline __kprobes bogus_32bit_fault_address(struct pt_regs *regs, | ||
| 263 | unsigned long addr) | ||
| 264 | { | ||
| 265 | static int times; | ||
| 266 | |||
| 267 | if (times++ < 10) | ||
| 268 | printk(KERN_ERR "FAULT[%s:%d]: 32-bit process " | ||
| 269 | "reports 64-bit fault address [%lx]\n", | ||
| 270 | current->comm, current->pid, addr); | ||
| 271 | show_regs(regs); | ||
| 272 | } | ||
| 273 | |||
| 274 | asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) | 284 | asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) |
| 275 | { | 285 | { |
| 276 | enum ctx_state prev_state = exception_enter(); | 286 | enum ctx_state prev_state = exception_enter(); |
| @@ -300,10 +310,8 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) | |||
| 300 | goto intr_or_no_mm; | 310 | goto intr_or_no_mm; |
| 301 | } | 311 | } |
| 302 | } | 312 | } |
| 303 | if (unlikely((address >> 32) != 0)) { | 313 | if (unlikely((address >> 32) != 0)) |
| 304 | bogus_32bit_fault_address(regs, address); | ||
| 305 | goto intr_or_no_mm; | 314 | goto intr_or_no_mm; |
| 306 | } | ||
| 307 | } | 315 | } |
| 308 | 316 | ||
| 309 | if (regs->tstate & TSTATE_PRIV) { | 317 | if (regs->tstate & TSTATE_PRIV) { |
| @@ -525,7 +533,7 @@ do_sigbus: | |||
| 525 | * Send a sigbus, regardless of whether we were in kernel | 533 | * Send a sigbus, regardless of whether we were in kernel |
| 526 | * or user mode. | 534 | * or user mode. |
| 527 | */ | 535 | */ |
| 528 | do_fault_siginfo(BUS_ADRERR, SIGBUS, regs, insn, fault_code); | 536 | do_fault_siginfo(BUS_ADRERR, SIGBUS, regs, address, insn, fault_code); |
| 529 | 537 | ||
| 530 | /* Kernel mode? Handle exceptions or die */ | 538 | /* Kernel mode? Handle exceptions or die */ |
| 531 | if (regs->tstate & TSTATE_PRIV) | 539 | if (regs->tstate & TSTATE_PRIV) |
