diff options
Diffstat (limited to 'arch/powerpc/mm/fault.c')
-rw-r--r-- | arch/powerpc/mm/fault.c | 43 |
1 files changed, 28 insertions, 15 deletions
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index 51ab9e7e6c39..24b3f4949df4 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/magic.h> | 33 | #include <linux/magic.h> |
34 | #include <linux/ratelimit.h> | 34 | #include <linux/ratelimit.h> |
35 | #include <linux/context_tracking.h> | 35 | #include <linux/context_tracking.h> |
36 | #include <linux/hugetlb.h> | ||
36 | 37 | ||
37 | #include <asm/firmware.h> | 38 | #include <asm/firmware.h> |
38 | #include <asm/page.h> | 39 | #include <asm/page.h> |
@@ -114,22 +115,37 @@ static int store_updates_sp(struct pt_regs *regs) | |||
114 | #define MM_FAULT_CONTINUE -1 | 115 | #define MM_FAULT_CONTINUE -1 |
115 | #define MM_FAULT_ERR(sig) (sig) | 116 | #define MM_FAULT_ERR(sig) (sig) |
116 | 117 | ||
117 | static int do_sigbus(struct pt_regs *regs, unsigned long address) | 118 | static int do_sigbus(struct pt_regs *regs, unsigned long address, |
119 | unsigned int fault) | ||
118 | { | 120 | { |
119 | siginfo_t info; | 121 | siginfo_t info; |
122 | unsigned int lsb = 0; | ||
120 | 123 | ||
121 | up_read(¤t->mm->mmap_sem); | 124 | up_read(¤t->mm->mmap_sem); |
122 | 125 | ||
123 | if (user_mode(regs)) { | 126 | if (!user_mode(regs)) |
124 | current->thread.trap_nr = BUS_ADRERR; | 127 | return MM_FAULT_ERR(SIGBUS); |
125 | info.si_signo = SIGBUS; | 128 | |
126 | info.si_errno = 0; | 129 | current->thread.trap_nr = BUS_ADRERR; |
127 | info.si_code = BUS_ADRERR; | 130 | info.si_signo = SIGBUS; |
128 | info.si_addr = (void __user *)address; | 131 | info.si_errno = 0; |
129 | force_sig_info(SIGBUS, &info, current); | 132 | info.si_code = BUS_ADRERR; |
130 | return MM_FAULT_RETURN; | 133 | info.si_addr = (void __user *)address; |
134 | #ifdef CONFIG_MEMORY_FAILURE | ||
135 | if (fault & (VM_FAULT_HWPOISON|VM_FAULT_HWPOISON_LARGE)) { | ||
136 | pr_err("MCE: Killing %s:%d due to hardware memory corruption fault at %lx\n", | ||
137 | current->comm, current->pid, address); | ||
138 | info.si_code = BUS_MCEERR_AR; | ||
131 | } | 139 | } |
132 | return MM_FAULT_ERR(SIGBUS); | 140 | |
141 | if (fault & VM_FAULT_HWPOISON_LARGE) | ||
142 | lsb = hstate_index_to_shift(VM_FAULT_GET_HINDEX(fault)); | ||
143 | if (fault & VM_FAULT_HWPOISON) | ||
144 | lsb = PAGE_SHIFT; | ||
145 | #endif | ||
146 | info.si_addr_lsb = lsb; | ||
147 | force_sig_info(SIGBUS, &info, current); | ||
148 | return MM_FAULT_RETURN; | ||
133 | } | 149 | } |
134 | 150 | ||
135 | static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault) | 151 | static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault) |
@@ -170,11 +186,8 @@ static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault) | |||
170 | return MM_FAULT_RETURN; | 186 | return MM_FAULT_RETURN; |
171 | } | 187 | } |
172 | 188 | ||
173 | /* Bus error. x86 handles HWPOISON here, we'll add this if/when | 189 | if (fault & (VM_FAULT_SIGBUS|VM_FAULT_HWPOISON|VM_FAULT_HWPOISON_LARGE)) |
174 | * we support the feature in HW | 190 | return do_sigbus(regs, addr, fault); |
175 | */ | ||
176 | if (fault & VM_FAULT_SIGBUS) | ||
177 | return do_sigbus(regs, addr); | ||
178 | 191 | ||
179 | /* We don't understand the fault code, this is fatal */ | 192 | /* We don't understand the fault code, this is fatal */ |
180 | BUG(); | 193 | BUG(); |