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