Guidance on Plugin Architecture for Firmware with FreeRTOS, Supporting PIC and Distinct RAM for Code and Data

Hello FreeRTOS Community,

I am working on an advanced firmware project for Cortex-M microcontrollers, leveraging FreeRTOS, and I’m exploring the implementation of a plugin system. My aim is to allow clients to independently develop and deploy their algorithms as plugins. Here are the key aspects of my current setup and requirements:

  • Firmware Base: The core firmware is currently not compiled as Position-Independent Code (PIC), but I am open to the possibility of having the plugins themselves be PIC.
  • Development Stack: The project uses LittleFS for file management and gcc-arm-none-eabi as the toolchain.
  • Memory Management: A unique requirement is that the plugins might have their code and data in different RAM locations. Additionally, I’m considering utilizing different types of memory for data - fast (DTCM) and standard SRAM.

Given this setup, I have several questions where I would greatly appreciate the community’s input:

  1. Plugin File Format and Loading: What file format would be most suitable for these plugins, and how should I approach loading them into the system, considering the need for different memory locations for code and data?
  2. Function Entry Points in Non-PIC Firmware: How can I manage function entry points for the plugins to interact with the non-PIC firmware, especially with the possibility of address changes upon recompilation?
  3. Memory Allocation: Any best practices or recommendations for handling distinct memory types for plugin code and data, especially in the context of FreeRTOS?
  4. Security and Stability Considerations: How can I ensure the security and stability of the system, particularly when dealing with dynamic loading of third-party plugins?

Any insights, suggestions, or references to similar projects or implementations would be incredibly helpful.

Thank you in advance for your assistance and guidance!

A few comments for some of your points.

#2, dealing with Function Entry Point addresses, the best practices that I know is to define a list of functions that the plug-ins are going to be allowed to call in your base system, and put jumps to there functions in a fixed block of memory. Thus, when the actual code being called moves, the jumps just need to be corrected (normally automatically). You can see an example of that in how many vector interrupt systems work, there is a table of fixed defined addresses for each ISR, and a jump to the actual implementation is put there. Similarly, the plugin file should have some jumps to the entry point(s) for the plug in at some predefined locations.

#3 Memory Allocation: Here you are going to need to make some decisions. You can let the plugins just call a memory allocator through the service vector list above, or you can have the plugins get allocated a fixed (maybe defined in the plugin) amount of ram that they need to handle sub-allocations for. The latter allows easier clean up if you need to “abort” a plugin, but might be a bit more wasteful. It also somewhat limits interactions between plugins.

#4, You are going to really need to fully define your “threat” profile here. FreeRTOS is NOT a fully protected system, so “bad” code can’t be handled as well. If your processor has a MPU, and you make all plugins run only as restricted tasks, and limit what sort of interactions they can do, you can perhaps hold some level of enforced stability. This isn’t the area that FreeRTOS is really aimed for, as it really isn’t an “OS” in the normal sense, but a support library to allow you to right a SINGLE program that can easily use multiple threads of execution interacting with each other.

Does the approach I described here work for you - Binary partitioning - #3 by aggarg?

For security, you may want to read our threat model - Kernel Threat Model - FreeRTOS