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>
This commit is contained in:
parent
baa3b6a5ba
commit
57011e4c1a
8 changed files with 262 additions and 0 deletions
39
samples/subsys/llext/modules/CMakeLists.txt
Normal file
39
samples/subsys/llext/modules/CMakeLists.txt
Normal file
|
@ -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()
|
18
samples/subsys/llext/modules/Kconfig
Normal file
18
samples/subsys/llext/modules/Kconfig
Normal file
|
@ -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.
|
63
samples/subsys/llext/modules/README.rst
Normal file
63
samples/subsys/llext/modules/README.rst
Normal file
|
@ -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.
|
14
samples/subsys/llext/modules/prj.conf
Normal file
14
samples/subsys/llext/modules/prj.conf
Normal file
|
@ -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
|
35
samples/subsys/llext/modules/sample.yaml
Normal file
35
samples/subsys/llext/modules/sample.yaml
Normal file
|
@ -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!"
|
27
samples/subsys/llext/modules/src/hello_world_ext.c
Normal file
27
samples/subsys/llext/modules/src/hello_world_ext.c
Normal file
|
@ -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);
|
20
samples/subsys/llext/modules/src/main_builtin.c
Normal file
20
samples/subsys/llext/modules/src/main_builtin.c
Normal file
|
@ -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;
|
||||
}
|
46
samples/subsys/llext/modules/src/main_module.c
Normal file
46
samples/subsys/llext/modules/src/main_module.c
Normal file
|
@ -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);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue