Skip to content

Commit

Permalink
tests/misc/llext-edk: Do run the test, instead of only building it
Browse files Browse the repository at this point in the history
As commit 6a1d987 ("cmake: Add LL_EXTENSION_BUILD to EDK flags")
made clear, only building an application with the EDK is not enough to
prevent regressions. This patch address that by also running the
application using the extension built with the EDK.

To provide a more comprehensive test, the application runs the extension
code on both kernel and user space, using a parameter sent by the
application. Assertions on the output from the application are used to
ensure expected results.

For now, tests only run on qemu_cortex_r5, but this can be expanded in
the future.

Signed-off-by: Ederson de Souza <[email protected]>
  • Loading branch information
edersondisouza authored and nashif committed Aug 14, 2024
1 parent 1893a12 commit fe573eb
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 10 deletions.
7 changes: 6 additions & 1 deletion tests/misc/llext-edk/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(llext_edk_test LANGUAGES C)

target_sources(app PRIVATE src/main.c)
target_sources(app PRIVATE src/main.c src/foo.c)
zephyr_include_directories(include)
zephyr_include_directories(${ZEPHYR_BASE}/boards/native/common)

if(EXTENSION_DIR)
target_include_directories(app PRIVATE ${EXTENSION_DIR})
target_compile_definitions(app PRIVATE LOAD_AND_RUN_EXTENSION)
endif()
11 changes: 11 additions & 0 deletions tests/misc/llext-edk/extension/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
cmake_minimum_required(VERSION 3.20.0)

set(CMAKE_C_COMPILER arm-zephyr-eabi-gcc)
set(CMAKE_FIND_ROOT_PATH $ENV{ZEPHYR_SDK_INSTALL_DIR}/arm-zephyr-eabi)
set(CMAKE_C_COMPILER_FORCED TRUE)
set(CMAKE_CXX_COMPILER_FORCED TRUE)

project(extension)

include($ENV{LLEXT_EDK_INSTALL_DIR}/cmake.cflags)
Expand All @@ -15,9 +20,15 @@ get_property(COMPILE_OPTIONS_PROP DIRECTORY PROPERTY COMPILE_OPTIONS)
add_custom_command(
OUTPUT
${PROJECT_BINARY_DIR}/${PROJECT_NAME}.llext
${PROJECT_BINARY_DIR}/${PROJECT_NAME}.inc
COMMAND ${CMAKE_C_COMPILER} ${COMPILE_OPTIONS_PROP}
-o ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.llext
${PROJECT_SOURCE_DIR}/src/main.c
COMMAND
${PYTHON_EXECUTABLE}
${PROJECT_SOURCE_DIR}/file2hex.py
--file ${PROJECT_NAME}.llext
> ${PROJECT_NAME}.inc
)

add_custom_target(extension ALL DEPENDS ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.llext)
6 changes: 3 additions & 3 deletions tests/misc/llext-edk/extension/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
*/

#include <zephyr/kernel.h>
#include <zephyr/llext/symbol.h>

#include <app_api.h>

int start(void)
int start(int bar)
{
int bar = 42;

printk("foo(%d) is %d\n", bar, foo(bar));
return 0;
}
LL_EXTENSION_SYMBOL(start);
4 changes: 3 additions & 1 deletion tests/misc/llext-edk/prj.conf
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
CONFIG_APPLICATION_DEFINED_SYSCALL=y
#CONFIG_USERSPACE=y
CONFIG_LLEXT=y
CONFIG_USERSPACE=y

CONFIG_MAIN_STACK_SIZE=2048
53 changes: 50 additions & 3 deletions tests/misc/llext-edk/pytest/test_edk.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import logging
import os
import pytest
import shutil
import tempfile

Expand All @@ -13,15 +14,23 @@

logger = logging.getLogger(__name__)

def test_edk(dut: DeviceAdapter):
def test_edk(unlaunched_dut: DeviceAdapter):
# Need to have the ZEPHYR_SDK_INSTALL_DIR environment variable set,
# otherwise can't actually build the edk
if os.environ.get("ZEPHYR_SDK_INSTALL_DIR") is None:
logger.warning("ZEPHYR_SDK_INSTALL_DIR is not set, skipping test")
pytest.skip("ZEPHYR_SDK_INSTALL_DIR is not set")

# Can we build the edk?
command = [
"west",
"build",
"-b",
unlaunched_dut.device_config.platform,
"-t",
"llext-edk",
"--build-dir",
dut.device_config.build_dir,
unlaunched_dut.device_config.build_dir,
]
output = check_output(command, text=True)
logger.info(output)
Expand All @@ -30,7 +39,7 @@ def test_edk(dut: DeviceAdapter):
with tempfile.TemporaryDirectory() as tempdir:
# Copy the edk to the temporary directory using python methods
logger.debug(f"Copying llext-edk.tar.xz to {tempdir}")
edk_path = Path(dut.device_config.build_dir) / "zephyr/llext-edk.tar.xz"
edk_path = Path(unlaunched_dut.device_config.build_dir) / "zephyr/llext-edk.tar.xz"
shutil.copy(edk_path, tempdir)

# Extract the edk using tar
Expand All @@ -45,6 +54,12 @@ def test_edk(dut: DeviceAdapter):
ext_dir = Path(os.environ["ZEPHYR_BASE"]) / "tests/misc/llext-edk/extension"
shutil.copytree(ext_dir, tempdir_extension, dirs_exist_ok=True)

# Also copy file2hex.py to the extension directory, so that it's possible
# to generate a hex file from the extension binary
logger.debug(f"Copying file2hex.py to {tempdir_extension}")
file2hex = Path(os.environ["ZEPHYR_BASE"]) / "scripts/build/file2hex.py"
shutil.copy(file2hex, tempdir_extension)

# Set the LLEXT_EDK_INSTALL_DIR environment variable so that the extension
# knows where the EDK is installed
edk_dir = Path(tempdir) / "llext-edk"
Expand All @@ -64,3 +79,35 @@ def test_edk(dut: DeviceAdapter):

# Check if the extension was built
assert os.path.exists(Path(tempdir_extension) / "build/extension.llext")

# Can we run it? First, rebuild the application, now including the extension
# build directory in the include path, so that the application can find the
# extension code.
logger.debug(f"Running application with extension in {tempdir_extension} - west build")
command = [
"west",
"build",
"-b",
unlaunched_dut.device_config.platform,
"--build-dir",
unlaunched_dut.device_config.build_dir,
"--",
f"-DEXTENSION_DIR={tempdir_extension}/build/"
]
logger.debug(f"west command: {command}")
output = check_output(command, text=True)
logger.info(output)

# Now that the application is built, run it
logger.debug(f"Running application with extension in {tempdir_extension}")
try:
unlaunched_dut.launch()
lines = unlaunched_dut.readlines_until("Done")

assert "Calling extension from kernel" in lines
assert "Calling extension from user" in lines
assert "foo(42) is 1764" in lines
assert "foo(43) is 1849" in lines

finally:
unlaunched_dut.close()
4 changes: 3 additions & 1 deletion tests/misc/llext-edk/src/foo.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
*/

#include <app_api.h>
#include <app_api.h>

#include <zephyr/llext/symbol.h>
#include <zephyr/internal/syscall_handler.h>

int z_impl_foo(int bar)
{
Expand Down
85 changes: 85 additions & 0 deletions tests/misc/llext-edk/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,94 @@

#include <app_api.h>

#include <zephyr/kernel.h>
#include <zephyr/llext/llext.h>
#include <zephyr/llext/buf_loader.h>
#include <zephyr/app_memory/mem_domain.h>

#ifdef LOAD_AND_RUN_EXTENSION
static const unsigned char extension_llext[] = {
#include <extension.inc>
};
static const size_t extension_llext_len = ARRAY_SIZE(extension_llext);
#endif

#define STACK_SIZE 1024
#define HEAP_SIZE 1024

#ifdef LOAD_AND_RUN_EXTENSION
struct k_thread kernel_thread, user_thread;

K_THREAD_STACK_DEFINE(stack_kernel, STACK_SIZE);
K_THREAD_STACK_DEFINE(stack_user, STACK_SIZE);

K_HEAP_DEFINE(heap_kernel, HEAP_SIZE);
K_HEAP_DEFINE(heap_user, HEAP_SIZE);

static void thread_entry(void *p1, void *p2, void *p3)
{
int bar;
int (*start_fn)(int) = p1;

printk("Calling extension from %s\n",
k_is_user_context() ? "user" : "kernel");

if (k_is_user_context()) {
bar = 42;
} else {
bar = 43;
}

start_fn(bar);
}

void load_and_run_extension(int thread_flags, struct k_thread *thread,
struct k_mem_domain *domain,
k_thread_stack_t *stack, struct k_heap *heap,
struct llext **ext)
{
struct llext_buf_loader buf_loader = LLEXT_BUF_LOADER(extension_llext,
extension_llext_len);
struct llext_loader *loader = &buf_loader.loader;
struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT;
int (*start_fn)(int bar);

llext_load(loader, "extension", ext, &ldr_parm);
start_fn = llext_find_sym(&(*ext)->exp_tab, "start");

llext_add_domain(*ext, domain);

k_thread_create(thread, stack, STACK_SIZE,
thread_entry, start_fn, NULL, NULL, -1,
K_INHERIT_PERMS | thread_flags,
K_FOREVER);
k_mem_domain_add_thread(domain, thread);
k_thread_heap_assign(thread, heap);

k_thread_start(thread);
k_thread_join(thread, K_FOREVER);

llext_unload(ext);
}
#endif

int main(void)
{
#ifdef LOAD_AND_RUN_EXTENSION
struct k_mem_domain domain_kernel, domain_user;
struct llext *ext_kernel, *ext_user;

k_mem_domain_init(&domain_kernel, 0, NULL);
k_mem_domain_init(&domain_user, 0, NULL);

load_and_run_extension(0, &kernel_thread, &domain_kernel,
stack_kernel, &heap_kernel, &ext_kernel);
load_and_run_extension(K_USER, &user_thread, &domain_user,
stack_user, &heap_user, &ext_user);

printk("Done\n");
#else
printk("Extension not loaded\n");
#endif
return 0;
}
2 changes: 1 addition & 1 deletion tests/misc/llext-edk/testcase.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ tests:
- pytest
- edk
platform_allow:
- native_sim
- qemu_cortex_r5
toolchain_exclude: llvm

0 comments on commit fe573eb

Please sign in to comment.