Hi all, Seems there is a bug in ARM breakpoint emulation. I am not sure how to fix it and I would appreciate any suggestion. It is best illustrated by a simple test which sets up and enables an unlinked address match breakpoint but does not enable debug exceptions globally by MDSCR_EL1.MDE bit.
cat >test.s <<EOF .text .global _start _start: adr x0, bp msr dbgbvr0_el1, x0 mov x0, #1 orr x0, x0, #(0xf << 5) msr dbgbcr0_el1, x0 bp: nop wfi b . EOF aarch64-linux-gnu-as -o test.o test.s aarch64-linux-gnu-ld -Ttext=0x40000000 -o test.elf test.o qemu-system-aarch64 -nographic -machine virt -cpu cortex-a57 -kernel test.elf -D qemu.log -d in_asm,exec -singlestep First, it fails with segmentation fault. What actually happens is a CPU breakpoint is inserted in hw_breakpoint_update(). After that, when translating bp() an internal debug exception is generated in gen_intermediate_code_internal_a64() since there is a CPU breakpoint which matches the address of the instruction being translated. Then arm_debug_excp_handler() get called in order to handle this breakpoint. It calls check_breakpoints() and discovers there is no breakpoints enabled since MDSCR_EL1.MDE is not set. It simply returns and we eventually get to cpu_handle_guest_debug(), then gdb_set_stop_cpu() which does segmentation fault. I managed to avoid this segmentation fault with this patch: diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 663c05d..223b939 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -889,6 +889,15 @@ void arm_debug_excp_handler(CPUState *cs) } } } else { + CPUBreakpoint *bp; + uint64_t pc = is_a64(env) ? env->pc : env->regs[15]; + + QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { + if (bp->pc == pc && !(bp->flags & BP_CPU)) { + return; + } + } + if (check_breakpoints(cpu)) { bool same_el = (arm_debug_target_el(env) == arm_current_el(env)); if (extended_addresses_enabled(env)) { @@ -900,6 +909,8 @@ void arm_debug_excp_handler(CPUState *cs) raise_exception(env, EXCP_PREFETCH_ABORT, syn_breakpoint(same_el), arm_debug_target_el(env)); + } else { + cpu_resume_from_signal(cs, NULL); } } } The patch adds a check for non-CPU breakpoints first, then calls cpu_resume_from_signal() if no CPU breakpoint matches. With this patch Qemu hangs generating internal debug exception over and over: head -40 qemu.log ---------------- IN: 0x0000000040000000: 100000a0 adr x0, #+0x14 (addr 0x40000014) Trace 0x7ff11e237000 [0000000040000000] ---------------- IN: 0x0000000040000004: d5100080 msr (unknown), x0 Trace 0x7ff11e237040 [0000000040000004] ---------------- IN: 0x0000000040000008: d2800020 mov x0, #0x1 Trace 0x7ff11e237080 [0000000040000008] ---------------- IN: 0x000000004000000c: b27b0c00 orr x0, x0, #0x1e0 Trace 0x7ff11e2370c0 [000000004000000c] ---------------- IN: 0x0000000040000010: d51000a0 msr (unknown), x0 Trace 0x7ff11e237110 [0000000040000010] ---------------- IN: 0x0000000040000014: d503201f nop Disassembler disagrees with translator over instruction decoding Please report this to qemu-devel@nongnu.org Trace 0x7ff11e237150 [0000000040000014] Trace 0x7ff11e237150 [0000000040000014] Trace 0x7ff11e237150 [0000000040000014] Trace 0x7ff11e237150 [0000000040000014] Trace 0x7ff11e237150 [0000000040000014] Trace 0x7ff11e237150 [0000000040000014] Trace 0x7ff11e237150 [0000000040000014] Trace 0x7ff11e237150 [0000000040000014] Trace 0x7ff11e237150 [0000000040000014] It looks like a bug, but I actually have no idea how would be best to overcome this situation. I would be thankful for any suggestion :) Best regards, Sergey