138 lines
4.5 KiB
ReStructuredText
138 lines
4.5 KiB
ReStructuredText
.. 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
|
|
(...)
|