Author Topic: STMG431 - Hardfault in startup assembly?  (Read 1931 times)

0 Members and 1 Guest are viewing this topic.

Online eTobeyTopic starter

  • Frequent Contributor
  • **
  • Posts: 557
  • Country: de
STMG431 - Hardfault in startup assembly?
« on: December 23, 2023, 03:00:21 am »
Hi,
i am working on a bootloader, and i need help while investigating the error cause. The problem is, that debugging the application is not easy, as the underlying bootloader.

From the addresses in the PC, i think i successfully jumped to the reset_handler of the application. But it seems, that in the reset_handler an error happens.

The assembly i find in the startup.s seems to be different to what i can find in the dissassembly.

« Last Edit: December 23, 2023, 03:03:30 am by eTobey »
"Sometimes, after talking with a person, you want to pet a dog, wave at a monkey, and take off your hat to an elephant." (Maxim Gorki)
 

Online uer166

  • Frequent Contributor
  • **
  • Posts: 893
  • Country: us
Re: STMG431 - Hardfault in startup assembly?
« Reply #1 on: December 23, 2023, 03:26:33 am »
STM32CubeIDE has the hard-fault viewer that decodes the registers to figure out what specific issue caused it. Stuff like unaligned access, bus fault, etc
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11261
  • Country: us
    • Personal site
Re: STMG431 - Hardfault in startup assembly?
« Reply #2 on: December 23, 2023, 03:34:31 am »
Are you looking at the bootloader code and comparing it to the application code?

This is not the best way to jump to the application. Once __set_MSP() is called, your code no longer has a valid stack, so if anything tries to use the stack, you will have a bad time. Here is a better code:
Code: [Select]
  uint32_t msp = *(uint32_t *)(APP_START);
  uint32_t reset_vector = *(uint32_t *)(APP_START + 4);
  asm("msr msp, %0 \n bx %1" :: "r" (msp), "r" (reset_vector));
This is atomic and guaranteed to work regardless of what the compiler is doing and optimization settings.

Also, disabling IRQs before jumping to the application is not the best. You really need to leave clean CPU. To do this, when you need to run the application, set a flag and do a reset. On reset check the flag and run the application without initializing any peripherals or clocks.

The flag can be a certain value written into the first few SRAM locations or similar.
« Last Edit: December 23, 2023, 03:37:09 am by ataradov »
Alex
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5912
  • Country: es
Re: STMG431 - Hardfault in startup assembly?
« Reply #3 on: December 23, 2023, 07:45:25 am »
This worked for me in a M0+ device, source here.
Code: [Select]
void JumpToBootloader(void)
{
  typedef struct {
      uint32_t Initial_SP;
      void (*Reset_Handler)(void);
  }boot_vectable_t;

  boot_vectable_t * BOOTVTAB =  (boot_vectable_t *)APP_START;
  __set_MSP(BOOTVTAB->Initial_SP);                                          // Set the MSP
  BOOTVTAB->Reset_Handler();                                                // Jump to bootloader
}
« Last Edit: December 23, 2023, 07:48:20 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Online eTobeyTopic starter

  • Frequent Contributor
  • **
  • Posts: 557
  • Country: de
Re: STMG431 - Hardfault in startup assembly?
« Reply #4 on: December 23, 2023, 03:32:27 pm »
I found the problem for the hardfault:
I havent finished the FLASH erase section, so there was abandoned code.

But now i have another issue: i can verify, that my code is startet. I measure the time, a pin goes high/low, and this is as it should happen. But the LPUART does not work any more. I use the very same function to init the bootloader and the app (the app uses the init only when using without bootloader). The bootloader happly prints stuff in the terminal, but then the app does not.


Once __set_MSP() is called, your code no longer has a valid stack
Do you mean with "code" the bootloader? Once i jumped, i will never come back there, so i dont care what is left behind. (Kind of a human thought to have, huh?  ::))
Isnt your code essentialy doing the same as i do? set the msp and jumpaddress (resethandler)?

This worked for me in a M0+ device, source ...
Whats the point to put it in to a struct, when you immediately use it out of there?
"Sometimes, after talking with a person, you want to pet a dog, wave at a monkey, and take off your hat to an elephant." (Maxim Gorki)
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5912
  • Country: es
Re: STMG431 - Hardfault in startup assembly?
« Reply #5 on: December 23, 2023, 03:56:18 pm »
You're not really putting anything into a struct, just pointing to it, basically explaining the compiler how to access the data.
It's doing the same as your code without messing with numbers.
Code: [Select]
void JumpToBootloader(void)
{
 8005614: b510      push {r4, lr}
      uint32_t Initial_SP;
      void (*Reset_Handler)(void);
  }boot_vectable_t;

  boot_vectable_t * BOOTVTAB =  (boot_vectable_t *)0x8004000;
  __set_MSP(BOOTVTAB->Initial_SP);                                          // Set the MSP
 8005618: 4b03      ldr r3, [pc, #12] ; (8005628 <JumpToBootloader+0x14>)
  __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : );
 800561a: 681a      ldr r2, [r3, #0]
 800561c: f382 8808 msr MSP, r2
  BOOTVTAB->Reset_Handler();                                                // Jump to bootloader
 8005620: 685b      ldr r3, [r3, #4]
 8005622: 4798      blx r3
}
« Last Edit: December 23, 2023, 04:00:03 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 11261
  • Country: us
    • Personal site
Re: STMG431 - Hardfault in startup assembly?
« Reply #6 on: December 23, 2023, 04:39:01 pm »
Do you mean with "code" the bootloader?
Yes, I do mean the bootloader code. Between the __set_MSP() and jump you have the code that assigns reset_handler variable. This code may still need stack. It is not likely, since optimizing compilers are likely to use registers. But there is still a chance.
Alex
 

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 3698
  • Country: gb
  • Doing electronics since the 1960s...
Re: STMG431 - Hardfault in startup assembly?
« Reply #7 on: December 25, 2023, 07:35:34 pm »
Do a search here for my posts about how to do a boot loader, with code running in RAM, etc. There are quite a few gotchas.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline abyrvalg

  • Frequent Contributor
  • **
  • Posts: 825
  • Country: es
Re: STMG431 - Hardfault in startup assembly?
« Reply #8 on: December 26, 2023, 01:01:37 am »
Does your UART code rely on interrupts? If yes, check that the app part re-enables them (but better listen to Alex and avoid disabling them before the jump) and that the VTOR reg is correct when the app is running already (some versions of ST’s SystemInit code are touching it).

Peter-h, please, no 😁
 

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 3698
  • Country: gb
  • Doing electronics since the 1960s...
Re: STMG431 - Hardfault in startup assembly?
« Reply #9 on: December 26, 2023, 08:21:36 am »
Most people leech from forums and never post the final working code (often employer-prohibited) whereas I do generally post the eventual solution :)
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5912
  • Country: es
Re: STMG431 - Hardfault in startup assembly?
« Reply #10 on: December 26, 2023, 10:13:00 am »
If the main app initializes everything, I would reset all peripherals and interrupts before jumping, it's pretty simple, see my 1st post.
Or just perform the RCC resets in the main app before init.
« Last Edit: December 26, 2023, 10:19:33 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 3698
  • Country: gb
  • Doing electronics since the 1960s...
Re: STMG431 - Hardfault in startup assembly?
« Reply #11 on: December 26, 2023, 11:04:06 am »
What is the "boot loader" actually doing? Is it the complete product i.e. one is trying to run everything from RAM (like one used to do in the Z80 days, for development) or is it just a bit of code which reads data (from a UART, SPI FLASH, etc) and write the data to the CPU FLASH? And what does this loader do when it is finished?

If the latter, one is not looking at much code. This is how I do it

Code: [Select]

#ifdef ITM_DEBUG
B_debug_puts("Entering Loader\n");
#endif

// This should not be needed
SCB->VTOR = FLASH_BASE;


// === At this point, interrupts and DMA must still be disabled ====
// Execute loader. Reboots afterwards.
// Parameters for loader are in the SSA.

extern char _loader_ram_start;
extern char _loader_ram_end;
extern char _loader_flash_start;

// Copy loader code and its init data to RAM. B_memcpy is a local memcpy() because we don't have access to stdlib at this point!!
B_memcpy(&_loader_ram_start, &_loader_flash_start, &_loader_ram_end - &_loader_ram_start);

// Set SP to top of CCM. This is not where the general stack is normally but it doesn't matter because
// the loader always reboots at the end. This assignment can't be done inside loader because it trashes
// the stack frame and any local variables which are allocated *at* the call.
// NOTE local variables cannot be used after the SP load.

asm volatile ("ldr sp, = 0x10010000 \n");

// See comments in loader.c for why the long call.

extern void loader_entry() __attribute__((long_call));
loader_entry();

// never get here (loader always reboots)
for (;;);

You have to be careful with stuff like memcpy because the compiler will try to detect code which looks like memcpy and will substitute it with a call to stdlib mempy which may not work, etc, etc, all dependent subtly on optimisation levels, etc, etc.

So...

Code: [Select]

// Local version
__attribute__((optimize("O0"))) // prevent replacement with memcpy()
static void B_memcpy (void *dest, const void *src, size_t len)
{
  char *d = dest;
  const char *s = src;
  while (len--)
    *d++ = *s++;
}

// Local version
__attribute__((optimize("O0"))) // prevent replacement with memset()
static void B_memset(void *s, uint8_t c,  uint32_t len)
{
    uint8_t * p=s;
    while(len--)
    {
    *p++ = c;
    }
}

and my loader ends with

Code: [Select]

static inline void L_reboot(void)
{
// Ensure all outstanding memory accesses including buffered write are completed before reset
__ASM volatile ("dsb 0xF":::"memory");
// Keep priority group unchanged
SCB->AIRCR  = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
                           (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
                            SCB_AIRCR_SYSRESETREQ_Msk    );
__ASM volatile ("dsb 0xF":::"memory");
// wait until reset
for(;;)
{
__NOP();
}
}

I don't do anything with MSP. The CPU starts with a reset (obviously), then does some stuff, then copies the loader from flash to ram (above) then jumps to it, the loader does its stuff, and does a software reset.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8173
  • Country: fi
Re: STMG431 - Hardfault in startup assembly?
« Reply #12 on: December 26, 2023, 11:59:06 am »
Is this "let's post our bootloaders?" If so, look at mine. This time, bigger isn't necessarily better:

Code: [Select]
static void __attribute__((naked)) start_app(uint32_t pc, uint32_t sp)
{
__asm(
"msr msp, r1\n\
bx r0\n");
}

static void __attribute__((noreturn)) boot(void* addr)
{
SCB->VTOR = (uint32_t)addr;
uint32_t sp = ((uint32_t*)addr)[0];
uint32_t pc = ((uint32_t*)addr)[1];
start_app(pc, sp);
while(1);
}

void main()
{
uint32_t crc_a = crc32b((uint8_t*)APP_A, meta_a.fw_len*4);
uint32_t crc_b = crc32b((uint8_t*)APP_B, meta_b.fw_len*4);

int a_valid = meta_a.fw_len > 64 && meta_a.fw_len <= (APP_B-APP_A)/4 && meta_a.update_cnt <= META_LARGEST_UPDATE_CNT
&& crc_a == meta_a.crc32;

int b_valid = meta_b.fw_len > 64 && meta_b.fw_len <= (APP_B-APP_A)/4 && meta_b.update_cnt <= META_LARGEST_UPDATE_CNT
&& crc_b == meta_b.crc32;

boot_app_shared.a_crc = crc_a;
boot_app_shared.b_crc = crc_b;
boot_app_shared.a_valid = a_valid;
boot_app_shared.b_valid = b_valid;


if( (a_valid && b_valid && meta_a.update_cnt >= meta_b.update_cnt ) || // Both valid, A newer, or
(a_valid && !b_valid))                                             // only A valid
{
boot_app_shared.bootloader_magic = MAGIC_BOOT_A;
boot((void*)APP_A);
}
else if( (a_valid && b_valid && meta_a.update_cnt < meta_b.update_cnt ) || // Both valid, B newer, or
(b_valid && !a_valid))                                             // only B valid
{
boot_app_shared.bootloader_magic = MAGIC_BOOT_B;
boot((void*)APP_B);
}
else
{
// No valid app: eternal LED blinker
while(1)
{
LED2_ON();
delay_ms(100);
LED2_OFF();
delay_ms(100);
}
}

}
 
The following users thanked this post: DavidAlfa

Offline abyrvalg

  • Frequent Contributor
  • **
  • Posts: 825
  • Country: es
Re: STMG431 - Hardfault in startup assembly?
« Reply #13 on: December 26, 2023, 09:58:37 pm »
Siwastaja, forgive my red teamer’s paranoia, no chance of CRCing a 4GB-1 size when one of the boot slots is empty due to meta_x.fw_len use before check?

Totally agree with "smaller is better" for bootloaders. Make it as simple as possible, test thoroughly, don’t touch/update until really necessary (definitely no "run from RAM" nonsense on an RWW-capable MCU).
 
The following users thanked this post: Siwastaja

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8173
  • Country: fi
Re: STMG431 - Hardfault in startup assembly?
« Reply #14 on: December 27, 2023, 10:28:52 am »
Siwastaja, forgive my red teamer’s paranoia, no chance of CRCing a 4GB-1 size when one of the boot slots is empty due to meta_x.fw_len use before check?

Great demonstration of how beneficial it is to post code online! Thanks.
 

Offline gpr

  • Contributor
  • Posts: 21
  • Country: gb
Re: STMG431 - Hardfault in startup assembly?
« Reply #15 on: December 28, 2023, 11:23:26 pm »

Code: [Select]

// Copy loader code and its init data to RAM. B_memcpy is a local memcpy() because we don't have access to stdlib at this point!!

You have to be careful with stuff like memcpy because the compiler will try to detect code which looks like memcpy and will substitute it with a call to stdlib mempy which may not work, etc, etc, all dependent subtly on optimisation levels, etc, etc.

btw, why memcpy from stdlib doesn't work here? I encountered this in my code, it tried to jump to some strange non-existing address or something like that.
I didn't find the reason and just replaced it with my implementation.
 

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 3719
  • Country: us
Re: STMG431 - Hardfault in startup assembly?
« Reply #16 on: December 29, 2023, 05:14:21 am »
btw, why memcpy from stdlib doesn't work here? I encountered this in my code, it tried to jump to some strange non-existing address or something like that.
I didn't find the reason and just replaced it with my implementation.

memcpy from stdlib should work fine as long as you are using a standard compile/link workflow to build the code.  If you use link options like -nostartfiles and -nostdlib which completely disable runtime support you will need to provide your own versions of some builtins such as memcpy.  The compiler may need to call memcpy for things like structure assignment so you really need to have it no matter what.  If you link with these options but don't provide memcpy you will get link errors about undefined symbols. Linker error messages can be a bit obtuse, but it shouldn't be too hard to figure out what's going on.

If you need to implement memcpy yourself you may have to seriously bully the compiler to avoid replacing part or all of it with calls to the expected built-in memcpy -- this can lead to unfortunate recursive loop that will quickly fault.  Or just implement memcpy in assembly.

peter's problem is that he was modifying the linker script to put some symbols (such as memcpy) into sections of the executable which didn't actually get loaded into the target (yet: it was part of the application code).  Since the symbol was actually present at link time, it didn't cause a link error.  If you don't do this, you won't have that problem.
 

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 3698
  • Country: gb
  • Doing electronics since the 1960s...
Re: STMG431 - Hardfault in startup assembly?
« Reply #17 on: December 29, 2023, 08:17:33 am »
Quote
btw, why memcpy from stdlib doesn't work here? I encountered this in my code, it tried to jump to some strange non-existing address or something like that.
I didn't find the reason and just replaced it with my implementation.

If you are building a boot block project, say 32k, you probably won't have stdlib available.

Quote
peter's problem is that he was modifying the linker script to put some symbols (such as memcpy) into sections of the executable which didn't actually get loaded into the target (yet: it was part of the application code).  Since the symbol was actually present at link time, it didn't cause a link error.

Not sure about that :)

The situation was more complicated.

Quote
If you need to implement memcpy yourself you may have to seriously bully the compiler to avoid replacing part or all of it with calls to the expected built-in memcpy

You give it a different name, and use the attribs I showed to make doubly sure.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline eutectique

  • Frequent Contributor
  • **
  • Posts: 392
  • Country: be
Re: STMG431 - Hardfault in startup assembly?
« Reply #18 on: December 29, 2023, 11:32:52 am »
btw, why memcpy from stdlib doesn't work here? I encountered this in my code, it tried to jump to some strange non-existing address or something like that.
I didn't find the reason and just replaced it with my implementation.

A map file could show what exactly is linked into the binary.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf