hw-breakpoints: use the new wrapper routines to access debug registers in process/thread code
This patch enables the use of abstract debug registers in process-handling routines, according to the new hardware breakpoint Api. [ Impact: adapt thread breakpoints handling code to the new breakpoint Api ] Original-patch-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: K.Prasad <prasad@linux.vnet.ibm.com> Reviewed-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
This commit is contained in:
parent
1e3500666f
commit
66cb591729
|
@ -15,6 +15,8 @@
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/i387.h>
|
#include <asm/i387.h>
|
||||||
#include <asm/ds.h>
|
#include <asm/ds.h>
|
||||||
|
#include <asm/debugreg.h>
|
||||||
|
#include <asm/hw_breakpoint.h>
|
||||||
|
|
||||||
unsigned long idle_halt;
|
unsigned long idle_halt;
|
||||||
EXPORT_SYMBOL(idle_halt);
|
EXPORT_SYMBOL(idle_halt);
|
||||||
|
@ -46,6 +48,8 @@ void free_thread_xstate(struct task_struct *tsk)
|
||||||
kmem_cache_free(task_xstate_cachep, tsk->thread.xstate);
|
kmem_cache_free(task_xstate_cachep, tsk->thread.xstate);
|
||||||
tsk->thread.xstate = NULL;
|
tsk->thread.xstate = NULL;
|
||||||
}
|
}
|
||||||
|
if (unlikely(test_tsk_thread_flag(tsk, TIF_DEBUG)))
|
||||||
|
flush_thread_hw_breakpoint(tsk);
|
||||||
|
|
||||||
WARN(tsk->thread.ds_ctx, "leaking DS context\n");
|
WARN(tsk->thread.ds_ctx, "leaking DS context\n");
|
||||||
}
|
}
|
||||||
|
@ -106,12 +110,8 @@ void flush_thread(void)
|
||||||
|
|
||||||
clear_tsk_thread_flag(tsk, TIF_DEBUG);
|
clear_tsk_thread_flag(tsk, TIF_DEBUG);
|
||||||
|
|
||||||
tsk->thread.debugreg[0] = 0;
|
if (unlikely(test_tsk_thread_flag(tsk, TIF_DEBUG)))
|
||||||
tsk->thread.debugreg[1] = 0;
|
flush_thread_hw_breakpoint(tsk);
|
||||||
tsk->thread.debugreg[2] = 0;
|
|
||||||
tsk->thread.debugreg[3] = 0;
|
|
||||||
tsk->thread.debugreg6 = 0;
|
|
||||||
tsk->thread.debugreg7 = 0;
|
|
||||||
memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array));
|
memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array));
|
||||||
/*
|
/*
|
||||||
* Forget coprocessor state..
|
* Forget coprocessor state..
|
||||||
|
@ -193,16 +193,6 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
|
||||||
else if (next->debugctlmsr != prev->debugctlmsr)
|
else if (next->debugctlmsr != prev->debugctlmsr)
|
||||||
update_debugctlmsr(next->debugctlmsr);
|
update_debugctlmsr(next->debugctlmsr);
|
||||||
|
|
||||||
if (test_tsk_thread_flag(next_p, TIF_DEBUG)) {
|
|
||||||
set_debugreg(next->debugreg[0], 0);
|
|
||||||
set_debugreg(next->debugreg[1], 1);
|
|
||||||
set_debugreg(next->debugreg[2], 2);
|
|
||||||
set_debugreg(next->debugreg[3], 3);
|
|
||||||
/* no 4 and 5 */
|
|
||||||
set_debugreg(next->debugreg6, 6);
|
|
||||||
set_debugreg(next->debugreg7, 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (test_tsk_thread_flag(prev_p, TIF_NOTSC) ^
|
if (test_tsk_thread_flag(prev_p, TIF_NOTSC) ^
|
||||||
test_tsk_thread_flag(next_p, TIF_NOTSC)) {
|
test_tsk_thread_flag(next_p, TIF_NOTSC)) {
|
||||||
/* prev and next are different */
|
/* prev and next are different */
|
||||||
|
|
|
@ -61,6 +61,8 @@
|
||||||
#include <asm/idle.h>
|
#include <asm/idle.h>
|
||||||
#include <asm/syscalls.h>
|
#include <asm/syscalls.h>
|
||||||
#include <asm/ds.h>
|
#include <asm/ds.h>
|
||||||
|
#include <asm/debugreg.h>
|
||||||
|
#include <asm/hw_breakpoint.h>
|
||||||
|
|
||||||
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
|
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
|
||||||
|
|
||||||
|
@ -265,7 +267,13 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
|
||||||
|
|
||||||
task_user_gs(p) = get_user_gs(regs);
|
task_user_gs(p) = get_user_gs(regs);
|
||||||
|
|
||||||
|
p->thread.io_bitmap_ptr = NULL;
|
||||||
tsk = current;
|
tsk = current;
|
||||||
|
err = -ENOMEM;
|
||||||
|
if (unlikely(test_tsk_thread_flag(tsk, TIF_DEBUG)))
|
||||||
|
if (copy_thread_hw_breakpoint(tsk, p, clone_flags))
|
||||||
|
goto out;
|
||||||
|
|
||||||
if (unlikely(test_tsk_thread_flag(tsk, TIF_IO_BITMAP))) {
|
if (unlikely(test_tsk_thread_flag(tsk, TIF_IO_BITMAP))) {
|
||||||
p->thread.io_bitmap_ptr = kmemdup(tsk->thread.io_bitmap_ptr,
|
p->thread.io_bitmap_ptr = kmemdup(tsk->thread.io_bitmap_ptr,
|
||||||
IO_BITMAP_BYTES, GFP_KERNEL);
|
IO_BITMAP_BYTES, GFP_KERNEL);
|
||||||
|
@ -285,10 +293,13 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
|
||||||
err = do_set_thread_area(p, -1,
|
err = do_set_thread_area(p, -1,
|
||||||
(struct user_desc __user *)childregs->si, 0);
|
(struct user_desc __user *)childregs->si, 0);
|
||||||
|
|
||||||
|
out:
|
||||||
if (err && p->thread.io_bitmap_ptr) {
|
if (err && p->thread.io_bitmap_ptr) {
|
||||||
kfree(p->thread.io_bitmap_ptr);
|
kfree(p->thread.io_bitmap_ptr);
|
||||||
p->thread.io_bitmap_max = 0;
|
p->thread.io_bitmap_max = 0;
|
||||||
}
|
}
|
||||||
|
if (err)
|
||||||
|
flush_thread_hw_breakpoint(p);
|
||||||
|
|
||||||
clear_tsk_thread_flag(p, TIF_DS_AREA_MSR);
|
clear_tsk_thread_flag(p, TIF_DS_AREA_MSR);
|
||||||
p->thread.ds_ctx = NULL;
|
p->thread.ds_ctx = NULL;
|
||||||
|
@ -427,6 +438,23 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
|
||||||
lazy_load_gs(next->gs);
|
lazy_load_gs(next->gs);
|
||||||
|
|
||||||
percpu_write(current_task, next_p);
|
percpu_write(current_task, next_p);
|
||||||
|
/*
|
||||||
|
* There's a problem with moving the arch_install_thread_hw_breakpoint()
|
||||||
|
* call before current is updated. Suppose a kernel breakpoint is
|
||||||
|
* triggered in between the two, the hw-breakpoint handler will see that
|
||||||
|
* the 'current' task does not have TIF_DEBUG flag set and will think it
|
||||||
|
* is leftover from an old task (lazy switching) and will erase it. Then
|
||||||
|
* until the next context switch, no user-breakpoints will be installed.
|
||||||
|
*
|
||||||
|
* The real problem is that it's impossible to update both current and
|
||||||
|
* physical debug registers at the same instant, so there will always be
|
||||||
|
* a window in which they disagree and a breakpoint might get triggered.
|
||||||
|
* Since we use lazy switching, we are forced to assume that a
|
||||||
|
* disagreement means that current is correct and the exception is due
|
||||||
|
* to lazy debug register switching.
|
||||||
|
*/
|
||||||
|
if (unlikely(test_tsk_thread_flag(next_p, TIF_DEBUG)))
|
||||||
|
arch_install_thread_hw_breakpoint(next_p);
|
||||||
|
|
||||||
return prev_p;
|
return prev_p;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,8 @@
|
||||||
#include <asm/idle.h>
|
#include <asm/idle.h>
|
||||||
#include <asm/syscalls.h>
|
#include <asm/syscalls.h>
|
||||||
#include <asm/ds.h>
|
#include <asm/ds.h>
|
||||||
|
#include <asm/debugreg.h>
|
||||||
|
#include <asm/hw_breakpoint.h>
|
||||||
|
|
||||||
asmlinkage extern void ret_from_fork(void);
|
asmlinkage extern void ret_from_fork(void);
|
||||||
|
|
||||||
|
@ -248,6 +250,8 @@ void release_thread(struct task_struct *dead_task)
|
||||||
BUG();
|
BUG();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (unlikely(dead_task->thread.debugreg7))
|
||||||
|
flush_thread_hw_breakpoint(dead_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void set_32bit_tls(struct task_struct *t, int tls, u32 addr)
|
static inline void set_32bit_tls(struct task_struct *t, int tls, u32 addr)
|
||||||
|
@ -303,12 +307,18 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
|
||||||
|
|
||||||
p->thread.fs = me->thread.fs;
|
p->thread.fs = me->thread.fs;
|
||||||
p->thread.gs = me->thread.gs;
|
p->thread.gs = me->thread.gs;
|
||||||
|
p->thread.io_bitmap_ptr = NULL;
|
||||||
|
|
||||||
savesegment(gs, p->thread.gsindex);
|
savesegment(gs, p->thread.gsindex);
|
||||||
savesegment(fs, p->thread.fsindex);
|
savesegment(fs, p->thread.fsindex);
|
||||||
savesegment(es, p->thread.es);
|
savesegment(es, p->thread.es);
|
||||||
savesegment(ds, p->thread.ds);
|
savesegment(ds, p->thread.ds);
|
||||||
|
|
||||||
|
err = -ENOMEM;
|
||||||
|
if (unlikely(test_tsk_thread_flag(me, TIF_DEBUG)))
|
||||||
|
if (copy_thread_hw_breakpoint(me, p, clone_flags))
|
||||||
|
goto out;
|
||||||
|
|
||||||
if (unlikely(test_tsk_thread_flag(me, TIF_IO_BITMAP))) {
|
if (unlikely(test_tsk_thread_flag(me, TIF_IO_BITMAP))) {
|
||||||
p->thread.io_bitmap_ptr = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL);
|
p->thread.io_bitmap_ptr = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL);
|
||||||
if (!p->thread.io_bitmap_ptr) {
|
if (!p->thread.io_bitmap_ptr) {
|
||||||
|
@ -347,6 +357,9 @@ out:
|
||||||
kfree(p->thread.io_bitmap_ptr);
|
kfree(p->thread.io_bitmap_ptr);
|
||||||
p->thread.io_bitmap_max = 0;
|
p->thread.io_bitmap_max = 0;
|
||||||
}
|
}
|
||||||
|
if (err)
|
||||||
|
flush_thread_hw_breakpoint(p);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,6 +505,24 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
|
||||||
*/
|
*/
|
||||||
if (tsk_used_math(next_p) && next_p->fpu_counter > 5)
|
if (tsk_used_math(next_p) && next_p->fpu_counter > 5)
|
||||||
math_state_restore();
|
math_state_restore();
|
||||||
|
/*
|
||||||
|
* There's a problem with moving the arch_install_thread_hw_breakpoint()
|
||||||
|
* call before current is updated. Suppose a kernel breakpoint is
|
||||||
|
* triggered in between the two, the hw-breakpoint handler will see that
|
||||||
|
* the 'current' task does not have TIF_DEBUG flag set and will think it
|
||||||
|
* is leftover from an old task (lazy switching) and will erase it. Then
|
||||||
|
* until the next context switch, no user-breakpoints will be installed.
|
||||||
|
*
|
||||||
|
* The real problem is that it's impossible to update both current and
|
||||||
|
* physical debug registers at the same instant, so there will always be
|
||||||
|
* a window in which they disagree and a breakpoint might get triggered.
|
||||||
|
* Since we use lazy switching, we are forced to assume that a
|
||||||
|
* disagreement means that current is correct and the exception is due
|
||||||
|
* to lazy debug register switching.
|
||||||
|
*/
|
||||||
|
if (unlikely(test_tsk_thread_flag(next_p, TIF_DEBUG)))
|
||||||
|
arch_install_thread_hw_breakpoint(next_p);
|
||||||
|
|
||||||
return prev_p;
|
return prev_p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue