diff options
author | Jonathan (Zhixiong) Zhang <zjzhang@codeaurora.org> | 2017-06-08 13:25:27 -0400 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2017-06-12 11:04:29 -0400 |
commit | e7c600f149b89e06073ab50f4f12e79828d3d2f0 (patch) | |
tree | 145f374e08d9e7207b2de9243227f26b906c0904 | |
parent | f02ab08afbe76ee7b0b2a34a9970e7dd200d8b01 (diff) |
arm64: hwpoison: add VM_FAULT_HWPOISON[_LARGE] handling
Add VM_FAULT_HWPOISON[_LARGE] handling to the arm64 page fault
handler. Handling of VM_FAULT_HWPOISON[_LARGE] is very similar
to VM_FAULT_OOM, the only difference is that a different si_code
(BUS_MCEERR_AR) is passed to user space and si_addr_lsb field is
initialized.
Signed-off-by: Jonathan (Zhixiong) Zhang <zjzhang@codeaurora.org>
Signed-off-by: Tyler Baicar <tbaicar@codeaurora.org>
(fix new __do_user_fault call-site)
Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
Acked-by: Steve Capper <steve.capper@arm.com>
Tested-by: Manoj Iyer <manoj.iyer@canonical.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r-- | arch/arm64/mm/fault.c | 22 |
1 files changed, 19 insertions, 3 deletions
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 222427ae23d6..d73e7f1fe184 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/highmem.h> | 31 | #include <linux/highmem.h> |
32 | #include <linux/perf_event.h> | 32 | #include <linux/perf_event.h> |
33 | #include <linux/preempt.h> | 33 | #include <linux/preempt.h> |
34 | #include <linux/hugetlb.h> | ||
34 | 35 | ||
35 | #include <asm/bug.h> | 36 | #include <asm/bug.h> |
36 | #include <asm/cpufeature.h> | 37 | #include <asm/cpufeature.h> |
@@ -256,10 +257,11 @@ static void __do_kernel_fault(unsigned long addr, unsigned int esr, | |||
256 | */ | 257 | */ |
257 | static void __do_user_fault(struct task_struct *tsk, unsigned long addr, | 258 | static void __do_user_fault(struct task_struct *tsk, unsigned long addr, |
258 | unsigned int esr, unsigned int sig, int code, | 259 | unsigned int esr, unsigned int sig, int code, |
259 | struct pt_regs *regs) | 260 | struct pt_regs *regs, int fault) |
260 | { | 261 | { |
261 | struct siginfo si; | 262 | struct siginfo si; |
262 | const struct fault_info *inf; | 263 | const struct fault_info *inf; |
264 | unsigned int lsb = 0; | ||
263 | 265 | ||
264 | if (unhandled_signal(tsk, sig) && show_unhandled_signals_ratelimited()) { | 266 | if (unhandled_signal(tsk, sig) && show_unhandled_signals_ratelimited()) { |
265 | inf = esr_to_fault_info(esr); | 267 | inf = esr_to_fault_info(esr); |
@@ -277,6 +279,17 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr, | |||
277 | si.si_errno = 0; | 279 | si.si_errno = 0; |
278 | si.si_code = code; | 280 | si.si_code = code; |
279 | si.si_addr = (void __user *)addr; | 281 | si.si_addr = (void __user *)addr; |
282 | /* | ||
283 | * Either small page or large page may be poisoned. | ||
284 | * In other words, VM_FAULT_HWPOISON_LARGE and | ||
285 | * VM_FAULT_HWPOISON are mutually exclusive. | ||
286 | */ | ||
287 | if (fault & VM_FAULT_HWPOISON_LARGE) | ||
288 | lsb = hstate_index_to_shift(VM_FAULT_GET_HINDEX(fault)); | ||
289 | else if (fault & VM_FAULT_HWPOISON) | ||
290 | lsb = PAGE_SHIFT; | ||
291 | si.si_addr_lsb = lsb; | ||
292 | |||
280 | force_sig_info(sig, &si, tsk); | 293 | force_sig_info(sig, &si, tsk); |
281 | } | 294 | } |
282 | 295 | ||
@@ -291,7 +304,7 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re | |||
291 | */ | 304 | */ |
292 | if (user_mode(regs)) { | 305 | if (user_mode(regs)) { |
293 | inf = esr_to_fault_info(esr); | 306 | inf = esr_to_fault_info(esr); |
294 | __do_user_fault(tsk, addr, esr, inf->sig, inf->code, regs); | 307 | __do_user_fault(tsk, addr, esr, inf->sig, inf->code, regs, 0); |
295 | } else | 308 | } else |
296 | __do_kernel_fault(addr, esr, regs); | 309 | __do_kernel_fault(addr, esr, regs); |
297 | } | 310 | } |
@@ -478,6 +491,9 @@ retry: | |||
478 | */ | 491 | */ |
479 | sig = SIGBUS; | 492 | sig = SIGBUS; |
480 | code = BUS_ADRERR; | 493 | code = BUS_ADRERR; |
494 | } else if (fault & (VM_FAULT_HWPOISON | VM_FAULT_HWPOISON_LARGE)) { | ||
495 | sig = SIGBUS; | ||
496 | code = BUS_MCEERR_AR; | ||
481 | } else { | 497 | } else { |
482 | /* | 498 | /* |
483 | * Something tried to access memory that isn't in our memory | 499 | * Something tried to access memory that isn't in our memory |
@@ -488,7 +504,7 @@ retry: | |||
488 | SEGV_ACCERR : SEGV_MAPERR; | 504 | SEGV_ACCERR : SEGV_MAPERR; |
489 | } | 505 | } |
490 | 506 | ||
491 | __do_user_fault(tsk, addr, esr, sig, code, regs); | 507 | __do_user_fault(tsk, addr, esr, sig, code, regs, fault); |
492 | return 0; | 508 | return 0; |
493 | 509 | ||
494 | no_context: | 510 | no_context: |