diff options
author | Michael Neuling <mikey@neuling.org> | 2012-12-20 09:06:44 -0500 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2013-01-10 01:01:44 -0500 |
commit | 9422de3e953d0e60eb95f5430a9dd803eec1c6d7 (patch) | |
tree | 7255a4a2b873a0c3daf7b312a0845202edf6b2d5 /arch/powerpc/kernel/ptrace.c | |
parent | a8190a59e7440a7e3f7c0889d72a13e157988b3c (diff) |
powerpc: Hardware breakpoints rewrite to handle non DABR breakpoint registers
This is a rewrite so that we don't assume we are using the DABR throughout the
code. We now use the arch_hw_breakpoint to store the breakpoint in a generic
manner in the thread_struct, rather than storing the raw DABR value.
The ptrace GET/SET_DEBUGREG interface currently passes the raw DABR in from
userspace. We keep this functionality, so that future changes (like the POWER8
DAWR), will still fake the DABR to userspace.
Signed-off-by: Michael Neuling <mikey@neuling.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/kernel/ptrace.c')
-rw-r--r-- | arch/powerpc/kernel/ptrace.c | 60 |
1 files changed, 32 insertions, 28 deletions
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index c4970004d44d..d4afcccf1238 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c | |||
@@ -905,6 +905,9 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, | |||
905 | struct perf_event *bp; | 905 | struct perf_event *bp; |
906 | struct perf_event_attr attr; | 906 | struct perf_event_attr attr; |
907 | #endif /* CONFIG_HAVE_HW_BREAKPOINT */ | 907 | #endif /* CONFIG_HAVE_HW_BREAKPOINT */ |
908 | #ifndef CONFIG_PPC_ADV_DEBUG_REGS | ||
909 | struct arch_hw_breakpoint hw_brk; | ||
910 | #endif | ||
908 | 911 | ||
909 | /* For ppc64 we support one DABR and no IABR's at the moment (ppc64). | 912 | /* For ppc64 we support one DABR and no IABR's at the moment (ppc64). |
910 | * For embedded processors we support one DAC and no IAC's at the | 913 | * For embedded processors we support one DAC and no IAC's at the |
@@ -931,14 +934,17 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, | |||
931 | */ | 934 | */ |
932 | 935 | ||
933 | /* Ensure breakpoint translation bit is set */ | 936 | /* Ensure breakpoint translation bit is set */ |
934 | if (data && !(data & DABR_TRANSLATION)) | 937 | if (data && !(data & HW_BRK_TYPE_TRANSLATE)) |
935 | return -EIO; | 938 | return -EIO; |
939 | hw_brk.address = data & (~HW_BRK_TYPE_DABR); | ||
940 | hw_brk.type = (data & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL; | ||
941 | hw_brk.len = 8; | ||
936 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | 942 | #ifdef CONFIG_HAVE_HW_BREAKPOINT |
937 | if (ptrace_get_breakpoints(task) < 0) | 943 | if (ptrace_get_breakpoints(task) < 0) |
938 | return -ESRCH; | 944 | return -ESRCH; |
939 | 945 | ||
940 | bp = thread->ptrace_bps[0]; | 946 | bp = thread->ptrace_bps[0]; |
941 | if ((!data) || !(data & (DABR_DATA_WRITE | DABR_DATA_READ))) { | 947 | if ((!data) || !(hw_brk.type & HW_BRK_TYPE_RDWR)) { |
942 | if (bp) { | 948 | if (bp) { |
943 | unregister_hw_breakpoint(bp); | 949 | unregister_hw_breakpoint(bp); |
944 | thread->ptrace_bps[0] = NULL; | 950 | thread->ptrace_bps[0] = NULL; |
@@ -948,10 +954,8 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, | |||
948 | } | 954 | } |
949 | if (bp) { | 955 | if (bp) { |
950 | attr = bp->attr; | 956 | attr = bp->attr; |
951 | attr.bp_addr = data & ~HW_BREAKPOINT_ALIGN; | 957 | attr.bp_addr = hw_brk.address; |
952 | arch_bp_generic_fields(data & | 958 | arch_bp_generic_fields(hw_brk.type, &attr.bp_type); |
953 | (DABR_DATA_WRITE | DABR_DATA_READ), | ||
954 | &attr.bp_type); | ||
955 | 959 | ||
956 | /* Enable breakpoint */ | 960 | /* Enable breakpoint */ |
957 | attr.disabled = false; | 961 | attr.disabled = false; |
@@ -963,16 +967,15 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, | |||
963 | } | 967 | } |
964 | thread->ptrace_bps[0] = bp; | 968 | thread->ptrace_bps[0] = bp; |
965 | ptrace_put_breakpoints(task); | 969 | ptrace_put_breakpoints(task); |
966 | thread->dabr = data; | 970 | thread->hw_brk = hw_brk; |
967 | thread->dabrx = DABRX_ALL; | ||
968 | return 0; | 971 | return 0; |
969 | } | 972 | } |
970 | 973 | ||
971 | /* Create a new breakpoint request if one doesn't exist already */ | 974 | /* Create a new breakpoint request if one doesn't exist already */ |
972 | hw_breakpoint_init(&attr); | 975 | hw_breakpoint_init(&attr); |
973 | attr.bp_addr = data & ~HW_BREAKPOINT_ALIGN; | 976 | attr.bp_addr = hw_brk.address; |
974 | arch_bp_generic_fields(data & (DABR_DATA_WRITE | DABR_DATA_READ), | 977 | arch_bp_generic_fields(hw_brk.type, |
975 | &attr.bp_type); | 978 | &attr.bp_type); |
976 | 979 | ||
977 | thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr, | 980 | thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr, |
978 | ptrace_triggered, NULL, task); | 981 | ptrace_triggered, NULL, task); |
@@ -985,10 +988,7 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, | |||
985 | ptrace_put_breakpoints(task); | 988 | ptrace_put_breakpoints(task); |
986 | 989 | ||
987 | #endif /* CONFIG_HAVE_HW_BREAKPOINT */ | 990 | #endif /* CONFIG_HAVE_HW_BREAKPOINT */ |
988 | 991 | task->thread.hw_brk = hw_brk; | |
989 | /* Move contents to the DABR register */ | ||
990 | task->thread.dabr = data; | ||
991 | task->thread.dabrx = DABRX_ALL; | ||
992 | #else /* CONFIG_PPC_ADV_DEBUG_REGS */ | 992 | #else /* CONFIG_PPC_ADV_DEBUG_REGS */ |
993 | /* As described above, it was assumed 3 bits were passed with the data | 993 | /* As described above, it was assumed 3 bits were passed with the data |
994 | * address, but we will assume only the mode bits will be passed | 994 | * address, but we will assume only the mode bits will be passed |
@@ -1349,7 +1349,7 @@ static long ppc_set_hwdebug(struct task_struct *child, | |||
1349 | struct perf_event_attr attr; | 1349 | struct perf_event_attr attr; |
1350 | #endif /* CONFIG_HAVE_HW_BREAKPOINT */ | 1350 | #endif /* CONFIG_HAVE_HW_BREAKPOINT */ |
1351 | #ifndef CONFIG_PPC_ADV_DEBUG_REGS | 1351 | #ifndef CONFIG_PPC_ADV_DEBUG_REGS |
1352 | unsigned long dabr; | 1352 | struct arch_hw_breakpoint brk; |
1353 | #endif | 1353 | #endif |
1354 | 1354 | ||
1355 | if (bp_info->version != 1) | 1355 | if (bp_info->version != 1) |
@@ -1397,12 +1397,12 @@ static long ppc_set_hwdebug(struct task_struct *child, | |||
1397 | if ((unsigned long)bp_info->addr >= TASK_SIZE) | 1397 | if ((unsigned long)bp_info->addr >= TASK_SIZE) |
1398 | return -EIO; | 1398 | return -EIO; |
1399 | 1399 | ||
1400 | dabr = (unsigned long)bp_info->addr & ~7UL; | 1400 | brk.address = bp_info->addr & ~7UL; |
1401 | dabr |= DABR_TRANSLATION; | 1401 | brk.type = HW_BRK_TYPE_TRANSLATE; |
1402 | if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_READ) | 1402 | if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_READ) |
1403 | dabr |= DABR_DATA_READ; | 1403 | brk.type |= HW_BRK_TYPE_READ; |
1404 | if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE) | 1404 | if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE) |
1405 | dabr |= DABR_DATA_WRITE; | 1405 | brk.type |= HW_BRK_TYPE_WRITE; |
1406 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | 1406 | #ifdef CONFIG_HAVE_HW_BREAKPOINT |
1407 | if (ptrace_get_breakpoints(child) < 0) | 1407 | if (ptrace_get_breakpoints(child) < 0) |
1408 | return -ESRCH; | 1408 | return -ESRCH; |
@@ -1427,8 +1427,7 @@ static long ppc_set_hwdebug(struct task_struct *child, | |||
1427 | hw_breakpoint_init(&attr); | 1427 | hw_breakpoint_init(&attr); |
1428 | attr.bp_addr = (unsigned long)bp_info->addr & ~HW_BREAKPOINT_ALIGN; | 1428 | attr.bp_addr = (unsigned long)bp_info->addr & ~HW_BREAKPOINT_ALIGN; |
1429 | attr.bp_len = len; | 1429 | attr.bp_len = len; |
1430 | arch_bp_generic_fields(dabr & (DABR_DATA_WRITE | DABR_DATA_READ), | 1430 | arch_bp_generic_fields(brk.type, &attr.bp_type); |
1431 | &attr.bp_type); | ||
1432 | 1431 | ||
1433 | thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr, | 1432 | thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr, |
1434 | ptrace_triggered, NULL, child); | 1433 | ptrace_triggered, NULL, child); |
@@ -1445,11 +1444,10 @@ static long ppc_set_hwdebug(struct task_struct *child, | |||
1445 | if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT) | 1444 | if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT) |
1446 | return -EINVAL; | 1445 | return -EINVAL; |
1447 | 1446 | ||
1448 | if (child->thread.dabr) | 1447 | if (child->thread.hw_brk.address) |
1449 | return -ENOSPC; | 1448 | return -ENOSPC; |
1450 | 1449 | ||
1451 | child->thread.dabr = dabr; | 1450 | child->thread.hw_brk = brk; |
1452 | child->thread.dabrx = DABRX_ALL; | ||
1453 | 1451 | ||
1454 | return 1; | 1452 | return 1; |
1455 | #endif /* !CONFIG_PPC_ADV_DEBUG_DVCS */ | 1453 | #endif /* !CONFIG_PPC_ADV_DEBUG_DVCS */ |
@@ -1495,10 +1493,11 @@ static long ppc_del_hwdebug(struct task_struct *child, long data) | |||
1495 | ptrace_put_breakpoints(child); | 1493 | ptrace_put_breakpoints(child); |
1496 | return ret; | 1494 | return ret; |
1497 | #else /* CONFIG_HAVE_HW_BREAKPOINT */ | 1495 | #else /* CONFIG_HAVE_HW_BREAKPOINT */ |
1498 | if (child->thread.dabr == 0) | 1496 | if (child->thread.hw_brk.address == 0) |
1499 | return -ENOENT; | 1497 | return -ENOENT; |
1500 | 1498 | ||
1501 | child->thread.dabr = 0; | 1499 | child->thread.hw_brk.address = 0; |
1500 | child->thread.hw_brk.type = 0; | ||
1502 | #endif /* CONFIG_HAVE_HW_BREAKPOINT */ | 1501 | #endif /* CONFIG_HAVE_HW_BREAKPOINT */ |
1503 | 1502 | ||
1504 | return 0; | 1503 | return 0; |
@@ -1642,6 +1641,9 @@ long arch_ptrace(struct task_struct *child, long request, | |||
1642 | } | 1641 | } |
1643 | 1642 | ||
1644 | case PTRACE_GET_DEBUGREG: { | 1643 | case PTRACE_GET_DEBUGREG: { |
1644 | #ifndef CONFIG_PPC_ADV_DEBUG_REGS | ||
1645 | unsigned long dabr_fake; | ||
1646 | #endif | ||
1645 | ret = -EINVAL; | 1647 | ret = -EINVAL; |
1646 | /* We only support one DABR and no IABRS at the moment */ | 1648 | /* We only support one DABR and no IABRS at the moment */ |
1647 | if (addr > 0) | 1649 | if (addr > 0) |
@@ -1649,7 +1651,9 @@ long arch_ptrace(struct task_struct *child, long request, | |||
1649 | #ifdef CONFIG_PPC_ADV_DEBUG_REGS | 1651 | #ifdef CONFIG_PPC_ADV_DEBUG_REGS |
1650 | ret = put_user(child->thread.dac1, datalp); | 1652 | ret = put_user(child->thread.dac1, datalp); |
1651 | #else | 1653 | #else |
1652 | ret = put_user(child->thread.dabr, datalp); | 1654 | dabr_fake = ((child->thread.hw_brk.address & (~HW_BRK_TYPE_DABR)) | |
1655 | (child->thread.hw_brk.type & HW_BRK_TYPE_DABR)); | ||
1656 | ret = put_user(dabr_fake, datalp); | ||
1653 | #endif | 1657 | #endif |
1654 | break; | 1658 | break; |
1655 | } | 1659 | } |