Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Soft reset on a Cortex-M using GDB

I'm working on a set of debugging tools for some in-house Cortex-M4 cores. I'm building embedded (no OS) ELF images using a gcc/binutils toolchain and simulating them using a modified version of QEMU. I'm creating the interrupt vector tables at the start (i.e. 0) of my images, and correctly initialising the stack pointer, and start address (i.e. the address of main).

The target programs are running as expected, and the debug tools which are built using the GDB remote protocol are working correctly.

What I'm currently trying to understand is how to initiate a soft-reset from GDB. That is, arrange for the target program to be reinitialised with the stack pointer reset to the initial value in the vector table, and the PC back at the start address.

I have already demonstrated to myself that the action of setting the PC value to 0, and running the core is not appropriate, and the result was that my "UsageFault" exception handler was invoked. (I assume that the core is in the wrong mode to execute this kind of action).

Is there a technique, i.e. by register writes using GDB remote protocol that I can soft-reset the simulated core, that is without having to power-cycle the QEMU session?

like image 214
Matthew Gardiner Avatar asked Jan 30 '26 21:01

Matthew Gardiner


2 Answers

You can make SW restart by writing SYSRESETREQ bit + KEY into AIRCR register:

AIRCR_REG_ADDRESS = 0xe000ed0c
AIRCR_KEY = 0x05fa0000
AIRCR_SYSRESETREQ_BIT = 0x00000004

AIRCR_REG = AIRCR_KEY | AIRCR_SYSRESETREQ_BIT

More info is here

This is working with all cortex-M

like image 51
vlk Avatar answered Feb 02 '26 12:02

vlk


This is a snippet of code for a Cortex-M3 I use to jump to any location with valid code in (including jumping to 0):

   IRQn_Type interrupt = (IRQn_Type)0;
   app_fn jump = *(app_fn *)(jumpaddr + sizeof(int));     //Reset is 2nd in table
   uint32_t stack_adr = *(uint32_t *)(jumpaddr);         //Stack pointer is 1st in table

   //Disable interrupts before jumping
   SysTick->CTRL &= ~ST_CTRL_ENABLE;

   while (interrupt <= CANActivity_IRQn)
   {
      NVIC_DisableIRQ(interrupt);
      interrupt++;
   }

   //Set the new stack pointer
   __set_MSP(stack_adr);

   //Set the new vector address
   SCB->VTOR = (location & 0x1FFFFF80);

   //Leap of faith!
   jump();

This code assumes jumpaddr is the base address of a vector table, as the stack pointer is first in the table and reset handler is second in the table.

Disabling interrupts is a must as you could have an interrupt go off after you have moved the stack pointer which normally causes an exception.

Of course, doing it this way will not reset any peripherals so that would need to be done before enabling the interrupts again.

Edit:

You can find the contents of __set_MSP here.

like image 20
NeRa Avatar answered Feb 02 '26 12:02

NeRa