1 /*
2 * Copyright (c) 2022-2023, Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * A mailbox client command shell on sip_svc service to communicate with SDM.
7 */
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/sip_svc/sip_svc.h>
11 #include <zephyr/drivers/sip_svc/sip_svc_agilex_mailbox.h>
12 #include <zephyr/drivers/sip_svc/sip_svc_agilex_smc.h>
13 #include <zephyr/shell/shell.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <errno.h>
17
18 #define MAX_TIMEOUT_MSECS (1 * 1000UL)
19
20 struct private_data {
21 struct k_sem semaphore;
22 const struct shell *sh;
23 };
24
25 static void *mb_smc_ctrl;
26 static uint32_t mb_c_token = SIP_SVC_ID_INVALID;
27
cmd_reg(const struct shell * sh,size_t argc,char ** argv)28 static int cmd_reg(const struct shell *sh, size_t argc, char **argv)
29 {
30 int err;
31
32 if (mb_smc_ctrl) {
33 shell_print(sh, "Mailbox client already registered");
34 return 0;
35 }
36
37 mb_smc_ctrl = sip_svc_get_controller(argv[1]);
38 if (!mb_smc_ctrl) {
39 shell_error(sh, "Arm SiP service %s not found", argv[1]);
40 return -ENODEV;
41 }
42
43 mb_c_token = sip_svc_register(mb_smc_ctrl, NULL);
44 if (mb_c_token == SIP_SVC_ID_INVALID) {
45 mb_smc_ctrl = NULL;
46 shell_error(sh, "Mailbox client register fail");
47 err = -1;
48 } else {
49 shell_print(sh, "Mailbox client register success (token %08x)", mb_c_token);
50 err = 0;
51 }
52
53 return err;
54 }
55
cmd_unreg(const struct shell * sh,size_t argc,char ** argv)56 static int cmd_unreg(const struct shell *sh, size_t argc, char **argv)
57 {
58 int err;
59
60 if (!mb_smc_ctrl) {
61 shell_print(sh, "Mailbox client is not registered");
62 return 0;
63 }
64
65 err = sip_svc_unregister(mb_smc_ctrl, mb_c_token);
66 if (err) {
67 shell_error(sh, "Mailbox client unregister fail (%d)", err);
68 } else {
69 shell_print(sh, "Mailbox client unregister success");
70 mb_c_token = SIP_SVC_ID_INVALID;
71 mb_smc_ctrl = NULL;
72 }
73
74 return err;
75 }
76
cmd_open(const struct shell * sh,size_t argc,char ** argv)77 static int cmd_open(const struct shell *sh, size_t argc, char **argv)
78 {
79 unsigned long mseconds = 0;
80 int err;
81 char *endptr;
82 k_timeout_t timeout = K_MSEC(MAX_TIMEOUT_MSECS);
83
84 if (!mb_smc_ctrl) {
85 shell_print(sh, "Mailbox client is not registered");
86 return 0;
87 }
88
89 if (argc > 1) {
90 errno = 0;
91 mseconds = strtoul(argv[1], &endptr, 10);
92 if (errno == ERANGE) {
93 shell_error(sh, "out of range value");
94 return -ERANGE;
95 } else if (errno || endptr == argv[1] || *endptr) {
96 return -errno;
97 } else if (mseconds <= MAX_TIMEOUT_MSECS) {
98 timeout = K_MSEC(mseconds);
99 } else {
100 timeout = K_MSEC(MAX_TIMEOUT_MSECS);
101 shell_error(sh, "Setting timeout value to %lu milliseconds",
102 MAX_TIMEOUT_MSECS);
103 }
104 }
105
106 err = sip_svc_open(mb_smc_ctrl, mb_c_token, timeout);
107 if (err) {
108 shell_error(sh, "Mailbox client open fail (%d)", err);
109 } else {
110 shell_print(sh, "Mailbox client open success");
111 }
112
113 return err;
114 }
115
cmd_close(const struct shell * sh,size_t argc,char ** argv)116 static int cmd_close(const struct shell *sh, size_t argc, char **argv)
117 {
118 int err;
119 uint32_t cmd_size = sizeof(uint32_t);
120 struct sip_svc_request request;
121
122 if (!mb_smc_ctrl) {
123 shell_print(sh, "Mailbox client is not registered");
124 return 0;
125 }
126
127 uint32_t *cmd_addr = k_malloc(cmd_size);
128
129 if (!cmd_addr) {
130 return -ENOMEM;
131 }
132
133 *cmd_addr = MAILBOX_CANCEL_COMMAND;
134
135 request.header = SIP_SVC_PROTO_HEADER(SIP_SVC_PROTO_CMD_ASYNC, 0);
136 request.a0 = SMC_FUNC_ID_MAILBOX_SEND_COMMAND;
137 request.a1 = 0;
138 request.a2 = (uint64_t)cmd_addr;
139 request.a3 = (uint64_t)cmd_size;
140 request.a4 = 0;
141 request.a5 = 0;
142 request.a6 = 0;
143 request.a7 = 0;
144 request.resp_data_addr = (uint64_t)NULL;
145 request.resp_data_size = 0;
146 request.priv_data = NULL;
147
148 err = sip_svc_close(mb_smc_ctrl, mb_c_token, &request);
149 if (err) {
150 k_free(cmd_addr);
151 shell_error(sh, "Mailbox client close fail (%d)", err);
152 } else {
153 shell_print(sh, "Mailbox client close success");
154 }
155
156 return err;
157 }
158
cmd_send_callback(uint32_t c_token,struct sip_svc_response * response)159 static void cmd_send_callback(uint32_t c_token, struct sip_svc_response *response)
160 {
161 if (response == NULL) {
162 return;
163 }
164
165 struct private_data *priv = (struct private_data *)response->priv_data;
166 const struct shell *sh = priv->sh;
167
168 uint32_t *resp_data;
169 uint32_t resp_len;
170 uint32_t i;
171
172 shell_print(sh, "\n\rsip_svc send command callback\n");
173 shell_print(sh, "\theader=%08x\n", response->header);
174 shell_print(sh, "\ta0=%016lx\n", response->a0);
175 shell_print(sh, "\ta1=%016lx\n", response->a1);
176 shell_print(sh, "\ta2=%016lx\n", response->a2);
177 shell_print(sh, "\ta3=%016lx\n", response->a3);
178 shell_print(sh, "\tresponse data=\n");
179
180 resp_data = (uint32_t *)response->resp_data_addr;
181 resp_len = response->resp_data_size / 4;
182 if (resp_data && resp_len) {
183 for (i = 0; i < resp_len; i++) {
184 shell_print(sh, "\t\t[%4d] %08x\n", i, resp_data[i]);
185 }
186 } else {
187 shell_error(sh, "\t\tInvalid addr (%p) or len (%d)\n", resp_data, resp_len);
188 }
189
190 /* Client only responsible to free the response data memory space,
191 * the command data memory space had been freed by sip_svc service.
192 */
193 if (resp_data) {
194 shell_print(sh, "response data %p is freed\n", resp_data);
195 k_free((char *)resp_data);
196 }
197
198 k_sem_give(&(priv->semaphore));
199 }
200
parse_mb_data(const struct shell * sh,char * hex_list,char ** cmd_addr,uint32_t * cmd_size)201 static int parse_mb_data(const struct shell *sh, char *hex_list, char **cmd_addr,
202 uint32_t *cmd_size)
203 {
204 char *hex_str = hex_list;
205 uint32_t hex_val;
206 uint32_t *buffer;
207 uint32_t i = 0;
208 char *state;
209 char *endptr;
210
211 if (!hex_list || !cmd_addr || !cmd_size) {
212 return -EINVAL;
213 }
214
215 *cmd_addr = k_malloc(SIP_SVP_MB_MAX_WORD_SIZE * 4);
216 if (!*cmd_addr) {
217 shell_error(sh, "Fail to allocate command memory");
218 return -ENOMEM;
219 }
220
221 buffer = (uint32_t *)*cmd_addr;
222 hex_str = strtok_r(hex_str, " ", &state);
223
224 while (hex_str) {
225 if (i >= SIP_SVP_MB_MAX_WORD_SIZE) {
226 k_free(*cmd_addr);
227 shell_error(sh, "Mailbox length too long");
228 return -EOVERFLOW;
229 }
230 errno = 0;
231 hex_val = strtoul(hex_str, &endptr, 16);
232 if (errno == ERANGE) {
233 shell_error(sh, " Value is out of range value");
234 k_free(*cmd_addr);
235 return -ERANGE;
236 } else if (errno || endptr == hex_str || *endptr) {
237 k_free(*cmd_addr);
238 shell_error(sh, " Invalid argument");
239 return -EINVAL;
240 }
241
242 buffer[i] = hex_val;
243 i++;
244
245 hex_str = strtok_r(NULL, " ", &state);
246 }
247
248 *cmd_size = i * 4;
249
250 return 0;
251 }
252
cmd_send(const struct shell * sh,size_t argc,char ** argv)253 static int cmd_send(const struct shell *sh, size_t argc, char **argv)
254 {
255 struct sip_svc_request request;
256 int trans_id;
257 uint32_t cmd_size = 0;
258 struct private_data priv;
259 char *cmd_addr;
260 char *resp_addr, *endptr;
261 int err;
262 k_timeout_t timeout = K_FOREVER;
263 unsigned long msecond = 0;
264
265 if (!mb_smc_ctrl) {
266 shell_print(sh, "Mailbox client is not registered");
267 return 0;
268 }
269
270 err = parse_mb_data(sh, argv[1], &cmd_addr, &cmd_size);
271 if (err < 0) {
272 return err;
273 }
274
275 if (argc > 2) {
276 errno = 0;
277 msecond = strtoul(argv[2], &endptr, 10);
278 if (errno == ERANGE) {
279 shell_error(sh, "Out of range value");
280 return -ERANGE;
281 } else if (errno || endptr == argv[2] || *endptr) {
282 shell_error(sh, "Invalid argument");
283 return -EINVAL;
284 } else if (msecond <= (MSEC_PER_SEC * MAX_TIMEOUT_MSECS)) {
285 timeout = K_MSEC(msecond);
286 } else {
287 timeout = K_SECONDS(MAX_TIMEOUT_MSECS);
288 shell_error(sh, "Setting timeout value to %lu seconds", MAX_TIMEOUT_MSECS);
289 }
290 }
291
292 resp_addr = k_malloc(SIP_SVP_MB_MAX_WORD_SIZE * 4);
293 if (!resp_addr) {
294 k_free(cmd_addr);
295 shell_error(sh, "Fail to allocate response memory");
296 return -ENOMEM;
297 }
298 shell_print(sh, "\tResponse memory %p\n", (char *)resp_addr);
299
300 k_sem_init(&(priv.semaphore), 0, 1);
301 priv.sh = sh;
302
303 request.header = SIP_SVC_PROTO_HEADER(SIP_SVC_PROTO_CMD_ASYNC, 0);
304 request.a0 = SMC_FUNC_ID_MAILBOX_SEND_COMMAND;
305 request.a1 = 0;
306 request.a2 = (uint64_t)cmd_addr;
307 request.a3 = (uint64_t)cmd_size;
308 request.a4 = 0;
309 request.a5 = 0;
310 request.a6 = 0;
311 request.a7 = 0;
312 request.resp_data_addr = (uint64_t)resp_addr;
313 request.resp_data_size = SIP_SVP_MB_MAX_WORD_SIZE * 4;
314 request.priv_data = (void *)&priv;
315
316 trans_id = sip_svc_send(mb_smc_ctrl, mb_c_token, &request, cmd_send_callback);
317
318 if (trans_id < 0) {
319 shell_error(sh, "Mailbox send fail (no open or no free trans_id)");
320 k_free(cmd_addr);
321 k_free(resp_addr);
322 err = -EBUSY;
323 } else {
324 /*wait for callback*/
325 if (!k_sem_take(&(priv.semaphore), timeout)) {
326 shell_print(sh, "Mailbox send success: trans_id %d", trans_id);
327 err = 0;
328 } else {
329 shell_error(sh, "Mailbox send timeout: trans_id %d", trans_id);
330 cmd_close(sh, 0, NULL);
331 err = -ETIMEDOUT;
332 }
333 }
334 return err;
335 }
336
337 SHELL_STATIC_SUBCMD_SET_CREATE(
338 sub_mailbox, SHELL_CMD_ARG(reg, NULL, "<service>", cmd_reg, 2, 0),
339 SHELL_CMD_ARG(unreg, NULL, NULL, cmd_unreg, 1, 0),
340 SHELL_CMD_ARG(open, NULL, "[<timeout_msec>]", cmd_open, 1, 1),
341 SHELL_CMD_ARG(close, NULL, NULL, cmd_close, 1, 0),
342 SHELL_CMD_ARG(send, NULL,
343 "<hex list, example (SYNC): \"2001 11223344 aabbccdd\"> [<timeout_msec>]",
344 cmd_send, 2, 1),
345 SHELL_SUBCMD_SET_END);
346
347 SHELL_CMD_REGISTER(mailbox, &sub_mailbox, "Intel SoC FPGA SDM mailbox client commands", NULL);
348