-
Notifications
You must be signed in to change notification settings - Fork 6.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
llext: add a "modules" Kconfig tristate example
This adds a new sample to demonstrate the use of tristate symbols in Kconfig to build a function as an llext module or as a built-in part of Zephyr. Signed-off-by: Luca Burelli <l.burelli@arduino.cc>
- Loading branch information
Showing
8 changed files
with
262 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
cmake_minimum_required(VERSION 3.20.0) | ||
|
||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) | ||
project(fs_shell) | ||
|
||
if(CONFIG_HELLO_WORLD_MODE STREQUAL "m") | ||
|
||
# Build the llext ... | ||
|
||
set(ext_name hello_world) | ||
set(ext_src src/${ext_name}_ext.c) | ||
set(ext_bin ${ZEPHYR_BINARY_DIR}/${ext_name}.llext) | ||
set(ext_inc ${ZEPHYR_BINARY_DIR}/include/generated/${ext_name}_ext.inc) | ||
add_llext_target(${ext_name}_ext | ||
OUTPUT ${ext_bin} | ||
SOURCES ${ext_src} | ||
) | ||
generate_inc_file_for_target(app ${ext_bin} ${ext_inc}) | ||
|
||
# ...and the code for loading and running it | ||
|
||
target_sources(app PRIVATE | ||
src/main_module.c | ||
) | ||
|
||
elseif(CONFIG_HELLO_WORLD_MODE STREQUAL "y") | ||
|
||
# Just build the two files together | ||
|
||
target_sources(app PRIVATE | ||
src/main_builtin.c | ||
src/hello_world_ext.c | ||
) | ||
|
||
else() | ||
message(FATAL_ERROR "Please choose 'y' or 'm' for CONFIG_HELLO_WORLD_MODE") | ||
endif() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# SPDX-License-Identifier: Apache-2.0 | ||
# Copyright (c) 2024 Intel Corporation. | ||
|
||
mainmenu "LLEXT functionality test" | ||
|
||
source "Kconfig.zephyr" | ||
|
||
config HELLO_WORLD_MODE | ||
tristate "Include the hello_world function" | ||
default m | ||
help | ||
This enables building the hello_world function, implemented in | ||
hello_world_ext.c, either as an llext module or as a built-in part of | ||
Zephyr. | ||
|
||
If you select 'm', the hello_world function will be built as an llext | ||
"module". If you select 'y', the hello_world function will be directly | ||
linked in the Zephyr image. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
.. zephyr:code-sample:: llext-modules | ||
:name: Linkable loadable extensions "module" sample | ||
:relevant-api: llext | ||
|
||
Call a function in a loadable extension module, | ||
either built-in or loaded at runtime. | ||
|
||
Overview | ||
******** | ||
|
||
This sample demonstrates the use of the :ref:`llext` subsystem in Zephyr. The | ||
llext subsystem allows for the loading of relocatable ELF files at runtime; | ||
their symbols can be accessed and functions called. | ||
|
||
Specifically, this shows how to call a simple "hello world" function, | ||
implemented in :zephyr_file:`samples/subsys/llext/modules/src/hello_world_ext.c`. | ||
This is achieved in two different ways, depending on the value of the Kconfig | ||
symbol ``CONFIG_HELLO_WORLD_MODE``: | ||
|
||
- if it is ``y``, the function is directly compiled and called by the Zephyr | ||
application. The caller code used in this case is in | ||
:zephyr_file:`samples/subsys/llext/modules/src/main_builtin.c`. | ||
|
||
- if it is ``m``, the function is compiled as an llext and it is included in | ||
the application as a binary blob. At runtime, the llext subsystem is used to | ||
load the extension and call the function. The caller code is in | ||
:zephyr_file:`samples/subsys/llext/modules/src/main_module.c`. | ||
|
||
Requirements | ||
************ | ||
|
||
A board with a supported llext architecture and console. This can also be | ||
executed in QEMU emulation on the :ref:`qemu_xtensa <qemu_xtensa>` or | ||
:ref:`qemu_cortex_r5 <qemu_cortex_r5>` virtual boards. | ||
|
||
Building and running | ||
******************** | ||
|
||
- The following commands build and run the sample so that the files are linked | ||
together in the same binary: | ||
|
||
.. zephyr-app-commands:: | ||
:zephyr-app: samples/subsys/llext/modules | ||
:board: qemu_xtensa | ||
:goals: build run | ||
:west-args: -T sample.llext.modules.builtin_build | ||
:compact: | ||
|
||
- The following commands build and run the sample so that the extension code is | ||
compiled separately and included in the Zephyr image as a binary blob: | ||
|
||
.. zephyr-app-commands:: | ||
:zephyr-app: samples/subsys/llext/modules | ||
:board: qemu_xtensa | ||
:goals: build run | ||
:west-args: -T sample.llext.modules.module_build | ||
:compact: | ||
|
||
Take a look at :zephyr_file:`samples/subsys/llext/modules/sample.yaml` for the | ||
additional architecture-specific configurations required in this case. | ||
|
||
To build for a different board, replace ``qemu_xtensa`` in the commands above | ||
with the desired board name. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
CONFIG_LOG=y | ||
CONFIG_LOG_MODE_IMMEDIATE=y | ||
|
||
CONFIG_MODULES=y | ||
|
||
# LLEXT is only required when loading the extension at runtime. Since in this | ||
# basic example there's only one llext, leaving it in when building the | ||
# extension as a built-in is redundant; in a real application, however, there | ||
# could be other uses of the llext subsystem. | ||
|
||
CONFIG_LLEXT=y | ||
CONFIG_LLEXT_LOG_LEVEL_DBG=y | ||
CONFIG_LLEXT_HEAP_SIZE=64 | ||
CONFIG_LLEXT_TYPE_ELF_RELOCATABLE=y |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
common: | ||
tags: llext | ||
arch_allow: | ||
- arm | ||
- xtensa | ||
platform_exclude: | ||
- apollo4p_evb # See #73443 | ||
- apollo4p_blue_kxr_evb # See #73443 | ||
- numaker_pfm_m487 # See #63167 | ||
integration_platforms: | ||
- qemu_xtensa | ||
- qemu_cortex_r5 | ||
- mps2/an385 | ||
harness: console | ||
sample: | ||
name: CONFIG_MODULES test | ||
description: Call code directly and from extensions | ||
tests: | ||
sample.llext.modules.module_build: | ||
filter: not CONFIG_MPU and not CONFIG_MMU and not CONFIG_SOC_SERIES_S32ZE | ||
extra_configs: | ||
- CONFIG_HELLO_WORLD_MODE=m | ||
- arch:arm:CONFIG_ARM_MPU=n | ||
- arch:xtensa:CONFIG_LLEXT_STORAGE_WRITABLE=y | ||
harness_config: | ||
type: one_line | ||
regex: | ||
- "Hello, world, from an llext!" | ||
sample.llext.modules.builtin_build: | ||
extra_configs: | ||
- CONFIG_HELLO_WORLD_MODE=y | ||
harness_config: | ||
type: one_line | ||
regex: | ||
- "Hello, world, from the main binary!" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* | ||
* Copyright (c) 2023 Intel Corporation. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
/* | ||
* This very simple hello world C code can be used as a test case for building | ||
* probably the simplest loadable extension. It requires a single symbol be | ||
* linked, section relocation support, and the ability to export and call out to | ||
* a function. | ||
*/ | ||
|
||
#include <zephyr/llext/symbol.h> | ||
#include <zephyr/sys/printk.h> | ||
|
||
void hello_world(void) | ||
{ | ||
#if defined(CONFIG_HELLO_WORLD_MODE) | ||
/* HELLO_WORLD_MODE=y: CONFIG_* is defined */ | ||
printk("Hello, world, from the main binary!\n"); | ||
#else | ||
/* HELLO_WORLD_MODE=m: CONFIG_*_MODULE is defined instead */ | ||
printk("Hello, world, from an llext!\n"); | ||
#endif | ||
} | ||
LL_EXTENSION_SYMBOL(hello_world); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/* | ||
* Copyright (c) 2024 Arduino SA | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL | ||
#include <zephyr/logging/log.h> | ||
LOG_MODULE_REGISTER(app); | ||
|
||
extern void hello_world(void); | ||
|
||
int main(void) | ||
{ | ||
LOG_INF("Calling hello world as a builtin"); | ||
|
||
hello_world(); | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Copyright (c) 2024 Arduino SA | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL | ||
#include <zephyr/logging/log.h> | ||
LOG_MODULE_REGISTER(app); | ||
|
||
#include <zephyr/llext/llext.h> | ||
#include <zephyr/llext/buf_loader.h> | ||
|
||
static uint8_t llext_buf[] = { | ||
#include "hello_world_ext.inc" | ||
}; | ||
|
||
int main(void) | ||
{ | ||
LOG_INF("Calling hello world as a module"); | ||
|
||
size_t llext_buf_len = ARRAY_SIZE(llext_buf); | ||
struct llext_buf_loader buf_loader = LLEXT_BUF_LOADER(llext_buf, llext_buf_len); | ||
struct llext_loader *ldr = &buf_loader.loader; | ||
|
||
struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT; | ||
struct llext *ext; | ||
int res; | ||
|
||
res = llext_load(ldr, "ext", &ext, &ldr_parm); | ||
if (res != 0) { | ||
LOG_ERR("Failed to load extension, return code %d\n", res); | ||
return res; | ||
} | ||
|
||
void (*hello_world_fn)() = llext_find_sym(&ext->exp_tab, "hello_world"); | ||
|
||
if (hello_world_fn == NULL) { | ||
LOG_ERR("Failed to find symbol\n"); | ||
return -1; | ||
} | ||
|
||
hello_world_fn(); | ||
|
||
return llext_unload(&ext); | ||
} |