Date: Mon, 9 Sep 2013 08:07:46 +0000 (UTC) From: Peter Grehan <grehan@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r255414 - in projects/hyperv/sys: conf dev/hyperv dev/hyperv/utilities dev/hyperv/vmbus modules/hyperv/netvsc modules/hyperv/utilities Message-ID: <201309090807.r8987kMm053821@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: grehan Date: Mon Sep 9 08:07:46 2013 New Revision: 255414 URL: http://svnweb.freebsd.org/changeset/base/255414 Log: Latest update from Microsoft. Obtained from: Microsoft Hyper-v dev team Added: projects/hyperv/sys/dev/hyperv/utilities/hv_kvp.c (contents, props changed) projects/hyperv/sys/dev/hyperv/utilities/hv_kvp.h (contents, props changed) Deleted: projects/hyperv/sys/dev/hyperv/README Modified: projects/hyperv/sys/conf/files.amd64 projects/hyperv/sys/dev/hyperv/utilities/hv_util.c projects/hyperv/sys/dev/hyperv/vmbus/hv_hv.c projects/hyperv/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c projects/hyperv/sys/dev/hyperv/vmbus/hv_vmbus_priv.h projects/hyperv/sys/modules/hyperv/netvsc/Makefile projects/hyperv/sys/modules/hyperv/utilities/Makefile Modified: projects/hyperv/sys/conf/files.amd64 ============================================================================== --- projects/hyperv/sys/conf/files.amd64 Mon Sep 9 06:02:30 2013 (r255413) +++ projects/hyperv/sys/conf/files.amd64 Mon Sep 9 08:07:46 2013 (r255414) @@ -226,6 +226,7 @@ dev/hyperv/netvsc/hv_netvsc_drv_freebsd. dev/hyperv/netvsc/hv_rndis_filter.c optional hyperv dev/hyperv/stordisengage/hv_ata_pci_disengage.c optional hyperv dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c optional hyperv +dev/hyperv/utilities/hv_kvp.c optional hyperv dev/hyperv/utilities/hv_util.c optional hyperv dev/hyperv/vmbus/hv_channel.c optional hyperv dev/hyperv/vmbus/hv_channel_mgmt.c optional hyperv Added: projects/hyperv/sys/dev/hyperv/utilities/hv_kvp.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ projects/hyperv/sys/dev/hyperv/utilities/hv_kvp.c Mon Sep 9 08:07:46 2013 (r255414) @@ -0,0 +1,982 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + /* An implementation of key value pair (KVP) functionality for FreeBSD */ + +/** + * Code for handling all KVP related messages + */ +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/reboot.h> +#include <sys/types.h> +#include <sys/sysctl.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/kthread.h> +#include <sys/socket.h> +#include <sys/syscallsubr.h> +#include <sys/sysproto.h> +#include <sys/un.h> +#include <sys/endian.h> + +#include <net/if_arp.h> + +#include <dev/hyperv/include/hyperv.h> +#include <dev/hyperv/netvsc/hv_net_vsc.h> +#include "hv_kvp.h" + +/* Unicode Conversions */ +#include <sys/cdefs.h> +#include <sys/_null.h> + +static int hv_kvp_daemon_ack = 0; +SYSCTL_INT(_dev, OID_AUTO, hv_kvp_daemon_ack, CTLFLAG_RW, &hv_kvp_daemon_ack, + 0, "First ack when daemon is ready"); + +static size_t convert8_to_16(uint16_t *, size_t, const char *, size_t, int *); +static size_t convert16_to_8(char *, size_t, const uint16_t *, size_t, int *); + +/* Unicode declarations ends */ +#define KVP_SUCCESS 0 +#define kvp_hdr hdr.kvp_hdr +typedef struct hv_kvp_msg hv_kvp_bsd_msg; + +static int hv_kvp_ready(void); +static int hv_kvp_transaction_active(void); +static void hv_kvp_transaction_init(uint32_t, hv_vmbus_channel *, uint64_t, + uint8_t *); +static void hv_kvp_conn_register(void); +static void hv_kvp_process_msg(void *p); + +/* + * We maintain a global state, assuming only one transaction can be active + * at any point in time. + * Inited by the kvp callback routine (utils file) when a valid message is + * received from the host; + */ +static struct { + boolean_t kvp_ready; /* indicates if kvp module is ready or not */ + boolean_t in_progress; /* transaction status - active or not */ + uint32_t host_msg_len; /* length of host message */ + hv_vmbus_channel *channelp; /* pointer to channel */ + uint64_t host_msg_id; /* host message id */ + hv_kvp_bsd_msg *host_kvp_msg; /* current message from the host */ + uint8_t *rcv_buf; /* rcv buffer for communicating with the host*/ +} kvp_msg_state; + + +/* We use an alternative, more convenient representation in the generator. */ + +/* + * Data Buffer used by kernel for to/from communication with user daemon + */ +static hv_kvp_bsd_msg hv_user_kvp_msg; + +static boolean_t conn_ready; /* indicates if connection to daemon done */ +static boolean_t register_done; /* indicates daemon registered with driver */ + +/* Original socket created during connection establishment */ +static int sock_fd; + +/* Handle to KVP device */ +struct hv_device* kvp_hv_dev; + +/* + * Check if kvp routines are ready to receive and respond + */ +static int +hv_kvp_ready(void) +{ + + return (kvp_msg_state.kvp_ready); +} + +/* + * Check if kvp transaction is in progres + */ +static int +hv_kvp_transaction_active(void) +{ + + return (kvp_msg_state.in_progress); +} + + +/* + * This routine is called whenever a message is received from the host + */ +static void +hv_kvp_transaction_init(uint32_t rcv_len, hv_vmbus_channel *rcv_channel, + uint64_t request_id, uint8_t *rcv_buf) +{ + + /* + * Store all the relevant message details in the global structure + */ + kvp_msg_state.in_progress = TRUE; + kvp_msg_state.host_msg_len = rcv_len; + kvp_msg_state.channelp = rcv_channel; + kvp_msg_state.host_msg_id = request_id; + kvp_msg_state.rcv_buf = rcv_buf; + kvp_msg_state.host_kvp_msg = (hv_kvp_bsd_msg *)&rcv_buf[ + sizeof(struct hv_vmbus_pipe_hdr) + + sizeof(struct hv_vmbus_icmsg_hdr)]; +} + +static void +hv_kvp_negotiate_version(struct hv_vmbus_icmsg_hdr *icmsghdrp, + struct hv_vmbus_icmsg_negotiate *negop, uint8_t *buf) +{ + int icframe_vercnt; + int icmsg_vercnt; + + if (bootverbose) + printf("hv_kvp_negotiate_version\n"); + + icmsghdrp->icmsgsize = 0x10; + + negop = (struct hv_vmbus_icmsg_negotiate *) &buf[ + sizeof(struct hv_vmbus_pipe_hdr) + + sizeof(struct hv_vmbus_icmsg_hdr)]; + icframe_vercnt = negop->icframe_vercnt; + icmsg_vercnt = negop->icmsg_vercnt; + + /* + * Select the framework version number we will + * support. + */ + if ((icframe_vercnt >= 2) && (negop->icversion_data[1].major == 3)) { + icframe_vercnt = 3; + if (icmsg_vercnt >=2) { + icmsg_vercnt = 4; + } else { + icmsg_vercnt = 3; + } + } else { + icframe_vercnt = 1; + icmsg_vercnt = 1; + } + + /* + * Respond with the maximum framework and service + * version numbers we can support. + */ + negop->icframe_vercnt = 1; + negop->icmsg_vercnt = 1; + negop->icversion_data[0].major = icframe_vercnt; + negop->icversion_data[0].minor = 0; + negop->icversion_data[1].major = icmsg_vercnt; + negop->icversion_data[1].minor = 0; +} + +/* + * Establish a UNIX socket connection with user daemon + */ +static int +kvp_connect_user(void) +{ + int sock_error; + struct socket_args unix_sock; + struct sockaddr_un sock_sun; + struct thread *thread_ptr; + + thread_ptr = curthread; + + /* Open a Unix Domain socket */ + unix_sock.domain = AF_UNIX; + unix_sock.type = SOCK_STREAM; + unix_sock.protocol = 0; + sock_error = sys_socket(thread_ptr, &unix_sock); + if (sock_error) { + return sock_error; + } + + /* Try to connect to user daemon using Unix socket */ + sock_fd = thread_ptr->td_retval[0]; + sock_sun.sun_family = AF_UNIX; + strcpy(sock_sun.sun_path, BSD_SOC_PATH); + sock_sun.sun_len = sizeof(struct sockaddr_un) - + sizeof(sock_sun.sun_path) + strlen(sock_sun.sun_path) + 1; + + sock_error = kern_connect(thread_ptr, sock_fd, + (struct sockaddr *) &sock_sun); + if (sock_error) { + kern_close(thread_ptr, sock_fd); + } + + return (sock_error); +} + + +/* + * Send kvp msg on the established unix socket connection to the user + */ +static int +kvp_send_user(void) +{ + int send_fd, send_error; + struct uio send_uio; + struct iovec send_iovec; + struct thread *thread_ptr = curthread; + + if (!hv_kvp_ready()) { + hv_kvp_conn_register(); + } + + send_fd = sock_fd; + memset(&send_uio, 0, sizeof(struct uio)); + + send_iovec.iov_base = (void *)&hv_user_kvp_msg; + send_iovec.iov_len = sizeof(hv_kvp_bsd_msg); + send_uio.uio_iov = &send_iovec; + send_uio.uio_iovcnt = 1; + send_uio.uio_resid = send_iovec.iov_len; + send_uio.uio_segflg = UIO_SYSSPACE; + send_error = kern_writev(thread_ptr, send_fd, &send_uio); + + return (send_error); +} + + +/* + * Receive kvp msg on the established unix socket connection from the user + */ +static int +kvp_rcv_user(void) +{ + int rcv_fd, rcv_error=0; + struct uio rcv_uio; + struct iovec rcv_iovec; + struct thread *thread_ptr = curthread; + + rcv_fd = sock_fd; + + memset(&rcv_uio, 0, sizeof(struct uio)); + + rcv_iovec.iov_base = (void *)&hv_user_kvp_msg; + rcv_iovec.iov_len = sizeof(hv_kvp_bsd_msg); + rcv_uio.uio_iov = &rcv_iovec; + rcv_uio.uio_iovcnt = 1; + rcv_uio.uio_resid = rcv_iovec.iov_len; + rcv_uio.uio_segflg = UIO_SYSSPACE; + rcv_error = kern_readv(thread_ptr, rcv_fd, &rcv_uio); + + return (rcv_error); +} + +/* + * Converts utf8 to utf16 + */ +static size_t +convert8_to_16(uint16_t *dst, size_t dst_len,const char *src, size_t src_len, + int *errp) +{ + const unsigned char *s; + size_t spos, dpos; + int error, flags = 1; + uint16_t c; +#define IS_CONT(c) (((c)&0xc0) == 0x80) + + error = 0; + s = (const unsigned char *)src; + spos = dpos = 0; + + while (spos<src_len) { + if (s[spos] < 0x80) + c = s[spos++]; + else if ((flags & 0x03) + && (spos >= src_len || !IS_CONT(s[spos+1])) + && s[spos]>=0xa0) { + /* not valid UTF-8, assume ISO 8859-1 */ + c = s[spos++]; + } + else if (s[spos] < 0xc0 || s[spos] >= 0xf5) { + /* continuation byte without lead byte + or lead byte for codepoint above 0x10ffff */ + error++; + spos++; + continue; + } + else if (s[spos] < 0xe0) { + if (spos >= src_len || !IS_CONT(s[spos+1])) { + spos++; + error++; + continue; + } + c = ((s[spos] & 0x3f) << 6) | (s[spos+1] & 0x3f); + spos += 2; + if (c < 0x80) { + /* overlong encoding */ + error++; + continue; + } + } + else if (s[spos] < 0xf0) { + if (spos >= src_len-2 + || !IS_CONT(s[spos+1]) || !IS_CONT(s[spos+2])) { + spos++; + error++; + continue; + } + c = ((s[spos] & 0x0f) << 12) | ((s[spos+1] & 0x3f) << 6) + | (s[spos+2] & 0x3f); + spos += 3; + if (c < 0x800 || (c & 0xdf00) == 0xd800 ) { + /* overlong encoding or encoded surrogate */ + error++; + continue; + } + } + else { + uint32_t cc; + /* UTF-16 surrogate pair */ + + if (spos >= src_len-3 || !IS_CONT(s[spos+1]) + || !IS_CONT(s[spos+2]) || !IS_CONT(s[spos+3])) { + spos++; + error++; + + continue; + } + cc = ((s[spos] & 0x03) << 18) | ((s[spos+1] & 0x3f) << 12) +| ((s[spos+2] & 0x3f) << 6) | (s[spos+3] & 0x3f); + spos += 4; + if (cc < 0x10000) { + /* overlong encoding */ + error++; + continue; + } + if (dst && dpos < dst_len) + dst[dpos] = (0xd800 | ((cc-0x10000)>>10)); + dpos++; + c = 0xdc00 | ((cc-0x10000) & 0x3ffff); + } + + if (dst && dpos < dst_len) + dst[dpos] = c; + dpos++; + } + + if (errp) + *errp = error; + + return dpos; + +#undef IS_CONT +} + +/* + * Converts utf16 to utf8 +*/ +static size_t +convert16_to_8(char *dst, size_t dst_len, const uint16_t *src, size_t src_len, + int *errp) +{ + uint16_t spos, dpos; + int error; +#define CHECK_LENGTH(l) (dpos > dst_len-(l) ? dst=NULL : NULL) +#define ADD_BYTE(b) (dst ? dst[dpos] = (b) : 0, dpos++) + + error = 0; + dpos = 0; + for (spos=0; spos<src_len; spos++) { + if (src[spos] < 0x80) { + CHECK_LENGTH(1); + ADD_BYTE(src[spos]); + } + else if (src[spos] < 0x800) { + CHECK_LENGTH(2); + ADD_BYTE(0xc0 | (src[spos]>>6)); + ADD_BYTE(0x80 | (src[spos] & 0x3f)); + } + else if ((src[spos] & 0xdc00) == 0xd800) { + uint32_t c; + /* first surrogate */ + if (spos == src_len - 1 || (src[spos] & 0xdc00) != 0xdc00) { + /* no second surrogate present */ + error++; + continue; + } + spos++; + CHECK_LENGTH(4); + c = (((src[spos]&0x3ff) << 10) | (src[spos+1]&0x3ff)) + 0x10000; + ADD_BYTE(0xf0 | (c>>18)); + ADD_BYTE(0x80 | ((c>>12) & 0x3f)); + ADD_BYTE(0x80 | ((c>>6) & 0x3f)); + ADD_BYTE(0x80 | (c & 0x3f)); + } + else if ((src[spos] & 0xdc00) == 0xdc00) { + /* second surrogate without preceding first surrogate */ + error++; + } + else { + CHECK_LENGTH(3); + ADD_BYTE(0xe0 | src[spos]>>12); + ADD_BYTE(0x80 | ((src[spos]>>6) & 0x3f)); + ADD_BYTE(0x80 | (src[spos] & 0x3f)); + } + } + + if (errp) + *errp = error; + return dpos; + +#undef ADD_BYTE +#undef CHECK_LENGTH +} + + +/* + * Convert ip related info in umsg from utf8 to utf16 and store in hmsg + */ +static int +ipinfo_utf8_utf16(hv_kvp_bsd_msg *umsg, struct hv_kvp_ip_msg *host_ip_msg) +{ + int err_ip, err_subnet, err_gway, err_dns, err_adap; + + size_t len=0; + len = convert8_to_16((uint16_t *)host_ip_msg->kvp_ip_val.ip_addr, + MAX_IP_ADDR_SIZE, + (char *)umsg->body.kvp_ip_val.ip_addr, + strlen((char *)umsg->body.kvp_ip_val.ip_addr), + &err_ip); + len = convert8_to_16((uint16_t *)host_ip_msg->kvp_ip_val.sub_net, + MAX_IP_ADDR_SIZE, + (char *)umsg->body.kvp_ip_val.sub_net, + strlen((char *)umsg->body.kvp_ip_val.sub_net), + &err_subnet); + len = convert8_to_16((uint16_t *)host_ip_msg->kvp_ip_val.gate_way, + MAX_GATEWAY_SIZE, + (char *)umsg->body.kvp_ip_val.gate_way, + strlen((char *)umsg->body.kvp_ip_val.gate_way), + &err_gway); + len = convert8_to_16((uint16_t *)host_ip_msg->kvp_ip_val.dns_addr, + MAX_IP_ADDR_SIZE, + (char *)umsg->body.kvp_ip_val.dns_addr, + strlen((char *)umsg->body.kvp_ip_val.dns_addr), + &err_dns); + len = convert8_to_16((uint16_t *)host_ip_msg->kvp_ip_val.adapter_id, + MAX_IP_ADDR_SIZE, + (char *)umsg->body.kvp_ip_val.adapter_id, + strlen((char *)umsg->body.kvp_ip_val.adapter_id), + &err_adap); + host_ip_msg->kvp_ip_val.dhcp_enabled = + umsg->body.kvp_ip_val.dhcp_enabled; + host_ip_msg->kvp_ip_val.addr_family = + umsg->body.kvp_ip_val.addr_family; + + return (err_ip | err_subnet | err_gway | err_dns | err_adap); +} + +/* + * Convert ip related info in hmsg from utf16 to utf8 and store in umsg + */ +static int +ipinfo_utf16_utf8(struct hv_kvp_ip_msg *host_ip_msg, hv_kvp_bsd_msg *umsg) +{ + int err_ip, err_subnet, err_gway, err_dns, err_adap; + int j; + struct hv_device *hv_dev; /* GUID Data Structure */ + hn_softc_t *sc; /* hn softc structure */ + char if_name[4]; + unsigned char guid_instance[40]; + char* guid_data = NULL; + char buf[39]; + int len=16; + struct guid_extract { + char a1[2]; + char a2[2]; + char a3[2]; + char a4[2]; + char b1[2]; + char b2[2]; + char c1[2]; + char c2[2]; + char d[4]; + char e[12]; + } *id; + device_t *devs; + int devcnt; + + /* IP Address */ + len = convert16_to_8((char *)umsg->body.kvp_ip_val.ip_addr, + MAX_IP_ADDR_SIZE, + (uint16_t *)host_ip_msg->kvp_ip_val.ip_addr, + MAX_IP_ADDR_SIZE, &err_ip); + + /* Adapter ID : GUID */ + len = convert16_to_8((char *)umsg->body.kvp_ip_val.adapter_id, + MAX_ADAPTER_ID_SIZE, + (uint16_t *)host_ip_msg->kvp_ip_val.adapter_id, + MAX_ADAPTER_ID_SIZE, &err_adap); + + if (devclass_get_devices(devclass_find("hn"), &devs, &devcnt) == 0) { + for (devcnt = devcnt - 1; devcnt >= 0; devcnt--) { + sc = device_get_softc( devs[devcnt] ); + + /* Trying to find GUID of Network Device */ + hv_dev = sc->hn_dev_obj; + + for (j = 0; j < 16 ; j++) { + sprintf(&guid_instance[j * 2], "%02x", + hv_dev->device_id.data[j]); + } + + guid_data =(char *) guid_instance; + id = (struct guid_extract *)guid_data; + snprintf(buf, sizeof(buf) , + "{%.2s%.2s%.2s%.2s-%.2s%.2s-%.2s%.2s-%.4s-%s}", + id->a4,id->a3,id->a2,id->a1, id->b2, id->b1, + id->c2, id->c1,id->d,id->e); + guid_data = NULL; + sprintf(if_name, "%s%d", "hn", + device_get_unit(devs[devcnt])); + + /* + * XXX Need to implement multiple network adapter + * handler + */ + if (strncmp(buf, + (char *)umsg->body.kvp_ip_val.adapter_id,39) == + 0) { + /* Pass interface Name */ + strcpy((char *)umsg->body.kvp_ip_val.adapter_id,if_name); + break; + } else { + /* XXX Implement safe exit */ + } + } + free( devs, M_TEMP ); + } + + /* Address Family , DHCP , SUBNET, Gateway, DNS */ + umsg->kvp_hdr.operation = host_ip_msg->operation; + umsg->body.kvp_ip_val.addr_family = + host_ip_msg->kvp_ip_val.addr_family; + umsg->body.kvp_ip_val.dhcp_enabled = + host_ip_msg->kvp_ip_val.dhcp_enabled; + convert16_to_8((char *)umsg->body.kvp_ip_val.sub_net, MAX_IP_ADDR_SIZE, + (uint16_t *)host_ip_msg->kvp_ip_val.sub_net, + MAX_IP_ADDR_SIZE, &err_subnet); + convert16_to_8((char *)umsg->body.kvp_ip_val.gate_way, + MAX_GATEWAY_SIZE, + (uint16_t *)host_ip_msg->kvp_ip_val.gate_way, + MAX_GATEWAY_SIZE, &err_gway); + convert16_to_8((char *)umsg->body.kvp_ip_val.dns_addr, MAX_IP_ADDR_SIZE, + (uint16_t *)host_ip_msg->kvp_ip_val.dns_addr, + MAX_IP_ADDR_SIZE, &err_dns); + + return (err_ip | err_subnet | err_gway | err_dns | err_adap); +} + + +/* + * Prepare a user kvp msg based on host kvp msg (utf16 to utf8) + * Ensure utf16_utf8 takes care of the additional string terminating char!! + */ +static void +host_user_kvp_msg(void) +{ + int utf_err=0; + uint32_t value_type; + struct hv_kvp_ip_msg *host_ip_msg = (struct hv_kvp_ip_msg *)kvp_msg_state.host_kvp_msg; + hv_kvp_bsd_msg *hmsg = kvp_msg_state.host_kvp_msg; + hv_kvp_bsd_msg *umsg = &hv_user_kvp_msg; + umsg->kvp_hdr.operation = hmsg->kvp_hdr.operation; + umsg->kvp_hdr.pool = hmsg->kvp_hdr.pool; + + switch (umsg->kvp_hdr.operation) { + case HV_KVP_OP_SET_IP_INFO: + ipinfo_utf16_utf8(host_ip_msg, umsg); + break; + case HV_KVP_OP_GET_IP_INFO: + convert16_to_8((char *)umsg->body.kvp_ip_val.adapter_id, + MAX_ADAPTER_ID_SIZE, + (uint16_t *)host_ip_msg->kvp_ip_val.adapter_id, + MAX_ADAPTER_ID_SIZE, &utf_err); + umsg->body.kvp_ip_val.addr_family = + host_ip_msg->kvp_ip_val.addr_family; + break; + case HV_KVP_OP_SET: + value_type = hmsg->body.kvp_set.data.value_type; + switch (value_type) { + case HV_REG_SZ: + umsg->body.kvp_set.data.value_size = + convert16_to_8( + (char *)umsg->body.kvp_set.data.msg_value.value, + HV_KVP_EXCHANGE_MAX_VALUE_SIZE - 1, + (uint16_t *)hmsg->body.kvp_set.data.msg_value.value, + hmsg->body.kvp_set.data.value_size, + &utf_err); + /* utf8 encoding */ + umsg->body.kvp_set.data.value_size = + umsg->body.kvp_set.data.value_size/2; + break; + case HV_REG_U32: + umsg->body.kvp_set.data.value_size = + sprintf(umsg->body.kvp_set.data.msg_value.value, "%d", + hmsg->body.kvp_set.data.msg_value.value_u32) + 1; + break; + case HV_REG_U64: + umsg->body.kvp_set.data.value_size = + sprintf(umsg->body.kvp_set.data.msg_value.value, "%llu", + (unsigned long long) + hmsg->body.kvp_set.data.msg_value.value_u64) + 1; + break; + } + + umsg->body.kvp_set.data.key_size = + convert16_to_8( + umsg->body.kvp_set.data.key, + HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1, + (uint16_t *)hmsg->body.kvp_set.data.key, + hmsg->body.kvp_set.data.key_size, + &utf_err); + + /* utf8 encoding */ + umsg->body.kvp_set.data.key_size = + umsg->body.kvp_set.data.key_size/2; + break; + case HV_KVP_OP_GET: + umsg->body.kvp_get.data.key_size = + convert16_to_8(umsg->body.kvp_get.data.key, + HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1, + (uint16_t *)hmsg->body.kvp_get.data.key, + hmsg->body.kvp_get.data.key_size, + &utf_err); + /* utf8 encoding */ + umsg->body.kvp_get.data.key_size = + umsg->body.kvp_get.data.key_size/2; + break; + case HV_KVP_OP_DELETE: + umsg->body.kvp_delete.key_size = + convert16_to_8(umsg->body.kvp_delete.key, + HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1, + (uint16_t *)hmsg->body.kvp_delete.key, + hmsg->body.kvp_delete.key_size, + &utf_err); + /* utf8 encoding */ + umsg->body.kvp_delete.key_size = + umsg->body.kvp_delete.key_size/2; + break; + case HV_KVP_OP_ENUMERATE: + umsg->body.kvp_enum_data.index = + hmsg->body.kvp_enum_data.index; + break; + default: + printf("host_user_kvp_msg: Invalid operation : %d\n", + umsg->kvp_hdr.operation); + } +} + +/* + * Prepare a host kvp msg based on user kvp msg (utf8 to utf16) + */ +static int +user_host_kvp_msg(void) +{ + int hkey_len=0, hvalue_len=0, utf_err=0; + struct hv_kvp_exchg_msg_value *host_exchg_data; + char *key_name, *value; + hv_kvp_bsd_msg *umsg = &hv_user_kvp_msg; + hv_kvp_bsd_msg *hmsg = kvp_msg_state.host_kvp_msg; + struct hv_kvp_ip_msg *host_ip_msg = (struct hv_kvp_ip_msg *)hmsg; + + switch (kvp_msg_state.host_kvp_msg->kvp_hdr.operation) { + case HV_KVP_OP_GET_IP_INFO: + return(ipinfo_utf8_utf16(umsg, host_ip_msg)); + + case HV_KVP_OP_SET_IP_INFO: + case HV_KVP_OP_SET: + case HV_KVP_OP_DELETE: + return (KVP_SUCCESS); + + case HV_KVP_OP_ENUMERATE: + host_exchg_data = &hmsg->body.kvp_enum_data.data; + key_name = umsg->body.kvp_enum_data.data.key; + hkey_len = convert8_to_16((uint16_t *) host_exchg_data->key, + ((HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2) - 2), + key_name, strlen(key_name), + &utf_err); + /* utf16 encoding */ + host_exchg_data->key_size = 2*(hkey_len + 1); + value = umsg->body.kvp_enum_data.data.msg_value.value; + hvalue_len = + convert8_to_16( (uint16_t *)host_exchg_data->msg_value.value, + ( (HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2), + value, strlen(value), + &utf_err); + host_exchg_data->value_size = 2 * (hvalue_len + 1); + host_exchg_data->value_type = HV_REG_SZ; + + if ((hkey_len < 0) || (hvalue_len < 0)) return(HV_KVP_E_FAIL); + return (KVP_SUCCESS); + + case HV_KVP_OP_GET: + host_exchg_data = &hmsg->body.kvp_get.data; + value = umsg->body.kvp_get.data.msg_value.value; + hvalue_len = convert8_to_16( + (uint16_t *) host_exchg_data->msg_value.value, + ((HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2), + value, strlen(value), + &utf_err); + /* Convert value size to uft16 */ + host_exchg_data->value_size = 2*(hvalue_len + 1); + /* Use values by string */ + host_exchg_data->value_type = HV_REG_SZ; + + if ((hkey_len < 0) || (hvalue_len < 0)) return(HV_KVP_E_FAIL); + return (KVP_SUCCESS); + default: + return (HV_KVP_E_FAIL); + } +} + + +/* + * Send the response back to the host. + */ +static void +kvp_respond_host(int error) +{ + struct hv_vmbus_icmsg_hdr *hv_icmsg_hdrp; + + if (!hv_kvp_transaction_active()) { + /* XXX Triage why we are here */ + goto finish; + } + + hv_icmsg_hdrp = (struct hv_vmbus_icmsg_hdr *) + &kvp_msg_state.rcv_buf[sizeof(struct hv_vmbus_pipe_hdr)]; + + if (error) error = HV_KVP_E_FAIL; + hv_icmsg_hdrp->status = error; + hv_icmsg_hdrp->icflags = + HV_ICMSGHDRFLAG_TRANSACTION | HV_ICMSGHDRFLAG_RESPONSE; + + error = hv_vmbus_channel_send_packet(kvp_msg_state.channelp, + kvp_msg_state.rcv_buf, + kvp_msg_state.host_msg_len, kvp_msg_state.host_msg_id, + HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); + + if (error) { + printf("kvp_respond_host: sendpacket error:%d\n", error); + } + + /* Now ready to process another transaction */ + kvp_msg_state.in_progress = FALSE; + +finish: + return; +} + +/* + * Initiate a connection and receive REGISTER message from the user daemon + */ +static void +hv_kvp_conn_register() +{ + int error = KVP_SUCCESS; + + if (conn_ready == FALSE) { + /* Wait until the user daemon is ready */ + if (!hv_kvp_daemon_ack) { + return; + } + + if (kvp_connect_user() != KVP_SUCCESS) { + return; + } else { + conn_ready = TRUE; + } + } + + /* + * First message from the user should be a HV_KVP_OP_REGISTER msg + */ + if (register_done == FALSE) { + error = kvp_rcv_user(); /* receive a message from user */ + + if (hv_user_kvp_msg.kvp_hdr.operation == HV_KVP_OP_REGISTER) { + register_done = TRUE; + kvp_msg_state.kvp_ready = TRUE; + } + } +} + + +/** + * This is the main kvp kernel process that interacts with both user daemon + * and the host + */ +static void +hv_kvp_process_msg(void *p) +{ + int error = KVP_SUCCESS; + + /* Prepare kvp_msg to be sent to user */ + host_user_kvp_msg(); + + /* Send msg to user on Unix Socket */ + error = kvp_send_user(); + + if (error != KVP_SUCCESS) { + if (error == EPIPE) { + conn_ready = FALSE; + register_done = FALSE; + kvp_msg_state.kvp_ready = FALSE; + } + + kvp_respond_host(HV_KVP_E_FAIL); + return; + } + + /* Rcv response from user on Unix Socket */ + hv_user_kvp_msg.hdr.error = HV_KVP_E_FAIL; + error = kvp_rcv_user(); + + if ((error == KVP_SUCCESS) && + (hv_user_kvp_msg.hdr.error != HV_KVP_E_FAIL)) { + /* Convert user kvp to host kvp and then respond */ + error = user_host_kvp_msg(); + + if (error != KVP_SUCCESS) { + kvp_respond_host(HV_KVP_E_FAIL); + } else { + kvp_respond_host(hv_user_kvp_msg.hdr.error); + } + } else { + if (error == EPIPE) { + conn_ready = FALSE; + register_done = FALSE; + kvp_msg_state.kvp_ready = FALSE; + } + kvp_respond_host(HV_KVP_E_FAIL); + } +} + +/* + * Callback routine that gets called whenever there is a message from host + */ +void +hv_kvp_callback(void *context) +{ + uint8_t* kvp_buf; + hv_vmbus_channel* channel = context; + uint32_t recvlen; + uint64_t requestid; + int ret = 0; + struct hv_vmbus_icmsg_hdr *icmsghdrp; + + kvp_buf = receive_buffer[HV_KVP]; + + /* + * Check if already one transaction is under process + */ + if (!hv_kvp_transaction_active()) { + ret = hv_vmbus_channel_recv_packet(channel, kvp_buf, + 2 * PAGE_SIZE, &recvlen, &requestid); + + if ((ret == 0) && (recvlen > 0)) { + icmsghdrp = (struct hv_vmbus_icmsg_hdr *) + &kvp_buf[sizeof(struct hv_vmbus_pipe_hdr)]; + + hv_kvp_transaction_init(recvlen, channel,requestid, + kvp_buf); + + if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { + hv_kvp_negotiate_version(icmsghdrp, NULL, + kvp_buf); + kvp_respond_host(ret); + } else { + /* + * Queue packet for processing. + */ + hv_queue_work_item( + service_table[HV_KVP].work_queue, + hv_kvp_process_msg, + NULL); + } + } else { + ret = HV_KVP_E_FAIL; + } + } else { + ret = HV_KVP_E_FAIL; + } + + if (ret != 0) + kvp_respond_host(ret); +} + +int +hv_kvp_init(hv_vmbus_service *srv) +{ + int error; + hv_work_queue *work_queue; + + error = 0; + + work_queue = hv_work_queue_create("KVP Service"); + if (work_queue == NULL) { + error = ENOMEM; + } else + srv->work_queue = work_queue; + + return (error); +} Added: projects/hyperv/sys/dev/hyperv/utilities/hv_kvp.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ projects/hyperv/sys/dev/hyperv/utilities/hv_kvp.h Mon Sep 9 08:07:46 2013 (r255414) @@ -0,0 +1,287 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201309090807.r8987kMm053821>