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