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 	"Load an elf file encoded in hex directly from the shell input. Syntax:\n"                 \
23 	"<ext_name> <ext_hex_string>"
24 
25 #define LLEXT_UNLOAD_HELP                                                                          \
26 	"Unload an extension by name. Syntax:\n"                                                   \
27 	"<ext_name>"
28 
29 #define LLEXT_LIST_SYMBOLS_HELP                                                                    \
30 	"List extension symbols. Syntax:\n"                                                        \
31 	"<ext_name>"
32 
33 #define LLEXT_CALL_FN_HELP                                                                         \
34 	"Call extension function with prototype void fn(void). Syntax:\n"                          \
35 	"<ext_name> <function_name>"
36 
37 #ifdef CONFIG_FILE_SYSTEM
38 #define LLEXT_LOAD_FS_HELP                                                                         \
39 	"Load an elf file directly from filesystem. Syntax:\n"                                     \
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->sym_tab.sym_cnt; i++) {
56 		shell_print(sh, "| %16s | %p |", m->sym_tab.syms[i].name,
57 			    m->sym_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];
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[16];
132 	size_t hex_len = strnlen(argv[2], CONFIG_LLEXT_SHELL_MAX_SIZE*2+1);
133 	size_t bin_len = hex_len/2;
134 
135 	if (bin_len > CONFIG_LLEXT_SHELL_MAX_SIZE) {
136 		shell_print(sh, "Extension %d bytes too large to load, max %d bytes\n", hex_len/2,
137 			    CONFIG_LLEXT_SHELL_MAX_SIZE);
138 		return -ENOMEM;
139 	}
140 
141 	strncpy(name, argv[1], sizeof(name));
142 
143 	size_t llext_buf_len = hex2bin(argv[2], hex_len, llext_buf, CONFIG_LLEXT_SHELL_MAX_SIZE);
144 	struct llext_buf_loader buf_loader = LLEXT_BUF_LOADER(llext_buf, llext_buf_len);
145 	struct llext_loader *ldr = &buf_loader.loader;
146 
147 	LOG_DBG("hex2bin hex len %d, llext buf sz %d, read %d",
148 		hex_len, CONFIG_LLEXT_SHELL_MAX_SIZE, llext_buf_len);
149 	LOG_HEXDUMP_DBG(llext_buf, 4, "4 byte MAGIC");
150 
151 	struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT;
152 	struct llext *ext;
153 	int res = llext_load(ldr, name, &ext, &ldr_parm);
154 
155 	if (res == 0) {
156 		shell_print(sh, "Successfully loaded extension %s, addr %p\n", ext->name, ext);
157 	} else {
158 		shell_print(sh, "Failed to load extension %s, return code %d\n", name, res);
159 	}
160 
161 	return 0;
162 }
163 
cmd_llext_unload(const struct shell * sh,size_t argc,char * argv[])164 static int cmd_llext_unload(const struct shell *sh, size_t argc, char *argv[])
165 {
166 	struct llext *ext = llext_by_name(argv[1]);
167 
168 	if (ext == NULL) {
169 		shell_print(sh, "No such extension %s", argv[1]);
170 		return -ENOENT;
171 	}
172 
173 	llext_unload(&ext);
174 	shell_print(sh, "Unloaded extension %s\n", argv[1]);
175 
176 	return 0;
177 }
178 
cmd_llext_call_fn(const struct shell * sh,size_t argc,char * argv[])179 static int cmd_llext_call_fn(const struct shell *sh, size_t argc, char *argv[])
180 {
181 	struct llext *ext = llext_by_name(argv[1]);
182 
183 	if (ext == NULL) {
184 		shell_print(sh, "No such extension %s", argv[1]);
185 		return -ENOENT;
186 	}
187 
188 	llext_call_fn(ext, argv[2]);
189 
190 	return 0;
191 }
192 
193 #ifdef CONFIG_FILE_SYSTEM
cmd_llext_load_fs(const struct shell * sh,size_t argc,char * argv[])194 static int cmd_llext_load_fs(const struct shell *sh, size_t argc, char *argv[])
195 {
196 	int res;
197 	struct fs_dirent dirent;
198 
199 	res = fs_stat(argv[2], &dirent);
200 	if (res) {
201 		shell_error(sh, "Failed to obtain file %s, return code %d\n", argv[2], res);
202 		return res;
203 	}
204 
205 	if (dirent.type != FS_DIR_ENTRY_FILE) {
206 		shell_error(sh, "Not a file %s", argv[2]);
207 		return -ENOEXEC;
208 	}
209 
210 	struct llext_fs_loader fs_loader = LLEXT_FS_LOADER(argv[2]);
211 	struct llext_loader *ldr = &fs_loader.loader;
212 	struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT;
213 	struct llext *ext;
214 
215 	res = llext_load(ldr, argv[1], &ext, &ldr_parm);
216 	if (res < 0) {
217 		shell_print(sh, "Failed to load extension %s, return code %d\n", ext->name, res);
218 		return -ENOEXEC;
219 	}
220 	shell_print(sh, "Successfully loaded extension %s, addr %p\n", ext->name, ext);
221 	return 0;
222 }
223 #endif
224 
225 /* clang-format off */
226 SHELL_STATIC_SUBCMD_SET_CREATE(sub_llext,
227 	SHELL_CMD(list, NULL, LLEXT_LIST_HELP, cmd_llext_list),
228 #ifdef CONFIG_FILE_SYSTEM
229 	SHELL_CMD_ARG(load_llext, NULL, LLEXT_LOAD_FS_HELP, cmd_llext_load_fs, 3, 0),
230 #endif
231 	SHELL_CMD_ARG(load_hex, NULL, LLEXT_LOAD_HEX_HELP, cmd_llext_load_hex, 3, 0),
232 	SHELL_CMD_ARG(unload, &msub_llext_name, LLEXT_UNLOAD_HELP, cmd_llext_unload, 2, 0),
233 	SHELL_CMD_ARG(list_symbols, &msub_llext_name, LLEXT_LIST_SYMBOLS_HELP,
234 		      cmd_llext_list_symbols, 2, 0),
235 	SHELL_CMD_ARG(call_fn, &msub_llext_name_arg, LLEXT_CALL_FN_HELP,
236 		      cmd_llext_call_fn, 3, 0),
237 
238 	SHELL_SUBCMD_SET_END
239 	);
240 /* clang-format on */
241 
242 SHELL_CMD_REGISTER(llext, &sub_llext, "Loadable extension commands", NULL);
243