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