STM32 Boot from specific NOR block - stm32

I've ported an STM32F4 project from Keil uVision5 to Eclipse (GNU MCU Plugin).
In my Keil uVision5 project, I used to use 7th block (0x080E0000) for my boot code (I verify that using STM32 ST-LINK Utility tool that my boot code is really at 7th block) which I used to configure very easily using Keil uVision5 interface as shown below:
If I'm not mistaken, in my Keil uVision project (whose memory configuration is shown above), the MCU boots from 0th block (0x08000000) and then jumps immediately to 7th block (0x080E0000) to execute my code. Tell me if I'm wrong.
My problem is with my Eclipse project. As you might already know, we configure the memory map using a linker script file named mem.ld. I tried to imitate the same behavior using mem.ld but I suppose that I've failed. Whenever I flash the executable generated by Eclipse, my code doesn't run. Moreover, I couldn't find a way to indicate an area for startup in mem.ld file. Below is my memory configuration in mem.ld file:
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 16K
FLASHB1 (rx) : ORIGIN = 0x080E0000, LENGTH = 640K
EXTMEMB0 (rx) : ORIGIN = 0x00000000, LENGTH = 0
EXTMEMB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0
EXTMEMB2 (rx) : ORIGIN = 0x00000000, LENGTH = 0
EXTMEMB3 (rx) : ORIGIN = 0x00000000, LENGTH = 0
MEMORY_ARRAY (xrw) : ORIGIN = 0x20002000, LENGTH = 32
}
Could you please help me to imitate the same behavior with Keil uVision5 using mem.ld file? What mistake do I do in mem.ld configuration? What should be the correct configuration?
Thanks a lot in advance.

The address of the code which is executed after the reset is taken from the 4-7 bytes of your vector table (it is called the reset vector).
In the gcc style linker script the ENTRY directive is important not your memory sections definitions.
you can change the entry to the address value or a symbol. For example
ENTRY(My_Startup_Function) or as it is in the standard linker scripts generated by the CubeMX ENTRY(Reset_Handler)
in keil as I remember you have couple of options:
command line
--entry=location
where location can be address or symbol
here is more information about the root region and the entry point. I do not use keil anymore and personally prefer gcc.

Related

GNU Linker: ELF has a LOAD segment with RWX permissions. Embedded ARM project

I have updated my arm-none-eabi GCC and the associated tools and rebuilt an embedded project I develop.
$ arm-none-eabi-ld --version
GNU ld (GNU Binutils) 2.39
Suddenly, I'm getting the warning
/usr/lib/gcc/arm-none-eabi/12.1.0/../../../../arm-none-eabi/bin/ld: warning: my_elf_file.elf has a LOAD segment with RWX permissions.
This warning seems to be newly introduced. I haven't changed the source / linkerscript lately. (EDIT: I checked an old ELF file created with a previous version. It didn't print the warning during linking but has the same issue). I develop for an STM32F407 microcontroller.
The memory configuration in my linker script is the following:
MEMORY
{
FLASH (xr) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
CCM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
}
Looking into the linked ELF I see:
$ readelf -l my_elf_file.elf
Elf file type is EXEC (Executable file)
Entry point 0x800b1f1
There are 5 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x010000 0x08000000 0x08000000 0x2220c 0x2220c RWE 0x10000
LOAD 0x040000 0x10000000 0x0802220c 0x003e8 0x00d30 RW 0x10000
LOAD 0x050000 0x20000000 0x080225f4 0x00c1c 0x00c1c RW 0x10000
LOAD 0x000c20 0x20000c20 0x08023210 0x00000 0x01e70 RW 0x10000
LOAD 0x002a90 0x20002a90 0x08023210 0x00000 0x08580 RW 0x10000
Section to Segment mapping:
Segment Sections...
00 .vectors .text .ARM .flashcrc
01 .ccmdata .ccmbss
02 .data
03 .bss
04 .heap_stack
Indeed, the first segment is flagged as RWE. It contains the sections .vectors, .text, .ARM and .flashcrc.
The .vectors section and the .text section contain the Vector table and the program code. I added another section in my linkerscript called .flashcrc
.flashcrc : ALIGN(4)
{
KEEP(*(.flashcrc))
KEEP(*(.flashcrc.*))
. = ALIGN(4);
} >FLASH =0xFF
I use this section in the source code to position a const struct in there, that contains CRC checksums. These checksums are calculated and patched into the ELF later by a spearate python script. The struct is easier to find in the ELF, if it resides in its own section.
Removing this section or simply relocating it to RAM like this:
.flashcrc : ALIGN(4)
{
KEEP(*(.flashcrc))
KEEP(*(.flashcrc.*))
. = ALIGN(4);
} >RAM AT >FLASH =0xFF
removed the "W" flag from the segment and the warning is gone.
I don't understand why the ELF file contains an "writable" flag for a section that is located in FLASH which is marked in the linkerscript as non writable. I tried using (xr!w) in the MEMORY definition, but it didn't change anything.
How do I convince the linker that the segment is not writable to silence this warning?
Why don't the flags given in the MEMORY part of the linker script have any impact?
Weirdly enough this doesn't happen with the .vectors section which contains a const array of function pointers. So this section is basically identical to the .flashcrc section.
EDIT2:
Today I found a little more time to play around. The struct in the .flashcrc section is defined in code (globally) like this:
volatile const struct flash_crcs __attribute__((section(".flashcrc"))) crcs_in_flash = {
.start_magic = 0xA8BE53F9UL,
.crc_section_ccm_data = 0UL,
.crc_section_text = 0UL,
.crc_section_data = 0UL,
.crc_section_vectors = 0UL,
.end_magic = 0xFFA582FFUL,
};
The crc values are patched into the ELF after linking.
I had to make the struct volatile. If it isn't volatile, the compiler optimizes away the access to the struct because the elements are all 0 from its perspective as it doesn't know that these are patched after linking.
It turns out, that the warning disappears, if the volatile keyword is removed. For some reason the volatile keyword tricks the compiler/linker into thinking that this should be writable.
Is there another way to prevent the compiler optimizing away the access to this struct but not using volatile?

linker script and changing the flash address

I would like to ask the following a question: im using stm32g0xx microcontroller and i want to change the flash address in linker script automatically and not be forced to changed manually every time i want to generate an apllication image to let it run from diffrent address. what im doing i wrote an application and i wrote it to tow different address"0x08001000 and 0x08004800" to have the apility to switch to other application incase one of them is updated or damaged. it worked fine but i need by every image to change the flash address manually and i would like to ask if it is possible to changed somewhere else out of the linker script like startup.s?
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 8K
FLASH (rx) : ORIGIN = 0x8001000, LENGTH = 32K
}
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 8K
FLASH (rx) : ORIGIN = 0x8004800, LENGTH = 32K
}
You can create two linker files and compile two times, each time with a different linker script and a different output binary. You will obtain the two necessary binaries. To integrate it on your project, it depends on your way of working (STM IDE, standalone Makefile...) which you did not mention.
As a side note, you should modify the LENGTH on your linker scripts, it will prevent the linker to place data where you have another application.
Your first application starts at 4KB (0x1000), and the second start at 18KB (0x4800), the lenght of the first application should be 18-4 = 16KB and the second LENGTH should be 32-18 = 14KB (if the FLASH total size is 32KB).
You can write two different linker script and apply one or the other in your building enviroment (with the -T linker flag) or you can use a variable for your ORIGIN and pass it with -Wl,--defsym=<VAR_NAME>=<VAR_VALUE>

External Flash Loader linker script file

I'm trying to make a custom external flash loader for my STM32F746 uC based board and i found some templates for this purpose from this link that is belongs to the ST workshop on this YouTube link. So when i dug into the corresponding files i saw that in a linker script file named linker.ld the RAM memory region is defined from base address 0x20000004 though i know that the RAM memory region in STM uCs starts from address 0x20000000:
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000004, LENGTH = 320K
}
I searched for many other templates file existing in this link and also they are using the same way for RAM memory region base address.
So the problem is why we must define RAM memory address from 0x20000004 for external flash loader algorithm to store in and not from 0x20000000 instead. Could anyone please help me to explain the reason and tell me that what will happen if we do the:
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K
}
instead of previous one and what errors might be occurred?
Thanks.

How can I change the start address on flash?

I'm using STM32F746ZG and FreeRTOS.
The start address of flash is 0x08000000. But I want to change it to 0x08040000. I've searched this issue through google but I didn't find the solution.
I changed the linker script like the following.
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K
/* FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K */
FLASH (rx) : ORIGIN = 0x8040000, LENGTH = 768K
}
If I only change it and run the debugger, it has the problem.
If I change the VECT_TAB_OFFSET from 0x00 to 0x4000, it works fine.
/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET 0x40000 /* 0x00 */
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
But if I don't use debugger, it doesn't work anything.
It means it only works when using ST-Linker.
Please let me know if you know the solution.
Thank you for in advance of your reply.
The boot address can be set in the option bytes.
You can set any address in the flash with 16k increments. There are two 16 bit registers in the option bytes area, one is used when the boot pin is low at reset, the other when the pin is high. Write the desired address shifted right by 14 bits, i.e. divided by 16384.
To boot from 0x08040000, write 0x2010 into the register as described in the Option bytes programming chapter of the reference manual.
You could also write a bootloader. Bootloader sits on the 0x0800 0000 address and loads your application firmware meaning jumps to it.
This is the other way to do it.
You need to place 8 bytes at the original beginning of the FLASH. Stm32 boots always from the address 0x00000000 which is aliased to the one of the memories (depending on the boot pins and options).
The first word contains the stack pointer the second one your reset handler. You never get to your code as it boots always from the same address.
You will need to modify your linker script and the startup files where vectors are defined

STM32L073RZ (rev Z) IAP jump to bootloader (system memory)

I use the STM32L073RZ (Nucleo 64 board).
I would like to jump into the system memory in application programming (IAP).
My code works on the revision B of the STM32L073 microcontroller but fails on the latest revision, rev Z.
I read the errata sheet, no details are given, just a limitation fixed on the dual boot mechanism into system memory according to the BFB2 bit.
Is the system memory no longer supports an IAP jumping to execute its code (to flash firmwares through USB or UART without using the BOOT0 pin) ?
The function is the first line of my main program, it tests if the code has to jump to the booloader:
void jumpBootLoader(void)
{
/* to do jump? */
if ( *((unsigned long *)0x20003FF0) == 0xDEADBEEF )
{
/* erase the label */
*((unsigned long *)0x20003FF0) = 0xCAFEFEED;
/* set stack pointer to the bootloader start address */
__set_MSP(*((uint32_t*)(0x1FF00000)));
/* system memory mapped at 0x00000000 */
__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
/* jump to #bootloader + 4 */
((void (*)(void))(*((uint32_t*)(0x1FF00004))))();
}
}
I call these two lines as soon as the BP1 button is pressed to trig the jump operation after resetting the µC:
*((unsigned long *)0x20003FF0) = 0xDEADBEEF;
NVIC_SystemReset();
I use the HSI 16Mhz clock source.
The solution is to jump twice to the system memory.
First Jump to bootloader startup to initialize Data in RAM until the Program counter will returned to Flash by the Dualbank management.
Second Jump: Jump to the Dualbank bypassed address
How to use: User has first to initialize a variable “ Data_Address” (must be an offset Flash sector aligned address) in Flash to distinguish between first/second Jump.
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.PageAddress = Data_Address;
EraseInitStruct.NbPages = 1;
First_jump = *(__IO uint32_t *)(Data_Address);
if (First_jump == 0) {
HAL_FLASH_Unlock();
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Data_Address, 0xAAAAAAAA);
HAL_FLASH_Lock();
/* Reinitialize the Stack pointer and jump to application address */
JumpAddress = *(__IO uint32_t *)(0x1FF00004);
}
if (First_jump != 0) {
HAL_FLASH_Unlock();
HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError);
HAL_FLASH_Lock();
/* Reinitialize the Stack pointer and jump to application address */
JumpAddress = (0x1FF00369);
}
Jump_To_Application = (pFunction) JumpAddress;
__set_MSP(*(__IO uint32_t *)(0x1FF00000));
Jump_To_Application();
First important thing: you use 0x1FF0 0000 as the addres where SP is stored, this is correct. Then you use 0x1 FF00 0004 as the address from which you load the function pointer. This is not correct - one zero too many.
Note that using __set_MSP() is generally not such a good idea if you also use MSP as your stack pointer (which you most likely are). The recent definition of this function, which marks "sp" as clobbered register, causes your change to be reverted almost immediately. Incidentally today I was doing exactly the same thing you are doing and I've found that problem. In your assembly listing you'll see that SP is saved into some other register before the msr msp, ... instruction and restored right after that.
Finally I wrote that manually (STM32F4, so different addresses):
constexpr uint32_t systemMemoryBase {0x1fff0000};
asm volatile
(
" msr msp, %[sp] \n"
" bx %[pc] \n"
:: [sp] "r" (*reinterpret_cast<const uint32_t*>(systemMemoryBase)),
[pc] "r" (*reinterpret_cast<const uint32_t*>(systemMemoryBase + 4))
);
BTW - you don't need to set memory remap for the bootloader to work.
Thanks for your help. I have my answer !
The v4.0 bootloader (initial version) does not implement the dual bank mechanism but this feature is supported by v4.1.
Software can jump to bootloader but it will execute the dual boot mechanism.
So the bootloader goes back to bank1 (or bank2 if a code is "valid").
Today it is not possible to bypass the dual bank mechanism to execute the bootloader with my configuration:
The boot0 pin is reset and the protection level is 0 (see "Table 11. Boot pin and BFB2 bit configuration" in the reference manual).
Where is your program counter when you call __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH()?
Remapping a memory region while you're executing out of that same region will end poorly! You may need to relocate this code into SRAM, or execute this code with PC set to the fixed FLASH memory mapping (0x0800xxxx).