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