14
votes

I am assembling a cross compiling unit testing rig for an ARM system and running the tests on a host machine with qemu-system-arm. Specifically, I'm using qemu to emulate the Stellaris LM3S6965 eval board as it contains a Cortex M3 processor like my target environment. The binary run in qemu is built with GNU Tools for ARM.

There is no OS involved. The test suite is run as a bare metal application with qemu in -nographic mode. The toolchain and the test rig itself are working fine. And the tests successfully run to completion and produce test results within qemu just fine as well.

The problem is in wrapping up qemu in an automated build tool (Rake in this case). Other than keyboard commands I have not yet found a good way to cause qemu to exit after the test suite runs and spits out its results. This causes the build environment to hang / rely on user intervention.

I have looked high and low and have found no good sources on how to accomplish a simple exit after program termination. I did find a couple suggestions of running qemu with the -no-reboot option and then triggering a system reset from the program running in the emulator. I have tried this. It works… kinda. I write the appropriate values to the emulated processor's reset vector after main() executes, and this does trigger a reset. After running the test suite qemu reports catching a system reset. However, it reports this as a hardware error, dumps register contents, and then exits angry (error message below). While this does accomplish exiting after the test suite runs, it then breaks the automated build script due to qemu exiting with an error condition.

qemu: hardware error: System reset

I'd like to avoid hacking the insertion of keyboard commands into the build to simulate user intervention. I'd also like to avoid relying on qemu exiting in an error state.

It seems I'm close to a clean exit but not quite there. Searching for the qemu error message (above) has produced no relevant documentation other than tangentially related bug reports.

Is there a mechanism for causing qemu to exit after main() returns in a bare metal program that I'm missing? Will this -no-reboot + system reset strategy work? If so, what else is necessary to allow qemu to exit cleanly?

6

6 Answers

6
votes

I recommend to use Angel interface for ARM processores. It is very helpful for debugging. You can read something about it on ARM Info Center. Especially look at operation angel_SWIreason_ReportException (0x18) and parameter ADP_Stopped_ApplicationExit to which QEMU will understand that your application has ended.

Don't forget to run QEMU with -semihosting argument, like this:

qemu-system-arm -nographic -semihosting -kernel your_binary

Here is the code to tell QEMU to stop (you have to use some assembler):

register int reg0 asm("r0");
register int reg1 asm("r1");

reg0 = 0x18;    // angel_SWIreason_ReportException
reg1 = 0x20026; // ADP_Stopped_ApplicationExit

asm("svc 0x00123456");  // make semihosting call

You can also look at my project at github where I used it.

2
votes

Typically you need to do whatever on the hardware would cause a system shutdown (power off); QEMU will make that do an 'exit QEMU'. Unfortunately not all of the hardware we emulate implements a power-off mechanism (and sometimes it's not wired up in the QEMU model, though that's generally an easy bug to fix).

1
votes

The cleanest option for me was to grab the source for a stable version of Qemu close to the one we were already using. The following refers to version 1.1.2 of the Qemu source.

I modified the emulation of the reset vector handling for the Cortex M3 + Stellaris LM3S6965 eval board in armv7m_nvic.c. I replaced the hw_error() call with a call to qemu_system_reset_request(). This internal system call will reset a virtual machine but also responds to the -no-reboot command line option for a clean shutdown as discussed in my original question.

These build instructions worked for me after grabbing a snapshot of Qemu 1.1.2. I encountered several build errors, but web searches quickly resolved each issue.

1
votes

The current ARMv7M qEmu (based on TI Stellaris LM3S6965 Microcontroller) supports reset from AICRCR register (Application Interrupt and Reset Control Register). Writing to the SYSRESETREQ bit of this register asserts a signal to the outer system that requests a reset.

Writing to AICRCR requires to write 0x5FA to the VECTKEY field, otherwise the processor ignores the write.

This line makes ARMv7M qEmu resets.

SCB->AIRCR = (0x5FA << SCB_AIRCR_VECTKEY_Pos) | SCB_AIRCR_SYSRESETREQ_Msk;

To prevent qEmu to indefinitly reboot, you can add the qEmu argument -no-reboot.

1
votes

aarch64 semihosting exit

https://stackoverflow.com/a/40957928/895245 gave A32, here is A64:

.global main
main:
    /* 0x20026 == ADP_Stopped_ApplicationExit */
    mov x1, #0x26
    movk x1, #2, lsl #16
    str x1, [sp,#0]

    /* Exit status code. Host QEMU process exits with that status. */
    mov x0, #0
    str x0, [sp,#8]

    /* x1 contains the address of parameter block.
     * Any memory address could be used. */
    mov x1, sp

    /* SYS_EXIT */
    mov w0, #0x18

    /* Do the semihosting call on A64. */
    hlt 0xf000

Here is an example on GitHub:

Documentation has moved to: https://developer.arm.com/docs/100863/latest

1
votes

Following J. Havran answer ,it worked using different assembly, and i was working with zephyr qemu cortex m3 :

static inline void _exit_qemu() {
  register u32_t r0 __asm__("r0");
  r0 = 0x18;
  register u32_t r1 __asm__("r1");
  r1 = 0x20026;
  __asm__ volatile("bkpt #0xAB");
}

and

qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -vga none -net none -pidfile qemu.pid -serial mon:stdio -semihosting -kernel build/zephyr/zephyr.elf