You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'tracing/hw-breakpoints' into perf/core
Conflicts:
arch/x86/kernel/kprobes.c
kernel/trace/Makefile
Merge reason: hw-breakpoints perf integration is looking
good in testing and in reviews, plus conflicts
are mounting up - so merge & resolve.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
@@ -126,4 +126,11 @@ config HAVE_DMA_API_DEBUG
|
|||||||
config HAVE_DEFAULT_NO_SPIN_MUTEXES
|
config HAVE_DEFAULT_NO_SPIN_MUTEXES
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
config HAVE_HW_BREAKPOINT
|
||||||
|
bool
|
||||||
|
depends on HAVE_PERF_EVENTS
|
||||||
|
select ANON_INODES
|
||||||
|
select PERF_EVENTS
|
||||||
|
|
||||||
|
|
||||||
source "kernel/gcov/Kconfig"
|
source "kernel/gcov/Kconfig"
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ config X86
|
|||||||
select HAVE_KERNEL_GZIP
|
select HAVE_KERNEL_GZIP
|
||||||
select HAVE_KERNEL_BZIP2
|
select HAVE_KERNEL_BZIP2
|
||||||
select HAVE_KERNEL_LZMA
|
select HAVE_KERNEL_LZMA
|
||||||
|
select HAVE_HW_BREAKPOINT
|
||||||
select HAVE_ARCH_KMEMCHECK
|
select HAVE_ARCH_KMEMCHECK
|
||||||
|
|
||||||
config OUTPUT_FORMAT
|
config OUTPUT_FORMAT
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ header-y += ptrace-abi.h
|
|||||||
header-y += sigcontext32.h
|
header-y += sigcontext32.h
|
||||||
header-y += ucontext.h
|
header-y += ucontext.h
|
||||||
header-y += processor-flags.h
|
header-y += processor-flags.h
|
||||||
|
header-y += hw_breakpoint.h
|
||||||
|
|
||||||
unifdef-y += e820.h
|
unifdef-y += e820.h
|
||||||
unifdef-y += ist.h
|
unifdef-y += ist.h
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <linux/user.h>
|
#include <linux/user.h>
|
||||||
#include <linux/elfcore.h>
|
#include <linux/elfcore.h>
|
||||||
|
#include <asm/debugreg.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* fill in the user structure for an a.out core dump
|
* fill in the user structure for an a.out core dump
|
||||||
@@ -32,14 +33,7 @@ static inline void aout_dump_thread(struct pt_regs *regs, struct user *dump)
|
|||||||
>> PAGE_SHIFT;
|
>> PAGE_SHIFT;
|
||||||
dump->u_dsize -= dump->u_tsize;
|
dump->u_dsize -= dump->u_tsize;
|
||||||
dump->u_ssize = 0;
|
dump->u_ssize = 0;
|
||||||
dump->u_debugreg[0] = current->thread.debugreg0;
|
aout_dump_debugregs(dump);
|
||||||
dump->u_debugreg[1] = current->thread.debugreg1;
|
|
||||||
dump->u_debugreg[2] = current->thread.debugreg2;
|
|
||||||
dump->u_debugreg[3] = current->thread.debugreg3;
|
|
||||||
dump->u_debugreg[4] = 0;
|
|
||||||
dump->u_debugreg[5] = 0;
|
|
||||||
dump->u_debugreg[6] = current->thread.debugreg6;
|
|
||||||
dump->u_debugreg[7] = current->thread.debugreg7;
|
|
||||||
|
|
||||||
if (dump->start_stack < TASK_SIZE)
|
if (dump->start_stack < TASK_SIZE)
|
||||||
dump->u_ssize = ((unsigned long)(TASK_SIZE - dump->start_stack))
|
dump->u_ssize = ((unsigned long)(TASK_SIZE - dump->start_stack))
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#define DR_TRAP1 (0x2) /* db1 */
|
#define DR_TRAP1 (0x2) /* db1 */
|
||||||
#define DR_TRAP2 (0x4) /* db2 */
|
#define DR_TRAP2 (0x4) /* db2 */
|
||||||
#define DR_TRAP3 (0x8) /* db3 */
|
#define DR_TRAP3 (0x8) /* db3 */
|
||||||
|
#define DR_TRAP_BITS (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)
|
||||||
|
|
||||||
#define DR_STEP (0x4000) /* single-step */
|
#define DR_STEP (0x4000) /* single-step */
|
||||||
#define DR_SWITCH (0x8000) /* task switch */
|
#define DR_SWITCH (0x8000) /* task switch */
|
||||||
@@ -49,6 +50,8 @@
|
|||||||
|
|
||||||
#define DR_LOCAL_ENABLE_SHIFT 0 /* Extra shift to the local enable bit */
|
#define DR_LOCAL_ENABLE_SHIFT 0 /* Extra shift to the local enable bit */
|
||||||
#define DR_GLOBAL_ENABLE_SHIFT 1 /* Extra shift to the global enable bit */
|
#define DR_GLOBAL_ENABLE_SHIFT 1 /* Extra shift to the global enable bit */
|
||||||
|
#define DR_LOCAL_ENABLE (0x1) /* Local enable for reg 0 */
|
||||||
|
#define DR_GLOBAL_ENABLE (0x2) /* Global enable for reg 0 */
|
||||||
#define DR_ENABLE_SIZE 2 /* 2 enable bits per register */
|
#define DR_ENABLE_SIZE 2 /* 2 enable bits per register */
|
||||||
|
|
||||||
#define DR_LOCAL_ENABLE_MASK (0x55) /* Set local bits for all 4 regs */
|
#define DR_LOCAL_ENABLE_MASK (0x55) /* Set local bits for all 4 regs */
|
||||||
@@ -67,4 +70,34 @@
|
|||||||
#define DR_LOCAL_SLOWDOWN (0x100) /* Local slow the pipeline */
|
#define DR_LOCAL_SLOWDOWN (0x100) /* Local slow the pipeline */
|
||||||
#define DR_GLOBAL_SLOWDOWN (0x200) /* Global slow the pipeline */
|
#define DR_GLOBAL_SLOWDOWN (0x200) /* Global slow the pipeline */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HW breakpoint additions
|
||||||
|
*/
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
|
||||||
|
DECLARE_PER_CPU(unsigned long, dr7);
|
||||||
|
|
||||||
|
static inline void hw_breakpoint_disable(void)
|
||||||
|
{
|
||||||
|
/* Zero the control register for HW Breakpoint */
|
||||||
|
set_debugreg(0UL, 7);
|
||||||
|
|
||||||
|
/* Zero-out the individual HW breakpoint address registers */
|
||||||
|
set_debugreg(0UL, 0);
|
||||||
|
set_debugreg(0UL, 1);
|
||||||
|
set_debugreg(0UL, 2);
|
||||||
|
set_debugreg(0UL, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hw_breakpoint_active(void)
|
||||||
|
{
|
||||||
|
return __get_cpu_var(dr7) & DR_GLOBAL_ENABLE_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void aout_dump_debugregs(struct user *dump);
|
||||||
|
|
||||||
|
extern void hw_breakpoint_restore(void);
|
||||||
|
|
||||||
|
#endif /* __KERNEL__ */
|
||||||
|
|
||||||
#endif /* _ASM_X86_DEBUGREG_H */
|
#endif /* _ASM_X86_DEBUGREG_H */
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
#ifndef _I386_HW_BREAKPOINT_H
|
||||||
|
#define _I386_HW_BREAKPOINT_H
|
||||||
|
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
#define __ARCH_HW_BREAKPOINT_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The name should probably be something dealt in
|
||||||
|
* a higher level. While dealing with the user
|
||||||
|
* (display/resolving)
|
||||||
|
*/
|
||||||
|
struct arch_hw_breakpoint {
|
||||||
|
char *name; /* Contains name of the symbol to set bkpt */
|
||||||
|
unsigned long address;
|
||||||
|
u8 len;
|
||||||
|
u8 type;
|
||||||
|
};
|
||||||
|
|
||||||
|
#include <linux/kdebug.h>
|
||||||
|
#include <linux/percpu.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
|
||||||
|
/* Available HW breakpoint length encodings */
|
||||||
|
#define X86_BREAKPOINT_LEN_1 0x40
|
||||||
|
#define X86_BREAKPOINT_LEN_2 0x44
|
||||||
|
#define X86_BREAKPOINT_LEN_4 0x4c
|
||||||
|
#define X86_BREAKPOINT_LEN_EXECUTE 0x40
|
||||||
|
|
||||||
|
#ifdef CONFIG_X86_64
|
||||||
|
#define X86_BREAKPOINT_LEN_8 0x48
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Available HW breakpoint type encodings */
|
||||||
|
|
||||||
|
/* trigger on instruction execute */
|
||||||
|
#define X86_BREAKPOINT_EXECUTE 0x80
|
||||||
|
/* trigger on memory write */
|
||||||
|
#define X86_BREAKPOINT_WRITE 0x81
|
||||||
|
/* trigger on memory read or write */
|
||||||
|
#define X86_BREAKPOINT_RW 0x83
|
||||||
|
|
||||||
|
/* Total number of available HW breakpoint registers */
|
||||||
|
#define HBP_NUM 4
|
||||||
|
|
||||||
|
struct perf_event;
|
||||||
|
struct pmu;
|
||||||
|
|
||||||
|
extern int arch_check_va_in_userspace(unsigned long va, u8 hbp_len);
|
||||||
|
extern int arch_validate_hwbkpt_settings(struct perf_event *bp,
|
||||||
|
struct task_struct *tsk);
|
||||||
|
extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
|
||||||
|
unsigned long val, void *data);
|
||||||
|
|
||||||
|
|
||||||
|
int arch_install_hw_breakpoint(struct perf_event *bp);
|
||||||
|
void arch_uninstall_hw_breakpoint(struct perf_event *bp);
|
||||||
|
void hw_breakpoint_pmu_read(struct perf_event *bp);
|
||||||
|
void hw_breakpoint_pmu_unthrottle(struct perf_event *bp);
|
||||||
|
|
||||||
|
extern void
|
||||||
|
arch_fill_perf_breakpoint(struct perf_event *bp);
|
||||||
|
|
||||||
|
unsigned long encode_dr7(int drnum, unsigned int len, unsigned int type);
|
||||||
|
int decode_dr7(unsigned long dr7, int bpnum, unsigned *len, unsigned *type);
|
||||||
|
|
||||||
|
extern int arch_bp_generic_fields(int x86_len, int x86_type,
|
||||||
|
int *gen_len, int *gen_type);
|
||||||
|
|
||||||
|
extern struct pmu perf_ops_bp;
|
||||||
|
|
||||||
|
#endif /* __KERNEL__ */
|
||||||
|
#endif /* _I386_HW_BREAKPOINT_H */
|
||||||
|
|
||||||
@@ -30,6 +30,7 @@ struct mm_struct;
|
|||||||
#include <linux/math64.h>
|
#include <linux/math64.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
|
||||||
|
#define HBP_NUM 4
|
||||||
/*
|
/*
|
||||||
* Default implementation of macro that returns current
|
* Default implementation of macro that returns current
|
||||||
* instruction pointer ("program counter").
|
* instruction pointer ("program counter").
|
||||||
@@ -422,6 +423,8 @@ extern unsigned int xstate_size;
|
|||||||
extern void free_thread_xstate(struct task_struct *);
|
extern void free_thread_xstate(struct task_struct *);
|
||||||
extern struct kmem_cache *task_xstate_cachep;
|
extern struct kmem_cache *task_xstate_cachep;
|
||||||
|
|
||||||
|
struct perf_event;
|
||||||
|
|
||||||
struct thread_struct {
|
struct thread_struct {
|
||||||
/* Cached TLS descriptors: */
|
/* Cached TLS descriptors: */
|
||||||
struct desc_struct tls_array[GDT_ENTRY_TLS_ENTRIES];
|
struct desc_struct tls_array[GDT_ENTRY_TLS_ENTRIES];
|
||||||
@@ -443,13 +446,10 @@ struct thread_struct {
|
|||||||
unsigned long fs;
|
unsigned long fs;
|
||||||
#endif
|
#endif
|
||||||
unsigned long gs;
|
unsigned long gs;
|
||||||
/* Hardware debugging registers: */
|
/* Save middle states of ptrace breakpoints */
|
||||||
unsigned long debugreg0;
|
struct perf_event *ptrace_bps[HBP_NUM];
|
||||||
unsigned long debugreg1;
|
/* Debug status used for traps, single steps, etc... */
|
||||||
unsigned long debugreg2;
|
unsigned long debugreg6;
|
||||||
unsigned long debugreg3;
|
|
||||||
unsigned long debugreg6;
|
|
||||||
unsigned long debugreg7;
|
|
||||||
/* Fault info: */
|
/* Fault info: */
|
||||||
unsigned long cr2;
|
unsigned long cr2;
|
||||||
unsigned long trap_no;
|
unsigned long trap_no;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ obj-$(CONFIG_X86_64) += sys_x86_64.o x8664_ksyms_64.o
|
|||||||
obj-$(CONFIG_X86_64) += syscall_64.o vsyscall_64.o
|
obj-$(CONFIG_X86_64) += syscall_64.o vsyscall_64.o
|
||||||
obj-y += bootflag.o e820.o
|
obj-y += bootflag.o e820.o
|
||||||
obj-y += pci-dma.o quirks.o i8237.o topology.o kdebugfs.o
|
obj-y += pci-dma.o quirks.o i8237.o topology.o kdebugfs.o
|
||||||
obj-y += alternative.o i8253.o pci-nommu.o
|
obj-y += alternative.o i8253.o pci-nommu.o hw_breakpoint.o
|
||||||
obj-y += tsc.o io_delay.o rtc.o
|
obj-y += tsc.o io_delay.o rtc.o
|
||||||
|
|
||||||
obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o
|
obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -43,6 +43,7 @@
|
|||||||
#include <linux/smp.h>
|
#include <linux/smp.h>
|
||||||
#include <linux/nmi.h>
|
#include <linux/nmi.h>
|
||||||
|
|
||||||
|
#include <asm/debugreg.h>
|
||||||
#include <asm/apicdef.h>
|
#include <asm/apicdef.h>
|
||||||
#include <asm/system.h>
|
#include <asm/system.h>
|
||||||
|
|
||||||
@@ -434,6 +435,11 @@ single_step_cont(struct pt_regs *regs, struct die_args *args)
|
|||||||
"resuming...\n");
|
"resuming...\n");
|
||||||
kgdb_arch_handle_exception(args->trapnr, args->signr,
|
kgdb_arch_handle_exception(args->trapnr, args->signr,
|
||||||
args->err, "c", "", regs);
|
args->err, "c", "", regs);
|
||||||
|
/*
|
||||||
|
* Reset the BS bit in dr6 (pointed by args->err) to
|
||||||
|
* denote completion of processing
|
||||||
|
*/
|
||||||
|
(*(unsigned long *)ERR_PTR(args->err)) &= ~DR_STEP;
|
||||||
|
|
||||||
return NOTIFY_STOP;
|
return NOTIFY_STOP;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/alternative.h>
|
#include <asm/alternative.h>
|
||||||
#include <asm/insn.h>
|
#include <asm/insn.h>
|
||||||
|
#include <asm/debugreg.h>
|
||||||
|
|
||||||
void jprobe_return_end(void);
|
void jprobe_return_end(void);
|
||||||
|
|
||||||
@@ -945,8 +946,14 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
|
|||||||
ret = NOTIFY_STOP;
|
ret = NOTIFY_STOP;
|
||||||
break;
|
break;
|
||||||
case DIE_DEBUG:
|
case DIE_DEBUG:
|
||||||
if (post_kprobe_handler(args->regs))
|
if (post_kprobe_handler(args->regs)) {
|
||||||
|
/*
|
||||||
|
* Reset the BS bit in dr6 (pointed by args->err) to
|
||||||
|
* denote completion of processing
|
||||||
|
*/
|
||||||
|
(*(unsigned long *)ERR_PTR(args->err)) &= ~DR_STEP;
|
||||||
ret = NOTIFY_STOP;
|
ret = NOTIFY_STOP;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case DIE_GPF:
|
case DIE_GPF:
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include <asm/desc.h>
|
#include <asm/desc.h>
|
||||||
#include <asm/system.h>
|
#include <asm/system.h>
|
||||||
#include <asm/cacheflush.h>
|
#include <asm/cacheflush.h>
|
||||||
|
#include <asm/debugreg.h>
|
||||||
|
|
||||||
static void set_idt(void *newidt, __u16 limit)
|
static void set_idt(void *newidt, __u16 limit)
|
||||||
{
|
{
|
||||||
@@ -202,6 +203,7 @@ void machine_kexec(struct kimage *image)
|
|||||||
|
|
||||||
/* Interrupts aren't acceptable while we reboot */
|
/* Interrupts aren't acceptable while we reboot */
|
||||||
local_irq_disable();
|
local_irq_disable();
|
||||||
|
hw_breakpoint_disable();
|
||||||
|
|
||||||
if (image->preserve_context) {
|
if (image->preserve_context) {
|
||||||
#ifdef CONFIG_X86_IO_APIC
|
#ifdef CONFIG_X86_IO_APIC
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include <asm/pgtable.h>
|
#include <asm/pgtable.h>
|
||||||
#include <asm/tlbflush.h>
|
#include <asm/tlbflush.h>
|
||||||
#include <asm/mmu_context.h>
|
#include <asm/mmu_context.h>
|
||||||
|
#include <asm/debugreg.h>
|
||||||
|
|
||||||
static int init_one_level2_page(struct kimage *image, pgd_t *pgd,
|
static int init_one_level2_page(struct kimage *image, pgd_t *pgd,
|
||||||
unsigned long addr)
|
unsigned long addr)
|
||||||
@@ -282,6 +283,7 @@ void machine_kexec(struct kimage *image)
|
|||||||
|
|
||||||
/* Interrupts aren't acceptable while we reboot */
|
/* Interrupts aren't acceptable while we reboot */
|
||||||
local_irq_disable();
|
local_irq_disable();
|
||||||
|
hw_breakpoint_disable();
|
||||||
|
|
||||||
if (image->preserve_context) {
|
if (image->preserve_context) {
|
||||||
#ifdef CONFIG_X86_IO_APIC
|
#ifdef CONFIG_X86_IO_APIC
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <linux/clockchips.h>
|
#include <linux/clockchips.h>
|
||||||
#include <linux/random.h>
|
#include <linux/random.h>
|
||||||
#include <trace/events/power.h>
|
#include <trace/events/power.h>
|
||||||
|
#include <linux/hw_breakpoint.h>
|
||||||
#include <asm/system.h>
|
#include <asm/system.h>
|
||||||
#include <asm/apic.h>
|
#include <asm/apic.h>
|
||||||
#include <asm/syscalls.h>
|
#include <asm/syscalls.h>
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
#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>
|
||||||
|
|
||||||
unsigned long idle_halt;
|
unsigned long idle_halt;
|
||||||
EXPORT_SYMBOL(idle_halt);
|
EXPORT_SYMBOL(idle_halt);
|
||||||
@@ -103,14 +105,7 @@ void flush_thread(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
clear_tsk_thread_flag(tsk, TIF_DEBUG);
|
flush_ptrace_hw_breakpoint(tsk);
|
||||||
|
|
||||||
tsk->thread.debugreg0 = 0;
|
|
||||||
tsk->thread.debugreg1 = 0;
|
|
||||||
tsk->thread.debugreg2 = 0;
|
|
||||||
tsk->thread.debugreg3 = 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..
|
||||||
@@ -192,16 +187,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->debugreg0, 0);
|
|
||||||
set_debugreg(next->debugreg1, 1);
|
|
||||||
set_debugreg(next->debugreg2, 2);
|
|
||||||
set_debugreg(next->debugreg3, 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 */
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
#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>
|
||||||
|
|
||||||
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
|
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
|
||||||
|
|
||||||
@@ -259,7 +260,12 @@ 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;
|
||||||
|
|
||||||
|
memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps));
|
||||||
|
|
||||||
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);
|
||||||
|
|||||||
@@ -52,6 +52,7 @@
|
|||||||
#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>
|
||||||
|
|
||||||
asmlinkage extern void ret_from_fork(void);
|
asmlinkage extern void ret_from_fork(void);
|
||||||
|
|
||||||
@@ -297,12 +298,16 @@ 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;
|
||||||
|
memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps));
|
||||||
|
|
||||||
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) {
|
||||||
@@ -341,6 +346,7 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,6 +501,7 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
|
|||||||
*/
|
*/
|
||||||
if (preload_fpu)
|
if (preload_fpu)
|
||||||
__math_state_restore();
|
__math_state_restore();
|
||||||
|
|
||||||
return prev_p;
|
return prev_p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+212
-93
@@ -22,6 +22,8 @@
|
|||||||
#include <linux/seccomp.h>
|
#include <linux/seccomp.h>
|
||||||
#include <linux/signal.h>
|
#include <linux/signal.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/perf_event.h>
|
||||||
|
#include <linux/hw_breakpoint.h>
|
||||||
|
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/pgtable.h>
|
#include <asm/pgtable.h>
|
||||||
@@ -34,6 +36,7 @@
|
|||||||
#include <asm/prctl.h>
|
#include <asm/prctl.h>
|
||||||
#include <asm/proto.h>
|
#include <asm/proto.h>
|
||||||
#include <asm/ds.h>
|
#include <asm/ds.h>
|
||||||
|
#include <asm/hw_breakpoint.h>
|
||||||
|
|
||||||
#include "tls.h"
|
#include "tls.h"
|
||||||
|
|
||||||
@@ -249,11 +252,6 @@ static int set_segment_reg(struct task_struct *task,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned long debugreg_addr_limit(struct task_struct *task)
|
|
||||||
{
|
|
||||||
return TASK_SIZE - 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else /* CONFIG_X86_64 */
|
#else /* CONFIG_X86_64 */
|
||||||
|
|
||||||
#define FLAG_MASK (FLAG_MASK_32 | X86_EFLAGS_NT)
|
#define FLAG_MASK (FLAG_MASK_32 | X86_EFLAGS_NT)
|
||||||
@@ -378,15 +376,6 @@ static int set_segment_reg(struct task_struct *task,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned long debugreg_addr_limit(struct task_struct *task)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_IA32_EMULATION
|
|
||||||
if (test_tsk_thread_flag(task, TIF_IA32))
|
|
||||||
return IA32_PAGE_OFFSET - 3;
|
|
||||||
#endif
|
|
||||||
return TASK_SIZE_MAX - 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* CONFIG_X86_32 */
|
#endif /* CONFIG_X86_32 */
|
||||||
|
|
||||||
static unsigned long get_flags(struct task_struct *task)
|
static unsigned long get_flags(struct task_struct *task)
|
||||||
@@ -566,96 +555,226 @@ static int genregs_set(struct task_struct *target,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void ptrace_triggered(struct perf_event *bp, void *data)
|
||||||
* This function is trivial and will be inlined by the compiler.
|
|
||||||
* Having it separates the implementation details of debug
|
|
||||||
* registers from the interface details of ptrace.
|
|
||||||
*/
|
|
||||||
static unsigned long ptrace_get_debugreg(struct task_struct *child, int n)
|
|
||||||
{
|
{
|
||||||
switch (n) {
|
int i;
|
||||||
case 0: return child->thread.debugreg0;
|
struct thread_struct *thread = &(current->thread);
|
||||||
case 1: return child->thread.debugreg1;
|
|
||||||
case 2: return child->thread.debugreg2;
|
/*
|
||||||
case 3: return child->thread.debugreg3;
|
* Store in the virtual DR6 register the fact that the breakpoint
|
||||||
case 6: return child->thread.debugreg6;
|
* was hit so the thread's debugger will see it.
|
||||||
case 7: return child->thread.debugreg7;
|
*/
|
||||||
|
for (i = 0; i < HBP_NUM; i++) {
|
||||||
|
if (thread->ptrace_bps[i] == bp)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thread->debugreg6 |= (DR_TRAP0 << i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Walk through every ptrace breakpoints for this thread and
|
||||||
|
* build the dr7 value on top of their attributes.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static unsigned long ptrace_get_dr7(struct perf_event *bp[])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int dr7 = 0;
|
||||||
|
struct arch_hw_breakpoint *info;
|
||||||
|
|
||||||
|
for (i = 0; i < HBP_NUM; i++) {
|
||||||
|
if (bp[i] && !bp[i]->attr.disabled) {
|
||||||
|
info = counter_arch_bp(bp[i]);
|
||||||
|
dr7 |= encode_dr7(i, info->len, info->type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dr7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle ptrace writes to debug register 7.
|
||||||
|
*/
|
||||||
|
static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data)
|
||||||
|
{
|
||||||
|
struct thread_struct *thread = &(tsk->thread);
|
||||||
|
unsigned long old_dr7;
|
||||||
|
int i, orig_ret = 0, rc = 0;
|
||||||
|
int enabled, second_pass = 0;
|
||||||
|
unsigned len, type;
|
||||||
|
int gen_len, gen_type;
|
||||||
|
struct perf_event *bp;
|
||||||
|
|
||||||
|
data &= ~DR_CONTROL_RESERVED;
|
||||||
|
old_dr7 = ptrace_get_dr7(thread->ptrace_bps);
|
||||||
|
restore:
|
||||||
|
/*
|
||||||
|
* Loop through all the hardware breakpoints, making the
|
||||||
|
* appropriate changes to each.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < HBP_NUM; i++) {
|
||||||
|
enabled = decode_dr7(data, i, &len, &type);
|
||||||
|
bp = thread->ptrace_bps[i];
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
if (bp) {
|
||||||
|
/*
|
||||||
|
* Don't unregister the breakpoints right-away,
|
||||||
|
* unless all register_user_hw_breakpoint()
|
||||||
|
* requests have succeeded. This prevents
|
||||||
|
* any window of opportunity for debug
|
||||||
|
* register grabbing by other users.
|
||||||
|
*/
|
||||||
|
if (!second_pass)
|
||||||
|
continue;
|
||||||
|
thread->ptrace_bps[i] = NULL;
|
||||||
|
unregister_hw_breakpoint(bp);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We shoud have at least an inactive breakpoint at this
|
||||||
|
* slot. It means the user is writing dr7 without having
|
||||||
|
* written the address register first
|
||||||
|
*/
|
||||||
|
if (!bp) {
|
||||||
|
rc = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = arch_bp_generic_fields(len, type, &gen_len, &gen_type);
|
||||||
|
if (rc)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a temporary thing as bp is unregistered/registered
|
||||||
|
* to simulate modification
|
||||||
|
*/
|
||||||
|
bp = modify_user_hw_breakpoint(bp, bp->attr.bp_addr, gen_len,
|
||||||
|
gen_type, bp->callback,
|
||||||
|
tsk, true);
|
||||||
|
thread->ptrace_bps[i] = NULL;
|
||||||
|
|
||||||
|
if (!bp) { /* incorrect bp, or we have a bug in bp API */
|
||||||
|
rc = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (IS_ERR(bp)) {
|
||||||
|
rc = PTR_ERR(bp);
|
||||||
|
bp = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
thread->ptrace_bps[i] = bp;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Make a second pass to free the remaining unused breakpoints
|
||||||
|
* or to restore the original breakpoints if an error occurred.
|
||||||
|
*/
|
||||||
|
if (!second_pass) {
|
||||||
|
second_pass = 1;
|
||||||
|
if (rc < 0) {
|
||||||
|
orig_ret = rc;
|
||||||
|
data = old_dr7;
|
||||||
|
}
|
||||||
|
goto restore;
|
||||||
|
}
|
||||||
|
return ((orig_ret < 0) ? orig_ret : rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle PTRACE_PEEKUSR calls for the debug register area.
|
||||||
|
*/
|
||||||
|
static unsigned long ptrace_get_debugreg(struct task_struct *tsk, int n)
|
||||||
|
{
|
||||||
|
struct thread_struct *thread = &(tsk->thread);
|
||||||
|
unsigned long val = 0;
|
||||||
|
|
||||||
|
if (n < HBP_NUM) {
|
||||||
|
struct perf_event *bp;
|
||||||
|
bp = thread->ptrace_bps[n];
|
||||||
|
if (!bp)
|
||||||
|
return 0;
|
||||||
|
val = bp->hw.info.address;
|
||||||
|
} else if (n == 6) {
|
||||||
|
val = thread->debugreg6;
|
||||||
|
} else if (n == 7) {
|
||||||
|
val = ptrace_get_dr7(thread->ptrace_bps);
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
|
||||||
|
unsigned long addr)
|
||||||
|
{
|
||||||
|
struct perf_event *bp;
|
||||||
|
struct thread_struct *t = &tsk->thread;
|
||||||
|
|
||||||
|
if (!t->ptrace_bps[nr]) {
|
||||||
|
/*
|
||||||
|
* Put stub len and type to register (reserve) an inactive but
|
||||||
|
* correct bp
|
||||||
|
*/
|
||||||
|
bp = register_user_hw_breakpoint(addr, HW_BREAKPOINT_LEN_1,
|
||||||
|
HW_BREAKPOINT_W,
|
||||||
|
ptrace_triggered, tsk,
|
||||||
|
false);
|
||||||
|
} else {
|
||||||
|
bp = t->ptrace_bps[nr];
|
||||||
|
t->ptrace_bps[nr] = NULL;
|
||||||
|
bp = modify_user_hw_breakpoint(bp, addr, bp->attr.bp_len,
|
||||||
|
bp->attr.bp_type,
|
||||||
|
bp->callback,
|
||||||
|
tsk,
|
||||||
|
bp->attr.disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bp)
|
||||||
|
return -EIO;
|
||||||
|
/*
|
||||||
|
* CHECKME: the previous code returned -EIO if the addr wasn't a
|
||||||
|
* valid task virtual addr. The new one will return -EINVAL in this
|
||||||
|
* case.
|
||||||
|
* -EINVAL may be what we want for in-kernel breakpoints users, but
|
||||||
|
* -EIO looks better for ptrace, since we refuse a register writing
|
||||||
|
* for the user. And anyway this is the previous behaviour.
|
||||||
|
*/
|
||||||
|
if (IS_ERR(bp))
|
||||||
|
return PTR_ERR(bp);
|
||||||
|
|
||||||
|
t->ptrace_bps[nr] = bp;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ptrace_set_debugreg(struct task_struct *child,
|
/*
|
||||||
int n, unsigned long data)
|
* Handle PTRACE_POKEUSR calls for the debug register area.
|
||||||
|
*/
|
||||||
|
int ptrace_set_debugreg(struct task_struct *tsk, int n, unsigned long val)
|
||||||
{
|
{
|
||||||
int i;
|
struct thread_struct *thread = &(tsk->thread);
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
if (unlikely(n == 4 || n == 5))
|
/* There are no DR4 or DR5 registers */
|
||||||
|
if (n == 4 || n == 5)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
if (n < 4 && unlikely(data >= debugreg_addr_limit(child)))
|
if (n == 6) {
|
||||||
return -EIO;
|
thread->debugreg6 = val;
|
||||||
|
goto ret_path;
|
||||||
switch (n) {
|
|
||||||
case 0: child->thread.debugreg0 = data; break;
|
|
||||||
case 1: child->thread.debugreg1 = data; break;
|
|
||||||
case 2: child->thread.debugreg2 = data; break;
|
|
||||||
case 3: child->thread.debugreg3 = data; break;
|
|
||||||
|
|
||||||
case 6:
|
|
||||||
if ((data & ~0xffffffffUL) != 0)
|
|
||||||
return -EIO;
|
|
||||||
child->thread.debugreg6 = data;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 7:
|
|
||||||
/*
|
|
||||||
* Sanity-check data. Take one half-byte at once with
|
|
||||||
* check = (val >> (16 + 4*i)) & 0xf. It contains the
|
|
||||||
* R/Wi and LENi bits; bits 0 and 1 are R/Wi, and bits
|
|
||||||
* 2 and 3 are LENi. Given a list of invalid values,
|
|
||||||
* we do mask |= 1 << invalid_value, so that
|
|
||||||
* (mask >> check) & 1 is a correct test for invalid
|
|
||||||
* values.
|
|
||||||
*
|
|
||||||
* R/Wi contains the type of the breakpoint /
|
|
||||||
* watchpoint, LENi contains the length of the watched
|
|
||||||
* data in the watchpoint case.
|
|
||||||
*
|
|
||||||
* The invalid values are:
|
|
||||||
* - LENi == 0x10 (undefined), so mask |= 0x0f00. [32-bit]
|
|
||||||
* - R/Wi == 0x10 (break on I/O reads or writes), so
|
|
||||||
* mask |= 0x4444.
|
|
||||||
* - R/Wi == 0x00 && LENi != 0x00, so we have mask |=
|
|
||||||
* 0x1110.
|
|
||||||
*
|
|
||||||
* Finally, mask = 0x0f00 | 0x4444 | 0x1110 == 0x5f54.
|
|
||||||
*
|
|
||||||
* See the Intel Manual "System Programming Guide",
|
|
||||||
* 15.2.4
|
|
||||||
*
|
|
||||||
* Note that LENi == 0x10 is defined on x86_64 in long
|
|
||||||
* mode (i.e. even for 32-bit userspace software, but
|
|
||||||
* 64-bit kernel), so the x86_64 mask value is 0x5454.
|
|
||||||
* See the AMD manual no. 24593 (AMD64 System Programming)
|
|
||||||
*/
|
|
||||||
#ifdef CONFIG_X86_32
|
|
||||||
#define DR7_MASK 0x5f54
|
|
||||||
#else
|
|
||||||
#define DR7_MASK 0x5554
|
|
||||||
#endif
|
|
||||||
data &= ~DR_CONTROL_RESERVED;
|
|
||||||
for (i = 0; i < 4; i++)
|
|
||||||
if ((DR7_MASK >> ((data >> (16 + 4*i)) & 0xf)) & 1)
|
|
||||||
return -EIO;
|
|
||||||
child->thread.debugreg7 = data;
|
|
||||||
if (data)
|
|
||||||
set_tsk_thread_flag(child, TIF_DEBUG);
|
|
||||||
else
|
|
||||||
clear_tsk_thread_flag(child, TIF_DEBUG);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if (n < HBP_NUM) {
|
||||||
|
rc = ptrace_set_breakpoint_addr(tsk, n, val);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
/* All that's left is DR7 */
|
||||||
|
if (n == 7)
|
||||||
|
rc = ptrace_write_dr7(tsk, val);
|
||||||
|
|
||||||
return 0;
|
ret_path:
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -799,15 +799,6 @@ static void do_signal(struct pt_regs *regs)
|
|||||||
|
|
||||||
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
|
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
|
||||||
if (signr > 0) {
|
if (signr > 0) {
|
||||||
/*
|
|
||||||
* Re-enable any watchpoints before delivering the
|
|
||||||
* signal to user space. The processor register will
|
|
||||||
* have been cleared if the watchpoint triggered
|
|
||||||
* inside the kernel.
|
|
||||||
*/
|
|
||||||
if (current->thread.debugreg7)
|
|
||||||
set_debugreg(current->thread.debugreg7, 7);
|
|
||||||
|
|
||||||
/* Whee! Actually deliver the signal. */
|
/* Whee! Actually deliver the signal. */
|
||||||
if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
|
if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
|
||||||
/*
|
/*
|
||||||
|
|||||||
+26
-47
@@ -529,77 +529,56 @@ asmlinkage __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs)
|
|||||||
dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
|
dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
|
||||||
{
|
{
|
||||||
struct task_struct *tsk = current;
|
struct task_struct *tsk = current;
|
||||||
unsigned long condition;
|
unsigned long dr6;
|
||||||
int si_code;
|
int si_code;
|
||||||
|
|
||||||
get_debugreg(condition, 6);
|
get_debugreg(dr6, 6);
|
||||||
|
|
||||||
/* Catch kmemcheck conditions first of all! */
|
/* Catch kmemcheck conditions first of all! */
|
||||||
if (condition & DR_STEP && kmemcheck_trap(regs))
|
if ((dr6 & DR_STEP) && kmemcheck_trap(regs))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* DR6 may or may not be cleared by the CPU */
|
||||||
|
set_debugreg(0, 6);
|
||||||
/*
|
/*
|
||||||
* The processor cleared BTF, so don't mark that we need it set.
|
* The processor cleared BTF, so don't mark that we need it set.
|
||||||
*/
|
*/
|
||||||
clear_tsk_thread_flag(tsk, TIF_DEBUGCTLMSR);
|
clear_tsk_thread_flag(tsk, TIF_DEBUGCTLMSR);
|
||||||
tsk->thread.debugctlmsr = 0;
|
tsk->thread.debugctlmsr = 0;
|
||||||
|
|
||||||
if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code,
|
/* Store the virtualized DR6 value */
|
||||||
SIGTRAP) == NOTIFY_STOP)
|
tsk->thread.debugreg6 = dr6;
|
||||||
|
|
||||||
|
if (notify_die(DIE_DEBUG, "debug", regs, PTR_ERR(&dr6), error_code,
|
||||||
|
SIGTRAP) == NOTIFY_STOP)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* It's safe to allow irq's after DR6 has been saved */
|
/* It's safe to allow irq's after DR6 has been saved */
|
||||||
preempt_conditional_sti(regs);
|
preempt_conditional_sti(regs);
|
||||||
|
|
||||||
/* Mask out spurious debug traps due to lazy DR7 setting */
|
if (regs->flags & X86_VM_MASK) {
|
||||||
if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) {
|
handle_vm86_trap((struct kernel_vm86_regs *) regs,
|
||||||
if (!tsk->thread.debugreg7)
|
error_code, 1);
|
||||||
goto clear_dr7;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_X86_32
|
|
||||||
if (regs->flags & X86_VM_MASK)
|
|
||||||
goto debug_vm86;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Save debug status register where ptrace can see it */
|
|
||||||
tsk->thread.debugreg6 = condition;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Single-stepping through TF: make sure we ignore any events in
|
* Single-stepping through system calls: ignore any exceptions in
|
||||||
* kernel space (but re-enable TF when returning to user mode).
|
* kernel space, but re-enable TF when returning to user mode.
|
||||||
|
*
|
||||||
|
* We already checked v86 mode above, so we can check for kernel mode
|
||||||
|
* by just checking the CPL of CS.
|
||||||
*/
|
*/
|
||||||
if (condition & DR_STEP) {
|
if ((dr6 & DR_STEP) && !user_mode(regs)) {
|
||||||
if (!user_mode(regs))
|
tsk->thread.debugreg6 &= ~DR_STEP;
|
||||||
goto clear_TF_reenable;
|
set_tsk_thread_flag(tsk, TIF_SINGLESTEP);
|
||||||
|
regs->flags &= ~X86_EFLAGS_TF;
|
||||||
}
|
}
|
||||||
|
si_code = get_si_code(tsk->thread.debugreg6);
|
||||||
si_code = get_si_code(condition);
|
if (tsk->thread.debugreg6 & (DR_STEP | DR_TRAP_BITS))
|
||||||
/* Ok, finally something we can handle */
|
send_sigtrap(tsk, regs, error_code, si_code);
|
||||||
send_sigtrap(tsk, regs, error_code, si_code);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Disable additional traps. They'll be re-enabled when
|
|
||||||
* the signal is delivered.
|
|
||||||
*/
|
|
||||||
clear_dr7:
|
|
||||||
set_debugreg(0, 7);
|
|
||||||
preempt_conditional_cli(regs);
|
preempt_conditional_cli(regs);
|
||||||
return;
|
|
||||||
|
|
||||||
#ifdef CONFIG_X86_32
|
|
||||||
debug_vm86:
|
|
||||||
/* reenable preemption: handle_vm86_trap() might sleep */
|
|
||||||
dec_preempt_count();
|
|
||||||
handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, 1);
|
|
||||||
conditional_cli(regs);
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
clear_TF_reenable:
|
|
||||||
set_tsk_thread_flag(tsk, TIF_SINGLESTEP);
|
|
||||||
regs->flags &= ~X86_EFLAGS_TF;
|
|
||||||
preempt_conditional_cli(regs);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-8
@@ -42,6 +42,7 @@
|
|||||||
#define CREATE_TRACE_POINTS
|
#define CREATE_TRACE_POINTS
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
|
#include <asm/debugreg.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/msr.h>
|
#include <asm/msr.h>
|
||||||
#include <asm/desc.h>
|
#include <asm/desc.h>
|
||||||
@@ -3643,14 +3644,15 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
|
|||||||
trace_kvm_entry(vcpu->vcpu_id);
|
trace_kvm_entry(vcpu->vcpu_id);
|
||||||
kvm_x86_ops->run(vcpu, kvm_run);
|
kvm_x86_ops->run(vcpu, kvm_run);
|
||||||
|
|
||||||
if (unlikely(vcpu->arch.switch_db_regs || test_thread_flag(TIF_DEBUG))) {
|
/*
|
||||||
set_debugreg(current->thread.debugreg0, 0);
|
* If the guest has used debug registers, at least dr7
|
||||||
set_debugreg(current->thread.debugreg1, 1);
|
* will be disabled while returning to the host.
|
||||||
set_debugreg(current->thread.debugreg2, 2);
|
* If we don't have active breakpoints in the host, we don't
|
||||||
set_debugreg(current->thread.debugreg3, 3);
|
* care about the messed up debug address registers. But if
|
||||||
set_debugreg(current->thread.debugreg6, 6);
|
* we have some of them active, restore the old state.
|
||||||
set_debugreg(current->thread.debugreg7, 7);
|
*/
|
||||||
}
|
if (hw_breakpoint_active())
|
||||||
|
hw_breakpoint_restore();
|
||||||
|
|
||||||
set_bit(KVM_REQ_KICK, &vcpu->requests);
|
set_bit(KVM_REQ_KICK, &vcpu->requests);
|
||||||
local_irq_enable();
|
local_irq_enable();
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user