162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * VMware VMCI Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012 VMware, Inc. All rights reserved.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/vmw_vmci_defs.h>
962306a36Sopenharmony_ci#include <linux/vmw_vmci_api.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "vmci_context.h"
1262306a36Sopenharmony_ci#include "vmci_driver.h"
1362306a36Sopenharmony_ci#include "vmci_route.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/*
1662306a36Sopenharmony_ci * Make a routing decision for the given source and destination handles.
1762306a36Sopenharmony_ci * This will try to determine the route using the handles and the available
1862306a36Sopenharmony_ci * devices.  Will set the source context if it is invalid.
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ciint vmci_route(struct vmci_handle *src,
2162306a36Sopenharmony_ci	       const struct vmci_handle *dst,
2262306a36Sopenharmony_ci	       bool from_guest,
2362306a36Sopenharmony_ci	       enum vmci_route *route)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	bool has_host_device = vmci_host_code_active();
2662306a36Sopenharmony_ci	bool has_guest_device = vmci_guest_code_active();
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	*route = VMCI_ROUTE_NONE;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	/*
3162306a36Sopenharmony_ci	 * "from_guest" is only ever set to true by
3262306a36Sopenharmony_ci	 * IOCTL_VMCI_DATAGRAM_SEND (or by the vmkernel equivalent),
3362306a36Sopenharmony_ci	 * which comes from the VMX, so we know it is coming from a
3462306a36Sopenharmony_ci	 * guest.
3562306a36Sopenharmony_ci	 *
3662306a36Sopenharmony_ci	 * To avoid inconsistencies, test these once.  We will test
3762306a36Sopenharmony_ci	 * them again when we do the actual send to ensure that we do
3862306a36Sopenharmony_ci	 * not touch a non-existent device.
3962306a36Sopenharmony_ci	 */
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	/* Must have a valid destination context. */
4262306a36Sopenharmony_ci	if (VMCI_INVALID_ID == dst->context)
4362306a36Sopenharmony_ci		return VMCI_ERROR_INVALID_ARGS;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/* Anywhere to hypervisor. */
4662306a36Sopenharmony_ci	if (VMCI_HYPERVISOR_CONTEXT_ID == dst->context) {
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci		/*
4962306a36Sopenharmony_ci		 * If this message already came from a guest then we
5062306a36Sopenharmony_ci		 * cannot send it to the hypervisor.  It must come
5162306a36Sopenharmony_ci		 * from a local client.
5262306a36Sopenharmony_ci		 */
5362306a36Sopenharmony_ci		if (from_guest)
5462306a36Sopenharmony_ci			return VMCI_ERROR_DST_UNREACHABLE;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci		/*
5762306a36Sopenharmony_ci		 * We must be acting as a guest in order to send to
5862306a36Sopenharmony_ci		 * the hypervisor.
5962306a36Sopenharmony_ci		 */
6062306a36Sopenharmony_ci		if (!has_guest_device)
6162306a36Sopenharmony_ci			return VMCI_ERROR_DEVICE_NOT_FOUND;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci		/* And we cannot send if the source is the host context. */
6462306a36Sopenharmony_ci		if (VMCI_HOST_CONTEXT_ID == src->context)
6562306a36Sopenharmony_ci			return VMCI_ERROR_INVALID_ARGS;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci		/*
6862306a36Sopenharmony_ci		 * If the client passed the ANON source handle then
6962306a36Sopenharmony_ci		 * respect it (both context and resource are invalid).
7062306a36Sopenharmony_ci		 * However, if they passed only an invalid context,
7162306a36Sopenharmony_ci		 * then they probably mean ANY, in which case we
7262306a36Sopenharmony_ci		 * should set the real context here before passing it
7362306a36Sopenharmony_ci		 * down.
7462306a36Sopenharmony_ci		 */
7562306a36Sopenharmony_ci		if (VMCI_INVALID_ID == src->context &&
7662306a36Sopenharmony_ci		    VMCI_INVALID_ID != src->resource)
7762306a36Sopenharmony_ci			src->context = vmci_get_context_id();
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci		/* Send from local client down to the hypervisor. */
8062306a36Sopenharmony_ci		*route = VMCI_ROUTE_AS_GUEST;
8162306a36Sopenharmony_ci		return VMCI_SUCCESS;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* Anywhere to local client on host. */
8562306a36Sopenharmony_ci	if (VMCI_HOST_CONTEXT_ID == dst->context) {
8662306a36Sopenharmony_ci		/*
8762306a36Sopenharmony_ci		 * If it is not from a guest but we are acting as a
8862306a36Sopenharmony_ci		 * guest, then we need to send it down to the host.
8962306a36Sopenharmony_ci		 * Note that if we are also acting as a host then this
9062306a36Sopenharmony_ci		 * will prevent us from sending from local client to
9162306a36Sopenharmony_ci		 * local client, but we accept that restriction as a
9262306a36Sopenharmony_ci		 * way to remove any ambiguity from the host context.
9362306a36Sopenharmony_ci		 */
9462306a36Sopenharmony_ci		if (src->context == VMCI_HYPERVISOR_CONTEXT_ID) {
9562306a36Sopenharmony_ci			/*
9662306a36Sopenharmony_ci			 * If the hypervisor is the source, this is
9762306a36Sopenharmony_ci			 * host local communication. The hypervisor
9862306a36Sopenharmony_ci			 * may send vmci event datagrams to the host
9962306a36Sopenharmony_ci			 * itself, but it will never send datagrams to
10062306a36Sopenharmony_ci			 * an "outer host" through the guest device.
10162306a36Sopenharmony_ci			 */
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci			if (has_host_device) {
10462306a36Sopenharmony_ci				*route = VMCI_ROUTE_AS_HOST;
10562306a36Sopenharmony_ci				return VMCI_SUCCESS;
10662306a36Sopenharmony_ci			} else {
10762306a36Sopenharmony_ci				return VMCI_ERROR_DEVICE_NOT_FOUND;
10862306a36Sopenharmony_ci			}
10962306a36Sopenharmony_ci		}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		if (!from_guest && has_guest_device) {
11262306a36Sopenharmony_ci			/* If no source context then use the current. */
11362306a36Sopenharmony_ci			if (VMCI_INVALID_ID == src->context)
11462306a36Sopenharmony_ci				src->context = vmci_get_context_id();
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci			/* Send it from local client down to the host. */
11762306a36Sopenharmony_ci			*route = VMCI_ROUTE_AS_GUEST;
11862306a36Sopenharmony_ci			return VMCI_SUCCESS;
11962306a36Sopenharmony_ci		}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci		/*
12262306a36Sopenharmony_ci		 * Otherwise we already received it from a guest and
12362306a36Sopenharmony_ci		 * it is destined for a local client on this host, or
12462306a36Sopenharmony_ci		 * it is from another local client on this host.  We
12562306a36Sopenharmony_ci		 * must be acting as a host to service it.
12662306a36Sopenharmony_ci		 */
12762306a36Sopenharmony_ci		if (!has_host_device)
12862306a36Sopenharmony_ci			return VMCI_ERROR_DEVICE_NOT_FOUND;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		if (VMCI_INVALID_ID == src->context) {
13162306a36Sopenharmony_ci			/*
13262306a36Sopenharmony_ci			 * If it came from a guest then it must have a
13362306a36Sopenharmony_ci			 * valid context.  Otherwise we can use the
13462306a36Sopenharmony_ci			 * host context.
13562306a36Sopenharmony_ci			 */
13662306a36Sopenharmony_ci			if (from_guest)
13762306a36Sopenharmony_ci				return VMCI_ERROR_INVALID_ARGS;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci			src->context = VMCI_HOST_CONTEXT_ID;
14062306a36Sopenharmony_ci		}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		/* Route to local client. */
14362306a36Sopenharmony_ci		*route = VMCI_ROUTE_AS_HOST;
14462306a36Sopenharmony_ci		return VMCI_SUCCESS;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/*
14862306a36Sopenharmony_ci	 * If we are acting as a host then this might be destined for
14962306a36Sopenharmony_ci	 * a guest.
15062306a36Sopenharmony_ci	 */
15162306a36Sopenharmony_ci	if (has_host_device) {
15262306a36Sopenharmony_ci		/* It will have a context if it is meant for a guest. */
15362306a36Sopenharmony_ci		if (vmci_ctx_exists(dst->context)) {
15462306a36Sopenharmony_ci			if (VMCI_INVALID_ID == src->context) {
15562306a36Sopenharmony_ci				/*
15662306a36Sopenharmony_ci				 * If it came from a guest then it
15762306a36Sopenharmony_ci				 * must have a valid context.
15862306a36Sopenharmony_ci				 * Otherwise we can use the host
15962306a36Sopenharmony_ci				 * context.
16062306a36Sopenharmony_ci				 */
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci				if (from_guest)
16362306a36Sopenharmony_ci					return VMCI_ERROR_INVALID_ARGS;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci				src->context = VMCI_HOST_CONTEXT_ID;
16662306a36Sopenharmony_ci			} else if (VMCI_CONTEXT_IS_VM(src->context) &&
16762306a36Sopenharmony_ci				   src->context != dst->context) {
16862306a36Sopenharmony_ci				/*
16962306a36Sopenharmony_ci				 * VM to VM communication is not
17062306a36Sopenharmony_ci				 * allowed. Since we catch all
17162306a36Sopenharmony_ci				 * communication destined for the host
17262306a36Sopenharmony_ci				 * above, this must be destined for a
17362306a36Sopenharmony_ci				 * VM since there is a valid context.
17462306a36Sopenharmony_ci				 */
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci				return VMCI_ERROR_DST_UNREACHABLE;
17762306a36Sopenharmony_ci			}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci			/* Pass it up to the guest. */
18062306a36Sopenharmony_ci			*route = VMCI_ROUTE_AS_HOST;
18162306a36Sopenharmony_ci			return VMCI_SUCCESS;
18262306a36Sopenharmony_ci		} else if (!has_guest_device) {
18362306a36Sopenharmony_ci			/*
18462306a36Sopenharmony_ci			 * The host is attempting to reach a CID
18562306a36Sopenharmony_ci			 * without an active context, and we can't
18662306a36Sopenharmony_ci			 * send it down, since we have no guest
18762306a36Sopenharmony_ci			 * device.
18862306a36Sopenharmony_ci			 */
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci			return VMCI_ERROR_DST_UNREACHABLE;
19162306a36Sopenharmony_ci		}
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/*
19562306a36Sopenharmony_ci	 * We must be a guest trying to send to another guest, which means
19662306a36Sopenharmony_ci	 * we need to send it down to the host. We do not filter out VM to
19762306a36Sopenharmony_ci	 * VM communication here, since we want to be able to use the guest
19862306a36Sopenharmony_ci	 * driver on older versions that do support VM to VM communication.
19962306a36Sopenharmony_ci	 */
20062306a36Sopenharmony_ci	if (!has_guest_device) {
20162306a36Sopenharmony_ci		/*
20262306a36Sopenharmony_ci		 * Ending up here means we have neither guest nor host
20362306a36Sopenharmony_ci		 * device.
20462306a36Sopenharmony_ci		 */
20562306a36Sopenharmony_ci		return VMCI_ERROR_DEVICE_NOT_FOUND;
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* If no source context then use the current context. */
20962306a36Sopenharmony_ci	if (VMCI_INVALID_ID == src->context)
21062306a36Sopenharmony_ci		src->context = vmci_get_context_id();
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	/*
21362306a36Sopenharmony_ci	 * Send it from local client down to the host, which will
21462306a36Sopenharmony_ci	 * route it to the other guest for us.
21562306a36Sopenharmony_ci	 */
21662306a36Sopenharmony_ci	*route = VMCI_ROUTE_AS_GUEST;
21762306a36Sopenharmony_ci	return VMCI_SUCCESS;
21862306a36Sopenharmony_ci}
219