-
Notifications
You must be signed in to change notification settings - Fork 6.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
llext: convert "modules" tests to sample #74060
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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() |
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. |
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. |
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 |
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!" |
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting... it did not in my tests, but I can't be sure it won't everywhere. I will fix this as we did for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure this would work in this case though; AFAICT, CONFIG_LLEXT is always |
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; | ||
} |
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); | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit, and definitely out of this PR's scope: I find this Kconfig option's name quite confusing at first glance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed it is. We can't change it unless we modify
kconfiglib
internals, though, since that is the name that triggers the whole "tristate" stuff in Kconfig. 🙁