1 /*
2 * Copyright (c) 2023 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 */
7
8 #include <zephyr/sys/slist.h>
9 #include <zephyr/kernel.h>
10 #include <zephyr/shell/shell.h>
11 #include <zephyr/llext/elf.h>
12 #include <zephyr/llext/llext.h>
13 #include <zephyr/llext/buf_loader.h>
14 #include <zephyr/llext/fs_loader.h>
15
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(llext_shell, CONFIG_LLEXT_LOG_LEVEL);
18
19 #define LLEXT_LIST_HELP "List loaded extensions and their size in memory"
20
21 #define LLEXT_LOAD_HEX_HELP \
22 SHELL_HELP("Load an elf file encoded in hex directly from the shell input.", \
23 "<ext_name> <ext_hex_string>")
24
25 #define LLEXT_UNLOAD_HELP \
26 SHELL_HELP("Unload an extension by name.", \
27 "<ext_name>")
28
29 #define LLEXT_LIST_SYMBOLS_HELP \
30 SHELL_HELP("List extension symbols.", \
31 "<ext_name>")
32
33 #define LLEXT_CALL_FN_HELP \
34 SHELL_HELP("Call extension function with prototype void fn(void).", \
35 "<ext_name> <function_name>")
36
37 #ifdef CONFIG_FILE_SYSTEM
38 #define LLEXT_LOAD_FS_HELP \
39 SHELL_HELP("Load an elf file directly from filesystem.", \
40 "<ext_name> <ext_llext_file_name>")
41
42 #endif /* CONFIG_FILE_SYSTEM */
43
cmd_llext_list_symbols(const struct shell * sh,size_t argc,char * argv[])44 static int cmd_llext_list_symbols(const struct shell *sh, size_t argc, char *argv[])
45 {
46 struct llext *m = llext_by_name(argv[1]);
47
48 if (m == NULL) {
49 shell_print(sh, "No such llext %s", argv[1]);
50 return -ENOENT;
51 }
52
53 shell_print(sh, "Extension: %s symbols", m->name);
54 shell_print(sh, "| Symbol | Address |");
55 for (elf_word i = 0; i < m->exp_tab.sym_cnt; i++) {
56 shell_print(sh, "| %16s | %p |", m->exp_tab.syms[i].name,
57 m->exp_tab.syms[i].addr);
58 }
59
60 return 0;
61 }
62
63 struct llext_shell_cmd {
64 unsigned int tgt;
65 unsigned int idx;
66 struct llext *ext;
67 };
68
llext_shell_name_cb(struct llext * ext,void * arg)69 static int llext_shell_name_cb(struct llext *ext, void *arg)
70 {
71 struct llext_shell_cmd *cmd = arg;
72
73 if (cmd->tgt == cmd->idx) {
74 cmd->ext = ext;
75 return 1;
76 }
77
78 cmd->idx++;
79
80 return 0;
81 }
82
llext_name_get(size_t idx,struct shell_static_entry * entry)83 static void llext_name_get(size_t idx, struct shell_static_entry *entry)
84 {
85 struct llext_shell_cmd cmd = {.tgt = idx};
86
87 llext_iterate(llext_shell_name_cb, &cmd);
88
89 entry->syntax = cmd.ext ? cmd.ext->name : NULL;
90 entry->help = NULL;
91 entry->subcmd = NULL;
92 entry->handler = NULL;
93 entry->args.mandatory = 0;
94 entry->args.optional = 0;
95 }
96 SHELL_DYNAMIC_CMD_CREATE(msub_llext_name, llext_name_get);
97
llext_name_arg_get(size_t idx,struct shell_static_entry * entry)98 static void llext_name_arg_get(size_t idx, struct shell_static_entry *entry)
99 {
100 llext_name_get(idx, entry);
101 if (entry->syntax) {
102 entry->args.mandatory = 1;
103 }
104 }
105 SHELL_DYNAMIC_CMD_CREATE(msub_llext_name_arg, llext_name_arg_get);
106
107 struct llext_shell_list {
108 const struct shell *sh;
109 };
110
llext_shell_list_cb(struct llext * ext,void * arg)111 static int llext_shell_list_cb(struct llext *ext, void *arg)
112 {
113 struct llext_shell_list *sl = arg;
114
115 shell_print(sl->sh, "| %16s | %12d |", ext->name, ext->alloc_size);
116 return 0;
117 }
118
cmd_llext_list(const struct shell * sh,size_t argc,char * argv[])119 static int cmd_llext_list(const struct shell *sh, size_t argc, char *argv[])
120 {
121 struct llext_shell_list sl = {.sh = sh};
122
123 shell_print(sh, "| Name | Size |");
124 return llext_iterate(llext_shell_list_cb, &sl);
125 }
126
127 static uint8_t llext_buf[CONFIG_LLEXT_SHELL_MAX_SIZE] __aligned(Z_KERNEL_STACK_OBJ_ALIGN);
128
cmd_llext_load_hex(const struct shell * sh,size_t argc,char * argv[])129 static int cmd_llext_load_hex(const struct shell *sh, size_t argc, char *argv[])
130 {
131 char *name = argv[1];
132 size_t hex_len = strlen(argv[2]);
133
134 if (strlen(name) > LLEXT_MAX_NAME_LEN) {
135 shell_print(sh, "Extension name too long, max %d chars\n", LLEXT_MAX_NAME_LEN);
136 return -EINVAL;
137 }
138
139 if (hex_len > CONFIG_LLEXT_SHELL_MAX_SIZE*2) {
140 shell_print(sh, "Extension %d bytes too large to load, max %d bytes\n", hex_len/2,
141 CONFIG_LLEXT_SHELL_MAX_SIZE);
142 return -ENOMEM;
143 }
144
145 size_t llext_buf_len = hex2bin(argv[2], hex_len, llext_buf, CONFIG_LLEXT_SHELL_MAX_SIZE);
146 struct llext_buf_loader buf_loader = LLEXT_BUF_LOADER(llext_buf, llext_buf_len);
147 struct llext_loader *ldr = &buf_loader.loader;
148
149 LOG_DBG("hex2bin hex len %d, llext buf sz %d, read %d",
150 hex_len, CONFIG_LLEXT_SHELL_MAX_SIZE, llext_buf_len);
151 LOG_HEXDUMP_DBG(llext_buf, 4, "4 byte MAGIC");
152
153 struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT;
154 struct llext *ext;
155 int res = llext_load(ldr, name, &ext, &ldr_parm);
156
157 if (res == 0) {
158 shell_print(sh, "Successfully loaded extension %s, addr %p\n", ext->name, ext);
159 } else {
160 shell_print(sh, "Failed to load extension %s, return code %d\n", name, res);
161 }
162
163 return 0;
164 }
165
cmd_llext_unload(const struct shell * sh,size_t argc,char * argv[])166 static int cmd_llext_unload(const struct shell *sh, size_t argc, char *argv[])
167 {
168 struct llext *ext = llext_by_name(argv[1]);
169
170 if (ext == NULL) {
171 shell_print(sh, "No such extension %s", argv[1]);
172 return -ENOENT;
173 }
174
175 llext_unload(&ext);
176 shell_print(sh, "Unloaded extension %s\n", argv[1]);
177
178 return 0;
179 }
180
cmd_llext_call_fn(const struct shell * sh,size_t argc,char * argv[])181 static int cmd_llext_call_fn(const struct shell *sh, size_t argc, char *argv[])
182 {
183 struct llext *ext = llext_by_name(argv[1]);
184
185 if (ext == NULL) {
186 shell_print(sh, "No such extension %s", argv[1]);
187 return -ENOENT;
188 }
189
190 llext_call_fn(ext, argv[2]);
191
192 return 0;
193 }
194
195 #ifdef CONFIG_FILE_SYSTEM
cmd_llext_load_fs(const struct shell * sh,size_t argc,char * argv[])196 static int cmd_llext_load_fs(const struct shell *sh, size_t argc, char *argv[])
197 {
198 int res;
199 struct fs_dirent dirent;
200
201 res = fs_stat(argv[2], &dirent);
202 if (res) {
203 shell_error(sh, "Failed to obtain file %s, return code %d\n", argv[2], res);
204 return res;
205 }
206
207 if (dirent.type != FS_DIR_ENTRY_FILE) {
208 shell_error(sh, "Not a file %s", argv[2]);
209 return -ENOEXEC;
210 }
211
212 struct llext_fs_loader fs_loader = LLEXT_FS_LOADER(argv[2]);
213 struct llext_loader *ldr = &fs_loader.loader;
214 struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT;
215 struct llext *ext;
216
217 res = llext_load(ldr, argv[1], &ext, &ldr_parm);
218 if (res < 0) {
219 shell_print(sh, "Failed to load extension %s, return code %d\n", ext->name, res);
220 return -ENOEXEC;
221 }
222 shell_print(sh, "Successfully loaded extension %s, addr %p\n", ext->name, ext);
223 return 0;
224 }
225 #endif
226
227 /* clang-format off */
228 SHELL_STATIC_SUBCMD_SET_CREATE(sub_llext,
229 SHELL_CMD(list, NULL, LLEXT_LIST_HELP, cmd_llext_list),
230 #ifdef CONFIG_FILE_SYSTEM
231 SHELL_CMD_ARG(load_llext, NULL, LLEXT_LOAD_FS_HELP, cmd_llext_load_fs, 3, 0),
232 #endif
233 SHELL_CMD_ARG(load_hex, NULL, LLEXT_LOAD_HEX_HELP, cmd_llext_load_hex, 3, 0),
234 SHELL_CMD_ARG(unload, &msub_llext_name, LLEXT_UNLOAD_HELP, cmd_llext_unload, 2, 0),
235 SHELL_CMD_ARG(list_symbols, &msub_llext_name, LLEXT_LIST_SYMBOLS_HELP,
236 cmd_llext_list_symbols, 2, 0),
237 SHELL_CMD_ARG(call_fn, &msub_llext_name_arg, LLEXT_CALL_FN_HELP,
238 cmd_llext_call_fn, 3, 0),
239
240 SHELL_SUBCMD_SET_END
241 );
242 /* clang-format on */
243
244 SHELL_CMD_REGISTER(llext, &sub_llext, "Loadable extension commands", NULL);
245