zephyr/samples/subsys/llext/edk
Guennadi Liakhovetski 19ccec6a03 llext: use EXPORT_SYMBOL() universally
Replace LL_EXTENSION_SYMBOL() with EXPORT_SYMBOL() in all tests and
samples.

Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
2024-09-02 12:31:52 -04:00
..
app
ext1
ext2
ext3
k-ext1
README.rst

README.rst

.. 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
    (...)