Skip to content

Commit

Permalink
powerpc: Exception hooks for context tracking subsystem
Browse files Browse the repository at this point in the history
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
  commit 6ba3c97

But after the exception handling moved to generic code, and some changes in
following two commits:
56dd947
  context_tracking: Move exception handling to generic code
6c1e025
  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>
  • Loading branch information
Li Zhong authored and Benjamin Herrenschmidt committed May 14, 2013
1 parent 22ecbe8 commit ba12eed
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 45 deletions.
80 changes: 58 additions & 22 deletions arch/powerpc/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <linux/kdebug.h>
#include <linux/debugfs.h>
#include <linux/ratelimit.h>
#include <linux/context_tracking.h>

#include <asm/emulated_ops.h>
#include <asm/pgtable.h>
Expand Down Expand Up @@ -667,6 +668,7 @@ int machine_check_generic(struct pt_regs *regs)

void machine_check_exception(struct pt_regs *regs)
{
enum ctx_state prev_state = exception_enter();
int recover = 0;

__get_cpu_var(irq_stat).mce_exceptions++;
Expand All @@ -683,7 +685,7 @@ void machine_check_exception(struct pt_regs *regs)
recover = cur_cpu_spec->machine_check(regs);

if (recover > 0)
return;
goto bail;

#if defined(CONFIG_8xx) && defined(CONFIG_PCI)
/* the qspan pci read routines can cause machine checks -- Cort
Expand All @@ -693,20 +695,23 @@ void machine_check_exception(struct pt_regs *regs)
* -- BenH
*/
bad_page_fault(regs, regs->dar, SIGBUS);
return;
goto bail;
#endif

if (debugger_fault_handler(regs))
return;
goto bail;

if (check_io_access(regs))
return;
goto bail;

die("Machine check", regs, SIGBUS);

/* Must die if the interrupt is not recoverable */
if (!(regs->msr & MSR_RI))
panic("Unrecoverable Machine check");

bail:
exception_exit(prev_state);
}

void SMIException(struct pt_regs *regs)
Expand All @@ -716,20 +721,29 @@ void SMIException(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",
regs->nip, regs->msr, regs->trap);

_exception(SIGTRAP, regs, 0, 0);

exception_exit(prev_state);
}

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,
5, SIGTRAP) == NOTIFY_STOP)
return;
goto bail;
if (debugger_iabr_match(regs))
return;
goto bail;
_exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip);

bail:
exception_exit(prev_state);
}

void RunModeException(struct pt_regs *regs)
Expand All @@ -739,15 +753,20 @@ void RunModeException(struct pt_regs *regs)

void __kprobes single_step_exception(struct pt_regs *regs)
{
enum ctx_state prev_state = exception_enter();

clear_single_step(regs);

if (notify_die(DIE_SSTEP, "single_step", regs, 5,
5, SIGTRAP) == NOTIFY_STOP)
return;
goto bail;
if (debugger_sstep(regs))
return;
goto bail;

_exception(SIGTRAP, regs, TRAP_TRACE, regs->nip);

bail:
exception_exit(prev_state);
}

/*
Expand Down Expand Up @@ -1005,6 +1024,7 @@ int is_valid_bugaddr(unsigned long addr)

void __kprobes program_check_exception(struct pt_regs *regs)
{
enum ctx_state prev_state = exception_enter();
unsigned int reason = get_reason(regs);
extern int do_mathemu(struct pt_regs *regs);

Expand All @@ -1014,26 +1034,26 @@ void __kprobes program_check_exception(struct pt_regs *regs)
if (reason & REASON_FP) {
/* IEEE FP exception */
parse_fpe(regs);
return;
goto bail;
}
if (reason & REASON_TRAP) {
/* Debugger is first in line to stop recursive faults in
* rcu_lock, notify_die, or atomic_notifier_call_chain */
if (debugger_bpt(regs))
return;
goto bail;

/* trap exception */
if (notify_die(DIE_BPT, "breakpoint", regs, 5, 5, SIGTRAP)
== NOTIFY_STOP)
return;
goto bail;

if (!(regs->msr & MSR_PR) && /* not user-mode */
report_bug(regs->nip, regs) == BUG_TRAP_TYPE_WARN) {
regs->nip += 4;
return;
goto bail;
}
_exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip);
return;
goto bail;
}
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
if (reason & REASON_TM) {
Expand All @@ -1049,7 +1069,7 @@ void __kprobes program_check_exception(struct pt_regs *regs)
if (!user_mode(regs) &&
report_bug(regs->nip, regs) == BUG_TRAP_TYPE_WARN) {
regs->nip += 4;
return;
goto bail;
}
/* If usermode caused this, it's done something illegal and
* gets a SIGILL slap on the wrist. We call it an illegal
Expand All @@ -1059,7 +1079,7 @@ void __kprobes program_check_exception(struct pt_regs *regs)
*/
if (user_mode(regs)) {
_exception(SIGILL, regs, ILL_ILLOPN, regs->nip);
return;
goto bail;
} else {
printk(KERN_EMERG "Unexpected TM Bad Thing exception "
"at %lx (msr 0x%x)\n", regs->nip, reason);
Expand All @@ -1083,16 +1103,16 @@ void __kprobes program_check_exception(struct pt_regs *regs)
switch (do_mathemu(regs)) {
case 0:
emulate_single_step(regs);
return;
goto bail;
case 1: {
int code = 0;
code = __parse_fpscr(current->thread.fpscr.val);
_exception(SIGFPE, regs, code, regs->nip);
return;
goto bail;
}
case -EFAULT:
_exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
return;
goto bail;
}
/* fall through on any other errors */
#endif /* CONFIG_MATH_EMULATION */
Expand All @@ -1103,21 +1123,25 @@ void __kprobes program_check_exception(struct pt_regs *regs)
case 0:
regs->nip += 4;
emulate_single_step(regs);
return;
goto bail;
case -EFAULT:
_exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
return;
goto bail;
}
}

if (reason & REASON_PRIVILEGED)
_exception(SIGILL, regs, ILL_PRVOPC, regs->nip);
else
_exception(SIGILL, regs, ILL_ILLOPC, regs->nip);

bail:
exception_exit(prev_state);
}

void alignment_exception(struct pt_regs *regs)
{
enum ctx_state prev_state = exception_enter();
int sig, code, fixed = 0;

/* We restore the interrupt state now */
Expand All @@ -1131,7 +1155,7 @@ void alignment_exception(struct pt_regs *regs)
if (fixed == 1) {
regs->nip += 4; /* skip over emulated instruction */
emulate_single_step(regs);
return;
goto bail;
}

/* Operand address was bad */
Expand All @@ -1146,6 +1170,9 @@ void alignment_exception(struct pt_regs *regs)
_exception(sig, regs, code, regs->dar);
else
bad_page_fault(regs, regs->dar, sig);

bail:
exception_exit(prev_state);
}

void StackOverflow(struct pt_regs *regs)
Expand Down Expand Up @@ -1174,23 +1201,32 @@ void trace_syscall(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 "
"%lx at %lx\n", regs->trap, regs->nip);
die("Unrecoverable FP Unavailable Exception", regs, SIGABRT);

exception_exit(prev_state);
}

void altivec_unavailable_exception(struct pt_regs *regs)
{
enum ctx_state prev_state = exception_enter();

if (user_mode(regs)) {
/* A user program has executed an altivec instruction,
but this kernel doesn't support altivec. */
_exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
return;
goto bail;
}

printk(KERN_EMERG "Unrecoverable VMX/Altivec Unavailable Exception "
"%lx at %lx\n", regs->trap, regs->nip);
die("Unrecoverable VMX/Altivec Unavailable Exception", regs, SIGABRT);

bail:
exception_exit(prev_state);
}

void vsx_unavailable_exception(struct pt_regs *regs)
Expand Down
41 changes: 27 additions & 14 deletions arch/powerpc/mm/fault.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <linux/perf_event.h>
#include <linux/magic.h>
#include <linux/ratelimit.h>
#include <linux/context_tracking.h>

#include <asm/firmware.h>
#include <asm/page.h>
Expand Down Expand Up @@ -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,
unsigned long error_code)
{
enum ctx_state prev_state = exception_enter();
struct vm_area_struct * vma;
struct mm_struct *mm = current->mm;
unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
Expand All @@ -204,6 +206,7 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
int trap = TRAP(regs);
int is_exec = trap == 0x400;
int fault;
int rc = 0;

#if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE))
/*
Expand All @@ -230,28 +233,30 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
* look at it
*/
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)
return rc;
goto bail;
}
#endif /* CONFIG_PPC_ICSWX */

if (notify_page_fault(regs))
return 0;
goto bail;

if (unlikely(debugger_fault_handler(regs)))
return 0;
goto bail;

/* On a kernel SLB miss we can only check for a valid exception entry */
if (!user_mode(regs) && (address >= TASK_SIZE))
return SIGSEGV;
if (!user_mode(regs) && (address >= TASK_SIZE)) {
rc = SIGSEGV;
goto bail;
}

#if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE) || \
defined(CONFIG_PPC_BOOK3S_64))
if (error_code & DSISR_DABRMATCH) {
/* breakpoint match */
do_break(regs, address, error_code);
return 0;
goto bail;
}
#endif

Expand All @@ -260,8 +265,10 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
local_irq_enable();

if (in_atomic() || mm == NULL) {
if (!user_mode(regs))
return SIGSEGV;
if (!user_mode(regs)) {
rc = SIGSEGV;
goto bail;
}
/* in_atomic() in user mode is really bad,
as is current->mm == NULL. */
printk(KERN_EMERG "Page fault in user mode with "
Expand Down Expand Up @@ -417,9 +424,11 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
*/
fault = handle_mm_fault(mm, vma, address, flags);
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)
return rc;
goto bail;
else
rc = 0;
}

/*
Expand Down Expand Up @@ -454,7 +463,7 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
}

up_read(&mm->mmap_sem);
return 0;
goto bail;

bad_area:
up_read(&mm->mmap_sem);
Expand All @@ -463,15 +472,19 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
/* User mode accesses cause a SIGSEGV */
if (user_mode(regs)) {
_exception(SIGSEGV, regs, code, address);
return 0;
goto bail;
}

if (is_exec && (error_code & DSISR_PROTFAULT))
printk_ratelimited(KERN_CRIT "kernel tried to execute NX-protected"
" page (%lx) - exploit attempt? (uid: %d)\n",
address, from_kuid(&init_user_ns, current_uid()));

return SIGSEGV;
rc = SIGSEGV;

bail:
exception_exit(prev_state);
return rc;

}

Expand Down
Loading

0 comments on commit ba12eed

Please sign in to comment.