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#include <linux/atomic.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "vmci_driver.h"
168c2ecf20Sopenharmony_ci#include "vmci_event.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic bool vmci_disable_host;
198c2ecf20Sopenharmony_cimodule_param_named(disable_host, vmci_disable_host, bool, 0);
208c2ecf20Sopenharmony_ciMODULE_PARM_DESC(disable_host,
218c2ecf20Sopenharmony_ci		 "Disable driver host personality (default=enabled)");
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic bool vmci_disable_guest;
248c2ecf20Sopenharmony_cimodule_param_named(disable_guest, vmci_disable_guest, bool, 0);
258c2ecf20Sopenharmony_ciMODULE_PARM_DESC(disable_guest,
268c2ecf20Sopenharmony_ci		 "Disable driver guest personality (default=enabled)");
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic bool vmci_guest_personality_initialized;
298c2ecf20Sopenharmony_cistatic bool vmci_host_personality_initialized;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(vmci_vsock_mutex); /* protects vmci_vsock_transport_cb */
328c2ecf20Sopenharmony_cistatic vmci_vsock_cb vmci_vsock_transport_cb;
338c2ecf20Sopenharmony_cistatic bool vmci_vsock_cb_host_called;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci * vmci_get_context_id() - Gets the current context ID.
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci * Returns the current context ID.  Note that since this is accessed only
398c2ecf20Sopenharmony_ci * from code running in the host, this always returns the host context ID.
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_ciu32 vmci_get_context_id(void)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	if (vmci_guest_code_active())
448c2ecf20Sopenharmony_ci		return vmci_get_vm_context_id();
458c2ecf20Sopenharmony_ci	else if (vmci_host_code_active())
468c2ecf20Sopenharmony_ci		return VMCI_HOST_CONTEXT_ID;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	return VMCI_INVALID_ID;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_get_context_id);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/*
538c2ecf20Sopenharmony_ci * vmci_register_vsock_callback() - Register the VSOCK vmci_transport callback.
548c2ecf20Sopenharmony_ci *
558c2ecf20Sopenharmony_ci * The callback will be called when the first host or guest becomes active,
568c2ecf20Sopenharmony_ci * or if they are already active when this function is called.
578c2ecf20Sopenharmony_ci * To unregister the callback, call this function with NULL parameter.
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci * Returns 0 on success. -EBUSY if a callback is already registered.
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_ciint vmci_register_vsock_callback(vmci_vsock_cb callback)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	int err = 0;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	mutex_lock(&vmci_vsock_mutex);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (vmci_vsock_transport_cb && callback) {
688c2ecf20Sopenharmony_ci		err = -EBUSY;
698c2ecf20Sopenharmony_ci		goto out;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	vmci_vsock_transport_cb = callback;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (!vmci_vsock_transport_cb) {
758c2ecf20Sopenharmony_ci		vmci_vsock_cb_host_called = false;
768c2ecf20Sopenharmony_ci		goto out;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (vmci_guest_code_active())
808c2ecf20Sopenharmony_ci		vmci_vsock_transport_cb(false);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if (vmci_host_users() > 0) {
838c2ecf20Sopenharmony_ci		vmci_vsock_cb_host_called = true;
848c2ecf20Sopenharmony_ci		vmci_vsock_transport_cb(true);
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ciout:
888c2ecf20Sopenharmony_ci	mutex_unlock(&vmci_vsock_mutex);
898c2ecf20Sopenharmony_ci	return err;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_register_vsock_callback);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_civoid vmci_call_vsock_callback(bool is_host)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	mutex_lock(&vmci_vsock_mutex);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (!vmci_vsock_transport_cb)
988c2ecf20Sopenharmony_ci		goto out;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* In the host, this function could be called multiple times,
1018c2ecf20Sopenharmony_ci	 * but we want to register it only once.
1028c2ecf20Sopenharmony_ci	 */
1038c2ecf20Sopenharmony_ci	if (is_host) {
1048c2ecf20Sopenharmony_ci		if (vmci_vsock_cb_host_called)
1058c2ecf20Sopenharmony_ci			goto out;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		vmci_vsock_cb_host_called = true;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	vmci_vsock_transport_cb(is_host);
1118c2ecf20Sopenharmony_ciout:
1128c2ecf20Sopenharmony_ci	mutex_unlock(&vmci_vsock_mutex);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic int __init vmci_drv_init(void)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	int vmci_err;
1188c2ecf20Sopenharmony_ci	int error;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	vmci_err = vmci_event_init();
1218c2ecf20Sopenharmony_ci	if (vmci_err < VMCI_SUCCESS) {
1228c2ecf20Sopenharmony_ci		pr_err("Failed to initialize VMCIEvent (result=%d)\n",
1238c2ecf20Sopenharmony_ci		       vmci_err);
1248c2ecf20Sopenharmony_ci		return -EINVAL;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (!vmci_disable_guest) {
1288c2ecf20Sopenharmony_ci		error = vmci_guest_init();
1298c2ecf20Sopenharmony_ci		if (error) {
1308c2ecf20Sopenharmony_ci			pr_warn("Failed to initialize guest personality (err=%d)\n",
1318c2ecf20Sopenharmony_ci				error);
1328c2ecf20Sopenharmony_ci		} else {
1338c2ecf20Sopenharmony_ci			vmci_guest_personality_initialized = true;
1348c2ecf20Sopenharmony_ci			pr_info("Guest personality initialized and is %s\n",
1358c2ecf20Sopenharmony_ci				vmci_guest_code_active() ?
1368c2ecf20Sopenharmony_ci				"active" : "inactive");
1378c2ecf20Sopenharmony_ci		}
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	if (!vmci_disable_host) {
1418c2ecf20Sopenharmony_ci		error = vmci_host_init();
1428c2ecf20Sopenharmony_ci		if (error) {
1438c2ecf20Sopenharmony_ci			pr_warn("Unable to initialize host personality (err=%d)\n",
1448c2ecf20Sopenharmony_ci				error);
1458c2ecf20Sopenharmony_ci		} else {
1468c2ecf20Sopenharmony_ci			vmci_host_personality_initialized = true;
1478c2ecf20Sopenharmony_ci			pr_info("Initialized host personality\n");
1488c2ecf20Sopenharmony_ci		}
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (!vmci_guest_personality_initialized &&
1528c2ecf20Sopenharmony_ci	    !vmci_host_personality_initialized) {
1538c2ecf20Sopenharmony_ci		vmci_event_exit();
1548c2ecf20Sopenharmony_ci		return -ENODEV;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return 0;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_cimodule_init(vmci_drv_init);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic void __exit vmci_drv_exit(void)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	if (vmci_guest_personality_initialized)
1648c2ecf20Sopenharmony_ci		vmci_guest_exit();
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (vmci_host_personality_initialized)
1678c2ecf20Sopenharmony_ci		vmci_host_exit();
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	vmci_event_exit();
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_cimodule_exit(vmci_drv_exit);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ciMODULE_AUTHOR("VMware, Inc.");
1748c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("VMware Virtual Machine Communication Interface.");
1758c2ecf20Sopenharmony_ciMODULE_VERSION("1.1.6.0-k");
1768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
177