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 
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(llext_shell, CONFIG_LLEXT_LOG_LEVEL);
17 
18 #define LLEXT_LIST_HELP "List loaded extensions and their size in memory"
19 
20 #define LLEXT_LOAD_HEX_HELP								\
21 	"Load an elf file encoded in hex directly from the shell input. Syntax:\n"	\
22 	"<ext_name> <ext_hex_string>"
23 
24 #define LLEXT_UNLOAD_HELP								\
25 	"Unload an extension by name. Syntax:\n"					\
26 	"<ext_name>"
27 
28 #define LLEXT_LIST_SYMBOLS_HELP								\
29 	"List extension symbols. Syntax:\n"						\
30 	"<ext_name>"
31 
32 #define LLEXT_CALL_FN_HELP								\
33 	"Call extension function with prototype void fn(void). Syntax:\n"		\
34 	"<ext_name> <function_name>"
35 
cmd_llext_list_symbols(const struct shell * sh,size_t argc,char * argv[])36 static int cmd_llext_list_symbols(const struct shell *sh, size_t argc, char *argv[])
37 {
38 	struct llext *m = llext_by_name(argv[1]);
39 
40 	if (m == NULL) {
41 		shell_print(sh, "No such llext %s", argv[1]);
42 		return -EINVAL;
43 	}
44 
45 	shell_print(sh, "Extension: %s symbols", m->name);
46 	shell_print(sh, "| Symbol           | Address    |\n");
47 	for (elf_word i = 0; i < m->sym_tab.sym_cnt; i++) {
48 		shell_print(sh, "| %16s | %p |\n", m->sym_tab.syms[i].name,
49 			    m->sym_tab.syms[i].addr);
50 	}
51 
52 	return 0;
53 }
54 
llext_name_get(size_t idx,struct shell_static_entry * entry)55 static void llext_name_get(size_t idx, struct shell_static_entry *entry)
56 {
57 	sys_slist_t *ext_list = llext_list();
58 	sys_snode_t *node = sys_slist_peek_head(ext_list);
59 
60 	entry->syntax = NULL;
61 
62 	for (int i = 0; i < idx; i++) {
63 		node = sys_slist_peek_next(node);
64 
65 		if (node == NULL) {
66 			goto out;
67 		}
68 	}
69 
70 	struct llext *ext = CONTAINER_OF(node, struct llext, _llext_list);
71 
72 	entry->syntax = ext->name;
73 out:
74 	entry->syntax = NULL;
75 	entry->help = NULL;
76 	entry->subcmd = NULL;
77 
78 }
79 
80 SHELL_DYNAMIC_CMD_CREATE(msub_llext_name, llext_name_get);
81 
cmd_llext_list(const struct shell * sh,size_t argc,char * argv[])82 static int cmd_llext_list(const struct shell *sh, size_t argc, char *argv[])
83 {
84 	sys_snode_t *node;
85 	struct llext *ext;
86 
87 	shell_print(sh, "| Name             | Size        |\n");
88 	SYS_SLIST_FOR_EACH_NODE(llext_list(), node) {
89 		ext = CONTAINER_OF(node, struct llext, _llext_list);
90 		shell_print(sh, "| %16s | %12d |\n", ext->name, ext->mem_size);
91 	}
92 
93 	return 0;
94 }
95 
96 static uint8_t llext_buf[CONFIG_LLEXT_SHELL_MAX_SIZE];
97 
cmd_llext_load_hex(const struct shell * sh,size_t argc,char * argv[])98 static int cmd_llext_load_hex(const struct shell *sh, size_t argc, char *argv[])
99 {
100 	char name[16];
101 	size_t hex_len = strnlen(argv[2], CONFIG_LLEXT_SHELL_MAX_SIZE*2+1);
102 	size_t bin_len = hex_len/2;
103 
104 	if (bin_len > CONFIG_LLEXT_SHELL_MAX_SIZE) {
105 		shell_print(sh, "Extension %d bytes too large to load, max %d bytes\n", hex_len/2,
106 			    CONFIG_LLEXT_SHELL_MAX_SIZE);
107 		return -ENOMEM;
108 	}
109 
110 	strncpy(name, argv[1], sizeof(name));
111 
112 	size_t llext_buf_len = hex2bin(argv[2], hex_len, llext_buf, CONFIG_LLEXT_SHELL_MAX_SIZE);
113 	struct llext_buf_loader buf_loader = LLEXT_BUF_LOADER(llext_buf, llext_buf_len);
114 	struct llext_loader *ldr = &buf_loader.loader;
115 
116 	LOG_DBG("hex2bin hex len %d, llext buf sz %d, read %d",
117 		hex_len, CONFIG_LLEXT_SHELL_MAX_SIZE, llext_buf_len);
118 	LOG_HEXDUMP_DBG(llext_buf, 4, "4 byte MAGIC");
119 
120 	struct llext *ext;
121 	int res = llext_load(ldr, name, &ext);
122 
123 	if (res == 0) {
124 		shell_print(sh, "Successfully loaded extension %s, addr %p\n", ext->name, ext);
125 	} else {
126 		shell_print(sh, "Failed to load extension %s, return code %d\n", name, res);
127 	}
128 
129 	return 0;
130 }
131 
cmd_llext_unload(const struct shell * sh,size_t argc,char * argv[])132 static int cmd_llext_unload(const struct shell *sh, size_t argc, char *argv[])
133 {
134 	struct llext *ext = llext_by_name(argv[1]);
135 
136 	if (ext == NULL) {
137 		shell_print(sh, "No such extension %s", argv[1]);
138 		return -EINVAL;
139 	}
140 
141 	llext_unload(ext);
142 	shell_print(sh, "Unloaded extension %s\n", argv[1]);
143 
144 	return 0;
145 }
146 
cmd_llext_call_fn(const struct shell * sh,size_t argc,char * argv[])147 static int cmd_llext_call_fn(const struct shell *sh, size_t argc, char *argv[])
148 {
149 	struct llext *ext = llext_by_name(argv[1]);
150 
151 	if (ext == NULL) {
152 		shell_print(sh, "No such extension %s", argv[1]);
153 		return -EINVAL;
154 	}
155 
156 	llext_call_fn(ext, argv[2]);
157 
158 	return 0;
159 }
160 
161 
162 /* clang-format off */
163 SHELL_STATIC_SUBCMD_SET_CREATE(sub_llext,
164 	SHELL_CMD(list, NULL, LLEXT_LIST_HELP, cmd_llext_list),
165 	SHELL_CMD_ARG(load_hex, NULL, LLEXT_LOAD_HEX_HELP, cmd_llext_load_hex,
166 		3, 0),
167 	SHELL_CMD_ARG(unload, &msub_llext_name, LLEXT_UNLOAD_HELP, cmd_llext_unload, 2, 0),
168 	SHELL_CMD_ARG(list_symbols, &msub_llext_name, LLEXT_LIST_SYMBOLS_HELP,
169 		      cmd_llext_list_symbols, 2, 0),
170 	SHELL_CMD_ARG(call_fn, &msub_llext_name, LLEXT_CALL_FN_HELP,
171 		      cmd_llext_call_fn, 3, 0),
172 
173 	SHELL_SUBCMD_SET_END
174 	);
175 /* clang-format on */
176 
177 SHELL_CMD_REGISTER(llext, &sub_llext, "Loadable extension commands", NULL);
178