Skip to content

Commit 70678db

Browse files
committed
llext: add bringup, teardown, and bootstrap APIs
llext_bringup() and llext_teardown() are intended to be used to call the extension's own initialization and cleanup functions, respectively. They are meant to be called by the developer after loading an extension and before unloading it. The list of function pointers to be called is obtained via the new llext_get_fn_table() syscall, so that they are compatible with user mode. llext_bootstrap() is intended to be used as the entry point for a thread created to run an extension, in either user or kernel contexts. It will call the extension's own initialization functions and then an additional entry point in the same context (if desired). The same function can also be called directly in the main thread, if only initialization is required. Signed-off-by: Luca Burelli <[email protected]>
1 parent d7543bd commit 70678db

File tree

4 files changed

+233
-2
lines changed

4 files changed

+233
-2
lines changed

include/zephyr/llext/llext.h

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,74 @@ int llext_load(struct llext_loader *loader, const char *name, struct llext **ext
186186
*/
187187
int llext_unload(struct llext **ext);
188188

189+
/** @brief Entry point function signature for an extension. */
190+
typedef void (*llext_entry_fn_t)(void *user_data);
191+
192+
/**
193+
* @brief Calls bringup functions for an extension.
194+
*
195+
* Must be called before accessing any symbol in the extension. Will execute
196+
* the extension's own setup functions in the caller context.
197+
* @see llext_bootstrap
198+
*
199+
* @param[in] ext Extension to initialize.
200+
* @returns 0 on success, or a negative error code.
201+
* @retval -EFAULT A relocation issue was detected
202+
*/
203+
int llext_bringup(struct llext *ext);
204+
205+
/**
206+
* @brief Calls teardown functions for an extension.
207+
*
208+
* Will execute the extension's own cleanup functions in the caller context.
209+
* After this function completes, the extension is no longer usable and must be
210+
* fully unloaded with @ref llext_unload.
211+
* @see llext_bootstrap
212+
*
213+
* @param[in] ext Extension to de-initialize.
214+
* @returns 0 on success, or a negative error code.
215+
* @retval -EFAULT A relocation issue was detected
216+
*/
217+
int llext_teardown(struct llext *ext);
218+
219+
/**
220+
* @brief Bring up, execute, and teardown an extension.
221+
*
222+
* Calls the extension's own setup functions, an additional entry point and
223+
* the extension's cleanup functions in the current thread context.
224+
*
225+
* This is a convenient wrapper around @ref llext_bringup and @ref
226+
* llext_teardown that matches the @ref k_thread_entry_t signature, so it can
227+
* be directly started as a new user or kernel thread via @ref k_thread_create.
228+
*
229+
* @param[in] ext Extension to execute. Passed as `p1` in @ref k_thread_create.
230+
* @param[in] entry_fn Main entry point of the thread after performing
231+
* extension setup. Passed as `p2` in @ref k_thread_create.
232+
* @param[in] user_data Argument passed to @a entry_fn. Passed as `p3` in
233+
* @ref k_thread_create.
234+
*/
235+
void llext_bootstrap(struct llext *ext, llext_entry_fn_t entry_fn, void *user_data);
236+
237+
/**
238+
* @brief Get pointers to setup or cleanup functions for an extension.
239+
*
240+
* This syscall can be used to get the addresses of all the functions that
241+
* have to be called for full extension setup or cleanup.
242+
*
243+
* @see llext_bootstrap
244+
*
245+
* @param[in] ext Extension to initialize.
246+
* @param[in] is_init `true` to get functions to be called at setup time,
247+
* `false` to get the cleanup ones.
248+
* @param[inout] buf Buffer to store the function pointers in. Can be `NULL`
249+
* to only get the minimum required size.
250+
* @param[in] size Allocated size of the buffer in bytes.
251+
* @returns the size used by the array in bytes, or a negative error code.
252+
* @retval -EFAULT A relocation issue was detected
253+
* @retval -ENOMEM Array does not fit in the allocated buffer
254+
*/
255+
__syscall ssize_t llext_get_fn_table(struct llext *ext, bool is_init, void *buf, size_t size);
256+
189257
/**
190258
* @brief Find the address for an arbitrary symbol.
191259
*
@@ -255,8 +323,8 @@ int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc,
255323
*
256324
* Searches for a section by name in the ELF file and returns its offset.
257325
*
258-
* @param loader Extension loader data and context
259-
* @param search_name Section name to search for
326+
* @param[in] loader Extension loader data and context
327+
* @param[in] search_name Section name to search for
260328
* @returns the section offset or a negative error code
261329
*/
262330
ssize_t llext_find_section(struct llext_loader *loader, const char *search_name);
@@ -281,4 +349,6 @@ void arch_elf_relocate_local(struct llext_loader *loader, struct llext *ext,
281349
}
282350
#endif
283351

352+
#include <zephyr/syscalls/llext.h>
353+
284354
#endif /* ZEPHYR_LLEXT_H */

subsys/llext/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
if(CONFIG_LLEXT)
2+
zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/llext/llext.h)
3+
24
zephyr_library()
35

46
# For strnlen()
@@ -10,6 +12,7 @@ if(CONFIG_LLEXT)
1012
llext_load.c
1113
llext_link.c
1214
llext_export.c
15+
llext_handlers.c
1316
buf_loader.c
1417
)
1518
zephyr_library_sources_ifdef(CONFIG_LLEXT_SHELL shell.c)

subsys/llext/llext.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,3 +273,66 @@ int llext_call_fn(struct llext *ext, const char *sym_name)
273273

274274
return 0;
275275
}
276+
277+
static int call_fn_table(struct llext *ext, bool is_init)
278+
{
279+
ssize_t ret;
280+
281+
ret = llext_get_fn_table(ext, is_init, NULL, 0);
282+
if (ret < 0) {
283+
LOG_ERR("Failed to get table size: %d", (int)ret);
284+
return ret;
285+
}
286+
287+
typedef void (*elf_void_fn_t)(void);
288+
289+
int fn_count = ret / sizeof(elf_void_fn_t);
290+
elf_void_fn_t fn_table[fn_count];
291+
292+
ret = llext_get_fn_table(ext, is_init, &fn_table, sizeof(fn_table));
293+
if (ret < 0) {
294+
LOG_ERR("Failed to get function table: %d", (int)ret);
295+
return ret;
296+
}
297+
298+
for (int i = 0; i < fn_count; i++) {
299+
LOG_DBG("calling %s function %p()",
300+
is_init ? "bringup" : "teardown", fn_table[i]);
301+
fn_table[i]();
302+
}
303+
304+
return 0;
305+
}
306+
307+
inline int llext_bringup(struct llext *ext)
308+
{
309+
return call_fn_table(ext, true);
310+
}
311+
312+
inline int llext_teardown(struct llext *ext)
313+
{
314+
return call_fn_table(ext, false);
315+
}
316+
317+
void llext_bootstrap(struct llext *ext, llext_entry_fn_t entry_fn, void *user_data)
318+
{
319+
int ret;
320+
321+
/* Call initialization functions */
322+
ret = llext_bringup(ext);
323+
if (ret < 0) {
324+
LOG_ERR("Failed to call init functions: %d", ret);
325+
return;
326+
}
327+
328+
/* Start extension main function */
329+
LOG_DBG("calling entry function %p(%p)", entry_fn, user_data);
330+
entry_fn(user_data);
331+
332+
/* Call de-initialization functions */
333+
ret = llext_teardown(ext);
334+
if (ret < 0) {
335+
LOG_ERR("Failed to call de-init functions: %d", ret);
336+
return;
337+
}
338+
}

subsys/llext/llext_handlers.c

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright (c) 2024 Arduino SA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <string.h>
8+
#include <zephyr/llext/llext.h>
9+
#include <zephyr/llext/loader.h>
10+
#include <zephyr/internal/syscall_handler.h>
11+
#include <zephyr/kernel.h>
12+
13+
#include <zephyr/logging/log.h>
14+
LOG_MODULE_DECLARE(llext, CONFIG_LLEXT_LOG_LEVEL);
15+
16+
#include "llext_priv.h"
17+
18+
ssize_t z_impl_llext_get_fn_table(struct llext *ext, bool is_init, void *buf, size_t buf_size)
19+
{
20+
size_t table_size;
21+
22+
if (!ext) {
23+
return -EINVAL;
24+
}
25+
26+
if (is_init) {
27+
table_size = ext->mem_size[LLEXT_MEM_PREINIT] +
28+
ext->mem_size[LLEXT_MEM_INIT];
29+
} else {
30+
table_size = ext->mem_size[LLEXT_MEM_FINI];
31+
}
32+
33+
if (buf) {
34+
char *byte_ptr = buf;
35+
36+
if (buf_size < table_size) {
37+
return -ENOMEM;
38+
}
39+
40+
if (is_init) {
41+
/* setup functions from preinit_array and init_array */
42+
memcpy(byte_ptr,
43+
ext->mem[LLEXT_MEM_PREINIT], ext->mem_size[LLEXT_MEM_PREINIT]);
44+
memcpy(byte_ptr + ext->mem_size[LLEXT_MEM_PREINIT],
45+
ext->mem[LLEXT_MEM_INIT], ext->mem_size[LLEXT_MEM_INIT]);
46+
} else {
47+
/* cleanup functions from fini_array */
48+
memcpy(byte_ptr,
49+
ext->mem[LLEXT_MEM_FINI], ext->mem_size[LLEXT_MEM_FINI]);
50+
}
51+
52+
/* Sanity check: pointers in this table must map inside the
53+
* text region of the extension. If this fails, something went
54+
* wrong during the relocation process.
55+
* Using "char *" for these simplifies pointer arithmetic.
56+
*/
57+
const char *text_start = ext->mem[LLEXT_MEM_TEXT];
58+
const char *text_end = text_start + ext->mem_size[LLEXT_MEM_TEXT];
59+
const char **fn_ptrs = buf;
60+
61+
for (int i = 0; i < table_size / sizeof(void *); i++) {
62+
if (fn_ptrs[i] < text_start || fn_ptrs[i] >= text_end) {
63+
LOG_ERR("init function %i (%p) outside text region",
64+
i, fn_ptrs[i]);
65+
return -EFAULT;
66+
}
67+
}
68+
}
69+
70+
return table_size;
71+
}
72+
73+
#ifdef CONFIG_USERSPACE
74+
75+
static int ext_is_valid(struct llext *ext, void *arg)
76+
{
77+
return ext == arg;
78+
}
79+
80+
static inline ssize_t z_vrfy_llext_get_fn_table(struct llext *ext, bool is_init,
81+
void *buf, size_t size)
82+
{
83+
/* Test that ext matches a loaded extension */
84+
K_OOPS(llext_iterate(ext_is_valid, ext) == 0);
85+
86+
if (buf) {
87+
/* Test that buf is a valid user-accessible pointer */
88+
K_OOPS(K_SYSCALL_MEMORY_WRITE(buf, size));
89+
}
90+
91+
return z_impl_llext_get_fn_table(ext, is_init, buf, size);
92+
}
93+
#include <zephyr/syscalls/llext_get_fn_table_mrsh.c>
94+
95+
#endif /* CONFIG_USERSPACE */

0 commit comments

Comments
 (0)