cancel
Showing results for 
Search instead for 
Did you mean: 

Running C functions transfered during runtime

Áde R.1
Associate II

Hi,

I'm currently developing a PLC-like device based on a STM32F429ZI. It works with FreeRTOS, and the I/O logic is implemented in a single C function, which only accesses a couple of buffers (the locations of which are known prior to compilation). This function runs as a thread on a firmware based on RTOS.

Thing is, i'd like the system to be reprogrammable at runtime, allowing the user to send the binary code of a new function (via ethernet, for example), so that the own firmare would relocate the code to flash and lauch it on a new thread. This would require two dedicated flash regions i suppose, in order to keep normal operation while the device is being reprogrammed.

What would be the best practices to achieve this? i just need a general overview about its feasability, but also i'd be grateful if someone could refer me to some resources/links on related topics, mainly on transfering the code from RAM to Flash so that it can be executed properly. I've searched online to no avail.

Thanks in advance.

1 ACCEPTED SOLUTION

Accepted Solutions
SBEN .2
Senior II

Hello @�?lvaro Alejandro de Rada Alonso​ ,

As @Community member​ stated, it is a standard internal flash programming sequence. You can check the example for your specific MCU: STM32CubeF4/Projects/STM32F429ZI-Nucleo/Examples/FLASH/FLASH_EraseProgram at master · STMicroelectronics/STM32CubeF4 · GitHub

Otherwise, what you're intending to do (dynamic loading) has its own caveats, the most important of them is security. Uploading arbitrary code to be executed is unsafe, and can be devastating especially for industrial automation. I'll give you some pointer on about how it can be done, but beware, it is not recommended unless you know what you're doing:

Code placement:

STM32F4 has no MMU, so code placement should be static or the code to be placed should be position independent.

  • Static code placement:

The code to be loaded is always placed in the same region (flash or RAM). The region's size would define the maximum binary size. If the the code would require static variables or global variables, a RAM region should also be allocated. This can be achieved through the linker file.

  • Position independent code:

It is possible to generate position indipendentt code (--rwpi option under MDK-ARM, -fPIC for gcc, --ropi for IAR). The generated code will run from anywhere (alignement should be respected), but the generated code will be relatively large and slow.

Memory protection:

When running external libraries, it is always a good idea to "sandbox" it. In this contexts it means using the MPU to disallow memory access to critical regions (OS memory, etc...). This can be done using FreeRTOS with MPU support and running the task in unprivileged mode: FreeRTOS-MPU - ARM Cortex-M3 and ARM Cortex-M4 Memory Protection Unit support in FreeRTOS.

LIBC and exported functions:

The loaded code won't have any idea on where to locate standard library functions and OS interaction functions (vTaskDelay() for example). Either the symbols need to be exported before hand from the main binary and used during the linking process (SYMDEFs for MDK-ARM and IAR). it the main binary is recompiled, these symbols would move. So you'd need to implement a branch table or use software interrupts (SVC calls) to resolve the issue.

From the other side, the loaded binary should have a format that indicates precisely the offset of the entry function and, perhaps, how much stack is needed if you intend to scale the stack dynamically.

Execution:

Once the code is loaded, it needs to be executed by the OS. For FreeRTOS, task creation requires a function pointer to create the task. The function pointer would be the entry point that we've talked before. If your binary contains more than one task, then the binary format should be able to express that.

Security:

Unless you're working on a personal project that will never be deployed or used in any capacity except as a curiosity, security is mandatory. Transferring the binary securely encrypting it and signing is necessary. You can check how SBSFU does it for example: X-CUBE-SBSFU - Secure boot & secure firmware update software expansion for STM32Cube - STMicroelectronics.

Finally, I remember (but am not sure), that the pebble smart watch is able to dynamically load code, and some of its code is available: GitHub - jwise/FreeRTOS-Pebble: basic FreeRTOS implementation for Pebble Time.

There's also the following github repo with an implementation of dynamic loading for ZephyrRTOS GitHub - rgujju/Dynamic_App_Loading: Dynamically load apps to zephyr RTOS.

Best regards,

@SBEN .2​ 

View solution in original post

4 REPLIES 4

> transfering the code from RAM to Flash

That's just ordinary FLASH programming - read the FLASH chapter in the RM.

JW

SBEN .2
Senior II

Hello @�?lvaro Alejandro de Rada Alonso​ ,

As @Community member​ stated, it is a standard internal flash programming sequence. You can check the example for your specific MCU: STM32CubeF4/Projects/STM32F429ZI-Nucleo/Examples/FLASH/FLASH_EraseProgram at master · STMicroelectronics/STM32CubeF4 · GitHub

Otherwise, what you're intending to do (dynamic loading) has its own caveats, the most important of them is security. Uploading arbitrary code to be executed is unsafe, and can be devastating especially for industrial automation. I'll give you some pointer on about how it can be done, but beware, it is not recommended unless you know what you're doing:

Code placement:

STM32F4 has no MMU, so code placement should be static or the code to be placed should be position independent.

  • Static code placement:

The code to be loaded is always placed in the same region (flash or RAM). The region's size would define the maximum binary size. If the the code would require static variables or global variables, a RAM region should also be allocated. This can be achieved through the linker file.

  • Position independent code:

It is possible to generate position indipendentt code (--rwpi option under MDK-ARM, -fPIC for gcc, --ropi for IAR). The generated code will run from anywhere (alignement should be respected), but the generated code will be relatively large and slow.

Memory protection:

When running external libraries, it is always a good idea to "sandbox" it. In this contexts it means using the MPU to disallow memory access to critical regions (OS memory, etc...). This can be done using FreeRTOS with MPU support and running the task in unprivileged mode: FreeRTOS-MPU - ARM Cortex-M3 and ARM Cortex-M4 Memory Protection Unit support in FreeRTOS.

LIBC and exported functions:

The loaded code won't have any idea on where to locate standard library functions and OS interaction functions (vTaskDelay() for example). Either the symbols need to be exported before hand from the main binary and used during the linking process (SYMDEFs for MDK-ARM and IAR). it the main binary is recompiled, these symbols would move. So you'd need to implement a branch table or use software interrupts (SVC calls) to resolve the issue.

From the other side, the loaded binary should have a format that indicates precisely the offset of the entry function and, perhaps, how much stack is needed if you intend to scale the stack dynamically.

Execution:

Once the code is loaded, it needs to be executed by the OS. For FreeRTOS, task creation requires a function pointer to create the task. The function pointer would be the entry point that we've talked before. If your binary contains more than one task, then the binary format should be able to express that.

Security:

Unless you're working on a personal project that will never be deployed or used in any capacity except as a curiosity, security is mandatory. Transferring the binary securely encrypting it and signing is necessary. You can check how SBSFU does it for example: X-CUBE-SBSFU - Secure boot & secure firmware update software expansion for STM32Cube - STMicroelectronics.

Finally, I remember (but am not sure), that the pebble smart watch is able to dynamically load code, and some of its code is available: GitHub - jwise/FreeRTOS-Pebble: basic FreeRTOS implementation for Pebble Time.

There's also the following github repo with an implementation of dynamic loading for ZephyrRTOS GitHub - rgujju/Dynamic_App_Loading: Dynamically load apps to zephyr RTOS.

Best regards,

@SBEN .2​ 

Hi Waclawek,

Thanks for the input, i'll take a look on how to properly do this now that i know the proper term for it.

Regards,

�?lvaro

Hello ***,

Thanks very much for such a thorough answer! now i have a good deal of information to get started.

I'll take look into the resources you linked and try to figure out the safest/most stable way to achieve this,

at least for basic home automation tasks.

Regards,

�?lvaro