162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * An implementation of key value pair (KVP) functionality for Linux. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010, Novell, Inc. 662306a36Sopenharmony_ci * Author : K. Y. Srinivasan <ksrinivasan@novell.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 962306a36Sopenharmony_ci * under the terms of the GNU General Public License version 2 as published 1062306a36Sopenharmony_ci * by the Free Software Foundation. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 1362306a36Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 1462306a36Sopenharmony_ci * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 1562306a36Sopenharmony_ci * NON INFRINGEMENT. See the GNU General Public License for more 1662306a36Sopenharmony_ci * details. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License 1962306a36Sopenharmony_ci * along with this program; if not, write to the Free Software 2062306a36Sopenharmony_ci * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/net.h> 2662306a36Sopenharmony_ci#include <linux/nls.h> 2762306a36Sopenharmony_ci#include <linux/connector.h> 2862306a36Sopenharmony_ci#include <linux/workqueue.h> 2962306a36Sopenharmony_ci#include <linux/hyperv.h> 3062306a36Sopenharmony_ci#include <asm/hyperv-tlfs.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "hyperv_vmbus.h" 3362306a36Sopenharmony_ci#include "hv_utils_transport.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * Pre win8 version numbers used in ws2008 and ws 2008 r2 (win7) 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci#define WS2008_SRV_MAJOR 1 3962306a36Sopenharmony_ci#define WS2008_SRV_MINOR 0 4062306a36Sopenharmony_ci#define WS2008_SRV_VERSION (WS2008_SRV_MAJOR << 16 | WS2008_SRV_MINOR) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define WIN7_SRV_MAJOR 3 4362306a36Sopenharmony_ci#define WIN7_SRV_MINOR 0 4462306a36Sopenharmony_ci#define WIN7_SRV_VERSION (WIN7_SRV_MAJOR << 16 | WIN7_SRV_MINOR) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define WIN8_SRV_MAJOR 4 4762306a36Sopenharmony_ci#define WIN8_SRV_MINOR 0 4862306a36Sopenharmony_ci#define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define KVP_VER_COUNT 3 5162306a36Sopenharmony_cistatic const int kvp_versions[] = { 5262306a36Sopenharmony_ci WIN8_SRV_VERSION, 5362306a36Sopenharmony_ci WIN7_SRV_VERSION, 5462306a36Sopenharmony_ci WS2008_SRV_VERSION 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define FW_VER_COUNT 2 5862306a36Sopenharmony_cistatic const int fw_versions[] = { 5962306a36Sopenharmony_ci UTIL_FW_VERSION, 6062306a36Sopenharmony_ci UTIL_WS2K8_FW_VERSION 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* 6462306a36Sopenharmony_ci * Global state maintained for transaction that is being processed. For a class 6562306a36Sopenharmony_ci * of integration services, including the "KVP service", the specified protocol 6662306a36Sopenharmony_ci * is a "request/response" protocol which means that there can only be single 6762306a36Sopenharmony_ci * outstanding transaction from the host at any given point in time. We use 6862306a36Sopenharmony_ci * this to simplify memory management in this driver - we cache and process 6962306a36Sopenharmony_ci * only one message at a time. 7062306a36Sopenharmony_ci * 7162306a36Sopenharmony_ci * While the request/response protocol is guaranteed by the host, we further 7262306a36Sopenharmony_ci * ensure this by serializing packet processing in this driver - we do not 7362306a36Sopenharmony_ci * read additional packets from the VMBUS until the current packet is fully 7462306a36Sopenharmony_ci * handled. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic struct { 7862306a36Sopenharmony_ci int state; /* hvutil_device_state */ 7962306a36Sopenharmony_ci int recv_len; /* number of bytes received. */ 8062306a36Sopenharmony_ci struct hv_kvp_msg *kvp_msg; /* current message */ 8162306a36Sopenharmony_ci struct vmbus_channel *recv_channel; /* chn we got the request */ 8262306a36Sopenharmony_ci u64 recv_req_id; /* request ID. */ 8362306a36Sopenharmony_ci} kvp_transaction; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* 8662306a36Sopenharmony_ci * This state maintains the version number registered by the daemon. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_cistatic int dm_reg_value; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic void kvp_send_key(struct work_struct *dummy); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void kvp_respond_to_host(struct hv_kvp_msg *msg, int error); 9462306a36Sopenharmony_cistatic void kvp_timeout_func(struct work_struct *dummy); 9562306a36Sopenharmony_cistatic void kvp_host_handshake_func(struct work_struct *dummy); 9662306a36Sopenharmony_cistatic void kvp_register(int); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic DECLARE_DELAYED_WORK(kvp_timeout_work, kvp_timeout_func); 9962306a36Sopenharmony_cistatic DECLARE_DELAYED_WORK(kvp_host_handshake_work, kvp_host_handshake_func); 10062306a36Sopenharmony_cistatic DECLARE_WORK(kvp_sendkey_work, kvp_send_key); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic const char kvp_devname[] = "vmbus/hv_kvp"; 10362306a36Sopenharmony_cistatic u8 *recv_buffer; 10462306a36Sopenharmony_cistatic struct hvutil_transport *hvt; 10562306a36Sopenharmony_ci/* 10662306a36Sopenharmony_ci * Register the kernel component with the user-level daemon. 10762306a36Sopenharmony_ci * As part of this registration, pass the LIC version number. 10862306a36Sopenharmony_ci * This number has no meaning, it satisfies the registration protocol. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci#define HV_DRV_VERSION "3.1" 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic void kvp_poll_wrapper(void *channel) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci /* Transaction is finished, reset the state here to avoid races. */ 11562306a36Sopenharmony_ci kvp_transaction.state = HVUTIL_READY; 11662306a36Sopenharmony_ci tasklet_schedule(&((struct vmbus_channel *)channel)->callback_event); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void kvp_register_done(void) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci /* 12262306a36Sopenharmony_ci * If we're still negotiating with the host cancel the timeout 12362306a36Sopenharmony_ci * work to not poll the channel twice. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci pr_debug("KVP: userspace daemon registered\n"); 12662306a36Sopenharmony_ci cancel_delayed_work_sync(&kvp_host_handshake_work); 12762306a36Sopenharmony_ci hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void 13162306a36Sopenharmony_cikvp_register(int reg_value) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci struct hv_kvp_msg *kvp_msg; 13562306a36Sopenharmony_ci char *version; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci kvp_msg = kzalloc(sizeof(*kvp_msg), GFP_KERNEL); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (kvp_msg) { 14062306a36Sopenharmony_ci version = kvp_msg->body.kvp_register.version; 14162306a36Sopenharmony_ci kvp_msg->kvp_hdr.operation = reg_value; 14262306a36Sopenharmony_ci strcpy(version, HV_DRV_VERSION); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci hvutil_transport_send(hvt, kvp_msg, sizeof(*kvp_msg), 14562306a36Sopenharmony_ci kvp_register_done); 14662306a36Sopenharmony_ci kfree(kvp_msg); 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic void kvp_timeout_func(struct work_struct *dummy) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci /* 15362306a36Sopenharmony_ci * If the timer fires, the user-mode component has not responded; 15462306a36Sopenharmony_ci * process the pending transaction. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci kvp_respond_to_host(NULL, HV_E_FAIL); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void kvp_host_handshake_func(struct work_struct *dummy) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci tasklet_schedule(&kvp_transaction.recv_channel->callback_event); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int kvp_handle_handshake(struct hv_kvp_msg *msg) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci switch (msg->kvp_hdr.operation) { 16962306a36Sopenharmony_ci case KVP_OP_REGISTER: 17062306a36Sopenharmony_ci dm_reg_value = KVP_OP_REGISTER; 17162306a36Sopenharmony_ci pr_info("KVP: IP injection functionality not available\n"); 17262306a36Sopenharmony_ci pr_info("KVP: Upgrade the KVP daemon\n"); 17362306a36Sopenharmony_ci break; 17462306a36Sopenharmony_ci case KVP_OP_REGISTER1: 17562306a36Sopenharmony_ci dm_reg_value = KVP_OP_REGISTER1; 17662306a36Sopenharmony_ci break; 17762306a36Sopenharmony_ci default: 17862306a36Sopenharmony_ci pr_info("KVP: incompatible daemon\n"); 17962306a36Sopenharmony_ci pr_info("KVP: KVP version: %d, Daemon version: %d\n", 18062306a36Sopenharmony_ci KVP_OP_REGISTER1, msg->kvp_hdr.operation); 18162306a36Sopenharmony_ci return -EINVAL; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* 18562306a36Sopenharmony_ci * We have a compatible daemon; complete the handshake. 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ci pr_debug("KVP: userspace daemon ver. %d connected\n", 18862306a36Sopenharmony_ci msg->kvp_hdr.operation); 18962306a36Sopenharmony_ci kvp_register(dm_reg_value); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci return 0; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/* 19662306a36Sopenharmony_ci * Callback when data is received from user mode. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic int kvp_on_msg(void *msg, int len) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct hv_kvp_msg *message = (struct hv_kvp_msg *)msg; 20262306a36Sopenharmony_ci struct hv_kvp_msg_enumerate *data; 20362306a36Sopenharmony_ci int error = 0; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (len < sizeof(*message)) 20662306a36Sopenharmony_ci return -EINVAL; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* 20962306a36Sopenharmony_ci * If we are negotiating the version information 21062306a36Sopenharmony_ci * with the daemon; handle that first. 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (kvp_transaction.state < HVUTIL_READY) { 21462306a36Sopenharmony_ci return kvp_handle_handshake(message); 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* We didn't send anything to userspace so the reply is spurious */ 21862306a36Sopenharmony_ci if (kvp_transaction.state < HVUTIL_USERSPACE_REQ) 21962306a36Sopenharmony_ci return -EINVAL; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci kvp_transaction.state = HVUTIL_USERSPACE_RECV; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* 22462306a36Sopenharmony_ci * Based on the version of the daemon, we propagate errors from the 22562306a36Sopenharmony_ci * daemon differently. 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci data = &message->body.kvp_enum_data; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci switch (dm_reg_value) { 23162306a36Sopenharmony_ci case KVP_OP_REGISTER: 23262306a36Sopenharmony_ci /* 23362306a36Sopenharmony_ci * Null string is used to pass back error condition. 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_ci if (data->data.key[0] == 0) 23662306a36Sopenharmony_ci error = HV_S_CONT; 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci case KVP_OP_REGISTER1: 24062306a36Sopenharmony_ci /* 24162306a36Sopenharmony_ci * We use the message header information from 24262306a36Sopenharmony_ci * the user level daemon to transmit errors. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci error = message->error; 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* 24962306a36Sopenharmony_ci * Complete the transaction by forwarding the key value 25062306a36Sopenharmony_ci * to the host. But first, cancel the timeout. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ci if (cancel_delayed_work_sync(&kvp_timeout_work)) { 25362306a36Sopenharmony_ci kvp_respond_to_host(message, error); 25462306a36Sopenharmony_ci hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic int process_ob_ipinfo(void *in_msg, void *out_msg, int op) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct hv_kvp_msg *in = in_msg; 26462306a36Sopenharmony_ci struct hv_kvp_ip_msg *out = out_msg; 26562306a36Sopenharmony_ci int len; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci switch (op) { 26862306a36Sopenharmony_ci case KVP_OP_GET_IP_INFO: 26962306a36Sopenharmony_ci /* 27062306a36Sopenharmony_ci * Transform all parameters into utf16 encoding. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.ip_addr, 27362306a36Sopenharmony_ci strlen((char *)in->body.kvp_ip_val.ip_addr), 27462306a36Sopenharmony_ci UTF16_HOST_ENDIAN, 27562306a36Sopenharmony_ci (wchar_t *)out->kvp_ip_val.ip_addr, 27662306a36Sopenharmony_ci MAX_IP_ADDR_SIZE); 27762306a36Sopenharmony_ci if (len < 0) 27862306a36Sopenharmony_ci return len; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.sub_net, 28162306a36Sopenharmony_ci strlen((char *)in->body.kvp_ip_val.sub_net), 28262306a36Sopenharmony_ci UTF16_HOST_ENDIAN, 28362306a36Sopenharmony_ci (wchar_t *)out->kvp_ip_val.sub_net, 28462306a36Sopenharmony_ci MAX_IP_ADDR_SIZE); 28562306a36Sopenharmony_ci if (len < 0) 28662306a36Sopenharmony_ci return len; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.gate_way, 28962306a36Sopenharmony_ci strlen((char *)in->body.kvp_ip_val.gate_way), 29062306a36Sopenharmony_ci UTF16_HOST_ENDIAN, 29162306a36Sopenharmony_ci (wchar_t *)out->kvp_ip_val.gate_way, 29262306a36Sopenharmony_ci MAX_GATEWAY_SIZE); 29362306a36Sopenharmony_ci if (len < 0) 29462306a36Sopenharmony_ci return len; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.dns_addr, 29762306a36Sopenharmony_ci strlen((char *)in->body.kvp_ip_val.dns_addr), 29862306a36Sopenharmony_ci UTF16_HOST_ENDIAN, 29962306a36Sopenharmony_ci (wchar_t *)out->kvp_ip_val.dns_addr, 30062306a36Sopenharmony_ci MAX_IP_ADDR_SIZE); 30162306a36Sopenharmony_ci if (len < 0) 30262306a36Sopenharmony_ci return len; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.adapter_id, 30562306a36Sopenharmony_ci strlen((char *)in->body.kvp_ip_val.adapter_id), 30662306a36Sopenharmony_ci UTF16_HOST_ENDIAN, 30762306a36Sopenharmony_ci (wchar_t *)out->kvp_ip_val.adapter_id, 30862306a36Sopenharmony_ci MAX_ADAPTER_ID_SIZE); 30962306a36Sopenharmony_ci if (len < 0) 31062306a36Sopenharmony_ci return len; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci out->kvp_ip_val.dhcp_enabled = 31362306a36Sopenharmony_ci in->body.kvp_ip_val.dhcp_enabled; 31462306a36Sopenharmony_ci out->kvp_ip_val.addr_family = 31562306a36Sopenharmony_ci in->body.kvp_ip_val.addr_family; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return 0; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic void process_ib_ipinfo(void *in_msg, void *out_msg, int op) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct hv_kvp_ip_msg *in = in_msg; 32462306a36Sopenharmony_ci struct hv_kvp_msg *out = out_msg; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci switch (op) { 32762306a36Sopenharmony_ci case KVP_OP_SET_IP_INFO: 32862306a36Sopenharmony_ci /* 32962306a36Sopenharmony_ci * Transform all parameters into utf8 encoding. 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_ci utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.ip_addr, 33262306a36Sopenharmony_ci MAX_IP_ADDR_SIZE, 33362306a36Sopenharmony_ci UTF16_LITTLE_ENDIAN, 33462306a36Sopenharmony_ci (__u8 *)out->body.kvp_ip_val.ip_addr, 33562306a36Sopenharmony_ci MAX_IP_ADDR_SIZE); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.sub_net, 33862306a36Sopenharmony_ci MAX_IP_ADDR_SIZE, 33962306a36Sopenharmony_ci UTF16_LITTLE_ENDIAN, 34062306a36Sopenharmony_ci (__u8 *)out->body.kvp_ip_val.sub_net, 34162306a36Sopenharmony_ci MAX_IP_ADDR_SIZE); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.gate_way, 34462306a36Sopenharmony_ci MAX_GATEWAY_SIZE, 34562306a36Sopenharmony_ci UTF16_LITTLE_ENDIAN, 34662306a36Sopenharmony_ci (__u8 *)out->body.kvp_ip_val.gate_way, 34762306a36Sopenharmony_ci MAX_GATEWAY_SIZE); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.dns_addr, 35062306a36Sopenharmony_ci MAX_IP_ADDR_SIZE, 35162306a36Sopenharmony_ci UTF16_LITTLE_ENDIAN, 35262306a36Sopenharmony_ci (__u8 *)out->body.kvp_ip_val.dns_addr, 35362306a36Sopenharmony_ci MAX_IP_ADDR_SIZE); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci out->body.kvp_ip_val.dhcp_enabled = in->kvp_ip_val.dhcp_enabled; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci fallthrough; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci case KVP_OP_GET_IP_INFO: 36062306a36Sopenharmony_ci utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.adapter_id, 36162306a36Sopenharmony_ci MAX_ADAPTER_ID_SIZE, 36262306a36Sopenharmony_ci UTF16_LITTLE_ENDIAN, 36362306a36Sopenharmony_ci (__u8 *)out->body.kvp_ip_val.adapter_id, 36462306a36Sopenharmony_ci MAX_ADAPTER_ID_SIZE); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci out->body.kvp_ip_val.addr_family = in->kvp_ip_val.addr_family; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic void 37462306a36Sopenharmony_cikvp_send_key(struct work_struct *dummy) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct hv_kvp_msg *message; 37762306a36Sopenharmony_ci struct hv_kvp_msg *in_msg; 37862306a36Sopenharmony_ci __u8 operation = kvp_transaction.kvp_msg->kvp_hdr.operation; 37962306a36Sopenharmony_ci __u8 pool = kvp_transaction.kvp_msg->kvp_hdr.pool; 38062306a36Sopenharmony_ci __u32 val32; 38162306a36Sopenharmony_ci __u64 val64; 38262306a36Sopenharmony_ci int rc; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* The transaction state is wrong. */ 38562306a36Sopenharmony_ci if (kvp_transaction.state != HVUTIL_HOSTMSG_RECEIVED) 38662306a36Sopenharmony_ci return; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci message = kzalloc(sizeof(*message), GFP_KERNEL); 38962306a36Sopenharmony_ci if (!message) 39062306a36Sopenharmony_ci return; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci message->kvp_hdr.operation = operation; 39362306a36Sopenharmony_ci message->kvp_hdr.pool = pool; 39462306a36Sopenharmony_ci in_msg = kvp_transaction.kvp_msg; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* 39762306a36Sopenharmony_ci * The key/value strings sent from the host are encoded 39862306a36Sopenharmony_ci * in utf16; convert it to utf8 strings. 39962306a36Sopenharmony_ci * The host assures us that the utf16 strings will not exceed 40062306a36Sopenharmony_ci * the max lengths specified. We will however, reserve room 40162306a36Sopenharmony_ci * for the string terminating character - in the utf16s_utf8s() 40262306a36Sopenharmony_ci * function we limit the size of the buffer where the converted 40362306a36Sopenharmony_ci * string is placed to HV_KVP_EXCHANGE_MAX_*_SIZE -1 to guarantee 40462306a36Sopenharmony_ci * that the strings can be properly terminated! 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci switch (message->kvp_hdr.operation) { 40862306a36Sopenharmony_ci case KVP_OP_SET_IP_INFO: 40962306a36Sopenharmony_ci process_ib_ipinfo(in_msg, message, KVP_OP_SET_IP_INFO); 41062306a36Sopenharmony_ci break; 41162306a36Sopenharmony_ci case KVP_OP_GET_IP_INFO: 41262306a36Sopenharmony_ci /* 41362306a36Sopenharmony_ci * We only need to pass on the info of operation, adapter_id 41462306a36Sopenharmony_ci * and addr_family to the userland kvp daemon. 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci process_ib_ipinfo(in_msg, message, KVP_OP_GET_IP_INFO); 41762306a36Sopenharmony_ci break; 41862306a36Sopenharmony_ci case KVP_OP_SET: 41962306a36Sopenharmony_ci switch (in_msg->body.kvp_set.data.value_type) { 42062306a36Sopenharmony_ci case REG_SZ: 42162306a36Sopenharmony_ci /* 42262306a36Sopenharmony_ci * The value is a string - utf16 encoding. 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ci message->body.kvp_set.data.value_size = 42562306a36Sopenharmony_ci utf16s_to_utf8s( 42662306a36Sopenharmony_ci (wchar_t *)in_msg->body.kvp_set.data.value, 42762306a36Sopenharmony_ci in_msg->body.kvp_set.data.value_size, 42862306a36Sopenharmony_ci UTF16_LITTLE_ENDIAN, 42962306a36Sopenharmony_ci message->body.kvp_set.data.value, 43062306a36Sopenharmony_ci HV_KVP_EXCHANGE_MAX_VALUE_SIZE - 1) + 1; 43162306a36Sopenharmony_ci break; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci case REG_U32: 43462306a36Sopenharmony_ci /* 43562306a36Sopenharmony_ci * The value is a 32 bit scalar. 43662306a36Sopenharmony_ci * We save this as a utf8 string. 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci val32 = in_msg->body.kvp_set.data.value_u32; 43962306a36Sopenharmony_ci message->body.kvp_set.data.value_size = 44062306a36Sopenharmony_ci sprintf(message->body.kvp_set.data.value, 44162306a36Sopenharmony_ci "%u", val32) + 1; 44262306a36Sopenharmony_ci break; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci case REG_U64: 44562306a36Sopenharmony_ci /* 44662306a36Sopenharmony_ci * The value is a 64 bit scalar. 44762306a36Sopenharmony_ci * We save this as a utf8 string. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_ci val64 = in_msg->body.kvp_set.data.value_u64; 45062306a36Sopenharmony_ci message->body.kvp_set.data.value_size = 45162306a36Sopenharmony_ci sprintf(message->body.kvp_set.data.value, 45262306a36Sopenharmony_ci "%llu", val64) + 1; 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* 45862306a36Sopenharmony_ci * The key is always a string - utf16 encoding. 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ci message->body.kvp_set.data.key_size = 46162306a36Sopenharmony_ci utf16s_to_utf8s( 46262306a36Sopenharmony_ci (wchar_t *)in_msg->body.kvp_set.data.key, 46362306a36Sopenharmony_ci in_msg->body.kvp_set.data.key_size, 46462306a36Sopenharmony_ci UTF16_LITTLE_ENDIAN, 46562306a36Sopenharmony_ci message->body.kvp_set.data.key, 46662306a36Sopenharmony_ci HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci break; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci case KVP_OP_GET: 47162306a36Sopenharmony_ci message->body.kvp_get.data.key_size = 47262306a36Sopenharmony_ci utf16s_to_utf8s( 47362306a36Sopenharmony_ci (wchar_t *)in_msg->body.kvp_get.data.key, 47462306a36Sopenharmony_ci in_msg->body.kvp_get.data.key_size, 47562306a36Sopenharmony_ci UTF16_LITTLE_ENDIAN, 47662306a36Sopenharmony_ci message->body.kvp_get.data.key, 47762306a36Sopenharmony_ci HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1; 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci case KVP_OP_DELETE: 48162306a36Sopenharmony_ci message->body.kvp_delete.key_size = 48262306a36Sopenharmony_ci utf16s_to_utf8s( 48362306a36Sopenharmony_ci (wchar_t *)in_msg->body.kvp_delete.key, 48462306a36Sopenharmony_ci in_msg->body.kvp_delete.key_size, 48562306a36Sopenharmony_ci UTF16_LITTLE_ENDIAN, 48662306a36Sopenharmony_ci message->body.kvp_delete.key, 48762306a36Sopenharmony_ci HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1; 48862306a36Sopenharmony_ci break; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci case KVP_OP_ENUMERATE: 49162306a36Sopenharmony_ci message->body.kvp_enum_data.index = 49262306a36Sopenharmony_ci in_msg->body.kvp_enum_data.index; 49362306a36Sopenharmony_ci break; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci kvp_transaction.state = HVUTIL_USERSPACE_REQ; 49762306a36Sopenharmony_ci rc = hvutil_transport_send(hvt, message, sizeof(*message), NULL); 49862306a36Sopenharmony_ci if (rc) { 49962306a36Sopenharmony_ci pr_debug("KVP: failed to communicate to the daemon: %d\n", rc); 50062306a36Sopenharmony_ci if (cancel_delayed_work_sync(&kvp_timeout_work)) { 50162306a36Sopenharmony_ci kvp_respond_to_host(message, HV_E_FAIL); 50262306a36Sopenharmony_ci kvp_transaction.state = HVUTIL_READY; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci kfree(message); 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci/* 51062306a36Sopenharmony_ci * Send a response back to the host. 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic void 51462306a36Sopenharmony_cikvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct hv_kvp_msg *kvp_msg; 51762306a36Sopenharmony_ci struct hv_kvp_exchg_msg_value *kvp_data; 51862306a36Sopenharmony_ci char *key_name; 51962306a36Sopenharmony_ci char *value; 52062306a36Sopenharmony_ci struct icmsg_hdr *icmsghdrp; 52162306a36Sopenharmony_ci int keylen = 0; 52262306a36Sopenharmony_ci int valuelen = 0; 52362306a36Sopenharmony_ci u32 buf_len; 52462306a36Sopenharmony_ci struct vmbus_channel *channel; 52562306a36Sopenharmony_ci u64 req_id; 52662306a36Sopenharmony_ci int ret; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* 52962306a36Sopenharmony_ci * Copy the global state for completing the transaction. Note that 53062306a36Sopenharmony_ci * only one transaction can be active at a time. 53162306a36Sopenharmony_ci */ 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci buf_len = kvp_transaction.recv_len; 53462306a36Sopenharmony_ci channel = kvp_transaction.recv_channel; 53562306a36Sopenharmony_ci req_id = kvp_transaction.recv_req_id; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci icmsghdrp = (struct icmsg_hdr *) 53862306a36Sopenharmony_ci &recv_buffer[sizeof(struct vmbuspipe_hdr)]; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (channel->onchannel_callback == NULL) 54162306a36Sopenharmony_ci /* 54262306a36Sopenharmony_ci * We have raced with util driver being unloaded; 54362306a36Sopenharmony_ci * silently return. 54462306a36Sopenharmony_ci */ 54562306a36Sopenharmony_ci return; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci icmsghdrp->status = error; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* 55062306a36Sopenharmony_ci * If the error parameter is set, terminate the host's enumeration 55162306a36Sopenharmony_ci * on this pool. 55262306a36Sopenharmony_ci */ 55362306a36Sopenharmony_ci if (error) { 55462306a36Sopenharmony_ci /* 55562306a36Sopenharmony_ci * Something failed or we have timed out; 55662306a36Sopenharmony_ci * terminate the current host-side iteration. 55762306a36Sopenharmony_ci */ 55862306a36Sopenharmony_ci goto response_done; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci kvp_msg = (struct hv_kvp_msg *) 56262306a36Sopenharmony_ci &recv_buffer[sizeof(struct vmbuspipe_hdr) + 56362306a36Sopenharmony_ci sizeof(struct icmsg_hdr)]; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci switch (kvp_transaction.kvp_msg->kvp_hdr.operation) { 56662306a36Sopenharmony_ci case KVP_OP_GET_IP_INFO: 56762306a36Sopenharmony_ci ret = process_ob_ipinfo(msg_to_host, 56862306a36Sopenharmony_ci (struct hv_kvp_ip_msg *)kvp_msg, 56962306a36Sopenharmony_ci KVP_OP_GET_IP_INFO); 57062306a36Sopenharmony_ci if (ret < 0) 57162306a36Sopenharmony_ci icmsghdrp->status = HV_E_FAIL; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci goto response_done; 57462306a36Sopenharmony_ci case KVP_OP_SET_IP_INFO: 57562306a36Sopenharmony_ci goto response_done; 57662306a36Sopenharmony_ci case KVP_OP_GET: 57762306a36Sopenharmony_ci kvp_data = &kvp_msg->body.kvp_get.data; 57862306a36Sopenharmony_ci goto copy_value; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci case KVP_OP_SET: 58162306a36Sopenharmony_ci case KVP_OP_DELETE: 58262306a36Sopenharmony_ci goto response_done; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci default: 58562306a36Sopenharmony_ci break; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci kvp_data = &kvp_msg->body.kvp_enum_data.data; 58962306a36Sopenharmony_ci key_name = msg_to_host->body.kvp_enum_data.data.key; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* 59262306a36Sopenharmony_ci * The windows host expects the key/value pair to be encoded 59362306a36Sopenharmony_ci * in utf16. Ensure that the key/value size reported to the host 59462306a36Sopenharmony_ci * will be less than or equal to the MAX size (including the 59562306a36Sopenharmony_ci * terminating character). 59662306a36Sopenharmony_ci */ 59762306a36Sopenharmony_ci keylen = utf8s_to_utf16s(key_name, strlen(key_name), UTF16_HOST_ENDIAN, 59862306a36Sopenharmony_ci (wchar_t *) kvp_data->key, 59962306a36Sopenharmony_ci (HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2) - 2); 60062306a36Sopenharmony_ci kvp_data->key_size = 2*(keylen + 1); /* utf16 encoding */ 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cicopy_value: 60362306a36Sopenharmony_ci value = msg_to_host->body.kvp_enum_data.data.value; 60462306a36Sopenharmony_ci valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN, 60562306a36Sopenharmony_ci (wchar_t *) kvp_data->value, 60662306a36Sopenharmony_ci (HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2); 60762306a36Sopenharmony_ci kvp_data->value_size = 2*(valuelen + 1); /* utf16 encoding */ 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* 61062306a36Sopenharmony_ci * If the utf8s to utf16s conversion failed; notify host 61162306a36Sopenharmony_ci * of the error. 61262306a36Sopenharmony_ci */ 61362306a36Sopenharmony_ci if ((keylen < 0) || (valuelen < 0)) 61462306a36Sopenharmony_ci icmsghdrp->status = HV_E_FAIL; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci kvp_data->value_type = REG_SZ; /* all our values are strings */ 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ciresponse_done: 61962306a36Sopenharmony_ci icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci vmbus_sendpacket(channel, recv_buffer, buf_len, req_id, 62262306a36Sopenharmony_ci VM_PKT_DATA_INBAND, 0); 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci/* 62662306a36Sopenharmony_ci * This callback is invoked when we get a KVP message from the host. 62762306a36Sopenharmony_ci * The host ensures that only one KVP transaction can be active at a time. 62862306a36Sopenharmony_ci * KVP implementation in Linux needs to forward the key to a user-mde 62962306a36Sopenharmony_ci * component to retrieve the corresponding value. Consequently, we cannot 63062306a36Sopenharmony_ci * respond to the host in the context of this callback. Since the host 63162306a36Sopenharmony_ci * guarantees that at most only one transaction can be active at a time, 63262306a36Sopenharmony_ci * we stash away the transaction state in a set of global variables. 63362306a36Sopenharmony_ci */ 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_civoid hv_kvp_onchannelcallback(void *context) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci struct vmbus_channel *channel = context; 63862306a36Sopenharmony_ci u32 recvlen; 63962306a36Sopenharmony_ci u64 requestid; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci struct hv_kvp_msg *kvp_msg; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci struct icmsg_hdr *icmsghdrp; 64462306a36Sopenharmony_ci int kvp_srv_version; 64562306a36Sopenharmony_ci static enum {NEGO_NOT_STARTED, 64662306a36Sopenharmony_ci NEGO_IN_PROGRESS, 64762306a36Sopenharmony_ci NEGO_FINISHED} host_negotiatied = NEGO_NOT_STARTED; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (kvp_transaction.state < HVUTIL_READY) { 65062306a36Sopenharmony_ci /* 65162306a36Sopenharmony_ci * If userspace daemon is not connected and host is asking 65262306a36Sopenharmony_ci * us to negotiate we need to delay to not lose messages. 65362306a36Sopenharmony_ci * This is important for Failover IP setting. 65462306a36Sopenharmony_ci */ 65562306a36Sopenharmony_ci if (host_negotiatied == NEGO_NOT_STARTED) { 65662306a36Sopenharmony_ci host_negotiatied = NEGO_IN_PROGRESS; 65762306a36Sopenharmony_ci schedule_delayed_work(&kvp_host_handshake_work, 65862306a36Sopenharmony_ci HV_UTIL_NEGO_TIMEOUT * HZ); 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci return; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci if (kvp_transaction.state > HVUTIL_READY) 66362306a36Sopenharmony_ci return; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 4, &recvlen, &requestid)) { 66662306a36Sopenharmony_ci pr_err_ratelimited("KVP request received. Could not read into recv buf\n"); 66762306a36Sopenharmony_ci return; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (!recvlen) 67162306a36Sopenharmony_ci return; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci /* Ensure recvlen is big enough to read header data */ 67462306a36Sopenharmony_ci if (recvlen < ICMSG_HDR) { 67562306a36Sopenharmony_ci pr_err_ratelimited("KVP request received. Packet length too small: %d\n", 67662306a36Sopenharmony_ci recvlen); 67762306a36Sopenharmony_ci return; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci icmsghdrp = (struct icmsg_hdr *)&recv_buffer[sizeof(struct vmbuspipe_hdr)]; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 68362306a36Sopenharmony_ci if (vmbus_prep_negotiate_resp(icmsghdrp, 68462306a36Sopenharmony_ci recv_buffer, recvlen, 68562306a36Sopenharmony_ci fw_versions, FW_VER_COUNT, 68662306a36Sopenharmony_ci kvp_versions, KVP_VER_COUNT, 68762306a36Sopenharmony_ci NULL, &kvp_srv_version)) { 68862306a36Sopenharmony_ci pr_info("KVP IC version %d.%d\n", 68962306a36Sopenharmony_ci kvp_srv_version >> 16, 69062306a36Sopenharmony_ci kvp_srv_version & 0xFFFF); 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci } else if (icmsghdrp->icmsgtype == ICMSGTYPE_KVPEXCHANGE) { 69362306a36Sopenharmony_ci /* 69462306a36Sopenharmony_ci * recvlen is not checked against sizeof(struct kvp_msg) because kvp_msg contains 69562306a36Sopenharmony_ci * a union of structs and the msg type received is not known. Code using this 69662306a36Sopenharmony_ci * struct should provide validation when accessing its fields. 69762306a36Sopenharmony_ci */ 69862306a36Sopenharmony_ci kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ICMSG_HDR]; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci /* 70162306a36Sopenharmony_ci * Stash away this global state for completing the 70262306a36Sopenharmony_ci * transaction; note transactions are serialized. 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci kvp_transaction.recv_len = recvlen; 70662306a36Sopenharmony_ci kvp_transaction.recv_req_id = requestid; 70762306a36Sopenharmony_ci kvp_transaction.kvp_msg = kvp_msg; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (kvp_transaction.state < HVUTIL_READY) { 71062306a36Sopenharmony_ci /* Userspace is not registered yet */ 71162306a36Sopenharmony_ci kvp_respond_to_host(NULL, HV_E_FAIL); 71262306a36Sopenharmony_ci return; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci kvp_transaction.state = HVUTIL_HOSTMSG_RECEIVED; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* 71762306a36Sopenharmony_ci * Get the information from the 71862306a36Sopenharmony_ci * user-mode component. 71962306a36Sopenharmony_ci * component. This transaction will be 72062306a36Sopenharmony_ci * completed when we get the value from 72162306a36Sopenharmony_ci * the user-mode component. 72262306a36Sopenharmony_ci * Set a timeout to deal with 72362306a36Sopenharmony_ci * user-mode not responding. 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_ci schedule_work(&kvp_sendkey_work); 72662306a36Sopenharmony_ci schedule_delayed_work(&kvp_timeout_work, 72762306a36Sopenharmony_ci HV_UTIL_TIMEOUT * HZ); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci return; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci } else { 73262306a36Sopenharmony_ci pr_err_ratelimited("KVP request received. Invalid msg type: %d\n", 73362306a36Sopenharmony_ci icmsghdrp->icmsgtype); 73462306a36Sopenharmony_ci return; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION 73862306a36Sopenharmony_ci | ICMSGHDRFLAG_RESPONSE; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci vmbus_sendpacket(channel, recv_buffer, 74162306a36Sopenharmony_ci recvlen, requestid, 74262306a36Sopenharmony_ci VM_PKT_DATA_INBAND, 0); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci host_negotiatied = NEGO_FINISHED; 74562306a36Sopenharmony_ci hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper); 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic void kvp_on_reset(void) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci if (cancel_delayed_work_sync(&kvp_timeout_work)) 75162306a36Sopenharmony_ci kvp_respond_to_host(NULL, HV_E_FAIL); 75262306a36Sopenharmony_ci kvp_transaction.state = HVUTIL_DEVICE_INIT; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ciint 75662306a36Sopenharmony_cihv_kvp_init(struct hv_util_service *srv) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci recv_buffer = srv->recv_buffer; 75962306a36Sopenharmony_ci kvp_transaction.recv_channel = srv->channel; 76062306a36Sopenharmony_ci kvp_transaction.recv_channel->max_pkt_size = HV_HYP_PAGE_SIZE * 4; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* 76362306a36Sopenharmony_ci * When this driver loads, the user level daemon that 76462306a36Sopenharmony_ci * processes the host requests may not yet be running. 76562306a36Sopenharmony_ci * Defer processing channel callbacks until the daemon 76662306a36Sopenharmony_ci * has registered. 76762306a36Sopenharmony_ci */ 76862306a36Sopenharmony_ci kvp_transaction.state = HVUTIL_DEVICE_INIT; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci hvt = hvutil_transport_init(kvp_devname, CN_KVP_IDX, CN_KVP_VAL, 77162306a36Sopenharmony_ci kvp_on_msg, kvp_on_reset); 77262306a36Sopenharmony_ci if (!hvt) 77362306a36Sopenharmony_ci return -EFAULT; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci return 0; 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic void hv_kvp_cancel_work(void) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci cancel_delayed_work_sync(&kvp_host_handshake_work); 78162306a36Sopenharmony_ci cancel_delayed_work_sync(&kvp_timeout_work); 78262306a36Sopenharmony_ci cancel_work_sync(&kvp_sendkey_work); 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ciint hv_kvp_pre_suspend(void) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci struct vmbus_channel *channel = kvp_transaction.recv_channel; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci tasklet_disable(&channel->callback_event); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* 79262306a36Sopenharmony_ci * If there is a pending transtion, it's unnecessary to tell the host 79362306a36Sopenharmony_ci * that the transaction will fail, because that is implied when 79462306a36Sopenharmony_ci * util_suspend() calls vmbus_close() later. 79562306a36Sopenharmony_ci */ 79662306a36Sopenharmony_ci hv_kvp_cancel_work(); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci /* 79962306a36Sopenharmony_ci * Forece the state to READY to handle the ICMSGTYPE_NEGOTIATE message 80062306a36Sopenharmony_ci * later. The user space daemon may go out of order and its write() 80162306a36Sopenharmony_ci * may fail with EINVAL: this doesn't matter since the daemon will 80262306a36Sopenharmony_ci * reset the device by closing and re-opening it. 80362306a36Sopenharmony_ci */ 80462306a36Sopenharmony_ci kvp_transaction.state = HVUTIL_READY; 80562306a36Sopenharmony_ci return 0; 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ciint hv_kvp_pre_resume(void) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci struct vmbus_channel *channel = kvp_transaction.recv_channel; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci tasklet_enable(&channel->callback_event); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci return 0; 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_civoid hv_kvp_deinit(void) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci kvp_transaction.state = HVUTIL_DEVICE_DYING; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci hv_kvp_cancel_work(); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci hvutil_transport_destroy(hvt); 82462306a36Sopenharmony_ci} 825