Latest update from Microsoft.
Obtained from: Microsoft Hyper-v dev team
This commit is contained in:
parent
46ed9e4908
commit
d940bfec8c
@ -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
|
||||
|
@ -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-
|
||||
|
||||
*******************************************************************************
|
982
sys/dev/hyperv/utilities/hv_kvp.c
Normal file
982
sys/dev/hyperv/utilities/hv_kvp.c
Normal file
@ -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);
|
||||
}
|
287
sys/dev/hyperv/utilities/hv_kvp.h
Normal file
287
sys/dev/hyperv/utilities/hv_kvp.h
Normal file
@ -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 */
|
@ -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;
|
||||
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);
|
||||
|
||||
tmp = host_tns;
|
||||
tsec = tmp / HV_NANO_SEC;
|
||||
host_ts.tv_nsec = (long) (tmp - (tsec * HV_NANO_SEC));
|
||||
host_ts.tv_sec = tsec;
|
||||
nanotime(&guest_ts);
|
||||
|
||||
diff = (int64_t)host_ts.tv_sec - (int64_t)guest_ts.tv_sec;
|
||||
|
||||
/* force time sync with host after reboot, restore, etc. */
|
||||
mtx_lock(&Giant);
|
||||
tc_setclock(&host_ts);
|
||||
resettodr();
|
||||
mtx_unlock(&Giant);
|
||||
/*
|
||||
* 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;
|
||||
|
||||
time_msg = malloc(sizeof(time_sync_data), M_DEVBUF, M_NOWAIT);
|
||||
|
||||
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, (void *) hosttime);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
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)
|
||||
|
@ -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
|
||||
@ -50,7 +51,6 @@
|
||||
#define HV_NANOSECONDS_PER_SEC 1000000000L
|
||||
|
||||
|
||||
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,
|
||||
@ -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);
|
||||
version = rdmsr(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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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__ */
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user