18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * VMware VMCI Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 VMware, Inc. All rights reserved. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/vmw_vmci_defs.h> 98c2ecf20Sopenharmony_ci#include <linux/vmw_vmci_api.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "vmci_context.h" 128c2ecf20Sopenharmony_ci#include "vmci_driver.h" 138c2ecf20Sopenharmony_ci#include "vmci_route.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* 168c2ecf20Sopenharmony_ci * Make a routing decision for the given source and destination handles. 178c2ecf20Sopenharmony_ci * This will try to determine the route using the handles and the available 188c2ecf20Sopenharmony_ci * devices. Will set the source context if it is invalid. 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ciint vmci_route(struct vmci_handle *src, 218c2ecf20Sopenharmony_ci const struct vmci_handle *dst, 228c2ecf20Sopenharmony_ci bool from_guest, 238c2ecf20Sopenharmony_ci enum vmci_route *route) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci bool has_host_device = vmci_host_code_active(); 268c2ecf20Sopenharmony_ci bool has_guest_device = vmci_guest_code_active(); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci *route = VMCI_ROUTE_NONE; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci /* 318c2ecf20Sopenharmony_ci * "from_guest" is only ever set to true by 328c2ecf20Sopenharmony_ci * IOCTL_VMCI_DATAGRAM_SEND (or by the vmkernel equivalent), 338c2ecf20Sopenharmony_ci * which comes from the VMX, so we know it is coming from a 348c2ecf20Sopenharmony_ci * guest. 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * To avoid inconsistencies, test these once. We will test 378c2ecf20Sopenharmony_ci * them again when we do the actual send to ensure that we do 388c2ecf20Sopenharmony_ci * not touch a non-existent device. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci /* Must have a valid destination context. */ 428c2ecf20Sopenharmony_ci if (VMCI_INVALID_ID == dst->context) 438c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* Anywhere to hypervisor. */ 468c2ecf20Sopenharmony_ci if (VMCI_HYPERVISOR_CONTEXT_ID == dst->context) { 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* 498c2ecf20Sopenharmony_ci * If this message already came from a guest then we 508c2ecf20Sopenharmony_ci * cannot send it to the hypervisor. It must come 518c2ecf20Sopenharmony_ci * from a local client. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci if (from_guest) 548c2ecf20Sopenharmony_ci return VMCI_ERROR_DST_UNREACHABLE; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* 578c2ecf20Sopenharmony_ci * We must be acting as a guest in order to send to 588c2ecf20Sopenharmony_ci * the hypervisor. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci if (!has_guest_device) 618c2ecf20Sopenharmony_ci return VMCI_ERROR_DEVICE_NOT_FOUND; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci /* And we cannot send if the source is the host context. */ 648c2ecf20Sopenharmony_ci if (VMCI_HOST_CONTEXT_ID == src->context) 658c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* 688c2ecf20Sopenharmony_ci * If the client passed the ANON source handle then 698c2ecf20Sopenharmony_ci * respect it (both context and resource are invalid). 708c2ecf20Sopenharmony_ci * However, if they passed only an invalid context, 718c2ecf20Sopenharmony_ci * then they probably mean ANY, in which case we 728c2ecf20Sopenharmony_ci * should set the real context here before passing it 738c2ecf20Sopenharmony_ci * down. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci if (VMCI_INVALID_ID == src->context && 768c2ecf20Sopenharmony_ci VMCI_INVALID_ID != src->resource) 778c2ecf20Sopenharmony_ci src->context = vmci_get_context_id(); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* Send from local client down to the hypervisor. */ 808c2ecf20Sopenharmony_ci *route = VMCI_ROUTE_AS_GUEST; 818c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* Anywhere to local client on host. */ 858c2ecf20Sopenharmony_ci if (VMCI_HOST_CONTEXT_ID == dst->context) { 868c2ecf20Sopenharmony_ci /* 878c2ecf20Sopenharmony_ci * If it is not from a guest but we are acting as a 888c2ecf20Sopenharmony_ci * guest, then we need to send it down to the host. 898c2ecf20Sopenharmony_ci * Note that if we are also acting as a host then this 908c2ecf20Sopenharmony_ci * will prevent us from sending from local client to 918c2ecf20Sopenharmony_ci * local client, but we accept that restriction as a 928c2ecf20Sopenharmony_ci * way to remove any ambiguity from the host context. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci if (src->context == VMCI_HYPERVISOR_CONTEXT_ID) { 958c2ecf20Sopenharmony_ci /* 968c2ecf20Sopenharmony_ci * If the hypervisor is the source, this is 978c2ecf20Sopenharmony_ci * host local communication. The hypervisor 988c2ecf20Sopenharmony_ci * may send vmci event datagrams to the host 998c2ecf20Sopenharmony_ci * itself, but it will never send datagrams to 1008c2ecf20Sopenharmony_ci * an "outer host" through the guest device. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (has_host_device) { 1048c2ecf20Sopenharmony_ci *route = VMCI_ROUTE_AS_HOST; 1058c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 1068c2ecf20Sopenharmony_ci } else { 1078c2ecf20Sopenharmony_ci return VMCI_ERROR_DEVICE_NOT_FOUND; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (!from_guest && has_guest_device) { 1128c2ecf20Sopenharmony_ci /* If no source context then use the current. */ 1138c2ecf20Sopenharmony_ci if (VMCI_INVALID_ID == src->context) 1148c2ecf20Sopenharmony_ci src->context = vmci_get_context_id(); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* Send it from local client down to the host. */ 1178c2ecf20Sopenharmony_ci *route = VMCI_ROUTE_AS_GUEST; 1188c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* 1228c2ecf20Sopenharmony_ci * Otherwise we already received it from a guest and 1238c2ecf20Sopenharmony_ci * it is destined for a local client on this host, or 1248c2ecf20Sopenharmony_ci * it is from another local client on this host. We 1258c2ecf20Sopenharmony_ci * must be acting as a host to service it. 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_ci if (!has_host_device) 1288c2ecf20Sopenharmony_ci return VMCI_ERROR_DEVICE_NOT_FOUND; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (VMCI_INVALID_ID == src->context) { 1318c2ecf20Sopenharmony_ci /* 1328c2ecf20Sopenharmony_ci * If it came from a guest then it must have a 1338c2ecf20Sopenharmony_ci * valid context. Otherwise we can use the 1348c2ecf20Sopenharmony_ci * host context. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ci if (from_guest) 1378c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci src->context = VMCI_HOST_CONTEXT_ID; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Route to local client. */ 1438c2ecf20Sopenharmony_ci *route = VMCI_ROUTE_AS_HOST; 1448c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* 1488c2ecf20Sopenharmony_ci * If we are acting as a host then this might be destined for 1498c2ecf20Sopenharmony_ci * a guest. 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_ci if (has_host_device) { 1528c2ecf20Sopenharmony_ci /* It will have a context if it is meant for a guest. */ 1538c2ecf20Sopenharmony_ci if (vmci_ctx_exists(dst->context)) { 1548c2ecf20Sopenharmony_ci if (VMCI_INVALID_ID == src->context) { 1558c2ecf20Sopenharmony_ci /* 1568c2ecf20Sopenharmony_ci * If it came from a guest then it 1578c2ecf20Sopenharmony_ci * must have a valid context. 1588c2ecf20Sopenharmony_ci * Otherwise we can use the host 1598c2ecf20Sopenharmony_ci * context. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (from_guest) 1638c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci src->context = VMCI_HOST_CONTEXT_ID; 1668c2ecf20Sopenharmony_ci } else if (VMCI_CONTEXT_IS_VM(src->context) && 1678c2ecf20Sopenharmony_ci src->context != dst->context) { 1688c2ecf20Sopenharmony_ci /* 1698c2ecf20Sopenharmony_ci * VM to VM communication is not 1708c2ecf20Sopenharmony_ci * allowed. Since we catch all 1718c2ecf20Sopenharmony_ci * communication destined for the host 1728c2ecf20Sopenharmony_ci * above, this must be destined for a 1738c2ecf20Sopenharmony_ci * VM since there is a valid context. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return VMCI_ERROR_DST_UNREACHABLE; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* Pass it up to the guest. */ 1808c2ecf20Sopenharmony_ci *route = VMCI_ROUTE_AS_HOST; 1818c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 1828c2ecf20Sopenharmony_ci } else if (!has_guest_device) { 1838c2ecf20Sopenharmony_ci /* 1848c2ecf20Sopenharmony_ci * The host is attempting to reach a CID 1858c2ecf20Sopenharmony_ci * without an active context, and we can't 1868c2ecf20Sopenharmony_ci * send it down, since we have no guest 1878c2ecf20Sopenharmony_ci * device. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return VMCI_ERROR_DST_UNREACHABLE; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* 1958c2ecf20Sopenharmony_ci * We must be a guest trying to send to another guest, which means 1968c2ecf20Sopenharmony_ci * we need to send it down to the host. We do not filter out VM to 1978c2ecf20Sopenharmony_ci * VM communication here, since we want to be able to use the guest 1988c2ecf20Sopenharmony_ci * driver on older versions that do support VM to VM communication. 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_ci if (!has_guest_device) { 2018c2ecf20Sopenharmony_ci /* 2028c2ecf20Sopenharmony_ci * Ending up here means we have neither guest nor host 2038c2ecf20Sopenharmony_ci * device. 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_ci return VMCI_ERROR_DEVICE_NOT_FOUND; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* If no source context then use the current context. */ 2098c2ecf20Sopenharmony_ci if (VMCI_INVALID_ID == src->context) 2108c2ecf20Sopenharmony_ci src->context = vmci_get_context_id(); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* 2138c2ecf20Sopenharmony_ci * Send it from local client down to the host, which will 2148c2ecf20Sopenharmony_ci * route it to the other guest for us. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci *route = VMCI_ROUTE_AS_GUEST; 2178c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 2188c2ecf20Sopenharmony_ci} 219