diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/conf/files.amd64 | 1 | ||||
-rw-r--r-- | sys/dev/hyperv/README | 34 | ||||
-rw-r--r-- | sys/dev/hyperv/utilities/hv_kvp.c | 982 | ||||
-rw-r--r-- | sys/dev/hyperv/utilities/hv_kvp.h | 287 | ||||
-rw-r--r-- | sys/dev/hyperv/utilities/hv_util.c | 135 | ||||
-rw-r--r-- | sys/dev/hyperv/vmbus/hv_hv.c | 79 | ||||
-rw-r--r-- | sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c | 33 | ||||
-rw-r--r-- | sys/dev/hyperv/vmbus/hv_vmbus_priv.h | 56 | ||||
-rw-r--r-- | sys/modules/hyperv/netvsc/Makefile | 3 | ||||
-rw-r--r-- | sys/modules/hyperv/utilities/Makefile | 3 |
10 files changed, 1395 insertions, 218 deletions
diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64 index c1647d3..2d32785 100644 --- a/sys/conf/files.amd64 +++ b/sys/conf/files.amd64 @@ -226,6 +226,7 @@ dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c optional hyperv 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 diff --git a/sys/dev/hyperv/README b/sys/dev/hyperv/README deleted file mode 100644 index 1c4488d..0000000 --- a/sys/dev/hyperv/README +++ /dev/null @@ -1,34 +0,0 @@ -***** Release rc2.3.0 4/27/2012 ************************************************** - -New features/limitations- - --Added Fast IDE --Massive code restructuring to meeting FreeBSD sytle guidelines - -***** Release rc2.2.0 1/4/2012 *************************************************** - -New features/limitations- - --Port of LIS 2.1 with FreeBSD support code from Citrix, drivers are linked with - Kernel (future drivers will be loadable), port has not been refactored to meet - BSD coding standards - --SCSI device driver functional, but support for scatter-gather lists is not - implemented-Fast IDE support has not been added-still using emulated IDE - support - --Network storage device support has been added - --While the storage and networking devices support multiple controllers, we're - waiting on a resolution from Microsoft to enable persistent and consistent - numbering between boots - --Hyper-V bus has been ported with support code from Citrix to handle clock - synchronization between guest and host. Clock synchronization and heartbeat - logic have been moved to two, separate drivers-this separation is part - of the initial steps for refactoring and restructuring the Hyper-V bus driver from the - LIS 2.1 codebase - -Bug fixes- - -*******************************************************************************
\ No newline at end of file diff --git a/sys/dev/hyperv/utilities/hv_kvp.c b/sys/dev/hyperv/utilities/hv_kvp.c new file mode 100644 index 0000000..f63e686 --- /dev/null +++ b/sys/dev/hyperv/utilities/hv_kvp.c @@ -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); +} diff --git a/sys/dev/hyperv/utilities/hv_kvp.h b/sys/dev/hyperv/utilities/hv_kvp.h new file mode 100644 index 0000000..08ee16a --- /dev/null +++ b/sys/dev/hyperv/utilities/hv_kvp.h @@ -0,0 +1,287 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2012 NetApp Inc. + * Copyright (c) 2012 Citrix Inc. + * 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. + */ + +#ifndef _KVP_H +#define _KVP_H + +/* + * An implementation of HyperV key value pair (KVP) functionality for FreeBSD + * + */ + +/* + * Maximum value size - used for both key names and value data, and includes + * any applicable NULL terminators. + * + * Note: This limit is somewhat arbitrary, but falls easily within what is + * supported for all native guests (back to Win 2000) and what is reasonable + * for the IC KVP exchange functionality. Note that Windows Me/98/95 are + * limited to 255 character key names. + * + * MSDN recommends not storing data values larger than 2048 bytes in the + * registry. + * + * Note: This value is used in defining the KVP exchange message - this value + * cannot be modified without affecting the message size and compatibility. + */ + +/* + * bytes, including any null terminators + */ +#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE (2048) + + +/* + * Maximum key size - the registry limit for the length of an entry name + * is 256 characters, including the null terminator + */ + +#define HV_KVP_EXCHANGE_MAX_KEY_SIZE (512) + +/* + * In FreeBSD, we implement the KVP functionality in two components: + * 1) The kernel component which is packaged as part of the hv_utils driver + * is responsible for communicating with the host and responsible for + * implementing the host/guest protocol. 2) A user level daemon that is + * responsible for data gathering. + * + * Host/Guest Protocol: The host iterates over an index and expects the guest + * to assign a key name to the index and also return the value corresponding to + * the key. The host will have atmost one KVP transaction outstanding at any + * given point in time. The host side iteration stops when the guest returns + * an error. Microsoft has specified the following mapping of key names to + * host specified index: + * + * Index Key Name + * 0 FullyQualifiedDomainName + * 1 IntegrationServicesVersion + * 2 NetworkAddressIPv4 + * 3 NetworkAddressIPv6 + * 4 OSBuildNumber + * 5 OSName + * 6 OSMajorVersion + * 7 OSMinorVersion + * 8 OSVersion + * 9 ProcessorArchitecture + * + * The Windows host expects the Key Name and Key Value to be encoded in utf16. + * + * Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the + * data gathering functionality in a user mode daemon. The user level daemon + * is also responsible for binding the key name to the index as well. The + * kernel and user-level daemon communicate using a connector channel. + * + * The user mode component first registers with the + * the kernel component. Subsequently, the kernel component requests, data + * for the specified keys. In response to this message the user mode component + * fills in the value corresponding to the specified key. We overload the + * sequence field in the cn_msg header to define our KVP message types. + * + * + * The kernel component simply acts as a conduit for communication between the + * Windows host and the user-level daemon. The kernel component passes up the + * index received from the Host to the user-level daemon. If the index is + * valid (supported), the corresponding key as well as its + * value (both are strings) is returned. If the index is invalid + * (not supported), a NULL key string is returned. + */ + + +/* + * Registry value types. + */ + +#define HV_REG_SZ 1 +#define HV_REG_U32 4 +#define HV_REG_U64 8 + + +/* + * Daemon code not supporting IP injection (legacy daemon). + */ + +#define HV_KVP_OP_REGISTER 4 + +/* + * Daemon code supporting IP injection. + * The KVP opcode field is used to communicate the + * registration information; so define a namespace that + * will be distinct from the host defined KVP opcode. + */ + +#define KVP_OP_REGISTER1 100 + +enum hv_kvp_exchg_op { + HV_KVP_OP_GET = 0, + HV_KVP_OP_SET, + HV_KVP_OP_DELETE, + HV_KVP_OP_ENUMERATE, + HV_KVP_OP_GET_IP_INFO, + HV_KVP_OP_SET_IP_INFO, + HV_KVP_OP_COUNT /* Number of operations, must be last. */ +}; + +enum hv_kvp_exchg_pool { + HV_KVP_POOL_EXTERNAL = 0, + HV_KVP_POOL_GUEST, + HV_KVP_POOL_AUTO, + HV_KVP_POOL_AUTO_EXTERNAL, + HV_KVP_POOL_AUTO_INTERNAL, + HV_KVP_POOL_COUNT /* Number of pools, must be last. */ +}; + +/* + * Some Hyper-V status codes. + */ +#define HV_KVP_S_OK 0x00000000 +#define HV_KVP_E_FAIL 0x80004005 +#define HV_KVP_S_CONT 0x80070103 +#define HV_ERROR_NOT_SUPPORTED 0x80070032 +#define HV_ERROR_MACHINE_LOCKED 0x800704F7 +#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F +#define HV_INVALIDARG 0x80070057 +#define HV_KVP_GUID_NOTFOUND 0x80041002 + +#define ADDR_FAMILY_NONE 0x00 +#define ADDR_FAMILY_IPV4 0x01 +#define ADDR_FAMILY_IPV6 0x02 + +#define MAX_ADAPTER_ID_SIZE 128 +#define MAX_IP_ADDR_SIZE 1024 +#define MAX_GATEWAY_SIZE 512 + + +struct hv_kvp_ipaddr_value { + uint16_t adapter_id[MAX_ADAPTER_ID_SIZE]; + uint8_t addr_family; + uint8_t dhcp_enabled; + uint16_t ip_addr[MAX_IP_ADDR_SIZE]; + uint16_t sub_net[MAX_IP_ADDR_SIZE]; + uint16_t gate_way[MAX_GATEWAY_SIZE]; + uint16_t dns_addr[MAX_IP_ADDR_SIZE]; +} __attribute__((packed)); + + +struct hv_kvp_hdr { + uint8_t operation; + uint8_t pool; + uint16_t pad; +} __attribute__((packed)); + +struct hv_kvp_exchg_msg_value { + uint32_t value_type; + uint32_t key_size; + uint32_t value_size; + uint8_t key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; + union { + uint8_t value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; + uint32_t value_u32; + uint64_t value_u64; + } msg_value; +} __attribute__((packed)); + +struct hv_kvp_msg_enumerate { + uint32_t index; + struct hv_kvp_exchg_msg_value data; +} __attribute__((packed)); + +struct hv_kvp_msg_get { + struct hv_kvp_exchg_msg_value data; +} __attribute__((packed)); + +struct hv_kvp_msg_set { + struct hv_kvp_exchg_msg_value data; +} __attribute__((packed)); + +struct hv_kvp_msg_delete { + uint32_t key_size; + uint8_t key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; +} __attribute__((packed)); + +struct hv_kvp_register { + uint8_t version[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; +} __attribute__((packed)); + +struct hv_kvp_msg { + union { + struct hv_kvp_hdr kvp_hdr; + int error; + } hdr; + union { + struct hv_kvp_msg_get kvp_get; + struct hv_kvp_msg_set kvp_set; + struct hv_kvp_msg_delete kvp_delete; + struct hv_kvp_msg_enumerate kvp_enum_data; + struct hv_kvp_ipaddr_value kvp_ip_val; + struct hv_kvp_register kvp_register; + } body; +} __attribute__((packed)); + +struct hv_kvp_ip_msg { + uint8_t operation; + uint8_t pool; + struct hv_kvp_ipaddr_value kvp_ip_val; +} __attribute__((packed)); + +#define BSD_SOC_PATH "/etc/hyperv/socket" + +#define HV_SHUT_DOWN 0 +#define HV_TIME_SYNCH 1 +#define HV_HEART_BEAT 2 +#define HV_KVP 3 +#define HV_MAX_UTIL_SERVICES 4 + +#define HV_WLTIMEDELTA 116444736000000000L /* in 100ns unit */ +#define HV_ICTIMESYNCFLAG_PROBE 0 +#define HV_ICTIMESYNCFLAG_SYNC 1 +#define HV_ICTIMESYNCFLAG_SAMPLE 2 +#define HV_NANO_SEC_PER_SEC 1000000000 + +typedef struct hv_vmbus_service { + hv_guid guid; /* Hyper-V GUID */ + char* name; /* name of service */ + boolean_t enabled; /* service enabled */ + hv_work_queue* work_queue; /* background work queue */ + + // + // function to initialize service + // + int (*init)(struct hv_vmbus_service *); + + // + // function to process Hyper-V messages + // + void (*callback)(void *); +} hv_vmbus_service; + +extern uint8_t* receive_buffer[]; +extern hv_vmbus_service service_table[]; + +void hv_kvp_callback(void *context); +int hv_kvp_init(hv_vmbus_service *serv); +#endif /* _KVP_H */ diff --git a/sys/dev/hyperv/utilities/hv_util.c b/sys/dev/hyperv/utilities/hv_util.c index 43e4213..63fe93d 100644 --- a/sys/dev/hyperv/utilities/hv_util.c +++ b/sys/dev/hyperv/utilities/hv_util.c @@ -37,41 +37,19 @@ #include <sys/module.h> #include <sys/reboot.h> #include <sys/timetc.h> +#include <sys/syscallsubr.h> #include <dev/hyperv/include/hyperv.h> +#include "hv_kvp.h" -#define HV_SHUT_DOWN 0 -#define HV_TIME_SYNCH 1 -#define HV_HEART_BEAT 2 -#define HV_KVP 3 -#define HV_MAX_UTIL_SERVICES 4 - -#define HV_NANO_SEC 1000000000L /* 10^ 9 nanosecs = 1 sec */ - -#define HV_WLTIMEDELTA 116444736000000000L /* in 100ns unit */ -#define HV_ICTIMESYNCFLAG_PROBE 0 -#define HV_ICTIMESYNCFLAG_SYNC 1 -#define HV_ICTIMESYNCFLAG_SAMPLE 2 - -typedef struct hv_vmbus_service { - hv_guid guid; /* Hyper-V GUID */ - char* name; /* name of service */ - boolean_t enabled; /* service enabled */ - hv_work_queue* work_queue; /* background work queue */ - /* - * function to initialize service - */ - int (*init)(struct hv_vmbus_service *); - /* - * function to process Hyper-V messages - */ - void (*callback)(void *); -} hv_vmbus_service; +/* Time Sync data */ +typedef struct { + uint64_t data; +} time_sync_data; static void hv_shutdown_cb(void *context); static void hv_heartbeat_cb(void *context); static void hv_timesync_cb(void *context); -static void hv_kvp_cb(void *context); static int hv_timesync_init(hv_vmbus_service *serv); @@ -79,7 +57,7 @@ static int hv_timesync_init(hv_vmbus_service *serv); * Note: GUID codes below are predefined by the host hypervisor * (Hyper-V and Azure)interface and required for correct operation. */ -static hv_vmbus_service service_table[] = { +hv_vmbus_service service_table[] = { /* Shutdown Service */ { .guid.data = {0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB}, @@ -103,36 +81,38 @@ static hv_vmbus_service service_table[] = { .name = "Hyper-V Heartbeat Service\n", .enabled = TRUE, .callback = hv_heartbeat_cb, - }, /* KVP (Key Value Pair) Service */ { .guid.data = {0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6}, .name = "Hyper-V KVP Service\n", - .enabled = FALSE, - .callback = hv_kvp_cb, + .enabled = TRUE, + .init = hv_kvp_init, + .callback = hv_kvp_callback, }, }; -/** - * Receive buffer pointers, there is one buffer per utility service. The +/* + * Receive buffer pointers. There is one buffer per utility service. The * buffer is allocated during attach(). */ -static uint8_t* receive_buffer[HV_MAX_UTIL_SERVICES]; +uint8_t *receive_buffer[HV_MAX_UTIL_SERVICES]; struct hv_ictimesync_data { uint64_t parenttime; uint64_t childtime; uint64_t roundtriptime; - uint8_t flags; + uint8_t flags; } __packed; -static int hv_timesync_init(hv_vmbus_service *serv) +static int +hv_timesync_init(hv_vmbus_service *serv) { + serv->work_queue = hv_work_queue_create("Time Sync"); if (serv->work_queue == NULL) - return (ENOMEM); + return (ENOMEM); return (0); } @@ -141,14 +121,14 @@ hv_negotiate_version( struct hv_vmbus_icmsg_hdr* icmsghdrp, struct hv_vmbus_icmsg_negotiate* negop, uint8_t* buf) - { +{ icmsghdrp->icmsgsize = 0x10; negop = (struct hv_vmbus_icmsg_negotiate *)&buf[ sizeof(struct hv_vmbus_pipe_hdr) + sizeof(struct hv_vmbus_icmsg_hdr)]; - if (negop->icframe_vercnt == 2 && + if (negop->icframe_vercnt >= 2 && negop->icversion_data[1].major == 3) { negop->icversion_data[0].major = 3; negop->icversion_data[0].minor = 0; @@ -165,9 +145,6 @@ hv_negotiate_version( negop->icmsg_vercnt = 1; } -static void hv_kvp_cb(void *context) -{ -} /** * Set host time based on time sync message from host @@ -175,24 +152,33 @@ static void hv_kvp_cb(void *context) static void hv_set_host_time(void *context) { - uint64_t hosttime = (uint64_t)context; - struct timespec ts, host_ts; - int64_t tns, host_tns, tmp, tsec; + time_sync_data *time_msg = context; + uint64_t hosttime = time_msg->data; + struct timespec guest_ts, host_ts; + uint64_t host_tns; + int64_t diff; + int error; - nanotime(&ts); - tns = ts.tv_sec * HV_NANO_SEC + ts.tv_nsec; host_tns = (hosttime - HV_WLTIMEDELTA) * 100; - - tmp = host_tns; - tsec = tmp / HV_NANO_SEC; - host_ts.tv_nsec = (long) (tmp - (tsec * HV_NANO_SEC)); - host_ts.tv_sec = tsec; - - /* force time sync with host after reboot, restore, etc. */ - mtx_lock(&Giant); - tc_setclock(&host_ts); - resettodr(); - mtx_unlock(&Giant); + host_ts.tv_sec = (time_t)(host_tns/HV_NANO_SEC_PER_SEC); + host_ts.tv_nsec = (long)(host_tns%HV_NANO_SEC_PER_SEC); + + nanotime(&guest_ts); + + diff = (int64_t)host_ts.tv_sec - (int64_t)guest_ts.tv_sec; + + /* + * If host differs by 5 seconds then make the guest catch up + */ + if (diff > 5 || diff < -5) { + error = kern_clock_settime(curthread, CLOCK_REALTIME, + &host_ts); + } + + /* + * Free the hosttime that was allocated in hv_adj_guesttime() + */ + free(time_msg, M_DEVBUF); } /** @@ -209,18 +195,23 @@ hv_set_host_time(void *context) static inline void hv_adj_guesttime(uint64_t hosttime, uint8_t flags) { - static int scnt = 50; + time_sync_data* time_msg; - if ((flags & HV_ICTIMESYNCFLAG_SYNC) != 0) { - hv_queue_work_item(service_table[HV_TIME_SYNCH].work_queue, - hv_set_host_time, (void *) hosttime); - return; - } + time_msg = malloc(sizeof(time_sync_data), M_DEVBUF, M_NOWAIT); - if ((flags & HV_ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) { - scnt--; - hv_queue_work_item(service_table[HV_TIME_SYNCH].work_queue, - hv_set_host_time, (void *) hosttime); + if (time_msg == NULL) + return; + + time_msg->data = hosttime; + + if ((flags & HV_ICTIMESYNCFLAG_SYNC) != 0) { + hv_queue_work_item(service_table[HV_TIME_SYNCH].work_queue, + hv_set_host_time, time_msg); + } else if ((flags & HV_ICTIMESYNCFLAG_SAMPLE) != 0) { + hv_queue_work_item(service_table[HV_TIME_SYNCH].work_queue, + hv_set_host_time, time_msg); + } else { + free(time_msg, M_DEVBUF); } } @@ -405,7 +396,7 @@ hv_util_attach(device_t dev) receive_buffer_offset = service - &service_table[0]; device_printf(dev, "Hyper-V Service attaching: %s\n", service->name); receive_buffer[receive_buffer_offset] = - malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO); + malloc(4 * PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO); if (service->init != NULL) { ret = service->init(service); @@ -415,8 +406,8 @@ hv_util_attach(device_t dev) } } - ret = hv_vmbus_channel_open(hv_dev->channel, 2 * PAGE_SIZE, - 2 * PAGE_SIZE, NULL, 0, + ret = hv_vmbus_channel_open(hv_dev->channel, 4 * PAGE_SIZE, + 4 * PAGE_SIZE, NULL, 0, service->callback, hv_dev->channel); if (ret) diff --git a/sys/dev/hyperv/vmbus/hv_hv.c b/sys/dev/hyperv/vmbus/hv_hv.c index 0e73bdc..e3f3ae0 100644 --- a/sys/dev/hyperv/vmbus/hv_hv.c +++ b/sys/dev/hyperv/vmbus/hv_hv.c @@ -43,6 +43,7 @@ #include "hv_vmbus_priv.h" #define HV_X64_MSR_GUEST_OS_ID 0x40000000 + #define HV_X64_CPUID_MIN 0x40000005 #define HV_X64_CPUID_MAX 0x4000ffff #define HV_X64_MSR_TIME_REF_COUNT 0x40000020 @@ -51,7 +52,6 @@ static u_int hv_get_timecount(struct timecounter *tc); -static u_int hv_get_timecount(struct timecounter *tc); static inline void do_cpuid_inline(unsigned int op, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) { @@ -76,7 +76,7 @@ static struct timecounter hv_timecounter = { static u_int hv_get_timecount(struct timecounter *tc) { - u_int now = hv_vmbus_read_msr(HV_X64_MSR_TIME_REF_COUNT); + u_int now = rdmsr(HV_X64_MSR_TIME_REF_COUNT); return (now); } @@ -217,13 +217,13 @@ hv_vmbus_init(void) * Write our OS info */ uint64_t os_guest_info = HV_FREEBSD_GUEST_ID; - hv_vmbus_write_msr(HV_X64_MSR_GUEST_OS_ID, os_guest_info); + wrmsr(HV_X64_MSR_GUEST_OS_ID, os_guest_info); hv_vmbus_g_context.guest_id = os_guest_info; /* * See if the hypercall page is already set */ - hypercall_msr.as_uint64_t = hv_vmbus_read_msr(HV_X64_MSR_HYPERCALL); + hypercall_msr.as_uint64_t = rdmsr(HV_X64_MSR_HYPERCALL); virt_addr = malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO); KASSERT(virt_addr != NULL, ("Error VMBUS: malloc failed to allocate page during init!")); @@ -233,13 +233,13 @@ hv_vmbus_init(void) hypercall_msr.enable = 1; hypercall_msr.guest_physical_address = (hv_get_phys_addr(virt_addr) >> PAGE_SHIFT); - hv_vmbus_write_msr(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64_t); + wrmsr(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64_t); /* * Confirm that hypercall page did get set up */ hypercall_msr.as_uint64_t = 0; - hypercall_msr.as_uint64_t = hv_vmbus_read_msr(HV_X64_MSR_HYPERCALL); + hypercall_msr.as_uint64_t = rdmsr(HV_X64_MSR_HYPERCALL); if (!hypercall_msr.enable) goto cleanup; @@ -276,7 +276,7 @@ hv_vmbus_init(void) if (virt_addr != NULL) { if (hypercall_msr.enable) { hypercall_msr.as_uint64_t = 0; - hv_vmbus_write_msr(HV_X64_MSR_HYPERCALL, + wrmsr(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64_t); } @@ -302,7 +302,7 @@ hv_vmbus_cleanup(void) if (hv_vmbus_g_context.guest_id == HV_FREEBSD_GUEST_ID) { if (hv_vmbus_g_context.hypercall_page != NULL) { hypercall_msr.as_uint64_t = 0; - hv_vmbus_write_msr(HV_X64_MSR_HYPERCALL, + wrmsr(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64_t); free(hv_vmbus_g_context.hypercall_page, M_DEVBUF); hv_vmbus_g_context.hypercall_page = NULL; @@ -376,18 +376,17 @@ hv_vmbus_signal_event() * @brief hv_vmbus_synic_init */ void -hv_vmbus_synic_init(void *irq_arg) +hv_vmbus_synic_init(void *arg) { int cpu; - uint32_t irq_vector; hv_vmbus_synic_simp simp; hv_vmbus_synic_siefp siefp; hv_vmbus_synic_scontrol sctrl; hv_vmbus_synic_sint shared_sint; uint64_t version; + hv_setup_args* setup_args = (hv_setup_args *)arg; - irq_vector = *((uint32_t *) (irq_arg)); cpu = PCPU_GET(cpuid); if (hv_vmbus_g_context.hypercall_page == NULL) @@ -406,65 +405,50 @@ hv_vmbus_synic_init(void *irq_arg) /* * TODO: Check the version */ - version = hv_vmbus_read_msr(HV_X64_MSR_SVERSION); - - hv_vmbus_g_context.syn_ic_msg_page[cpu] = - malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO); - KASSERT(hv_vmbus_g_context.syn_ic_msg_page[cpu] != NULL, - ("Error VMBUS: malloc failed for allocating page!")); - if (hv_vmbus_g_context.syn_ic_msg_page[cpu] == NULL) - goto cleanup; + version = rdmsr(HV_X64_MSR_SVERSION); - hv_vmbus_g_context.syn_ic_event_page[cpu] = - malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO); - KASSERT(hv_vmbus_g_context.syn_ic_event_page[cpu] != NULL, - ("Error VMBUS: malloc failed to allocate page!")); - if (hv_vmbus_g_context.syn_ic_event_page[cpu] == NULL) - goto cleanup; + + hv_vmbus_g_context.syn_ic_msg_page[cpu] = setup_args->page_buffers[0]; + hv_vmbus_g_context.syn_ic_event_page[cpu] = setup_args->page_buffers[1]; /* * Setup the Synic's message page */ - simp.as_uint64_t = hv_vmbus_read_msr(HV_X64_MSR_SIMP); + simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP); simp.simp_enabled = 1; simp.base_simp_gpa = ((hv_get_phys_addr( hv_vmbus_g_context.syn_ic_msg_page[cpu])) >> PAGE_SHIFT); - hv_vmbus_write_msr(HV_X64_MSR_SIMP, simp.as_uint64_t); + wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t); /* * Setup the Synic's event page */ - siefp.as_uint64_t = hv_vmbus_read_msr(HV_X64_MSR_SIEFP); + siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP); siefp.siefp_enabled = 1; siefp.base_siefp_gpa = ((hv_get_phys_addr( hv_vmbus_g_context.syn_ic_event_page[cpu])) >> PAGE_SHIFT); - hv_vmbus_write_msr(HV_X64_MSR_SIEFP, siefp.as_uint64_t); + wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t); - shared_sint.vector = irq_vector; /*HV_SHARED_SINT_IDT_VECTOR + 0x20; */ + /*HV_SHARED_SINT_IDT_VECTOR + 0x20; */ + shared_sint.vector = setup_args->vector; shared_sint.masked = FALSE; shared_sint.auto_eoi = FALSE; - hv_vmbus_write_msr( - HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT, + wrmsr(HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT, shared_sint.as_uint64_t); /* Enable the global synic bit */ - sctrl.as_uint64_t = hv_vmbus_read_msr(HV_X64_MSR_SCONTROL); + sctrl.as_uint64_t = rdmsr(HV_X64_MSR_SCONTROL); sctrl.enable = 1; - hv_vmbus_write_msr(HV_X64_MSR_SCONTROL, sctrl.as_uint64_t); + wrmsr(HV_X64_MSR_SCONTROL, sctrl.as_uint64_t); hv_vmbus_g_context.syn_ic_initialized = TRUE; return; - - cleanup: - - free(hv_vmbus_g_context.syn_ic_msg_page[cpu], M_DEVBUF); - free(hv_vmbus_g_context.syn_ic_msg_page[cpu], M_DEVBUF); } /** @@ -483,7 +467,7 @@ void hv_vmbus_synic_cleanup(void *arg) if (cpu != 0) return; /* TODO: XXXKYS: SMP? */ - shared_sint.as_uint64_t = hv_vmbus_read_msr( + shared_sint.as_uint64_t = rdmsr( HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT); shared_sint.masked = 1; @@ -491,25 +475,20 @@ void hv_vmbus_synic_cleanup(void *arg) /* * Disable the interrupt */ - hv_vmbus_write_msr( + wrmsr( HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT, shared_sint.as_uint64_t); - simp.as_uint64_t = hv_vmbus_read_msr(HV_X64_MSR_SIMP); + simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP); simp.simp_enabled = 0; simp.base_simp_gpa = 0; - hv_vmbus_write_msr(HV_X64_MSR_SIMP, simp.as_uint64_t); + wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t); - siefp.as_uint64_t = hv_vmbus_read_msr(HV_X64_MSR_SIEFP); + siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP); siefp.siefp_enabled = 0; siefp.base_siefp_gpa = 0; - hv_vmbus_write_msr(HV_X64_MSR_SIEFP, siefp.as_uint64_t); - - contigfree(hv_vmbus_g_context.syn_ic_msg_page[cpu], - PAGE_SIZE, M_DEVBUF); - contigfree(hv_vmbus_g_context.syn_ic_event_page[cpu], - PAGE_SIZE, M_DEVBUF); + wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t); } diff --git a/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c b/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c index e010308..08c6112 100644 --- a/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c +++ b/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c @@ -68,6 +68,7 @@ static int vmbus_rid; struct resource *intr_res; static int vmbus_irq = VMBUS_IRQ; static int vmbus_inited; +static hv_setup_args setup_args; /* only CPU 0 supported at this time */ /** * @brief Software interrupt thread routine to handle channel messages from @@ -117,7 +118,7 @@ vmbus_msg_swintr(void *dummy) * This will cause message queue rescan to possibly * deliver another msg from the hypervisor */ - hv_vmbus_write_msr(HV_X64_MSR_EOM, 0); + wrmsr(HV_X64_MSR_EOM, 0); } } } @@ -339,8 +340,7 @@ vmbus_bus_init(void) int io_bus:4; uint32_t io_lowreg; }; - - int ret; + int i, ret; unsigned int vector = 0; struct intsrc *isrc; struct ioapic_intsrc *intpin; @@ -418,15 +418,28 @@ vmbus_bus_init(void) /** * Notify the hypervisor of our irq. */ + setup_args.vector = vector; + for(i = 0; i < 2; i++) { + setup_args.page_buffers[i] = + malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO); + if (setup_args.page_buffers[i] == NULL) { + KASSERT(setup_args.page_buffers[i] != NULL, + ("Error VMBUS: malloc failed!")); + if (i > 0) + free(setup_args.page_buffers[0], M_DEVBUF); + goto cleanup4; + } + } - smp_rendezvous(NULL, hv_vmbus_synic_init, NULL, &vector); + /* only CPU #0 supported at this time */ + smp_rendezvous(NULL, hv_vmbus_synic_init, NULL, &setup_args); - /** + /* * Connect to VMBus in the root partition */ ret = hv_vmbus_connect(); - if (ret) + if (ret != 0) goto cleanup4; hv_vmbus_request_channel_offers(); @@ -440,7 +453,6 @@ vmbus_bus_init(void) bus_teardown_intr(vmbus_devp, intr_res, vmbus_cookiep); cleanup3: - bus_release_resource(vmbus_devp, SYS_RES_IRQ, vmbus_rid, intr_res); cleanup2: @@ -490,11 +502,18 @@ vmbus_init(void) static void vmbus_bus_exit(void) { + int i; + hv_vmbus_release_unattached_channels(); hv_vmbus_disconnect(); smp_rendezvous(NULL, hv_vmbus_synic_cleanup, NULL, NULL); + for(i = 0; i < 2; i++) { + if (setup_args.page_buffers[i] != 0) + free(setup_args.page_buffers[i], M_DEVBUF); + } + hv_vmbus_cleanup(); /* remove swi, bus and intr resource */ diff --git a/sys/dev/hyperv/vmbus/hv_vmbus_priv.h b/sys/dev/hyperv/vmbus/hv_vmbus_priv.h index 739acb1..4607d50 100644 --- a/sys/dev/hyperv/vmbus/hv_vmbus_priv.h +++ b/sys/dev/hyperv/vmbus/hv_vmbus_priv.h @@ -675,58 +675,6 @@ int hv_vmbus_post_message(void *buffer, size_t buf_size); int hv_vmbus_set_event(uint32_t child_rel_id); void hv_vmbus_on_events(void *); -/* - * static inline functions - * (with some helper macros for reading/writing to model specific registers) - */ - -#ifdef __x86_64__ - -#define HV_VMBUS_READ_MSR(reg, v) { \ - uint32_t h, l; \ - __asm__ __volatile__("rdmsr" \ - : "=a" (l), "=d" (h) \ - : "c" (reg)); \ - v = (((uint64_t)h) << 32) | l; \ -} - -#define HV_VMBUS_WRITE_MSR(reg, v) { \ - uint32_t h, l; \ - l = (uint32_t)(((uint64_t)(v)) & 0xFFFFFFFF); \ - h = (uint32_t)((((uint64_t)(v)) >> 32) & 0xFFFFFFFF); \ - __asm__ __volatile__("wrmsr" \ - : /* no outputs */ \ - : "c" (reg), "a" (l), "d" (h)); \ -} - -#else - -#define HV_VMBUS_READ_MSR(reg, v) \ - __asm__ __volatile__("rdmsr" \ - : "=A" (v) \ - : "c" (reg)) - -#define HV_VMBUS_WRITE_MSR(reg, v) \ - __asm__ __volatile__("wrmsr" \ - : /* no outputs */ \ - : "c" (reg), "A" ((uint64_t)v)) - -#endif - -static inline unsigned long long -hv_vmbus_read_msr(int msr) -{ - unsigned long long val; - HV_VMBUS_READ_MSR(msr, val); - return (val); -} - -static inline -void hv_vmbus_write_msr(int msr, uint64_t val) -{ - HV_VMBUS_WRITE_MSR(msr, val); - return; -} /* * The guest OS needs to register the guest ID with the hypervisor. @@ -766,5 +714,9 @@ static inline uint64_t hv_generate_guest_id( return guest_id; } +typedef struct { + unsigned int vector; + void *page_buffers[2]; +} hv_setup_args; #endif /* __HYPERV_PRIV_H__ */ diff --git a/sys/modules/hyperv/netvsc/Makefile b/sys/modules/hyperv/netvsc/Makefile index cf89826..b368510 100644 --- a/sys/modules/hyperv/netvsc/Makefile +++ b/sys/modules/hyperv/netvsc/Makefile @@ -8,7 +8,6 @@ SRCS = hv_net_vsc.c \ hv_netvsc_drv_freebsd.c \ hv_rndis_filter.c -CFLAGS += -I${.CURDIR}/../../../contrib/dev/hyperv/include \ - -I${.CURDIR}/../../../contrib/dev/hyperv/netvsc +CFLAGS += -I${.CURDIR}/../../../dev/hyperv/netvsc .include <bsd.kmod.mk> diff --git a/sys/modules/hyperv/utilities/Makefile b/sys/modules/hyperv/utilities/Makefile index e2351a3..30fc846 100644 --- a/sys/modules/hyperv/utilities/Makefile +++ b/sys/modules/hyperv/utilities/Makefile @@ -4,7 +4,8 @@ KMOD= hv_utils -SRCS = hv_util.c +SRCS = hv_kvp.c \ + hv_util.c CFLAGS+= -I${.CURDIR}/../../../dev/hyperv/include \ -I${.CURDIR}/../../../dev/hyperv/vmbus |