Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARM: hw_breakpoint: correct and simplify alignment fixup code

The current hw_breakpoint code tries to fix up the alignment of
breakpoints so that we can make use of sparse byte-address-select
bits in the control register and give the illusion that we can
set breakpoints on unaligned addresses.

Although this works on v6 cores, v7 forbids this behaviour, instead
requiring breakpoints to be set on aligned addresses and have contiguous
byte-address-select ranges depending on the instruction set in use.
For ARM the only supported size is 4 bytes, whilst Thumb-2 also permits
2 byte breakpoints (watchpoints can be of 1, 2, 4 or 8 bytes long).

This patch simplifies the alignment fixup code so that we require
addresses to be aligned to the size of the corresponding breakpoint.
This allows us to handle the common case of breaking on a half-word
aligned Thumb-2 instruction and also allows us to set byte watchpoints
on arbitrary addresses.

Signed-off-by: Will Deacon <will.deacon@arm.com>

+31 -26
+31 -26
arch/arm/kernel/hw_breakpoint.c
··· 537 537 return -EINVAL; 538 538 } 539 539 540 + /* 541 + * Breakpoints must be of length 2 (thumb) or 4 (ARM) bytes. 542 + * Watchpoints can be of length 1, 2, 4 or 8 bytes if supported 543 + * by the hardware and must be aligned to the appropriate number of 544 + * bytes. 545 + */ 546 + if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE && 547 + info->ctrl.len != ARM_BREAKPOINT_LEN_2 && 548 + info->ctrl.len != ARM_BREAKPOINT_LEN_4) 549 + return -EINVAL; 550 + 540 551 /* Address */ 541 552 info->address = bp->attr.bp_addr; 542 553 ··· 572 561 { 573 562 struct arch_hw_breakpoint *info = counter_arch_bp(bp); 574 563 int ret = 0; 575 - u32 bytelen, max_len, offset, alignment_mask = 0x3; 564 + u32 offset, alignment_mask = 0x3; 576 565 577 566 /* Build the arch_hw_breakpoint. */ 578 567 ret = arch_build_bp_info(bp); ··· 582 571 /* Check address alignment. */ 583 572 if (info->ctrl.len == ARM_BREAKPOINT_LEN_8) 584 573 alignment_mask = 0x7; 585 - if (info->address & alignment_mask) { 586 - /* 587 - * Try to fix the alignment. This may result in a length 588 - * that is too large, so we must check for that. 589 - */ 590 - bytelen = get_hbp_len(info->ctrl.len); 591 - max_len = info->ctrl.type == ARM_BREAKPOINT_EXECUTE ? 4 : 592 - max_watchpoint_len; 593 - 594 - if (max_len >= 8) 595 - offset = info->address & 0x7; 596 - else 597 - offset = info->address & 0x3; 598 - 599 - if (bytelen > (1 << ((max_len - (offset + 1)) >> 1))) { 600 - ret = -EFBIG; 601 - goto out; 602 - } 603 - 604 - info->ctrl.len <<= offset; 605 - info->address &= ~offset; 606 - 607 - pr_debug("breakpoint alignment fixup: length = 0x%x, " 608 - "address = 0x%x\n", info->ctrl.len, info->address); 574 + offset = info->address & alignment_mask; 575 + switch (offset) { 576 + case 0: 577 + /* Aligned */ 578 + break; 579 + case 1: 580 + /* Allow single byte watchpoint. */ 581 + if (info->ctrl.len == ARM_BREAKPOINT_LEN_1) 582 + break; 583 + case 2: 584 + /* Allow halfword watchpoints and breakpoints. */ 585 + if (info->ctrl.len == ARM_BREAKPOINT_LEN_2) 586 + break; 587 + default: 588 + ret = -EINVAL; 589 + goto out; 609 590 } 591 + 592 + info->address &= ~alignment_mask; 593 + info->ctrl.len <<= offset; 610 594 611 595 /* 612 596 * Currently we rely on an overflow handler to take ··· 613 607 (arch_check_bp_in_kernelspace(bp) || !core_has_mismatch_bps()), 614 608 "overflow handler required but none found")) { 615 609 ret = -EINVAL; 616 - goto out; 617 610 } 618 611 out: 619 612 return ret;