Skip to content
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 full ARM relocation support (adding executable) #70171

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions samples/subsys/llext/fs_loader/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright (c) 2024 Schneider Electric
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

project(fs_loader)

target_sources(app PRIVATE src/main.c src/log_extension.c src/export.c)

# declare extension
zephyr_llext(hello_world)
zephyr_llext_sources(hello_world.llext/hello_world.c)

zephyr_llext(hello_world_pic PIC)
zephyr_llext_sources(hello_world.llext/hello_world.c)

set(FAT_IMG ${CMAKE_CURRENT_BINARY_DIR}/llext_vfat.img)

# generate image
add_custom_target(vfat.img ALL
COMMAND ${CMAKE_CURRENT_LIST_DIR}/makeimg.sh ${FAT_IMG} ${CMAKE_CURRENT_BINARY_DIR}/hello_world.llext ${CMAKE_CURRENT_BINARY_DIR}/hello_world_pic.llext
WORKING_DIRECTORY ${ZEPHYR_BASE}
SOURCES ${CMAKE_CURRENT_LIST_DIR}/makeimg.sh
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/hello_world.llext ${CMAKE_CURRENT_BINARY_DIR}/hello_world_pic.llext
)
17 changes: 17 additions & 0 deletions samples/subsys/llext/fs_loader/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#
# Copyright (c) 2024 Schneider Electric
#
# SPDX-License-Identifier: Apache-2.0
#

config APP_WIPE_STORAGE
bool "Option to clear the flash area before mounting"
help
Use this to force an existing file system to be created.

config CONFIG_LLEXT_PIC
bool "Build LL extension with PIC enabled"
help
Use this to force building LL extension in Position Independant Code.

source "Kconfig.zephyr"
71 changes: 71 additions & 0 deletions samples/subsys/llext/fs_loader/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
.. zephyr:code-sample:: llext-fs-loader
:name: Linkable loadable extensions filesystem module
:relevant-api: llext

Manage loadable extensions using fielsystem.

Overview
********

This example provides filesystem access to the :ref:`llext` system and provides the
ability to manage loadable code extensions in the filesystem.

Requirements
************

A board with a supported llext architecture and filesystem.

Building
********

.. zephyr-app-commands::
:zephyr-app: samples/subsys/llext/fs_loader
:board: nucleo_l496zg
:goals: build
:compact:

Running
*******

Once the board has booted, you will be presented with a shell console.
All the llext system related commands are available as sub-commands of llext
which can be seen with llext help

.. code-block:: console

uart:~$ llext help
llext - Loadable extension commands
Subcommands:
list :List loaded extensions and their size in memory
load_hex :Load an elf file encoded in hex directly from the shell input.
Syntax:
<ext_name> <ext_hex_string>
load_fs :Load an elf file encoded in elf directly from a filesystem.
Syntax:
<ext_name> <filepath>
unload :Unload an extension by name. Syntax:
<ext_name>
list_symbols :List extension symbols. Syntax:
<ext_name>
call_fn :Call extension function with prototype void fn(void). Syntax:
<ext_name> <function_name>

A hello world llext extension can be found in hello_world.llext folder.

This is built in parallel of zephyr and must be copied in filesystem.



.. code-block:: console

uart:~$ llext load_fs hello_world /NAND:/hello_world.llext

This extension can then be seen in the list of loaded extensions (`list`), its symbols printed
(`list_symbols`), and the hello_world function which the extension exports can be called and
run (`call_fn`).

.. code-block:: console

uart:~$ llext call_fn hello_world start
hello world from new thread
thread id is ....
5 changes: 5 additions & 0 deletions samples/subsys/llext/fs_loader/VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
VERSION_MAJOR = 0
VERSION_MINOR = 1
PATCHLEVEL = 0
VERSION_TWEAK = 0
EXTRAVERSION = fs_llext
50 changes: 50 additions & 0 deletions samples/subsys/llext/fs_loader/boards/nucleo_l496zg.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2024 Schneider Electric
*
* SPDX-License-Identifier: Apache-2.0
*/

&usbotg_fs {
pinctrl-0 = <&usb_otg_fs_dm_pa11 &usb_otg_fs_dp_pa12>;
pinctrl-names = "default";
status = "okay";
};
/* hsi48 must be enabled for usb */
&clk_hsi48 {
status = "okay";
};

&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;

code_partition: partition@0 {
label = "code";
reg = <0x0000000 DT_SIZE_K(1024-128)>;
read-only;
};

/*
* The final 128 KiB is reserved for the application.
* Storage partition will be used by FCB/LittleFS/NVS
* if enabled.
*/
storage_partition: partition@E0000 {
label = "storage";
reg = <0x00E0000 DT_SIZE_K(128)>;
};
};
};

/ {
msc_disk0 {
status="okay";
compatible = "zephyr,flash-disk";
partition = <&storage_partition>;
disk-name = "NAND";
/* cache-size == page erase size */
cache-size = <2048>;
};
};
41 changes: 41 additions & 0 deletions samples/subsys/llext/fs_loader/boards/stm32f4_disco.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2024 Schneider Electric
*
* SPDX-License-Identifier: Apache-2.0
*/

&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;

code_partition: partition@0 {
label = "code";
reg = <0x0000000 DT_SIZE_K(1024-128)>;
read-only;
};

/*
* The final 128 KiB is reserved for the application.
* Storage partition will be used by FCB/LittleFS/NVS
* if enabled.
*/
storage_partition: partition@E0000 {
label = "storage";
reg = <0x00E0000 DT_SIZE_K(128)>;
read-only;
};
};
};

/ {
msc_disk0 {
status="okay";
compatible = "zephyr,flash-disk";
partition = <&storage_partition>;
disk-name = "NAND";
/* cache-size == page erase size */
cache-size = <2048>;
};
};
70 changes: 70 additions & 0 deletions samples/subsys/llext/fs_loader/hello_world.llext/hello_world.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* Copyright (c) 2024 Schneider Electric
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <string.h>
#include <zephyr/llext/symbol.h>


#include <zephyr/logging/log.h>
/* In all files comprising the module but one */
LOG_MODULE_DECLARE(extension, LOG_LEVEL_INF);


/* semaphore to end extension thread */
struct k_sem sem_end_thread;


static void extension_thread(void *p1, void *p2, void *p3)
{
LOG_INF("llext thread: start");

while (true) {
int ret = k_sem_take(&sem_end_thread, K_MSEC(1000));
if (ret != 0) {
/* timeout */
k_tid_t thread_id = k_sched_current_thread_query();
printk("Hello World from extension thread %p\n", thread_id);
} else {
/* quit thread */
break;
}
}

LOG_INF("llext thread: end");
}

#define STACK_SIZE 2048
#define PRIORITY 5

K_THREAD_STACK_DEFINE(hello_world_stack, STACK_SIZE);
struct k_thread new_thread;

void start(void)
{
LOG_INF("Starting extension...");

k_sem_init(&sem_end_thread, 0, 10);

k_tid_t tid = k_thread_create(&new_thread, hello_world_stack, STACK_SIZE,
extension_thread, NULL, NULL, NULL, PRIORITY,
K_ESSENTIAL, K_MSEC(0));
#ifdef CONFIG_THREAD_NAME
strncpy(new_thread.name, "extension", CONFIG_THREAD_MAX_NAME_LEN - 1);
/* Ensure NULL termination, truncate if longer */
new_thread.name[CONFIG_THREAD_MAX_NAME_LEN - 1] = '\0';

#endif

LOG_INF("Extension Started tid=%p", tid);
}

void stop(void)
{
k_sem_give(&sem_end_thread);
}

LL_EXTENSION_SYMBOL(start);
LL_EXTENSION_SYMBOL(stop);
47 changes: 47 additions & 0 deletions samples/subsys/llext/fs_loader/makeimg.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/bin/sh

#
# Copyright (c) 2024 Schneider Electric.
#
# SPDX-License-Identifier: Apache-2.0
#

# Create a FAT12 disk image to flash in memory
# This tool requires the following to be available on the host system:
#
# - dosfstools
# - mtools
set -e

PROGNAME=$(basename "$0")
OUTPUT=$1
LOGICAL_SECTOR_SIZE=512
DISK_SIZE_KB=128

usage() {
printf "Usage:\n\t%s output.img input1 [input2] [...]" "$PROGNAME"
}

die() {
>&2 printf "%s ERROR: " "$PROGNAME"
# We want die() to be usable exactly like printf
# shellcheck disable=SC2059
>&2 printf "%s\n" "$@"
exit 1
}

if [ $# -lt 2 ]; then
usage
die "Not enough arguments."
fi

shift
INPUTS="$*"

printf "Creating empty '%s' image..." "$(basename "$OUTPUT")"
dd if=/dev/zero of="$OUTPUT" bs=1k count=${DISK_SIZE_KB} status=none || die "dd to $OUTPUT"
printf "done\nCreating FAT partition image..."
mkfs.fat -F12 -S"$LOGICAL_SECTOR_SIZE" "$OUTPUT" >/dev/null || die "mkfs.vfat failed"
printf "done\nCopying input files..."
mcopy -i "$OUTPUT" "$INPUTS" "::/" || die "mcopy $OUTPUT $INPUTS"
Comment on lines +41 to +46
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

afaict this can also be done in Python. Perhaps take a look at pyfatfs.

Could we have a Python implementation instead, and thereby be one step closer to supporting llext in windows and MacOS ?

printf "done\n"
Loading