When using Gdb to remote-debug a program, if we try to single-step an invalid instruction, Qemu will never return control to the remote Gdb. The source of this problem is external interrupts being masked out in cpu_exec if cpu->singlestep_enabled has the SSTEP_NOIRQ flag set. To solve this I've added an additional flag, SSTEP_EXCEPTION, that will be set in the exception_with_syndrome instruction generated when trying to translate the invalid instruction and will be cleared after checking for cpu->singlestep_enabled in cpu_exec.
Signed-off-by: Martin Galvan <martin.gal...@tallertechnologies.com> --- The long story: Qemu generates an exception_with_syndrome instruction when it realizes the instruction it's trying to translate is invalid. That instruction in turn modifies cs->exception_index and calls cpu_loop_exit. Normally, the value in cs->exception_index would cause do_interrupt to set the PC to point to the corresponding exception handler. However, since we're masking out IRQs, the PC will never be set correctly. Even worse, since Qemu will have generated an internal exception to return control back to the remote Gdb *after* it generated the syndrome one, its code will appear after the call to cpu_loop_exit. Since the PC won't have changed, we'll try to excecute the same translation block as before, thus calling cpu_loop_exit again, and so on. Here's the bug tracker link: https://bugs.launchpad.net/qemu/+bug/1364501 cpu-exec.c | 6 +++++- include/qom/cpu.h | 5 +++++ target-arm/op_helper.c | 5 +++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/cpu-exec.c b/cpu-exec.c index 5fa172c..5d9f231 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -448,9 +448,13 @@ int cpu_exec(CPUArchState *env) for(;;) { interrupt_request = cpu->interrupt_request; if (unlikely(interrupt_request)) { - if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) { + if (unlikely((cpu->singlestep_enabled & SSTEP_NOIRQ) && + !(cpu->singlestep_enabled & SSTEP_EXCEPTION))) { /* Mask out external interrupts for this step. */ interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK; + } else { /* An exception occured; don't mask out this one */ + /* Mask out any future external interrupts */ + cpu->singlestep_enabled &= ~SSTEP_EXCEPTION; } if (interrupt_request & CPU_INTERRUPT_DEBUG) { cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG; diff --git a/include/qom/cpu.h b/include/qom/cpu.h index f2df033..a57800f 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -607,6 +607,11 @@ void qemu_init_vcpu(CPUState *cpu); #define SSTEP_ENABLE 0x1 /* Enable simulated HW single stepping */ #define SSTEP_NOIRQ 0x2 /* Do not use IRQ while single stepping */ #define SSTEP_NOTIMER 0x4 /* Do not Timers while single stepping */ +#define SSTEP_EXCEPTION 0x8 /* Don't mask out exception-related IRQs. Set only if + * we have to process an exception while single- + * stepping (such as when single-stepping an invalid + * instruction). + */ /** * cpu_single_step: diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index fe40358..2139ea6 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -251,6 +251,11 @@ void HELPER(exception_with_syndrome)(CPUARMState *env, uint32_t excp, CPUState *cs = CPU(arm_env_get_cpu(env)); assert(!excp_is_internal(excp)); + + if (unlikely(cs->singlestep_enabled & SSTEP_NOIRQ)) { + cs->singlestep_enabled |= SSTEP_EXCEPTION; + } + cs->exception_index = excp; env->exception.syndrome = syndrome; cpu_loop_exit(cs); -- 1.9.1 -- Martín Galván Software Engineer Taller Technologies Argentina San Lorenzo 47, 3rd Floor, Office 5 Córdoba, Argentina Phone: 54 351 4217888 / +54 351 4218211