xref: /kernel/linux/linux-5.10/drivers/hv/hv_kvp.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * An implementation of key value pair (KVP) functionality for Linux.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2010, Novell, Inc.
68c2ecf20Sopenharmony_ci * Author : K. Y. Srinivasan <ksrinivasan@novell.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it
98c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License version 2 as published
108c2ecf20Sopenharmony_ci * by the Free Software Foundation.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but
138c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of
148c2ecf20Sopenharmony_ci * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
158c2ecf20Sopenharmony_ci * NON INFRINGEMENT.  See the GNU General Public License for more
168c2ecf20Sopenharmony_ci * details.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License
198c2ecf20Sopenharmony_ci * along with this program; if not, write to the Free Software
208c2ecf20Sopenharmony_ci * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <linux/net.h>
268c2ecf20Sopenharmony_ci#include <linux/nls.h>
278c2ecf20Sopenharmony_ci#include <linux/connector.h>
288c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
298c2ecf20Sopenharmony_ci#include <linux/hyperv.h>
308c2ecf20Sopenharmony_ci#include <asm/hyperv-tlfs.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include "hyperv_vmbus.h"
338c2ecf20Sopenharmony_ci#include "hv_utils_transport.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci * Pre win8 version numbers used in ws2008 and ws 2008 r2 (win7)
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_ci#define WS2008_SRV_MAJOR	1
398c2ecf20Sopenharmony_ci#define WS2008_SRV_MINOR	0
408c2ecf20Sopenharmony_ci#define WS2008_SRV_VERSION     (WS2008_SRV_MAJOR << 16 | WS2008_SRV_MINOR)
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define WIN7_SRV_MAJOR   3
438c2ecf20Sopenharmony_ci#define WIN7_SRV_MINOR   0
448c2ecf20Sopenharmony_ci#define WIN7_SRV_VERSION     (WIN7_SRV_MAJOR << 16 | WIN7_SRV_MINOR)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define WIN8_SRV_MAJOR   4
478c2ecf20Sopenharmony_ci#define WIN8_SRV_MINOR   0
488c2ecf20Sopenharmony_ci#define WIN8_SRV_VERSION     (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define KVP_VER_COUNT 3
518c2ecf20Sopenharmony_cistatic const int kvp_versions[] = {
528c2ecf20Sopenharmony_ci	WIN8_SRV_VERSION,
538c2ecf20Sopenharmony_ci	WIN7_SRV_VERSION,
548c2ecf20Sopenharmony_ci	WS2008_SRV_VERSION
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define FW_VER_COUNT 2
588c2ecf20Sopenharmony_cistatic const int fw_versions[] = {
598c2ecf20Sopenharmony_ci	UTIL_FW_VERSION,
608c2ecf20Sopenharmony_ci	UTIL_WS2K8_FW_VERSION
618c2ecf20Sopenharmony_ci};
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/*
648c2ecf20Sopenharmony_ci * Global state maintained for transaction that is being processed. For a class
658c2ecf20Sopenharmony_ci * of integration services, including the "KVP service", the specified protocol
668c2ecf20Sopenharmony_ci * is a "request/response" protocol which means that there can only be single
678c2ecf20Sopenharmony_ci * outstanding transaction from the host at any given point in time. We use
688c2ecf20Sopenharmony_ci * this to simplify memory management in this driver - we cache and process
698c2ecf20Sopenharmony_ci * only one message at a time.
708c2ecf20Sopenharmony_ci *
718c2ecf20Sopenharmony_ci * While the request/response protocol is guaranteed by the host, we further
728c2ecf20Sopenharmony_ci * ensure this by serializing packet processing in this driver - we do not
738c2ecf20Sopenharmony_ci * read additional packets from the VMBUS until the current packet is fully
748c2ecf20Sopenharmony_ci * handled.
758c2ecf20Sopenharmony_ci */
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic struct {
788c2ecf20Sopenharmony_ci	int state;   /* hvutil_device_state */
798c2ecf20Sopenharmony_ci	int recv_len; /* number of bytes received. */
808c2ecf20Sopenharmony_ci	struct hv_kvp_msg  *kvp_msg; /* current message */
818c2ecf20Sopenharmony_ci	struct vmbus_channel *recv_channel; /* chn we got the request */
828c2ecf20Sopenharmony_ci	u64 recv_req_id; /* request ID. */
838c2ecf20Sopenharmony_ci} kvp_transaction;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/*
868c2ecf20Sopenharmony_ci * This state maintains the version number registered by the daemon.
878c2ecf20Sopenharmony_ci */
888c2ecf20Sopenharmony_cistatic int dm_reg_value;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic void kvp_send_key(struct work_struct *dummy);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic void kvp_respond_to_host(struct hv_kvp_msg *msg, int error);
948c2ecf20Sopenharmony_cistatic void kvp_timeout_func(struct work_struct *dummy);
958c2ecf20Sopenharmony_cistatic void kvp_host_handshake_func(struct work_struct *dummy);
968c2ecf20Sopenharmony_cistatic void kvp_register(int);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic DECLARE_DELAYED_WORK(kvp_timeout_work, kvp_timeout_func);
998c2ecf20Sopenharmony_cistatic DECLARE_DELAYED_WORK(kvp_host_handshake_work, kvp_host_handshake_func);
1008c2ecf20Sopenharmony_cistatic DECLARE_WORK(kvp_sendkey_work, kvp_send_key);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic const char kvp_devname[] = "vmbus/hv_kvp";
1038c2ecf20Sopenharmony_cistatic u8 *recv_buffer;
1048c2ecf20Sopenharmony_cistatic struct hvutil_transport *hvt;
1058c2ecf20Sopenharmony_ci/*
1068c2ecf20Sopenharmony_ci * Register the kernel component with the user-level daemon.
1078c2ecf20Sopenharmony_ci * As part of this registration, pass the LIC version number.
1088c2ecf20Sopenharmony_ci * This number has no meaning, it satisfies the registration protocol.
1098c2ecf20Sopenharmony_ci */
1108c2ecf20Sopenharmony_ci#define HV_DRV_VERSION           "3.1"
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic void kvp_poll_wrapper(void *channel)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	/* Transaction is finished, reset the state here to avoid races. */
1158c2ecf20Sopenharmony_ci	kvp_transaction.state = HVUTIL_READY;
1168c2ecf20Sopenharmony_ci	tasklet_schedule(&((struct vmbus_channel *)channel)->callback_event);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic void kvp_register_done(void)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	/*
1228c2ecf20Sopenharmony_ci	 * If we're still negotiating with the host cancel the timeout
1238c2ecf20Sopenharmony_ci	 * work to not poll the channel twice.
1248c2ecf20Sopenharmony_ci	 */
1258c2ecf20Sopenharmony_ci	pr_debug("KVP: userspace daemon registered\n");
1268c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&kvp_host_handshake_work);
1278c2ecf20Sopenharmony_ci	hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic void
1318c2ecf20Sopenharmony_cikvp_register(int reg_value)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	struct hv_kvp_msg *kvp_msg;
1358c2ecf20Sopenharmony_ci	char *version;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	kvp_msg = kzalloc(sizeof(*kvp_msg), GFP_KERNEL);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (kvp_msg) {
1408c2ecf20Sopenharmony_ci		version = kvp_msg->body.kvp_register.version;
1418c2ecf20Sopenharmony_ci		kvp_msg->kvp_hdr.operation = reg_value;
1428c2ecf20Sopenharmony_ci		strcpy(version, HV_DRV_VERSION);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci		hvutil_transport_send(hvt, kvp_msg, sizeof(*kvp_msg),
1458c2ecf20Sopenharmony_ci				      kvp_register_done);
1468c2ecf20Sopenharmony_ci		kfree(kvp_msg);
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic void kvp_timeout_func(struct work_struct *dummy)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	/*
1538c2ecf20Sopenharmony_ci	 * If the timer fires, the user-mode component has not responded;
1548c2ecf20Sopenharmony_ci	 * process the pending transaction.
1558c2ecf20Sopenharmony_ci	 */
1568c2ecf20Sopenharmony_ci	kvp_respond_to_host(NULL, HV_E_FAIL);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper);
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic void kvp_host_handshake_func(struct work_struct *dummy)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	tasklet_schedule(&kvp_transaction.recv_channel->callback_event);
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic int kvp_handle_handshake(struct hv_kvp_msg *msg)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	switch (msg->kvp_hdr.operation) {
1698c2ecf20Sopenharmony_ci	case KVP_OP_REGISTER:
1708c2ecf20Sopenharmony_ci		dm_reg_value = KVP_OP_REGISTER;
1718c2ecf20Sopenharmony_ci		pr_info("KVP: IP injection functionality not available\n");
1728c2ecf20Sopenharmony_ci		pr_info("KVP: Upgrade the KVP daemon\n");
1738c2ecf20Sopenharmony_ci		break;
1748c2ecf20Sopenharmony_ci	case KVP_OP_REGISTER1:
1758c2ecf20Sopenharmony_ci		dm_reg_value = KVP_OP_REGISTER1;
1768c2ecf20Sopenharmony_ci		break;
1778c2ecf20Sopenharmony_ci	default:
1788c2ecf20Sopenharmony_ci		pr_info("KVP: incompatible daemon\n");
1798c2ecf20Sopenharmony_ci		pr_info("KVP: KVP version: %d, Daemon version: %d\n",
1808c2ecf20Sopenharmony_ci			KVP_OP_REGISTER1, msg->kvp_hdr.operation);
1818c2ecf20Sopenharmony_ci		return -EINVAL;
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	/*
1858c2ecf20Sopenharmony_ci	 * We have a compatible daemon; complete the handshake.
1868c2ecf20Sopenharmony_ci	 */
1878c2ecf20Sopenharmony_ci	pr_debug("KVP: userspace daemon ver. %d connected\n",
1888c2ecf20Sopenharmony_ci		 msg->kvp_hdr.operation);
1898c2ecf20Sopenharmony_ci	kvp_register(dm_reg_value);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return 0;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/*
1968c2ecf20Sopenharmony_ci * Callback when data is received from user mode.
1978c2ecf20Sopenharmony_ci */
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic int kvp_on_msg(void *msg, int len)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct hv_kvp_msg *message = (struct hv_kvp_msg *)msg;
2028c2ecf20Sopenharmony_ci	struct hv_kvp_msg_enumerate *data;
2038c2ecf20Sopenharmony_ci	int	error = 0;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (len < sizeof(*message))
2068c2ecf20Sopenharmony_ci		return -EINVAL;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/*
2098c2ecf20Sopenharmony_ci	 * If we are negotiating the version information
2108c2ecf20Sopenharmony_ci	 * with the daemon; handle that first.
2118c2ecf20Sopenharmony_ci	 */
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (kvp_transaction.state < HVUTIL_READY) {
2148c2ecf20Sopenharmony_ci		return kvp_handle_handshake(message);
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/* We didn't send anything to userspace so the reply is spurious */
2188c2ecf20Sopenharmony_ci	if (kvp_transaction.state < HVUTIL_USERSPACE_REQ)
2198c2ecf20Sopenharmony_ci		return -EINVAL;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	kvp_transaction.state = HVUTIL_USERSPACE_RECV;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	/*
2248c2ecf20Sopenharmony_ci	 * Based on the version of the daemon, we propagate errors from the
2258c2ecf20Sopenharmony_ci	 * daemon differently.
2268c2ecf20Sopenharmony_ci	 */
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	data = &message->body.kvp_enum_data;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	switch (dm_reg_value) {
2318c2ecf20Sopenharmony_ci	case KVP_OP_REGISTER:
2328c2ecf20Sopenharmony_ci		/*
2338c2ecf20Sopenharmony_ci		 * Null string is used to pass back error condition.
2348c2ecf20Sopenharmony_ci		 */
2358c2ecf20Sopenharmony_ci		if (data->data.key[0] == 0)
2368c2ecf20Sopenharmony_ci			error = HV_S_CONT;
2378c2ecf20Sopenharmony_ci		break;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	case KVP_OP_REGISTER1:
2408c2ecf20Sopenharmony_ci		/*
2418c2ecf20Sopenharmony_ci		 * We use the message header information from
2428c2ecf20Sopenharmony_ci		 * the user level daemon to transmit errors.
2438c2ecf20Sopenharmony_ci		 */
2448c2ecf20Sopenharmony_ci		error = message->error;
2458c2ecf20Sopenharmony_ci		break;
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	/*
2498c2ecf20Sopenharmony_ci	 * Complete the transaction by forwarding the key value
2508c2ecf20Sopenharmony_ci	 * to the host. But first, cancel the timeout.
2518c2ecf20Sopenharmony_ci	 */
2528c2ecf20Sopenharmony_ci	if (cancel_delayed_work_sync(&kvp_timeout_work)) {
2538c2ecf20Sopenharmony_ci		kvp_respond_to_host(message, error);
2548c2ecf20Sopenharmony_ci		hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper);
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	return 0;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic int process_ob_ipinfo(void *in_msg, void *out_msg, int op)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	struct hv_kvp_msg *in = in_msg;
2648c2ecf20Sopenharmony_ci	struct hv_kvp_ip_msg *out = out_msg;
2658c2ecf20Sopenharmony_ci	int len;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	switch (op) {
2688c2ecf20Sopenharmony_ci	case KVP_OP_GET_IP_INFO:
2698c2ecf20Sopenharmony_ci		/*
2708c2ecf20Sopenharmony_ci		 * Transform all parameters into utf16 encoding.
2718c2ecf20Sopenharmony_ci		 */
2728c2ecf20Sopenharmony_ci		len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.ip_addr,
2738c2ecf20Sopenharmony_ci				strlen((char *)in->body.kvp_ip_val.ip_addr),
2748c2ecf20Sopenharmony_ci				UTF16_HOST_ENDIAN,
2758c2ecf20Sopenharmony_ci				(wchar_t *)out->kvp_ip_val.ip_addr,
2768c2ecf20Sopenharmony_ci				MAX_IP_ADDR_SIZE);
2778c2ecf20Sopenharmony_ci		if (len < 0)
2788c2ecf20Sopenharmony_ci			return len;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.sub_net,
2818c2ecf20Sopenharmony_ci				strlen((char *)in->body.kvp_ip_val.sub_net),
2828c2ecf20Sopenharmony_ci				UTF16_HOST_ENDIAN,
2838c2ecf20Sopenharmony_ci				(wchar_t *)out->kvp_ip_val.sub_net,
2848c2ecf20Sopenharmony_ci				MAX_IP_ADDR_SIZE);
2858c2ecf20Sopenharmony_ci		if (len < 0)
2868c2ecf20Sopenharmony_ci			return len;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.gate_way,
2898c2ecf20Sopenharmony_ci				strlen((char *)in->body.kvp_ip_val.gate_way),
2908c2ecf20Sopenharmony_ci				UTF16_HOST_ENDIAN,
2918c2ecf20Sopenharmony_ci				(wchar_t *)out->kvp_ip_val.gate_way,
2928c2ecf20Sopenharmony_ci				MAX_GATEWAY_SIZE);
2938c2ecf20Sopenharmony_ci		if (len < 0)
2948c2ecf20Sopenharmony_ci			return len;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci		len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.dns_addr,
2978c2ecf20Sopenharmony_ci				strlen((char *)in->body.kvp_ip_val.dns_addr),
2988c2ecf20Sopenharmony_ci				UTF16_HOST_ENDIAN,
2998c2ecf20Sopenharmony_ci				(wchar_t *)out->kvp_ip_val.dns_addr,
3008c2ecf20Sopenharmony_ci				MAX_IP_ADDR_SIZE);
3018c2ecf20Sopenharmony_ci		if (len < 0)
3028c2ecf20Sopenharmony_ci			return len;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci		len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.adapter_id,
3058c2ecf20Sopenharmony_ci				strlen((char *)in->body.kvp_ip_val.adapter_id),
3068c2ecf20Sopenharmony_ci				UTF16_HOST_ENDIAN,
3078c2ecf20Sopenharmony_ci				(wchar_t *)out->kvp_ip_val.adapter_id,
3088c2ecf20Sopenharmony_ci				MAX_ADAPTER_ID_SIZE);
3098c2ecf20Sopenharmony_ci		if (len < 0)
3108c2ecf20Sopenharmony_ci			return len;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci		out->kvp_ip_val.dhcp_enabled =
3138c2ecf20Sopenharmony_ci			in->body.kvp_ip_val.dhcp_enabled;
3148c2ecf20Sopenharmony_ci		out->kvp_ip_val.addr_family =
3158c2ecf20Sopenharmony_ci			in->body.kvp_ip_val.addr_family;
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return 0;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic void process_ib_ipinfo(void *in_msg, void *out_msg, int op)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct hv_kvp_ip_msg *in = in_msg;
3248c2ecf20Sopenharmony_ci	struct hv_kvp_msg *out = out_msg;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	switch (op) {
3278c2ecf20Sopenharmony_ci	case KVP_OP_SET_IP_INFO:
3288c2ecf20Sopenharmony_ci		/*
3298c2ecf20Sopenharmony_ci		 * Transform all parameters into utf8 encoding.
3308c2ecf20Sopenharmony_ci		 */
3318c2ecf20Sopenharmony_ci		utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.ip_addr,
3328c2ecf20Sopenharmony_ci				MAX_IP_ADDR_SIZE,
3338c2ecf20Sopenharmony_ci				UTF16_LITTLE_ENDIAN,
3348c2ecf20Sopenharmony_ci				(__u8 *)out->body.kvp_ip_val.ip_addr,
3358c2ecf20Sopenharmony_ci				MAX_IP_ADDR_SIZE);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci		utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.sub_net,
3388c2ecf20Sopenharmony_ci				MAX_IP_ADDR_SIZE,
3398c2ecf20Sopenharmony_ci				UTF16_LITTLE_ENDIAN,
3408c2ecf20Sopenharmony_ci				(__u8 *)out->body.kvp_ip_val.sub_net,
3418c2ecf20Sopenharmony_ci				MAX_IP_ADDR_SIZE);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci		utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.gate_way,
3448c2ecf20Sopenharmony_ci				MAX_GATEWAY_SIZE,
3458c2ecf20Sopenharmony_ci				UTF16_LITTLE_ENDIAN,
3468c2ecf20Sopenharmony_ci				(__u8 *)out->body.kvp_ip_val.gate_way,
3478c2ecf20Sopenharmony_ci				MAX_GATEWAY_SIZE);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci		utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.dns_addr,
3508c2ecf20Sopenharmony_ci				MAX_IP_ADDR_SIZE,
3518c2ecf20Sopenharmony_ci				UTF16_LITTLE_ENDIAN,
3528c2ecf20Sopenharmony_ci				(__u8 *)out->body.kvp_ip_val.dns_addr,
3538c2ecf20Sopenharmony_ci				MAX_IP_ADDR_SIZE);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci		out->body.kvp_ip_val.dhcp_enabled = in->kvp_ip_val.dhcp_enabled;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci		fallthrough;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	case KVP_OP_GET_IP_INFO:
3608c2ecf20Sopenharmony_ci		utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.adapter_id,
3618c2ecf20Sopenharmony_ci				MAX_ADAPTER_ID_SIZE,
3628c2ecf20Sopenharmony_ci				UTF16_LITTLE_ENDIAN,
3638c2ecf20Sopenharmony_ci				(__u8 *)out->body.kvp_ip_val.adapter_id,
3648c2ecf20Sopenharmony_ci				MAX_ADAPTER_ID_SIZE);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci		out->body.kvp_ip_val.addr_family = in->kvp_ip_val.addr_family;
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic void
3748c2ecf20Sopenharmony_cikvp_send_key(struct work_struct *dummy)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	struct hv_kvp_msg *message;
3778c2ecf20Sopenharmony_ci	struct hv_kvp_msg *in_msg;
3788c2ecf20Sopenharmony_ci	__u8 operation = kvp_transaction.kvp_msg->kvp_hdr.operation;
3798c2ecf20Sopenharmony_ci	__u8 pool = kvp_transaction.kvp_msg->kvp_hdr.pool;
3808c2ecf20Sopenharmony_ci	__u32 val32;
3818c2ecf20Sopenharmony_ci	__u64 val64;
3828c2ecf20Sopenharmony_ci	int rc;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	/* The transaction state is wrong. */
3858c2ecf20Sopenharmony_ci	if (kvp_transaction.state != HVUTIL_HOSTMSG_RECEIVED)
3868c2ecf20Sopenharmony_ci		return;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	message = kzalloc(sizeof(*message), GFP_KERNEL);
3898c2ecf20Sopenharmony_ci	if (!message)
3908c2ecf20Sopenharmony_ci		return;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	message->kvp_hdr.operation = operation;
3938c2ecf20Sopenharmony_ci	message->kvp_hdr.pool = pool;
3948c2ecf20Sopenharmony_ci	in_msg = kvp_transaction.kvp_msg;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	/*
3978c2ecf20Sopenharmony_ci	 * The key/value strings sent from the host are encoded in
3988c2ecf20Sopenharmony_ci	 * in utf16; convert it to utf8 strings.
3998c2ecf20Sopenharmony_ci	 * The host assures us that the utf16 strings will not exceed
4008c2ecf20Sopenharmony_ci	 * the max lengths specified. We will however, reserve room
4018c2ecf20Sopenharmony_ci	 * for the string terminating character - in the utf16s_utf8s()
4028c2ecf20Sopenharmony_ci	 * function we limit the size of the buffer where the converted
4038c2ecf20Sopenharmony_ci	 * string is placed to HV_KVP_EXCHANGE_MAX_*_SIZE -1 to guarantee
4048c2ecf20Sopenharmony_ci	 * that the strings can be properly terminated!
4058c2ecf20Sopenharmony_ci	 */
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	switch (message->kvp_hdr.operation) {
4088c2ecf20Sopenharmony_ci	case KVP_OP_SET_IP_INFO:
4098c2ecf20Sopenharmony_ci		process_ib_ipinfo(in_msg, message, KVP_OP_SET_IP_INFO);
4108c2ecf20Sopenharmony_ci		break;
4118c2ecf20Sopenharmony_ci	case KVP_OP_GET_IP_INFO:
4128c2ecf20Sopenharmony_ci		/*
4138c2ecf20Sopenharmony_ci		 * We only need to pass on the info of operation, adapter_id
4148c2ecf20Sopenharmony_ci		 * and addr_family to the userland kvp daemon.
4158c2ecf20Sopenharmony_ci		 */
4168c2ecf20Sopenharmony_ci		process_ib_ipinfo(in_msg, message, KVP_OP_GET_IP_INFO);
4178c2ecf20Sopenharmony_ci		break;
4188c2ecf20Sopenharmony_ci	case KVP_OP_SET:
4198c2ecf20Sopenharmony_ci		switch (in_msg->body.kvp_set.data.value_type) {
4208c2ecf20Sopenharmony_ci		case REG_SZ:
4218c2ecf20Sopenharmony_ci			/*
4228c2ecf20Sopenharmony_ci			 * The value is a string - utf16 encoding.
4238c2ecf20Sopenharmony_ci			 */
4248c2ecf20Sopenharmony_ci			message->body.kvp_set.data.value_size =
4258c2ecf20Sopenharmony_ci				utf16s_to_utf8s(
4268c2ecf20Sopenharmony_ci				(wchar_t *)in_msg->body.kvp_set.data.value,
4278c2ecf20Sopenharmony_ci				in_msg->body.kvp_set.data.value_size,
4288c2ecf20Sopenharmony_ci				UTF16_LITTLE_ENDIAN,
4298c2ecf20Sopenharmony_ci				message->body.kvp_set.data.value,
4308c2ecf20Sopenharmony_ci				HV_KVP_EXCHANGE_MAX_VALUE_SIZE - 1) + 1;
4318c2ecf20Sopenharmony_ci			break;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci		case REG_U32:
4348c2ecf20Sopenharmony_ci			/*
4358c2ecf20Sopenharmony_ci			 * The value is a 32 bit scalar.
4368c2ecf20Sopenharmony_ci			 * We save this as a utf8 string.
4378c2ecf20Sopenharmony_ci			 */
4388c2ecf20Sopenharmony_ci			val32 = in_msg->body.kvp_set.data.value_u32;
4398c2ecf20Sopenharmony_ci			message->body.kvp_set.data.value_size =
4408c2ecf20Sopenharmony_ci				sprintf(message->body.kvp_set.data.value,
4418c2ecf20Sopenharmony_ci					"%u", val32) + 1;
4428c2ecf20Sopenharmony_ci			break;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci		case REG_U64:
4458c2ecf20Sopenharmony_ci			/*
4468c2ecf20Sopenharmony_ci			 * The value is a 64 bit scalar.
4478c2ecf20Sopenharmony_ci			 * We save this as a utf8 string.
4488c2ecf20Sopenharmony_ci			 */
4498c2ecf20Sopenharmony_ci			val64 = in_msg->body.kvp_set.data.value_u64;
4508c2ecf20Sopenharmony_ci			message->body.kvp_set.data.value_size =
4518c2ecf20Sopenharmony_ci				sprintf(message->body.kvp_set.data.value,
4528c2ecf20Sopenharmony_ci					"%llu", val64) + 1;
4538c2ecf20Sopenharmony_ci			break;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci		}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci		/*
4588c2ecf20Sopenharmony_ci		 * The key is always a string - utf16 encoding.
4598c2ecf20Sopenharmony_ci		 */
4608c2ecf20Sopenharmony_ci		message->body.kvp_set.data.key_size =
4618c2ecf20Sopenharmony_ci			utf16s_to_utf8s(
4628c2ecf20Sopenharmony_ci			(wchar_t *)in_msg->body.kvp_set.data.key,
4638c2ecf20Sopenharmony_ci			in_msg->body.kvp_set.data.key_size,
4648c2ecf20Sopenharmony_ci			UTF16_LITTLE_ENDIAN,
4658c2ecf20Sopenharmony_ci			message->body.kvp_set.data.key,
4668c2ecf20Sopenharmony_ci			HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci		break;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	case KVP_OP_GET:
4718c2ecf20Sopenharmony_ci		message->body.kvp_get.data.key_size =
4728c2ecf20Sopenharmony_ci			utf16s_to_utf8s(
4738c2ecf20Sopenharmony_ci			(wchar_t *)in_msg->body.kvp_get.data.key,
4748c2ecf20Sopenharmony_ci			in_msg->body.kvp_get.data.key_size,
4758c2ecf20Sopenharmony_ci			UTF16_LITTLE_ENDIAN,
4768c2ecf20Sopenharmony_ci			message->body.kvp_get.data.key,
4778c2ecf20Sopenharmony_ci			HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1;
4788c2ecf20Sopenharmony_ci		break;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	case KVP_OP_DELETE:
4818c2ecf20Sopenharmony_ci		message->body.kvp_delete.key_size =
4828c2ecf20Sopenharmony_ci			utf16s_to_utf8s(
4838c2ecf20Sopenharmony_ci			(wchar_t *)in_msg->body.kvp_delete.key,
4848c2ecf20Sopenharmony_ci			in_msg->body.kvp_delete.key_size,
4858c2ecf20Sopenharmony_ci			UTF16_LITTLE_ENDIAN,
4868c2ecf20Sopenharmony_ci			message->body.kvp_delete.key,
4878c2ecf20Sopenharmony_ci			HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1;
4888c2ecf20Sopenharmony_ci		break;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	case KVP_OP_ENUMERATE:
4918c2ecf20Sopenharmony_ci		message->body.kvp_enum_data.index =
4928c2ecf20Sopenharmony_ci			in_msg->body.kvp_enum_data.index;
4938c2ecf20Sopenharmony_ci		break;
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	kvp_transaction.state = HVUTIL_USERSPACE_REQ;
4978c2ecf20Sopenharmony_ci	rc = hvutil_transport_send(hvt, message, sizeof(*message), NULL);
4988c2ecf20Sopenharmony_ci	if (rc) {
4998c2ecf20Sopenharmony_ci		pr_debug("KVP: failed to communicate to the daemon: %d\n", rc);
5008c2ecf20Sopenharmony_ci		if (cancel_delayed_work_sync(&kvp_timeout_work)) {
5018c2ecf20Sopenharmony_ci			kvp_respond_to_host(message, HV_E_FAIL);
5028c2ecf20Sopenharmony_ci			kvp_transaction.state = HVUTIL_READY;
5038c2ecf20Sopenharmony_ci		}
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	kfree(message);
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci/*
5108c2ecf20Sopenharmony_ci * Send a response back to the host.
5118c2ecf20Sopenharmony_ci */
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cistatic void
5148c2ecf20Sopenharmony_cikvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci	struct hv_kvp_msg  *kvp_msg;
5178c2ecf20Sopenharmony_ci	struct hv_kvp_exchg_msg_value  *kvp_data;
5188c2ecf20Sopenharmony_ci	char	*key_name;
5198c2ecf20Sopenharmony_ci	char	*value;
5208c2ecf20Sopenharmony_ci	struct icmsg_hdr *icmsghdrp;
5218c2ecf20Sopenharmony_ci	int	keylen = 0;
5228c2ecf20Sopenharmony_ci	int	valuelen = 0;
5238c2ecf20Sopenharmony_ci	u32	buf_len;
5248c2ecf20Sopenharmony_ci	struct vmbus_channel *channel;
5258c2ecf20Sopenharmony_ci	u64	req_id;
5268c2ecf20Sopenharmony_ci	int ret;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	/*
5298c2ecf20Sopenharmony_ci	 * Copy the global state for completing the transaction. Note that
5308c2ecf20Sopenharmony_ci	 * only one transaction can be active at a time.
5318c2ecf20Sopenharmony_ci	 */
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	buf_len = kvp_transaction.recv_len;
5348c2ecf20Sopenharmony_ci	channel = kvp_transaction.recv_channel;
5358c2ecf20Sopenharmony_ci	req_id = kvp_transaction.recv_req_id;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	icmsghdrp = (struct icmsg_hdr *)
5388c2ecf20Sopenharmony_ci			&recv_buffer[sizeof(struct vmbuspipe_hdr)];
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	if (channel->onchannel_callback == NULL)
5418c2ecf20Sopenharmony_ci		/*
5428c2ecf20Sopenharmony_ci		 * We have raced with util driver being unloaded;
5438c2ecf20Sopenharmony_ci		 * silently return.
5448c2ecf20Sopenharmony_ci		 */
5458c2ecf20Sopenharmony_ci		return;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	icmsghdrp->status = error;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	/*
5508c2ecf20Sopenharmony_ci	 * If the error parameter is set, terminate the host's enumeration
5518c2ecf20Sopenharmony_ci	 * on this pool.
5528c2ecf20Sopenharmony_ci	 */
5538c2ecf20Sopenharmony_ci	if (error) {
5548c2ecf20Sopenharmony_ci		/*
5558c2ecf20Sopenharmony_ci		 * Something failed or we have timed out;
5568c2ecf20Sopenharmony_ci		 * terminate the current host-side iteration.
5578c2ecf20Sopenharmony_ci		 */
5588c2ecf20Sopenharmony_ci		goto response_done;
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	kvp_msg = (struct hv_kvp_msg *)
5628c2ecf20Sopenharmony_ci			&recv_buffer[sizeof(struct vmbuspipe_hdr) +
5638c2ecf20Sopenharmony_ci			sizeof(struct icmsg_hdr)];
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	switch (kvp_transaction.kvp_msg->kvp_hdr.operation) {
5668c2ecf20Sopenharmony_ci	case KVP_OP_GET_IP_INFO:
5678c2ecf20Sopenharmony_ci		ret = process_ob_ipinfo(msg_to_host,
5688c2ecf20Sopenharmony_ci				 (struct hv_kvp_ip_msg *)kvp_msg,
5698c2ecf20Sopenharmony_ci				 KVP_OP_GET_IP_INFO);
5708c2ecf20Sopenharmony_ci		if (ret < 0)
5718c2ecf20Sopenharmony_ci			icmsghdrp->status = HV_E_FAIL;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci		goto response_done;
5748c2ecf20Sopenharmony_ci	case KVP_OP_SET_IP_INFO:
5758c2ecf20Sopenharmony_ci		goto response_done;
5768c2ecf20Sopenharmony_ci	case KVP_OP_GET:
5778c2ecf20Sopenharmony_ci		kvp_data = &kvp_msg->body.kvp_get.data;
5788c2ecf20Sopenharmony_ci		goto copy_value;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	case KVP_OP_SET:
5818c2ecf20Sopenharmony_ci	case KVP_OP_DELETE:
5828c2ecf20Sopenharmony_ci		goto response_done;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	default:
5858c2ecf20Sopenharmony_ci		break;
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	kvp_data = &kvp_msg->body.kvp_enum_data.data;
5898c2ecf20Sopenharmony_ci	key_name = msg_to_host->body.kvp_enum_data.data.key;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	/*
5928c2ecf20Sopenharmony_ci	 * The windows host expects the key/value pair to be encoded
5938c2ecf20Sopenharmony_ci	 * in utf16. Ensure that the key/value size reported to the host
5948c2ecf20Sopenharmony_ci	 * will be less than or equal to the MAX size (including the
5958c2ecf20Sopenharmony_ci	 * terminating character).
5968c2ecf20Sopenharmony_ci	 */
5978c2ecf20Sopenharmony_ci	keylen = utf8s_to_utf16s(key_name, strlen(key_name), UTF16_HOST_ENDIAN,
5988c2ecf20Sopenharmony_ci				(wchar_t *) kvp_data->key,
5998c2ecf20Sopenharmony_ci				(HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2) - 2);
6008c2ecf20Sopenharmony_ci	kvp_data->key_size = 2*(keylen + 1); /* utf16 encoding */
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cicopy_value:
6038c2ecf20Sopenharmony_ci	value = msg_to_host->body.kvp_enum_data.data.value;
6048c2ecf20Sopenharmony_ci	valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN,
6058c2ecf20Sopenharmony_ci				(wchar_t *) kvp_data->value,
6068c2ecf20Sopenharmony_ci				(HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2);
6078c2ecf20Sopenharmony_ci	kvp_data->value_size = 2*(valuelen + 1); /* utf16 encoding */
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	/*
6108c2ecf20Sopenharmony_ci	 * If the utf8s to utf16s conversion failed; notify host
6118c2ecf20Sopenharmony_ci	 * of the error.
6128c2ecf20Sopenharmony_ci	 */
6138c2ecf20Sopenharmony_ci	if ((keylen < 0) || (valuelen < 0))
6148c2ecf20Sopenharmony_ci		icmsghdrp->status = HV_E_FAIL;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	kvp_data->value_type = REG_SZ; /* all our values are strings */
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ciresponse_done:
6198c2ecf20Sopenharmony_ci	icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	vmbus_sendpacket(channel, recv_buffer, buf_len, req_id,
6228c2ecf20Sopenharmony_ci				VM_PKT_DATA_INBAND, 0);
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci/*
6268c2ecf20Sopenharmony_ci * This callback is invoked when we get a KVP message from the host.
6278c2ecf20Sopenharmony_ci * The host ensures that only one KVP transaction can be active at a time.
6288c2ecf20Sopenharmony_ci * KVP implementation in Linux needs to forward the key to a user-mde
6298c2ecf20Sopenharmony_ci * component to retrieve the corresponding value. Consequently, we cannot
6308c2ecf20Sopenharmony_ci * respond to the host in the context of this callback. Since the host
6318c2ecf20Sopenharmony_ci * guarantees that at most only one transaction can be active at a time,
6328c2ecf20Sopenharmony_ci * we stash away the transaction state in a set of global variables.
6338c2ecf20Sopenharmony_ci */
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_civoid hv_kvp_onchannelcallback(void *context)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	struct vmbus_channel *channel = context;
6388c2ecf20Sopenharmony_ci	u32 recvlen;
6398c2ecf20Sopenharmony_ci	u64 requestid;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	struct hv_kvp_msg *kvp_msg;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	struct icmsg_hdr *icmsghdrp;
6448c2ecf20Sopenharmony_ci	int kvp_srv_version;
6458c2ecf20Sopenharmony_ci	static enum {NEGO_NOT_STARTED,
6468c2ecf20Sopenharmony_ci		     NEGO_IN_PROGRESS,
6478c2ecf20Sopenharmony_ci		     NEGO_FINISHED} host_negotiatied = NEGO_NOT_STARTED;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	if (kvp_transaction.state < HVUTIL_READY) {
6508c2ecf20Sopenharmony_ci		/*
6518c2ecf20Sopenharmony_ci		 * If userspace daemon is not connected and host is asking
6528c2ecf20Sopenharmony_ci		 * us to negotiate we need to delay to not lose messages.
6538c2ecf20Sopenharmony_ci		 * This is important for Failover IP setting.
6548c2ecf20Sopenharmony_ci		 */
6558c2ecf20Sopenharmony_ci		if (host_negotiatied == NEGO_NOT_STARTED) {
6568c2ecf20Sopenharmony_ci			host_negotiatied = NEGO_IN_PROGRESS;
6578c2ecf20Sopenharmony_ci			schedule_delayed_work(&kvp_host_handshake_work,
6588c2ecf20Sopenharmony_ci				      HV_UTIL_NEGO_TIMEOUT * HZ);
6598c2ecf20Sopenharmony_ci		}
6608c2ecf20Sopenharmony_ci		return;
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci	if (kvp_transaction.state > HVUTIL_READY)
6638c2ecf20Sopenharmony_ci		return;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 4, &recvlen,
6668c2ecf20Sopenharmony_ci			 &requestid);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	if (recvlen > 0) {
6698c2ecf20Sopenharmony_ci		icmsghdrp = (struct icmsg_hdr *)&recv_buffer[
6708c2ecf20Sopenharmony_ci			sizeof(struct vmbuspipe_hdr)];
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci		if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
6738c2ecf20Sopenharmony_ci			if (vmbus_prep_negotiate_resp(icmsghdrp,
6748c2ecf20Sopenharmony_ci				 recv_buffer, fw_versions, FW_VER_COUNT,
6758c2ecf20Sopenharmony_ci				 kvp_versions, KVP_VER_COUNT,
6768c2ecf20Sopenharmony_ci				 NULL, &kvp_srv_version)) {
6778c2ecf20Sopenharmony_ci				pr_info("KVP IC version %d.%d\n",
6788c2ecf20Sopenharmony_ci					kvp_srv_version >> 16,
6798c2ecf20Sopenharmony_ci					kvp_srv_version & 0xFFFF);
6808c2ecf20Sopenharmony_ci			}
6818c2ecf20Sopenharmony_ci		} else {
6828c2ecf20Sopenharmony_ci			kvp_msg = (struct hv_kvp_msg *)&recv_buffer[
6838c2ecf20Sopenharmony_ci				sizeof(struct vmbuspipe_hdr) +
6848c2ecf20Sopenharmony_ci				sizeof(struct icmsg_hdr)];
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci			/*
6878c2ecf20Sopenharmony_ci			 * Stash away this global state for completing the
6888c2ecf20Sopenharmony_ci			 * transaction; note transactions are serialized.
6898c2ecf20Sopenharmony_ci			 */
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci			kvp_transaction.recv_len = recvlen;
6928c2ecf20Sopenharmony_ci			kvp_transaction.recv_req_id = requestid;
6938c2ecf20Sopenharmony_ci			kvp_transaction.kvp_msg = kvp_msg;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci			if (kvp_transaction.state < HVUTIL_READY) {
6968c2ecf20Sopenharmony_ci				/* Userspace is not registered yet */
6978c2ecf20Sopenharmony_ci				kvp_respond_to_host(NULL, HV_E_FAIL);
6988c2ecf20Sopenharmony_ci				return;
6998c2ecf20Sopenharmony_ci			}
7008c2ecf20Sopenharmony_ci			kvp_transaction.state = HVUTIL_HOSTMSG_RECEIVED;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci			/*
7038c2ecf20Sopenharmony_ci			 * Get the information from the
7048c2ecf20Sopenharmony_ci			 * user-mode component.
7058c2ecf20Sopenharmony_ci			 * component. This transaction will be
7068c2ecf20Sopenharmony_ci			 * completed when we get the value from
7078c2ecf20Sopenharmony_ci			 * the user-mode component.
7088c2ecf20Sopenharmony_ci			 * Set a timeout to deal with
7098c2ecf20Sopenharmony_ci			 * user-mode not responding.
7108c2ecf20Sopenharmony_ci			 */
7118c2ecf20Sopenharmony_ci			schedule_work(&kvp_sendkey_work);
7128c2ecf20Sopenharmony_ci			schedule_delayed_work(&kvp_timeout_work,
7138c2ecf20Sopenharmony_ci					      HV_UTIL_TIMEOUT * HZ);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci			return;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci		}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci		icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
7208c2ecf20Sopenharmony_ci			| ICMSGHDRFLAG_RESPONSE;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci		vmbus_sendpacket(channel, recv_buffer,
7238c2ecf20Sopenharmony_ci				       recvlen, requestid,
7248c2ecf20Sopenharmony_ci				       VM_PKT_DATA_INBAND, 0);
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci		host_negotiatied = NEGO_FINISHED;
7278c2ecf20Sopenharmony_ci		hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper);
7288c2ecf20Sopenharmony_ci	}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_cistatic void kvp_on_reset(void)
7338c2ecf20Sopenharmony_ci{
7348c2ecf20Sopenharmony_ci	if (cancel_delayed_work_sync(&kvp_timeout_work))
7358c2ecf20Sopenharmony_ci		kvp_respond_to_host(NULL, HV_E_FAIL);
7368c2ecf20Sopenharmony_ci	kvp_transaction.state = HVUTIL_DEVICE_INIT;
7378c2ecf20Sopenharmony_ci}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ciint
7408c2ecf20Sopenharmony_cihv_kvp_init(struct hv_util_service *srv)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	recv_buffer = srv->recv_buffer;
7438c2ecf20Sopenharmony_ci	kvp_transaction.recv_channel = srv->channel;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	/*
7468c2ecf20Sopenharmony_ci	 * When this driver loads, the user level daemon that
7478c2ecf20Sopenharmony_ci	 * processes the host requests may not yet be running.
7488c2ecf20Sopenharmony_ci	 * Defer processing channel callbacks until the daemon
7498c2ecf20Sopenharmony_ci	 * has registered.
7508c2ecf20Sopenharmony_ci	 */
7518c2ecf20Sopenharmony_ci	kvp_transaction.state = HVUTIL_DEVICE_INIT;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	hvt = hvutil_transport_init(kvp_devname, CN_KVP_IDX, CN_KVP_VAL,
7548c2ecf20Sopenharmony_ci				    kvp_on_msg, kvp_on_reset);
7558c2ecf20Sopenharmony_ci	if (!hvt)
7568c2ecf20Sopenharmony_ci		return -EFAULT;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	return 0;
7598c2ecf20Sopenharmony_ci}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cistatic void hv_kvp_cancel_work(void)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&kvp_host_handshake_work);
7648c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&kvp_timeout_work);
7658c2ecf20Sopenharmony_ci	cancel_work_sync(&kvp_sendkey_work);
7668c2ecf20Sopenharmony_ci}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ciint hv_kvp_pre_suspend(void)
7698c2ecf20Sopenharmony_ci{
7708c2ecf20Sopenharmony_ci	struct vmbus_channel *channel = kvp_transaction.recv_channel;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	tasklet_disable(&channel->callback_event);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	/*
7758c2ecf20Sopenharmony_ci	 * If there is a pending transtion, it's unnecessary to tell the host
7768c2ecf20Sopenharmony_ci	 * that the transaction will fail, because that is implied when
7778c2ecf20Sopenharmony_ci	 * util_suspend() calls vmbus_close() later.
7788c2ecf20Sopenharmony_ci	 */
7798c2ecf20Sopenharmony_ci	hv_kvp_cancel_work();
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	/*
7828c2ecf20Sopenharmony_ci	 * Forece the state to READY to handle the ICMSGTYPE_NEGOTIATE message
7838c2ecf20Sopenharmony_ci	 * later. The user space daemon may go out of order and its write()
7848c2ecf20Sopenharmony_ci	 * may fail with EINVAL: this doesn't matter since the daemon will
7858c2ecf20Sopenharmony_ci	 * reset the device by closing and re-opening it.
7868c2ecf20Sopenharmony_ci	 */
7878c2ecf20Sopenharmony_ci	kvp_transaction.state = HVUTIL_READY;
7888c2ecf20Sopenharmony_ci	return 0;
7898c2ecf20Sopenharmony_ci}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ciint hv_kvp_pre_resume(void)
7928c2ecf20Sopenharmony_ci{
7938c2ecf20Sopenharmony_ci	struct vmbus_channel *channel = kvp_transaction.recv_channel;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	tasklet_enable(&channel->callback_event);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	return 0;
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_civoid hv_kvp_deinit(void)
8018c2ecf20Sopenharmony_ci{
8028c2ecf20Sopenharmony_ci	kvp_transaction.state = HVUTIL_DEVICE_DYING;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	hv_kvp_cancel_work();
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	hvutil_transport_destroy(hvt);
8078c2ecf20Sopenharmony_ci}
808