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