powerpc: Exception hooks for context tracking subsystem
This is the exception hooks for context tracking subsystem, including data access, program check, single step, instruction breakpoint, machine check, alignment, fp unavailable, altivec assist, unknown exception, whose handlers might use RCU. This patch corresponds to [PATCH] x86: Exception hooks for userspace RCU extended QS commit6ba3c97a38
But after the exception handling moved to generic code, and some changes in following two commits:56dd9470d7
context_tracking: Move exception handling to generic code6c1e0256fa
context_tracking: Restore correct previous context state on exception exit it is able for exception hooks to use the generic code above instead of a redundant arch implementation. Signed-off-by: Li Zhong <zhong@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This commit is contained in:
parent
22ecbe8dce
commit
ba12eedee3
3 changed files with 112 additions and 45 deletions
|
@ -35,6 +35,7 @@
|
||||||
#include <linux/kdebug.h>
|
#include <linux/kdebug.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
#include <linux/ratelimit.h>
|
#include <linux/ratelimit.h>
|
||||||
|
#include <linux/context_tracking.h>
|
||||||
|
|
||||||
#include <asm/emulated_ops.h>
|
#include <asm/emulated_ops.h>
|
||||||
#include <asm/pgtable.h>
|
#include <asm/pgtable.h>
|
||||||
|
@ -667,6 +668,7 @@ int machine_check_generic(struct pt_regs *regs)
|
||||||
|
|
||||||
void machine_check_exception(struct pt_regs *regs)
|
void machine_check_exception(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
enum ctx_state prev_state = exception_enter();
|
||||||
int recover = 0;
|
int recover = 0;
|
||||||
|
|
||||||
__get_cpu_var(irq_stat).mce_exceptions++;
|
__get_cpu_var(irq_stat).mce_exceptions++;
|
||||||
|
@ -683,7 +685,7 @@ void machine_check_exception(struct pt_regs *regs)
|
||||||
recover = cur_cpu_spec->machine_check(regs);
|
recover = cur_cpu_spec->machine_check(regs);
|
||||||
|
|
||||||
if (recover > 0)
|
if (recover > 0)
|
||||||
return;
|
goto bail;
|
||||||
|
|
||||||
#if defined(CONFIG_8xx) && defined(CONFIG_PCI)
|
#if defined(CONFIG_8xx) && defined(CONFIG_PCI)
|
||||||
/* the qspan pci read routines can cause machine checks -- Cort
|
/* the qspan pci read routines can cause machine checks -- Cort
|
||||||
|
@ -693,20 +695,23 @@ void machine_check_exception(struct pt_regs *regs)
|
||||||
* -- BenH
|
* -- BenH
|
||||||
*/
|
*/
|
||||||
bad_page_fault(regs, regs->dar, SIGBUS);
|
bad_page_fault(regs, regs->dar, SIGBUS);
|
||||||
return;
|
goto bail;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (debugger_fault_handler(regs))
|
if (debugger_fault_handler(regs))
|
||||||
return;
|
goto bail;
|
||||||
|
|
||||||
if (check_io_access(regs))
|
if (check_io_access(regs))
|
||||||
return;
|
goto bail;
|
||||||
|
|
||||||
die("Machine check", regs, SIGBUS);
|
die("Machine check", regs, SIGBUS);
|
||||||
|
|
||||||
/* Must die if the interrupt is not recoverable */
|
/* Must die if the interrupt is not recoverable */
|
||||||
if (!(regs->msr & MSR_RI))
|
if (!(regs->msr & MSR_RI))
|
||||||
panic("Unrecoverable Machine check");
|
panic("Unrecoverable Machine check");
|
||||||
|
|
||||||
|
bail:
|
||||||
|
exception_exit(prev_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SMIException(struct pt_regs *regs)
|
void SMIException(struct pt_regs *regs)
|
||||||
|
@ -716,20 +721,29 @@ void SMIException(struct pt_regs *regs)
|
||||||
|
|
||||||
void unknown_exception(struct pt_regs *regs)
|
void unknown_exception(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
enum ctx_state prev_state = exception_enter();
|
||||||
|
|
||||||
printk("Bad trap at PC: %lx, SR: %lx, vector=%lx\n",
|
printk("Bad trap at PC: %lx, SR: %lx, vector=%lx\n",
|
||||||
regs->nip, regs->msr, regs->trap);
|
regs->nip, regs->msr, regs->trap);
|
||||||
|
|
||||||
_exception(SIGTRAP, regs, 0, 0);
|
_exception(SIGTRAP, regs, 0, 0);
|
||||||
|
|
||||||
|
exception_exit(prev_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void instruction_breakpoint_exception(struct pt_regs *regs)
|
void instruction_breakpoint_exception(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
enum ctx_state prev_state = exception_enter();
|
||||||
|
|
||||||
if (notify_die(DIE_IABR_MATCH, "iabr_match", regs, 5,
|
if (notify_die(DIE_IABR_MATCH, "iabr_match", regs, 5,
|
||||||
5, SIGTRAP) == NOTIFY_STOP)
|
5, SIGTRAP) == NOTIFY_STOP)
|
||||||
return;
|
goto bail;
|
||||||
if (debugger_iabr_match(regs))
|
if (debugger_iabr_match(regs))
|
||||||
return;
|
goto bail;
|
||||||
_exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip);
|
_exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip);
|
||||||
|
|
||||||
|
bail:
|
||||||
|
exception_exit(prev_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunModeException(struct pt_regs *regs)
|
void RunModeException(struct pt_regs *regs)
|
||||||
|
@ -739,15 +753,20 @@ void RunModeException(struct pt_regs *regs)
|
||||||
|
|
||||||
void __kprobes single_step_exception(struct pt_regs *regs)
|
void __kprobes single_step_exception(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
enum ctx_state prev_state = exception_enter();
|
||||||
|
|
||||||
clear_single_step(regs);
|
clear_single_step(regs);
|
||||||
|
|
||||||
if (notify_die(DIE_SSTEP, "single_step", regs, 5,
|
if (notify_die(DIE_SSTEP, "single_step", regs, 5,
|
||||||
5, SIGTRAP) == NOTIFY_STOP)
|
5, SIGTRAP) == NOTIFY_STOP)
|
||||||
return;
|
goto bail;
|
||||||
if (debugger_sstep(regs))
|
if (debugger_sstep(regs))
|
||||||
return;
|
goto bail;
|
||||||
|
|
||||||
_exception(SIGTRAP, regs, TRAP_TRACE, regs->nip);
|
_exception(SIGTRAP, regs, TRAP_TRACE, regs->nip);
|
||||||
|
|
||||||
|
bail:
|
||||||
|
exception_exit(prev_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1005,6 +1024,7 @@ int is_valid_bugaddr(unsigned long addr)
|
||||||
|
|
||||||
void __kprobes program_check_exception(struct pt_regs *regs)
|
void __kprobes program_check_exception(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
enum ctx_state prev_state = exception_enter();
|
||||||
unsigned int reason = get_reason(regs);
|
unsigned int reason = get_reason(regs);
|
||||||
extern int do_mathemu(struct pt_regs *regs);
|
extern int do_mathemu(struct pt_regs *regs);
|
||||||
|
|
||||||
|
@ -1014,26 +1034,26 @@ void __kprobes program_check_exception(struct pt_regs *regs)
|
||||||
if (reason & REASON_FP) {
|
if (reason & REASON_FP) {
|
||||||
/* IEEE FP exception */
|
/* IEEE FP exception */
|
||||||
parse_fpe(regs);
|
parse_fpe(regs);
|
||||||
return;
|
goto bail;
|
||||||
}
|
}
|
||||||
if (reason & REASON_TRAP) {
|
if (reason & REASON_TRAP) {
|
||||||
/* Debugger is first in line to stop recursive faults in
|
/* Debugger is first in line to stop recursive faults in
|
||||||
* rcu_lock, notify_die, or atomic_notifier_call_chain */
|
* rcu_lock, notify_die, or atomic_notifier_call_chain */
|
||||||
if (debugger_bpt(regs))
|
if (debugger_bpt(regs))
|
||||||
return;
|
goto bail;
|
||||||
|
|
||||||
/* trap exception */
|
/* trap exception */
|
||||||
if (notify_die(DIE_BPT, "breakpoint", regs, 5, 5, SIGTRAP)
|
if (notify_die(DIE_BPT, "breakpoint", regs, 5, 5, SIGTRAP)
|
||||||
== NOTIFY_STOP)
|
== NOTIFY_STOP)
|
||||||
return;
|
goto bail;
|
||||||
|
|
||||||
if (!(regs->msr & MSR_PR) && /* not user-mode */
|
if (!(regs->msr & MSR_PR) && /* not user-mode */
|
||||||
report_bug(regs->nip, regs) == BUG_TRAP_TYPE_WARN) {
|
report_bug(regs->nip, regs) == BUG_TRAP_TYPE_WARN) {
|
||||||
regs->nip += 4;
|
regs->nip += 4;
|
||||||
return;
|
goto bail;
|
||||||
}
|
}
|
||||||
_exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip);
|
_exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip);
|
||||||
return;
|
goto bail;
|
||||||
}
|
}
|
||||||
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
|
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
|
||||||
if (reason & REASON_TM) {
|
if (reason & REASON_TM) {
|
||||||
|
@ -1049,7 +1069,7 @@ void __kprobes program_check_exception(struct pt_regs *regs)
|
||||||
if (!user_mode(regs) &&
|
if (!user_mode(regs) &&
|
||||||
report_bug(regs->nip, regs) == BUG_TRAP_TYPE_WARN) {
|
report_bug(regs->nip, regs) == BUG_TRAP_TYPE_WARN) {
|
||||||
regs->nip += 4;
|
regs->nip += 4;
|
||||||
return;
|
goto bail;
|
||||||
}
|
}
|
||||||
/* If usermode caused this, it's done something illegal and
|
/* If usermode caused this, it's done something illegal and
|
||||||
* gets a SIGILL slap on the wrist. We call it an illegal
|
* gets a SIGILL slap on the wrist. We call it an illegal
|
||||||
|
@ -1059,7 +1079,7 @@ void __kprobes program_check_exception(struct pt_regs *regs)
|
||||||
*/
|
*/
|
||||||
if (user_mode(regs)) {
|
if (user_mode(regs)) {
|
||||||
_exception(SIGILL, regs, ILL_ILLOPN, regs->nip);
|
_exception(SIGILL, regs, ILL_ILLOPN, regs->nip);
|
||||||
return;
|
goto bail;
|
||||||
} else {
|
} else {
|
||||||
printk(KERN_EMERG "Unexpected TM Bad Thing exception "
|
printk(KERN_EMERG "Unexpected TM Bad Thing exception "
|
||||||
"at %lx (msr 0x%x)\n", regs->nip, reason);
|
"at %lx (msr 0x%x)\n", regs->nip, reason);
|
||||||
|
@ -1083,16 +1103,16 @@ void __kprobes program_check_exception(struct pt_regs *regs)
|
||||||
switch (do_mathemu(regs)) {
|
switch (do_mathemu(regs)) {
|
||||||
case 0:
|
case 0:
|
||||||
emulate_single_step(regs);
|
emulate_single_step(regs);
|
||||||
return;
|
goto bail;
|
||||||
case 1: {
|
case 1: {
|
||||||
int code = 0;
|
int code = 0;
|
||||||
code = __parse_fpscr(current->thread.fpscr.val);
|
code = __parse_fpscr(current->thread.fpscr.val);
|
||||||
_exception(SIGFPE, regs, code, regs->nip);
|
_exception(SIGFPE, regs, code, regs->nip);
|
||||||
return;
|
goto bail;
|
||||||
}
|
}
|
||||||
case -EFAULT:
|
case -EFAULT:
|
||||||
_exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
|
_exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
|
||||||
return;
|
goto bail;
|
||||||
}
|
}
|
||||||
/* fall through on any other errors */
|
/* fall through on any other errors */
|
||||||
#endif /* CONFIG_MATH_EMULATION */
|
#endif /* CONFIG_MATH_EMULATION */
|
||||||
|
@ -1103,10 +1123,10 @@ void __kprobes program_check_exception(struct pt_regs *regs)
|
||||||
case 0:
|
case 0:
|
||||||
regs->nip += 4;
|
regs->nip += 4;
|
||||||
emulate_single_step(regs);
|
emulate_single_step(regs);
|
||||||
return;
|
goto bail;
|
||||||
case -EFAULT:
|
case -EFAULT:
|
||||||
_exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
|
_exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
|
||||||
return;
|
goto bail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1114,10 +1134,14 @@ void __kprobes program_check_exception(struct pt_regs *regs)
|
||||||
_exception(SIGILL, regs, ILL_PRVOPC, regs->nip);
|
_exception(SIGILL, regs, ILL_PRVOPC, regs->nip);
|
||||||
else
|
else
|
||||||
_exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
|
_exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
|
||||||
|
|
||||||
|
bail:
|
||||||
|
exception_exit(prev_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void alignment_exception(struct pt_regs *regs)
|
void alignment_exception(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
enum ctx_state prev_state = exception_enter();
|
||||||
int sig, code, fixed = 0;
|
int sig, code, fixed = 0;
|
||||||
|
|
||||||
/* We restore the interrupt state now */
|
/* We restore the interrupt state now */
|
||||||
|
@ -1131,7 +1155,7 @@ void alignment_exception(struct pt_regs *regs)
|
||||||
if (fixed == 1) {
|
if (fixed == 1) {
|
||||||
regs->nip += 4; /* skip over emulated instruction */
|
regs->nip += 4; /* skip over emulated instruction */
|
||||||
emulate_single_step(regs);
|
emulate_single_step(regs);
|
||||||
return;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Operand address was bad */
|
/* Operand address was bad */
|
||||||
|
@ -1146,6 +1170,9 @@ void alignment_exception(struct pt_regs *regs)
|
||||||
_exception(sig, regs, code, regs->dar);
|
_exception(sig, regs, code, regs->dar);
|
||||||
else
|
else
|
||||||
bad_page_fault(regs, regs->dar, sig);
|
bad_page_fault(regs, regs->dar, sig);
|
||||||
|
|
||||||
|
bail:
|
||||||
|
exception_exit(prev_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StackOverflow(struct pt_regs *regs)
|
void StackOverflow(struct pt_regs *regs)
|
||||||
|
@ -1174,23 +1201,32 @@ void trace_syscall(struct pt_regs *regs)
|
||||||
|
|
||||||
void kernel_fp_unavailable_exception(struct pt_regs *regs)
|
void kernel_fp_unavailable_exception(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
enum ctx_state prev_state = exception_enter();
|
||||||
|
|
||||||
printk(KERN_EMERG "Unrecoverable FP Unavailable Exception "
|
printk(KERN_EMERG "Unrecoverable FP Unavailable Exception "
|
||||||
"%lx at %lx\n", regs->trap, regs->nip);
|
"%lx at %lx\n", regs->trap, regs->nip);
|
||||||
die("Unrecoverable FP Unavailable Exception", regs, SIGABRT);
|
die("Unrecoverable FP Unavailable Exception", regs, SIGABRT);
|
||||||
|
|
||||||
|
exception_exit(prev_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void altivec_unavailable_exception(struct pt_regs *regs)
|
void altivec_unavailable_exception(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
enum ctx_state prev_state = exception_enter();
|
||||||
|
|
||||||
if (user_mode(regs)) {
|
if (user_mode(regs)) {
|
||||||
/* A user program has executed an altivec instruction,
|
/* A user program has executed an altivec instruction,
|
||||||
but this kernel doesn't support altivec. */
|
but this kernel doesn't support altivec. */
|
||||||
_exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
|
_exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
|
||||||
return;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
||||||
printk(KERN_EMERG "Unrecoverable VMX/Altivec Unavailable Exception "
|
printk(KERN_EMERG "Unrecoverable VMX/Altivec Unavailable Exception "
|
||||||
"%lx at %lx\n", regs->trap, regs->nip);
|
"%lx at %lx\n", regs->trap, regs->nip);
|
||||||
die("Unrecoverable VMX/Altivec Unavailable Exception", regs, SIGABRT);
|
die("Unrecoverable VMX/Altivec Unavailable Exception", regs, SIGABRT);
|
||||||
|
|
||||||
|
bail:
|
||||||
|
exception_exit(prev_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vsx_unavailable_exception(struct pt_regs *regs)
|
void vsx_unavailable_exception(struct pt_regs *regs)
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <linux/perf_event.h>
|
#include <linux/perf_event.h>
|
||||||
#include <linux/magic.h>
|
#include <linux/magic.h>
|
||||||
#include <linux/ratelimit.h>
|
#include <linux/ratelimit.h>
|
||||||
|
#include <linux/context_tracking.h>
|
||||||
|
|
||||||
#include <asm/firmware.h>
|
#include <asm/firmware.h>
|
||||||
#include <asm/page.h>
|
#include <asm/page.h>
|
||||||
|
@ -196,6 +197,7 @@ static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault)
|
||||||
int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
|
int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
|
||||||
unsigned long error_code)
|
unsigned long error_code)
|
||||||
{
|
{
|
||||||
|
enum ctx_state prev_state = exception_enter();
|
||||||
struct vm_area_struct * vma;
|
struct vm_area_struct * vma;
|
||||||
struct mm_struct *mm = current->mm;
|
struct mm_struct *mm = current->mm;
|
||||||
unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
|
unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
|
||||||
|
@ -204,6 +206,7 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
|
||||||
int trap = TRAP(regs);
|
int trap = TRAP(regs);
|
||||||
int is_exec = trap == 0x400;
|
int is_exec = trap == 0x400;
|
||||||
int fault;
|
int fault;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
#if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE))
|
#if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE))
|
||||||
/*
|
/*
|
||||||
|
@ -230,28 +233,30 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
|
||||||
* look at it
|
* look at it
|
||||||
*/
|
*/
|
||||||
if (error_code & ICSWX_DSI_UCT) {
|
if (error_code & ICSWX_DSI_UCT) {
|
||||||
int rc = acop_handle_fault(regs, address, error_code);
|
rc = acop_handle_fault(regs, address, error_code);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
goto bail;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_PPC_ICSWX */
|
#endif /* CONFIG_PPC_ICSWX */
|
||||||
|
|
||||||
if (notify_page_fault(regs))
|
if (notify_page_fault(regs))
|
||||||
return 0;
|
goto bail;
|
||||||
|
|
||||||
if (unlikely(debugger_fault_handler(regs)))
|
if (unlikely(debugger_fault_handler(regs)))
|
||||||
return 0;
|
goto bail;
|
||||||
|
|
||||||
/* On a kernel SLB miss we can only check for a valid exception entry */
|
/* On a kernel SLB miss we can only check for a valid exception entry */
|
||||||
if (!user_mode(regs) && (address >= TASK_SIZE))
|
if (!user_mode(regs) && (address >= TASK_SIZE)) {
|
||||||
return SIGSEGV;
|
rc = SIGSEGV;
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
#if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE) || \
|
#if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE) || \
|
||||||
defined(CONFIG_PPC_BOOK3S_64))
|
defined(CONFIG_PPC_BOOK3S_64))
|
||||||
if (error_code & DSISR_DABRMATCH) {
|
if (error_code & DSISR_DABRMATCH) {
|
||||||
/* breakpoint match */
|
/* breakpoint match */
|
||||||
do_break(regs, address, error_code);
|
do_break(regs, address, error_code);
|
||||||
return 0;
|
goto bail;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -260,8 +265,10 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
|
||||||
local_irq_enable();
|
local_irq_enable();
|
||||||
|
|
||||||
if (in_atomic() || mm == NULL) {
|
if (in_atomic() || mm == NULL) {
|
||||||
if (!user_mode(regs))
|
if (!user_mode(regs)) {
|
||||||
return SIGSEGV;
|
rc = SIGSEGV;
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
/* in_atomic() in user mode is really bad,
|
/* in_atomic() in user mode is really bad,
|
||||||
as is current->mm == NULL. */
|
as is current->mm == NULL. */
|
||||||
printk(KERN_EMERG "Page fault in user mode with "
|
printk(KERN_EMERG "Page fault in user mode with "
|
||||||
|
@ -417,9 +424,11 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
|
||||||
*/
|
*/
|
||||||
fault = handle_mm_fault(mm, vma, address, flags);
|
fault = handle_mm_fault(mm, vma, address, flags);
|
||||||
if (unlikely(fault & (VM_FAULT_RETRY|VM_FAULT_ERROR))) {
|
if (unlikely(fault & (VM_FAULT_RETRY|VM_FAULT_ERROR))) {
|
||||||
int rc = mm_fault_error(regs, address, fault);
|
rc = mm_fault_error(regs, address, fault);
|
||||||
if (rc >= MM_FAULT_RETURN)
|
if (rc >= MM_FAULT_RETURN)
|
||||||
return rc;
|
goto bail;
|
||||||
|
else
|
||||||
|
rc = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -454,7 +463,7 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
|
||||||
}
|
}
|
||||||
|
|
||||||
up_read(&mm->mmap_sem);
|
up_read(&mm->mmap_sem);
|
||||||
return 0;
|
goto bail;
|
||||||
|
|
||||||
bad_area:
|
bad_area:
|
||||||
up_read(&mm->mmap_sem);
|
up_read(&mm->mmap_sem);
|
||||||
|
@ -463,7 +472,7 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
|
||||||
/* User mode accesses cause a SIGSEGV */
|
/* User mode accesses cause a SIGSEGV */
|
||||||
if (user_mode(regs)) {
|
if (user_mode(regs)) {
|
||||||
_exception(SIGSEGV, regs, code, address);
|
_exception(SIGSEGV, regs, code, address);
|
||||||
return 0;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_exec && (error_code & DSISR_PROTFAULT))
|
if (is_exec && (error_code & DSISR_PROTFAULT))
|
||||||
|
@ -471,7 +480,11 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
|
||||||
" page (%lx) - exploit attempt? (uid: %d)\n",
|
" page (%lx) - exploit attempt? (uid: %d)\n",
|
||||||
address, from_kuid(&init_user_ns, current_uid()));
|
address, from_kuid(&init_user_ns, current_uid()));
|
||||||
|
|
||||||
return SIGSEGV;
|
rc = SIGSEGV;
|
||||||
|
|
||||||
|
bail:
|
||||||
|
exception_exit(prev_state);
|
||||||
|
return rc;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/signal.h>
|
#include <linux/signal.h>
|
||||||
#include <linux/memblock.h>
|
#include <linux/memblock.h>
|
||||||
|
#include <linux/context_tracking.h>
|
||||||
|
|
||||||
#include <asm/processor.h>
|
#include <asm/processor.h>
|
||||||
#include <asm/pgtable.h>
|
#include <asm/pgtable.h>
|
||||||
|
@ -954,6 +955,7 @@ void hash_failure_debug(unsigned long ea, unsigned long access,
|
||||||
*/
|
*/
|
||||||
int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
|
int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
|
||||||
{
|
{
|
||||||
|
enum ctx_state prev_state = exception_enter();
|
||||||
pgd_t *pgdir;
|
pgd_t *pgdir;
|
||||||
unsigned long vsid;
|
unsigned long vsid;
|
||||||
struct mm_struct *mm;
|
struct mm_struct *mm;
|
||||||
|
@ -973,7 +975,8 @@ int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
|
||||||
mm = current->mm;
|
mm = current->mm;
|
||||||
if (! mm) {
|
if (! mm) {
|
||||||
DBG_LOW(" user region with no mm !\n");
|
DBG_LOW(" user region with no mm !\n");
|
||||||
return 1;
|
rc = 1;
|
||||||
|
goto bail;
|
||||||
}
|
}
|
||||||
psize = get_slice_psize(mm, ea);
|
psize = get_slice_psize(mm, ea);
|
||||||
ssize = user_segment_size(ea);
|
ssize = user_segment_size(ea);
|
||||||
|
@ -992,19 +995,23 @@ int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
|
||||||
/* Not a valid range
|
/* Not a valid range
|
||||||
* Send the problem up to do_page_fault
|
* Send the problem up to do_page_fault
|
||||||
*/
|
*/
|
||||||
return 1;
|
rc = 1;
|
||||||
|
goto bail;
|
||||||
}
|
}
|
||||||
DBG_LOW(" mm=%p, mm->pgdir=%p, vsid=%016lx\n", mm, mm->pgd, vsid);
|
DBG_LOW(" mm=%p, mm->pgdir=%p, vsid=%016lx\n", mm, mm->pgd, vsid);
|
||||||
|
|
||||||
/* Bad address. */
|
/* Bad address. */
|
||||||
if (!vsid) {
|
if (!vsid) {
|
||||||
DBG_LOW("Bad address!\n");
|
DBG_LOW("Bad address!\n");
|
||||||
return 1;
|
rc = 1;
|
||||||
|
goto bail;
|
||||||
}
|
}
|
||||||
/* Get pgdir */
|
/* Get pgdir */
|
||||||
pgdir = mm->pgd;
|
pgdir = mm->pgd;
|
||||||
if (pgdir == NULL)
|
if (pgdir == NULL) {
|
||||||
return 1;
|
rc = 1;
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check CPU locality */
|
/* Check CPU locality */
|
||||||
tmp = cpumask_of(smp_processor_id());
|
tmp = cpumask_of(smp_processor_id());
|
||||||
|
@ -1027,7 +1034,8 @@ int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
|
||||||
ptep = find_linux_pte_or_hugepte(pgdir, ea, &hugeshift);
|
ptep = find_linux_pte_or_hugepte(pgdir, ea, &hugeshift);
|
||||||
if (ptep == NULL || !pte_present(*ptep)) {
|
if (ptep == NULL || !pte_present(*ptep)) {
|
||||||
DBG_LOW(" no PTE !\n");
|
DBG_LOW(" no PTE !\n");
|
||||||
return 1;
|
rc = 1;
|
||||||
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add _PAGE_PRESENT to the required access perm */
|
/* Add _PAGE_PRESENT to the required access perm */
|
||||||
|
@ -1038,13 +1046,16 @@ int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
|
||||||
*/
|
*/
|
||||||
if (access & ~pte_val(*ptep)) {
|
if (access & ~pte_val(*ptep)) {
|
||||||
DBG_LOW(" no access !\n");
|
DBG_LOW(" no access !\n");
|
||||||
return 1;
|
rc = 1;
|
||||||
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_HUGETLB_PAGE
|
#ifdef CONFIG_HUGETLB_PAGE
|
||||||
if (hugeshift)
|
if (hugeshift) {
|
||||||
return __hash_page_huge(ea, access, vsid, ptep, trap, local,
|
rc = __hash_page_huge(ea, access, vsid, ptep, trap, local,
|
||||||
ssize, hugeshift, psize);
|
ssize, hugeshift, psize);
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
#endif /* CONFIG_HUGETLB_PAGE */
|
#endif /* CONFIG_HUGETLB_PAGE */
|
||||||
|
|
||||||
#ifndef CONFIG_PPC_64K_PAGES
|
#ifndef CONFIG_PPC_64K_PAGES
|
||||||
|
@ -1124,6 +1135,9 @@ int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
|
||||||
pte_val(*(ptep + PTRS_PER_PTE)));
|
pte_val(*(ptep + PTRS_PER_PTE)));
|
||||||
#endif
|
#endif
|
||||||
DBG_LOW(" -> rc=%d\n", rc);
|
DBG_LOW(" -> rc=%d\n", rc);
|
||||||
|
|
||||||
|
bail:
|
||||||
|
exception_exit(prev_state);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(hash_page);
|
EXPORT_SYMBOL_GPL(hash_page);
|
||||||
|
@ -1259,6 +1273,8 @@ void flush_hash_range(unsigned long number, int local)
|
||||||
*/
|
*/
|
||||||
void low_hash_fault(struct pt_regs *regs, unsigned long address, int rc)
|
void low_hash_fault(struct pt_regs *regs, unsigned long address, int rc)
|
||||||
{
|
{
|
||||||
|
enum ctx_state prev_state = exception_enter();
|
||||||
|
|
||||||
if (user_mode(regs)) {
|
if (user_mode(regs)) {
|
||||||
#ifdef CONFIG_PPC_SUBPAGE_PROT
|
#ifdef CONFIG_PPC_SUBPAGE_PROT
|
||||||
if (rc == -2)
|
if (rc == -2)
|
||||||
|
@ -1268,6 +1284,8 @@ void low_hash_fault(struct pt_regs *regs, unsigned long address, int rc)
|
||||||
_exception(SIGBUS, regs, BUS_ADRERR, address);
|
_exception(SIGBUS, regs, BUS_ADRERR, address);
|
||||||
} else
|
} else
|
||||||
bad_page_fault(regs, address, SIGBUS);
|
bad_page_fault(regs, address, SIGBUS);
|
||||||
|
|
||||||
|
exception_exit(prev_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
long hpte_insert_repeating(unsigned long hash, unsigned long vpn,
|
long hpte_insert_repeating(unsigned long hash, unsigned long vpn,
|
||||||
|
|
Loading…
Reference in a new issue