Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Dynamically load apps to zephyr RTOS

License

NotificationsYou must be signed in to change notification settings

rgujju/Dynamic_App_Loading

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

96 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Minimal example of dynamic apps for Cortex-M
Doxygen docs »

Note: Initial implementation using jump tables and FreeRTOS is in the freertos branchhttps://github.com/rgujju/Dynamic_App_Loading/tree/freertos

About The Project

This is a minimal example of having dynamic apps with syscall functionality. It is divided into 2 parts, the kerneland the app. The kernel is responsible for loading the app and handling syscall functionality. The syscall functionalityis implemented usingzephyr syscalls.

Kernel

At the moment the kernel is nothing but zephyr and 2 functions, theSetLed andLoadApp.The syscall functionality is implemented in themodules/sys_module while the app loading is implemented in themodules/app_loader.

The kernel contains the actual implementation ofSetLed, This code does not get compiled into the app again. So this APIcan be changed without needing to rebuild the app.

There are some changes required in zephyr to support this. Have a look at [zephyr changes](#Changes required in zephyr) and a diffhere

App

The app is compiled as a Position independent Code.The following flags are used:-fpic -msingle-pic-base -mpic-register=r9 -mno-pic-data-is-text-relative -mlong-calls.This creates an executable which can be run from any memory location given that the GOT address is in the registerR9.This value is given as a parameter while calling the app, so this gets placed inR0.TheAppStart function defined inapp_startup.s is used to copy the value fromR0 toR9. This function contains thefirst instruction that gets executed when the app is started. Followed by themain function defined in app. This sequence isdefined using the app linker scriptapp_base.ld.
gcc generates the executable in elf which contains a lot of information and is quite large for a mcu. So this elf file isconverted intoTINF, which can be easily loaded onto a mcu.

The current example app blinky inapps/blinky turns on the green LED while the kernel turns on the red LED on the STM32F429i-DISC1 board.But should be able to port to any arm cortex m board.

To learn more about GOT and PIC refer theAcknowledgements, they do a much better job of explaining the concepts.

Why did I do this?

Mainly to learn about the GOT, PIC, memory layout of mcu, elf format, and a lot more.Gained a lot of knowledge from this project.

Usage

This example is for the STM32F429i DISC1 board. But should beportable to any other mcu.

1 Build libsys_module.a. This is a static library aka archive. The app will be linked to this archive.

cd kernelmkdir build
cd build
cmake .. -DBOARD=stm32f429i_disc1 -DUSERLIB=1
make userlib
A folder calleduserlib will be created. This folder will contain everything required to build the app.
It contains the header and archive files.
Copy this folder to the apps folder.
cp -R userlib ../../apps/

2 Build app. A example app called blinky is provided.

This app turns on LED1. There is some extra code in the example to purposely populate the data and bss sections
And also to verify if the GOT is copied properly. ie global variable access.
cd apps/blinky
mkdir build/
cd build
cmake ..
make
This will createblinky.elf file. This file now needs to be converted into TINF.

3 Create TINF of the app

python3 ../../../elf2tinf/elf2tinf.py --major 1 --minor 0 blinky.elf blinky
This will generate 2 files, theblinky.tinf andblinky_tinf.h
blink.tinf is in a binary format which can be loaded over uart, ble, usb, etc.
blinky_tinf.h is the same binary data in the form of a header file, so the app can be tested easily, bycompiling into the kernel, without implementing the actual transfer of the binary to the mcu.
More details about this tool is in the README.md in folder elf2tinf
Copy theblinky_tinf file to the kernel include folder.
cp -rf blinky_tinf.h ../../../kernel/include

4 Build the kernel

cd kernel/build
Build the kernel, this is the code that will actually load the app and run it
cmake .. -DBOARD=stm32f429i_disc1 -DUSERLIB=0
make clean Need to clean the build files of the userlib.makeThis will generate themain.elf file which needs to be loaded onto the board.

5 Load main.elf to hardware

This can vary depending on your method. but if zephyr flash method works for you then simply
make flash

Documentation

User documentation is in header files
To go deeper see the source files
Online docs:https://rgujju.github.io/Dynamic_App_Loading/html/index.html

Porting

  • This project uses the STM32F429 mcu but should be portable to any mcu.
  • Zephyr makes porting extremely easy. Simply change the-DBOARD param given to cmake to match your board
  • Modify theapps/blinky/CMakeLists.txt mainly theMCU andMCU_SPEC
  • To port to other architecture need to modify how the GOT base address is passed to the app

Limitations:

  • Cannot reset thread stack to 0x00 while entering userpsace
  • Statically allocated stack.
  • No stack overflow detection yet

Changes required in zephyr

Have a look at the diffhere

  • Allocate space in the userspace thread stack for GOT, bss and data section of dynamic app. Right now I am just notresetting the thread stack to 0 or 0xaaaaaaaa while entering userspace so that it does not overwrite these sections.

  • The thread stack is statically allocated due to limiations in zephyr heap allocator. The current heap implementationcannot allocate stack which is aligned properly according to MPU

  • Do not compile syscall APIs as static inline while building userlib, and setCONFIG_USERLIB=1 and__ZEPHYR_USER__=1

    • CONFIG_USERLIB=1 is used to remove thestatic inline from the syscall declaration so that the API is exported.
    • __ZEPHYR_USER__=1 is used to compile only the userspace version of the syscall
    • The modified api definition is formed as follows
externint8_tz_impl_some_api(uint8_ta,uint8_tb);#ifndefCONFIG_USERLIBstaticinline#endifint8_tsome_api(uint8_ta,uint8_tb){#ifdefCONFIG_USERSPACEif (z_syscall_trap()) {return (int8_t)arch_syscall_invoke2(*(uintptr_t*)&Led_Num,*(uintptr_t*)&Led_State,K_SYSCALL_SETLED);}#endifcompiler_barrier();returnz_impl_SetLed(Led_Num,Led_State);}
  • The source of the C files not to be compiled in the userlib must be inside#ifndef CONFIG_USERLIB .. #endif
  • Only the generated syscall header files and its dependent header filesare required in the userlib so a typical module can be compiled as follows

module_name.c

#include<zephyr.h>#include<device.h>#include"<module_name>.h"#ifndefCONFIG_USERLIBint8_tz_impl_some_api(uint8_ta,uint8_tb){    ...}int8_tz_vrfy_some_api(uint8_ta,uint8_tb){}#include<syscalls/some_api_mrsh.c>#endif/*CONFIG_USERLIB*/

module_name.h

#ifndefCONFIG_USERLIB_syscallint8_tsome_api(uint8_ta,uint8_tb);#endif# The source for jumping to the syscall willgetincludedfromhere#include<syscalls/module_name.h>
  1. Should be able to just add#include <syscalls/file_name.h> along with dependencies to include the APIs from that file.

Acknowledgements

Following resources helped me:

Releases

No releases published

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp