| .. zephyr:code-sample:: llext-edk |
| :name: Linkable loadable extensions EDK |
| :relevant-api: llext_apis |
| |
| Enable linkable loadable extension development outside the Zephyr tree using |
| LLEXT EDK (Extension Development Kit). |
| |
| Overview |
| ******** |
| |
| This sample demonstrates how to use the Zephyr LLEXT EDK (Extension Development |
| Kit). It is composed of one Zephyr application, which provides APIs for the |
| extensions that it loads. The provided API is a simple publish/subscribe system, |
| based on :ref:`Zbus <zbus>`, which extensions use to communicate with each other. |
| |
| The application is composed of a subscriber thread, which listens for events |
| published and republishes them via Zbus to the extensions that are |
| subscribers. There are four extensions, which are loaded by the application and |
| run in different contexts. Extensions ``ext1``, ``ext2`` and ``ext3`` run in |
| userspace, each demonstrating different application and Zephyr API usage, such as |
| semaphores, spawning threads to listen for events or simply publishing or |
| subscribing to events. Extension ``kext1`` runs in a kernel thread, albeit similar |
| to ``ext3``. |
| |
| The application also creates different memory domains for each extension, thus |
| providing some level of isolation - although the kernel one still has access |
| to all of Zephyr kernel. |
| |
| Note that the kernel extension is only available when the EDK is built with |
| the :kconfig:option:`CONFIG_LLEXT_EDK_USERSPACE_ONLY` option disabled. |
| |
| |
| The application is built using the Zephyr build system. The EDK is built using |
| the Zephyr build system as well, via ``llext-edk`` target. The EDK is then |
| extracted and the extensions are built using CMake. |
| |
| Finally, the way the application loads the extensions is by including them |
| during build time, which is not really practical. This sample is about the EDK |
| providing the ability to build extensions independently from the application. |
| One could build the extensions in different directories, not related to the |
| Zephyr application - even on different machines, using only the EDK. At the |
| limit, one could even imagine a scenario where the extensions are built by |
| different teams, using the EDK provided by the application developer. |
| |
| Building the EDK |
| **************** |
| |
| To build the EDK, use the ``llext-edk`` target. For example: |
| |
| .. zephyr-app-commands:: |
| :zephyr-app: samples/subsys/llext/edk/app |
| :board: qemu_cortex_r5 |
| :goals: build llext-edk |
| :west-args: -p=always |
| :compact: |
| |
| Copy the EDK to some place and extract it: |
| |
| .. code-block:: console |
| |
| mkdir /tmp/edk |
| cp build/zephyr/llext-edk.tar.xz /tmp/edk |
| cd /tmp/edk |
| tar -xf llext-edk.tar.xz |
| |
| Then set ``LLEXT_EDK_INSTALL_DIR`` to the extracted directory: |
| |
| .. code-block:: console |
| |
| export LLEXT_EDK_INSTALL_DIR=/tmp/edk/llext-edk |
| |
| This variable is used by the extensions to find the EDK. |
| |
| Building the extensions |
| *********************** |
| |
| The :envvar:`ZEPHYR_SDK_INSTALL_DIR` environment variable is used by the |
| extensions to find the Zephyr SDK, so you need to ensure it's properly set: |
| |
| .. code-block:: console |
| |
| export ZEPHYR_SDK_INSTALL_DIR=</path/to/zephyr-sdk> |
| |
| To build the extensions, in the ``ext1``, ``ext2``, ``ext3`` and ``kext1`` |
| directories: |
| |
| .. code-block:: console |
| |
| cmake -B build |
| make -C build |
| |
| Alternatively, you can set the ``LLEXT_EDK_INSTALL_DIR`` directly in the |
| CMake invocation: |
| |
| .. code-block:: console |
| |
| cmake -B build -DLLEXT_EDK_INSTALL_DIR=/tmp/edk/llext-edk |
| make -C build |
| |
| Building the application |
| ************************ |
| |
| Now, build the application, including the extensions, and run it: |
| |
| .. zephyr-app-commands:: |
| :zephyr-app: samples/subsys/llext/edk/app |
| :board: qemu_cortex_r5 |
| :goals: build run |
| :west-args: -p=always |
| :compact: |
| |
| You should see something like: |
| |
| .. code-block:: console |
| |
| [app]Subscriber thread [0x20b28] started. |
| [app]Loading extension [kext1]. |
| [app]Thread 0x20840 created to run extension [kext1], at privileged mode. |
| [k-ext1]Waiting sem |
| [app]Thread [0x222a0] registered event [0x223c0] |
| [k-ext1]Waiting event |
| [app]Loading extension [ext1]. |
| [app]Thread 0x20a30 created to run extension [ext1], at userspace. |
| [app]Thread [0x20a30] registered event [0x26060] |
| [ext1]Waiting event |
| [app]Loading extension [ext2]. |
| [app]Thread 0x20938 created to run extension [ext2], at userspace. |
| [ext2]Publishing tick |
| [app][subscriber_thread]Got channel tick_chan |
| [ext1]Got event, reading channel |
| [ext1]Read val: 0 |
| [ext1]Waiting event |
| [k-ext1]Got event, giving sem |
| [k-ext1]Got sem, reading channel |
| [k-ext1]Read val: 0 |
| [k-ext1]Waiting sem |
| (...) |