| /* |
| * Copyright (c) 2020 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <shell/shell.h> |
| #include <stdlib.h> |
| #include <drivers/virtualization/ivshmem.h> |
| |
| static const struct device *ivshmem; |
| |
| #ifdef CONFIG_IVSHMEM_DOORBELL |
| |
| #define STACK_SIZE 512 |
| static struct k_poll_signal doorbell_sig = |
| K_POLL_SIGNAL_INITIALIZER(doorbell_sig); |
| static struct k_poll_event doorbell_evt = |
| K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, |
| K_POLL_MODE_NOTIFY_ONLY, |
| &doorbell_sig); |
| K_THREAD_STACK_DEFINE(doorbell_stack, STACK_SIZE); |
| static bool doorbell_started; |
| static struct k_thread doorbell_thread; |
| |
| static void doorbell_notification_thread(const struct shell *shell) |
| { |
| while (1) { |
| unsigned int signaled; |
| int vector; |
| |
| k_poll(&doorbell_evt, 1, K_FOREVER); |
| |
| k_poll_signal_check(&doorbell_sig, &signaled, &vector); |
| if (signaled == 0) { |
| continue; |
| } |
| |
| shell_fprintf(shell, SHELL_NORMAL, |
| "Received a notification on vector %u\n", |
| (unsigned int)vector); |
| |
| k_poll_signal_init(&doorbell_sig); |
| } |
| } |
| |
| #endif /* CONFIG_IVSHMEM_DOORBELL */ |
| |
| static bool get_ivshmem(const struct shell *shell) |
| { |
| if (ivshmem == NULL) { |
| ivshmem = device_get_binding(CONFIG_IVSHMEM_DEV_NAME); |
| if (!ivshmem) { |
| shell_error(shell, "IVshmem device cannot be found"); |
| } |
| } |
| |
| return ivshmem != NULL ? true : false; |
| } |
| |
| static int cmd_ivshmem_shmem(const struct shell *shell, |
| size_t argc, char **argv) |
| { |
| uintptr_t mem; |
| size_t size; |
| uint32_t id; |
| uint16_t vectors; |
| |
| if (!get_ivshmem(shell)) { |
| return 0; |
| } |
| |
| size = ivshmem_get_mem(ivshmem, &mem); |
| id = ivshmem_get_id(ivshmem); |
| vectors = ivshmem_get_vectors(ivshmem); |
| |
| shell_fprintf(shell, SHELL_NORMAL, |
| "IVshmem up and running: \n" |
| "\tShared memory: 0x%x of size %u bytes\n" |
| "\tPeer id: %u\n" |
| "\tNotification vectors: %u\n", |
| mem, size, id, vectors); |
| |
| return 0; |
| } |
| |
| static int cmd_ivshmem_dump(const struct shell *shell, |
| size_t argc, char **argv) |
| { |
| uintptr_t dump_pos; |
| size_t dump_size; |
| uintptr_t mem; |
| size_t size; |
| |
| if (!get_ivshmem(shell)) { |
| return 0; |
| } |
| |
| dump_pos = strtol(argv[1], NULL, 10); |
| dump_size = strtol(argv[2], NULL, 10); |
| |
| size = ivshmem_get_mem(ivshmem, &mem); |
| |
| if (dump_size > size) { |
| shell_error(shell, "Size is too big"); |
| } else if (dump_pos > size) { |
| shell_error(shell, "Position is out of the shared memory"); |
| } else if ((mem + dump_pos + dump_size) > (mem + size)) { |
| shell_error(shell, "Position and size overflow"); |
| } else { |
| shell_hexdump(shell, (const uint8_t *)mem+dump_pos, dump_size); |
| } |
| |
| return 0; |
| } |
| |
| static int cmd_ivshmem_int(const struct shell *shell, |
| size_t argc, char **argv) |
| { |
| int peer_id; |
| int vector; |
| int ret; |
| |
| if (!IS_ENABLED(CONFIG_IVSHMEM_DOORBELL)) { |
| shell_error(shell, "CONFIG_IVSHMEM_DOORBELL is not enabled"); |
| return 0; |
| } |
| |
| if (!get_ivshmem(shell)) { |
| return 0; |
| } |
| |
| peer_id = strtol(argv[1], NULL, 10); |
| vector = strtol(argv[2], NULL, 10); |
| |
| ret = ivshmem_int_peer(ivshmem, (uint16_t)peer_id, (uint16_t)vector); |
| if (ret != 0) { |
| shell_error(shell, |
| "Could not notify peer %u on %u. status %d", |
| peer_id, vector, ret); |
| return -EIO; |
| } |
| |
| shell_fprintf(shell, SHELL_NORMAL, |
| "Notification sent to peer %u on vector %u\n", |
| peer_id, vector); |
| |
| return 0; |
| } |
| |
| static int cmd_ivshmem_get_notified(const struct shell *shell, |
| size_t argc, char **argv) |
| { |
| #ifdef CONFIG_IVSHMEM_DOORBELL |
| int vector; |
| |
| if (!get_ivshmem(shell)) { |
| return 0; |
| } |
| |
| vector = strtol(argv[1], NULL, 10); |
| |
| if (ivshmem_register_handler(ivshmem, &doorbell_sig, |
| (uint16_t)vector)) { |
| shell_error(shell, "Could not get notifications on vector %u", |
| vector); |
| return -EIO; |
| } |
| |
| shell_fprintf(shell, SHELL_NORMAL, |
| "Notifications enabled for vector %u\n", vector); |
| |
| if (!doorbell_started) { |
| k_tid_t tid; |
| |
| tid = k_thread_create( |
| &doorbell_thread, |
| doorbell_stack, STACK_SIZE, |
| (k_thread_entry_t)doorbell_notification_thread, |
| (void *)shell, NULL, NULL, |
| K_PRIO_COOP(2), 0, K_NO_WAIT); |
| if (!tid) { |
| shell_error(shell, "Cannot start notification thread"); |
| return -ENOEXEC; |
| } |
| |
| k_thread_name_set(tid, "notification_thread"); |
| |
| k_thread_start(tid); |
| |
| doorbell_started = true; |
| } |
| #else |
| shell_error(shell, "CONFIG_IVSHMEM_DOORBELL is not enabled"); |
| #endif |
| return 0; |
| } |
| |
| SHELL_STATIC_SUBCMD_SET_CREATE(sub_ivshmem_cmds, |
| SHELL_CMD(shmem, NULL, |
| "Show shared memory info", |
| cmd_ivshmem_shmem), |
| SHELL_CMD_ARG(dump, NULL, |
| "Dump shared memory content", |
| cmd_ivshmem_dump, 3, 0), |
| SHELL_CMD_ARG(int_peer, NULL, |
| "Notify a vector on a peer", |
| cmd_ivshmem_int, 3, 0), |
| SHELL_CMD_ARG(get_notified, NULL, |
| "Get notification on vector", |
| cmd_ivshmem_get_notified, 2, 0), |
| SHELL_SUBCMD_SET_END |
| ); |
| |
| SHELL_CMD_REGISTER(ivshmem, &sub_ivshmem_cmds, |
| "IVshmem information", cmd_ivshmem_shmem); |
| |
| SHELL_CMD_ARG_REGISTER(ivshmem_dump, &sub_ivshmem_cmds, |
| "Dump shared memory content", |
| cmd_ivshmem_dump, 3, 0); |
| |
| SHELL_CMD_ARG_REGISTER(ivshmem_int, &sub_ivshmem_cmds, |
| "Notify a vector on an ivshmem peer", |
| cmd_ivshmem_int, 3, 0); |
| |
| SHELL_CMD_ARG_REGISTER(ivshmem_get_notified, &sub_ivshmem_cmds, |
| "Get notification on vector", |
| cmd_ivshmem_get_notified, 2, 0); |