1 /*
2  * Copyright (c) 2020 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/shell/shell.h>
8 #include <stdlib.h>
9 #include <zephyr/drivers/virtualization/ivshmem.h>
10 
11 static const struct device *ivshmem;
12 
13 #ifdef CONFIG_IVSHMEM_DOORBELL
14 
15 #define STACK_SIZE 512
16 static struct k_poll_signal doorbell_sig =
17 	K_POLL_SIGNAL_INITIALIZER(doorbell_sig);
18 static struct k_poll_event doorbell_evt =
19 	K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL,
20 				 K_POLL_MODE_NOTIFY_ONLY,
21 				 &doorbell_sig);
22 K_THREAD_STACK_DEFINE(doorbell_stack, STACK_SIZE);
23 static bool doorbell_started;
24 static struct k_thread doorbell_thread;
25 
doorbell_notification_thread(const struct shell * sh)26 static void doorbell_notification_thread(const struct shell *sh)
27 {
28 	while (1) {
29 		unsigned int signaled;
30 		int vector;
31 
32 		k_poll(&doorbell_evt, 1, K_FOREVER);
33 
34 		k_poll_signal_check(&doorbell_sig, &signaled, &vector);
35 		if (signaled == 0) {
36 			continue;
37 		}
38 
39 		shell_fprintf(sh, SHELL_NORMAL,
40 			      "Received a notification on vector %u\n",
41 			      (unsigned int)vector);
42 
43 		k_poll_signal_init(&doorbell_sig);
44 	}
45 }
46 
47 #endif /* CONFIG_IVSHMEM_DOORBELL */
48 
get_ivshmem(const struct shell * sh)49 static bool get_ivshmem(const struct shell *sh)
50 {
51 	if (ivshmem == NULL) {
52 		ivshmem = DEVICE_DT_GET_ONE(qemu_ivshmem);
53 		if (!device_is_ready(ivshmem)) {
54 			shell_error(sh, "IVshmem device is not ready");
55 		}
56 	}
57 
58 	return ivshmem != NULL ? true : false;
59 }
60 
cmd_ivshmem_shmem(const struct shell * sh,size_t argc,char ** argv)61 static int cmd_ivshmem_shmem(const struct shell *sh,
62 			     size_t argc, char **argv)
63 {
64 	uintptr_t mem;
65 	size_t size;
66 	uint32_t id;
67 	uint16_t vectors;
68 
69 	if (!get_ivshmem(sh)) {
70 		return 0;
71 	}
72 
73 	size = ivshmem_get_mem(ivshmem, &mem);
74 	id = ivshmem_get_id(ivshmem);
75 	vectors = ivshmem_get_vectors(ivshmem);
76 
77 	shell_fprintf(sh, SHELL_NORMAL,
78 		      "IVshmem up and running: \n"
79 		      "\tShared memory: 0x%x of size %u bytes\n"
80 		      "\tPeer id: %u\n"
81 		      "\tNotification vectors: %u\n",
82 		      mem, size, id, vectors);
83 
84 	return 0;
85 }
86 
cmd_ivshmem_dump(const struct shell * sh,size_t argc,char ** argv)87 static int cmd_ivshmem_dump(const struct shell *sh,
88 			    size_t argc, char **argv)
89 {
90 	uintptr_t dump_pos;
91 	size_t dump_size;
92 	uintptr_t mem;
93 	size_t size;
94 
95 	if (!get_ivshmem(sh)) {
96 		return 0;
97 	}
98 
99 	dump_pos = strtol(argv[1], NULL, 10);
100 	dump_size = strtol(argv[2], NULL, 10);
101 
102 	size = ivshmem_get_mem(ivshmem, &mem);
103 
104 	if (dump_size > size) {
105 		shell_error(sh, "Size is too big");
106 	} else if (dump_pos > size) {
107 		shell_error(sh, "Position is out of the shared memory");
108 	} else if ((mem + dump_pos + dump_size) > (mem + size)) {
109 		shell_error(sh, "Position and size overflow");
110 	} else {
111 		shell_hexdump(sh, (const uint8_t *)mem+dump_pos, dump_size);
112 	}
113 
114 	return 0;
115 }
116 
cmd_ivshmem_int(const struct shell * sh,size_t argc,char ** argv)117 static int cmd_ivshmem_int(const struct shell *sh,
118 			   size_t argc, char **argv)
119 {
120 	int peer_id;
121 	int vector;
122 	int ret;
123 
124 	if (!IS_ENABLED(CONFIG_IVSHMEM_DOORBELL)) {
125 		shell_error(sh, "CONFIG_IVSHMEM_DOORBELL is not enabled");
126 		return 0;
127 	}
128 
129 	if (!get_ivshmem(sh)) {
130 		return 0;
131 	}
132 
133 	peer_id = strtol(argv[1], NULL, 10);
134 	vector = strtol(argv[2], NULL, 10);
135 
136 	ret = ivshmem_int_peer(ivshmem, (uint16_t)peer_id, (uint16_t)vector);
137 	if (ret != 0) {
138 		shell_error(sh,
139 			    "Could not notify peer %u on %u. status %d",
140 			    peer_id, vector, ret);
141 		return -EIO;
142 	}
143 
144 	shell_fprintf(sh, SHELL_NORMAL,
145 		      "Notification sent to peer %u on vector %u\n",
146 		      peer_id, vector);
147 
148 	return 0;
149 }
150 
cmd_ivshmem_get_notified(const struct shell * sh,size_t argc,char ** argv)151 static int cmd_ivshmem_get_notified(const struct shell *sh,
152 				    size_t argc, char **argv)
153 {
154 #ifdef CONFIG_IVSHMEM_DOORBELL
155 	int vector;
156 
157 	if (!get_ivshmem(sh)) {
158 		return 0;
159 	}
160 
161 	vector = strtol(argv[1], NULL, 10);
162 
163 	if (ivshmem_register_handler(ivshmem, &doorbell_sig,
164 				     (uint16_t)vector)) {
165 		shell_error(sh, "Could not get notifications on vector %u",
166 			    vector);
167 		return -EIO;
168 	}
169 
170 	shell_fprintf(sh, SHELL_NORMAL,
171 		      "Notifications enabled for vector %u\n", vector);
172 
173 	if (!doorbell_started) {
174 		k_tid_t tid;
175 
176 		tid = k_thread_create(
177 			&doorbell_thread,
178 			doorbell_stack, STACK_SIZE,
179 			(k_thread_entry_t)doorbell_notification_thread,
180 			(void *)sh, NULL, NULL,
181 			K_PRIO_COOP(2), 0, K_NO_WAIT);
182 		if (!tid) {
183 			shell_error(sh, "Cannot start notification thread");
184 			return -ENOEXEC;
185 		}
186 
187 		k_thread_name_set(tid, "notification_thread");
188 
189 		k_thread_start(tid);
190 
191 		doorbell_started = true;
192 	}
193 #else
194 	shell_error(sh, "CONFIG_IVSHMEM_DOORBELL is not enabled");
195 #endif
196 	return 0;
197 }
198 
199 SHELL_STATIC_SUBCMD_SET_CREATE(sub_ivshmem_cmds,
200 			       SHELL_CMD(shmem, NULL,
201 					 "Show shared memory info",
202 					 cmd_ivshmem_shmem),
203 			       SHELL_CMD_ARG(dump, NULL,
204 					     "Dump shared memory content",
205 					     cmd_ivshmem_dump, 3, 0),
206 			       SHELL_CMD_ARG(int_peer, NULL,
207 					     "Notify a vector on a peer",
208 					     cmd_ivshmem_int, 3, 0),
209 			       SHELL_CMD_ARG(get_notified, NULL,
210 					     "Get notification on vector",
211 					     cmd_ivshmem_get_notified, 2, 0),
212 			       SHELL_SUBCMD_SET_END
213 		);
214 
215 SHELL_CMD_REGISTER(ivshmem, &sub_ivshmem_cmds,
216 		   "IVshmem information", cmd_ivshmem_shmem);
217 
218 SHELL_CMD_ARG_REGISTER(ivshmem_dump, &sub_ivshmem_cmds,
219 		       "Dump shared memory content",
220 		       cmd_ivshmem_dump, 3, 0);
221 
222 SHELL_CMD_ARG_REGISTER(ivshmem_int, &sub_ivshmem_cmds,
223 		       "Notify a vector on an ivshmem peer",
224 		       cmd_ivshmem_int, 3, 0);
225 
226 SHELL_CMD_ARG_REGISTER(ivshmem_get_notified, &sub_ivshmem_cmds,
227 		       "Get notification on vector",
228 		       cmd_ivshmem_get_notified, 2, 0);
229