diff options
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/kernel/ptrace.c | 78 |
1 files changed, 74 insertions, 4 deletions
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 79d8e56470df..140238aad718 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c | |||
@@ -1338,6 +1338,12 @@ static int set_dac_range(struct task_struct *child, | |||
1338 | static long ppc_set_hwdebug(struct task_struct *child, | 1338 | static long ppc_set_hwdebug(struct task_struct *child, |
1339 | struct ppc_hw_breakpoint *bp_info) | 1339 | struct ppc_hw_breakpoint *bp_info) |
1340 | { | 1340 | { |
1341 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
1342 | int len = 0; | ||
1343 | struct thread_struct *thread = &(child->thread); | ||
1344 | struct perf_event *bp; | ||
1345 | struct perf_event_attr attr; | ||
1346 | #endif /* CONFIG_HAVE_HW_BREAKPOINT */ | ||
1341 | #ifndef CONFIG_PPC_ADV_DEBUG_REGS | 1347 | #ifndef CONFIG_PPC_ADV_DEBUG_REGS |
1342 | unsigned long dabr; | 1348 | unsigned long dabr; |
1343 | #endif | 1349 | #endif |
@@ -1381,13 +1387,9 @@ static long ppc_set_hwdebug(struct task_struct *child, | |||
1381 | */ | 1387 | */ |
1382 | if ((bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_RW) == 0 || | 1388 | if ((bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_RW) == 0 || |
1383 | (bp_info->trigger_type & ~PPC_BREAKPOINT_TRIGGER_RW) != 0 || | 1389 | (bp_info->trigger_type & ~PPC_BREAKPOINT_TRIGGER_RW) != 0 || |
1384 | bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT || | ||
1385 | bp_info->condition_mode != PPC_BREAKPOINT_CONDITION_NONE) | 1390 | bp_info->condition_mode != PPC_BREAKPOINT_CONDITION_NONE) |
1386 | return -EINVAL; | 1391 | return -EINVAL; |
1387 | 1392 | ||
1388 | if (child->thread.dabr) | ||
1389 | return -ENOSPC; | ||
1390 | |||
1391 | if ((unsigned long)bp_info->addr >= TASK_SIZE) | 1393 | if ((unsigned long)bp_info->addr >= TASK_SIZE) |
1392 | return -EIO; | 1394 | return -EIO; |
1393 | 1395 | ||
@@ -1397,6 +1399,50 @@ static long ppc_set_hwdebug(struct task_struct *child, | |||
1397 | dabr |= DABR_DATA_READ; | 1399 | dabr |= DABR_DATA_READ; |
1398 | if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE) | 1400 | if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE) |
1399 | dabr |= DABR_DATA_WRITE; | 1401 | dabr |= DABR_DATA_WRITE; |
1402 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
1403 | if (ptrace_get_breakpoints(child) < 0) | ||
1404 | return -ESRCH; | ||
1405 | |||
1406 | /* | ||
1407 | * Check if the request is for 'range' breakpoints. We can | ||
1408 | * support it if range < 8 bytes. | ||
1409 | */ | ||
1410 | if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE) { | ||
1411 | len = bp_info->addr2 - bp_info->addr; | ||
1412 | } else if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT) { | ||
1413 | ptrace_put_breakpoints(child); | ||
1414 | return -EINVAL; | ||
1415 | } | ||
1416 | bp = thread->ptrace_bps[0]; | ||
1417 | if (bp) { | ||
1418 | ptrace_put_breakpoints(child); | ||
1419 | return -ENOSPC; | ||
1420 | } | ||
1421 | |||
1422 | /* Create a new breakpoint request if one doesn't exist already */ | ||
1423 | hw_breakpoint_init(&attr); | ||
1424 | attr.bp_addr = (unsigned long)bp_info->addr & ~HW_BREAKPOINT_ALIGN; | ||
1425 | attr.bp_len = len; | ||
1426 | arch_bp_generic_fields(dabr & (DABR_DATA_WRITE | DABR_DATA_READ), | ||
1427 | &attr.bp_type); | ||
1428 | |||
1429 | thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr, | ||
1430 | ptrace_triggered, NULL, child); | ||
1431 | if (IS_ERR(bp)) { | ||
1432 | thread->ptrace_bps[0] = NULL; | ||
1433 | ptrace_put_breakpoints(child); | ||
1434 | return PTR_ERR(bp); | ||
1435 | } | ||
1436 | |||
1437 | ptrace_put_breakpoints(child); | ||
1438 | return 1; | ||
1439 | #endif /* CONFIG_HAVE_HW_BREAKPOINT */ | ||
1440 | |||
1441 | if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT) | ||
1442 | return -EINVAL; | ||
1443 | |||
1444 | if (child->thread.dabr) | ||
1445 | return -ENOSPC; | ||
1400 | 1446 | ||
1401 | child->thread.dabr = dabr; | 1447 | child->thread.dabr = dabr; |
1402 | child->thread.dabrx = DABRX_ALL; | 1448 | child->thread.dabrx = DABRX_ALL; |
@@ -1407,6 +1453,11 @@ static long ppc_set_hwdebug(struct task_struct *child, | |||
1407 | 1453 | ||
1408 | static long ppc_del_hwdebug(struct task_struct *child, long addr, long data) | 1454 | static long ppc_del_hwdebug(struct task_struct *child, long addr, long data) |
1409 | { | 1455 | { |
1456 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
1457 | int ret = 0; | ||
1458 | struct thread_struct *thread = &(child->thread); | ||
1459 | struct perf_event *bp; | ||
1460 | #endif /* CONFIG_HAVE_HW_BREAKPOINT */ | ||
1410 | #ifdef CONFIG_PPC_ADV_DEBUG_REGS | 1461 | #ifdef CONFIG_PPC_ADV_DEBUG_REGS |
1411 | int rc; | 1462 | int rc; |
1412 | 1463 | ||
@@ -1426,10 +1477,25 @@ static long ppc_del_hwdebug(struct task_struct *child, long addr, long data) | |||
1426 | #else | 1477 | #else |
1427 | if (data != 1) | 1478 | if (data != 1) |
1428 | return -EINVAL; | 1479 | return -EINVAL; |
1480 | |||
1481 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
1482 | if (ptrace_get_breakpoints(child) < 0) | ||
1483 | return -ESRCH; | ||
1484 | |||
1485 | bp = thread->ptrace_bps[0]; | ||
1486 | if (bp) { | ||
1487 | unregister_hw_breakpoint(bp); | ||
1488 | thread->ptrace_bps[0] = NULL; | ||
1489 | } else | ||
1490 | ret = -ENOENT; | ||
1491 | ptrace_put_breakpoints(child); | ||
1492 | return ret; | ||
1493 | #else /* CONFIG_HAVE_HW_BREAKPOINT */ | ||
1429 | if (child->thread.dabr == 0) | 1494 | if (child->thread.dabr == 0) |
1430 | return -ENOENT; | 1495 | return -ENOENT; |
1431 | 1496 | ||
1432 | child->thread.dabr = 0; | 1497 | child->thread.dabr = 0; |
1498 | #endif /* CONFIG_HAVE_HW_BREAKPOINT */ | ||
1433 | 1499 | ||
1434 | return 0; | 1500 | return 0; |
1435 | #endif | 1501 | #endif |
@@ -1536,7 +1602,11 @@ long arch_ptrace(struct task_struct *child, long request, | |||
1536 | dbginfo.data_bp_alignment = 4; | 1602 | dbginfo.data_bp_alignment = 4; |
1537 | #endif | 1603 | #endif |
1538 | dbginfo.sizeof_condition = 0; | 1604 | dbginfo.sizeof_condition = 0; |
1605 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
1606 | dbginfo.features = PPC_DEBUG_FEATURE_DATA_BP_RANGE; | ||
1607 | #else | ||
1539 | dbginfo.features = 0; | 1608 | dbginfo.features = 0; |
1609 | #endif /* CONFIG_HAVE_HW_BREAKPOINT */ | ||
1540 | #endif /* CONFIG_PPC_ADV_DEBUG_REGS */ | 1610 | #endif /* CONFIG_PPC_ADV_DEBUG_REGS */ |
1541 | 1611 | ||
1542 | if (!access_ok(VERIFY_WRITE, datavp, | 1612 | if (!access_ok(VERIFY_WRITE, datavp, |