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
z_impl_llext_get_fn_table(struct llext * ext,bool is_init,void * buf,size_t buf_size)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("%s function %i (%p) outside text region",
64 is_init ? "bringup" : "teardown",
65 i, fn_ptrs[i]);
66 return -EFAULT;
67 }
68 }
69 }
70
71 return table_size;
72 }
73
74 #ifdef CONFIG_USERSPACE
75
ext_is_valid(struct llext * ext,void * arg)76 static int ext_is_valid(struct llext *ext, void *arg)
77 {
78 return ext == arg;
79 }
80
z_vrfy_llext_get_fn_table(struct llext * ext,bool is_init,void * buf,size_t size)81 static inline ssize_t z_vrfy_llext_get_fn_table(struct llext *ext, bool is_init,
82 void *buf, size_t size)
83 {
84 /* Test that ext matches a loaded extension */
85 K_OOPS(llext_iterate(ext_is_valid, ext) == 0);
86
87 if (buf) {
88 /* Test that buf is a valid user-accessible pointer */
89 K_OOPS(K_SYSCALL_MEMORY_WRITE(buf, size));
90 }
91
92 return z_impl_llext_get_fn_table(ext, is_init, buf, size);
93 }
94 #include <zephyr/syscalls/llext_get_fn_table_mrsh.c>
95
96 #endif /* CONFIG_USERSPACE */
97