Skip to content

Commit

Permalink
samples/subsys/llext: EDK sample
Browse files Browse the repository at this point in the history
Shows a simple application which loads extensions and some simple
extensions. While everything is inside Zephyr tree, everything can
actually be build from different directories (even machines), as long as
the EDK is generated from the application and used by the extensions.
More information is available at sample's README.

This sample is build only for twister, as it requires a few steps to be
properly run, namely build the EDK, install it somewhere, build the
extensions using the EDK and finally build the application with the
extensions.

Signed-off-by: Ederson de Souza <ederson.desouza@intel.com>
  • Loading branch information
edersondisouza committed Mar 5, 2024
1 parent d6d136b commit 48953b0
Show file tree
Hide file tree
Showing 19 changed files with 964 additions and 0 deletions.
131 changes: 131 additions & 0 deletions samples/subsys/llext/edk/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
.. zephyr:code-sample:: llext-edk
:name: Linkable loadable extensions EDK

About
*****

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 API provided is a simple publish/subscribe system,
based on Zbus, which extensions use to communicate with each other.

The application is composed of a subscriber thread, which listens for events
published and republishes then 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
``CONFIG_LLEXT_EDK_USERSPACE_ONLY=n``.

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
:gen-args: -DSDK_BUILD=1
:compact:

Note that the ``SDK_BUILD`` option is necessary to build the EDK initially,
as it prevents the application from trying to load the extensions before
they are built.

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
***********************

First, ensure the Zephyr SDK location is available at the environment variable
``ZEPHYR_SDK_INSTALL_DIR``:

.. code-block:: console
export ZEPHYR_SDK_INSTALL_DIR=</path/to/zephyr-sdk>
As the ``ZEPHYR_SDK_INSTALL_DIR`` is used by the extensions to find the Zephyr SDK.

To build the extensions, in the ``ext1``, ``ext2``, ``ext3`` and ``kext1``
directories:

.. code-block:: console
cmake -B build
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
(...)
15 changes: 15 additions & 0 deletions samples/subsys/llext/edk/app/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

project(app LANGUAGES C)

target_sources(app PRIVATE src/main.c src/pubsub.c)
zephyr_include_directories(include)

option(SDK_BUILD "Build without including extension" OFF)

if (SDK_BUILD)
add_definitions(-DSDK_BUILD)
endif()
38 changes: 38 additions & 0 deletions samples/subsys/llext/edk/app/include/app_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2024 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef _TEST_EDK_H_
#define _TEST_EDK_H_
#include <zephyr/kernel.h>
#include <zephyr/toolchain.h>

#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

enum Channels {
CHAN_TICK = 1,
CHAN_LAST
};

struct channel_tick_data {
unsigned long l;
};

__syscall int publish(enum Channels channel, void *data,
size_t data_len);
__syscall int receive(enum Channels channel, void *data,
size_t data_len);
__syscall int register_subscriber(enum Channels channel,
struct k_event *evt);
#ifdef __cplusplus
}
#endif

#include <syscalls/app_api.h>
#endif /* _TEST_EDK_H_ */
24 changes: 24 additions & 0 deletions samples/subsys/llext/edk/app/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
CONFIG_APPLICATION_DEFINED_SYSCALL=y
CONFIG_USERSPACE=y
CONFIG_LLEXT=y
CONFIG_LLEXT_LOG_LEVEL_WRN=y

CONFIG_LOG=y
CONFIG_LOG_MODE_IMMEDIATE=y

CONFIG_MAIN_STACK_SIZE=4096

CONFIG_DYNAMIC_OBJECTS=y
CONFIG_DYNAMIC_THREAD=y
CONFIG_DYNAMIC_THREAD_ALLOC=y
CONFIG_DYNAMIC_THREAD_PREFER_ALLOC=y
CONFIG_ZBUS=y
CONFIG_ZBUS_CHANNEL_NAME=y

CONFIG_LLEXT_HEAP_SIZE=32

# Comment/remove to enable kext1.
#CONFIG_LLEXT_EDK_USERSPACE_ONLY=y

CONFIG_EVENTS=y
#CONFIG_HEAP_MEM_POOL_SIZE=8192
13 changes: 13 additions & 0 deletions samples/subsys/llext/edk/app/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
common:
tags: llext edk
arch_allow:
- arm
filter: CONFIG_ARCH_HAS_USERSPACE
sample:
description: EDK sample application
name: EDK sample application
tests:
sample.edk.app:
build_only: true
extra_args: SDK_BUILD=1
tags: edk llext
Loading

0 comments on commit 48953b0

Please sign in to comment.