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