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