Index: head/sys/dev/hyperv/include/hyperv.h =================================================================== --- head/sys/dev/hyperv/include/hyperv.h +++ head/sys/dev/hyperv/include/hyperv.h @@ -77,10 +77,6 @@ #define HV_VMBUS_VERSION_WIN8 ((2 << 16) | (4)) #define HV_VMBUS_VERSION_WIN8_1 ((3 << 16) | (0)) -#define HV_VMBUS_VERSION_INVALID -1 - -#define HV_VMBUS_VERSION_CURRENT HV_VMBUS_VERSION_WIN8_1 - /* * Make maximum size of pipe payload of 16K */ @@ -537,20 +533,6 @@ uint32_t child_rel_id; } __packed hv_vmbus_channel_relid_released; -typedef struct { - hv_vmbus_channel_msg_header header; - uint32_t vmbus_version_requested; - uint32_t padding2; - uint64_t interrupt_page; - uint64_t monitor_page_1; - uint64_t monitor_page_2; -} __packed hv_vmbus_channel_initiate_contact; - -typedef struct { - hv_vmbus_channel_msg_header header; - hv_bool_uint8_t version_supported; -} __packed hv_vmbus_channel_version_response; - typedef hv_vmbus_channel_msg_header hv_vmbus_channel_unload; #define HW_MACADDR_LEN 6 Index: head/sys/dev/hyperv/vmbus/hv_channel_mgmt.c =================================================================== --- head/sys/dev/hyperv/vmbus/hv_channel_mgmt.c +++ head/sys/dev/hyperv/vmbus/hv_channel_mgmt.c @@ -40,29 +40,30 @@ * Internal functions */ -typedef void (*vmbus_msg_handler)(const hv_vmbus_channel_msg_header *msg); - typedef struct hv_vmbus_channel_msg_table_entry { hv_vmbus_channel_msg_type messageType; - vmbus_msg_handler messageHandler; + void (*messageHandler) + (struct vmbus_softc *sc, + const struct vmbus_message *msg); } hv_vmbus_channel_msg_table_entry; static void vmbus_channel_on_offer_internal(void *context); static void vmbus_channel_on_offer_rescind_internal(void *context); -static void vmbus_channel_on_offer(const hv_vmbus_channel_msg_header *hdr); -static void vmbus_channel_on_open_result( - const hv_vmbus_channel_msg_header *hdr); -static void vmbus_channel_on_offer_rescind( - const hv_vmbus_channel_msg_header *hdr); -static void vmbus_channel_on_gpadl_created( - const hv_vmbus_channel_msg_header *hdr); -static void vmbus_channel_on_gpadl_torndown( - const hv_vmbus_channel_msg_header *hdr); -static void vmbus_channel_on_offers_delivered( - const hv_vmbus_channel_msg_header *hdr); -static void vmbus_channel_on_version_response( - const hv_vmbus_channel_msg_header *hdr); +static void vmbus_channel_on_offer(struct vmbus_softc *, + const struct vmbus_message *); +static void vmbus_channel_on_open_result(struct vmbus_softc *, + const struct vmbus_message *); +static void vmbus_channel_on_offer_rescind(struct vmbus_softc *, + const struct vmbus_message *); +static void vmbus_channel_on_gpadl_created(struct vmbus_softc *, + const struct vmbus_message *); +static void vmbus_channel_on_gpadl_torndown(struct vmbus_softc *, + const struct vmbus_message *); +static void vmbus_channel_on_offers_delivered(struct vmbus_softc *, + const struct vmbus_message *); +static void vmbus_channel_on_version_response(struct vmbus_softc *, + const struct vmbus_message *); /** * Channel message dispatch table @@ -398,8 +399,11 @@ * object to process the offer synchronously */ static void -vmbus_channel_on_offer(const hv_vmbus_channel_msg_header *hdr) +vmbus_channel_on_offer(struct vmbus_softc *sc, const struct vmbus_message *msg) { + const hv_vmbus_channel_msg_header *hdr = + (const hv_vmbus_channel_msg_header *)msg->msg_data; + const hv_vmbus_channel_offer_channel *offer; hv_vmbus_channel_offer_channel *copied; @@ -476,8 +480,12 @@ * synchronously */ static void -vmbus_channel_on_offer_rescind(const hv_vmbus_channel_msg_header *hdr) +vmbus_channel_on_offer_rescind(struct vmbus_softc *sc, + const struct vmbus_message *msg) { + const hv_vmbus_channel_msg_header *hdr = + (const hv_vmbus_channel_msg_header *)msg->msg_data; + const hv_vmbus_channel_rescind_offer *rescind; hv_vmbus_channel* channel; @@ -508,8 +516,8 @@ * @brief Invoked when all offers have been delivered. */ static void -vmbus_channel_on_offers_delivered( - const hv_vmbus_channel_msg_header *hdr __unused) +vmbus_channel_on_offers_delivered(struct vmbus_softc *sc __unused, + const struct vmbus_message *msg __unused) { mtx_lock(&vmbus_chwait_lock); @@ -526,8 +534,12 @@ * response and signal the requesting thread. */ static void -vmbus_channel_on_open_result(const hv_vmbus_channel_msg_header *hdr) +vmbus_channel_on_open_result(struct vmbus_softc *sc, + const struct vmbus_message *msg) { + const hv_vmbus_channel_msg_header *hdr = + (const hv_vmbus_channel_msg_header *)msg->msg_data; + const hv_vmbus_channel_open_result *result; hv_vmbus_channel_msg_info* msg_info; hv_vmbus_channel_msg_header* requestHeader; @@ -568,8 +580,12 @@ * response and signal the requesting thread. */ static void -vmbus_channel_on_gpadl_created(const hv_vmbus_channel_msg_header *hdr) +vmbus_channel_on_gpadl_created(struct vmbus_softc *sc, + const struct vmbus_message *msg) { + const hv_vmbus_channel_msg_header *hdr = + (const hv_vmbus_channel_msg_header *)msg->msg_data; + const hv_vmbus_channel_gpadl_created *gpadl_created; hv_vmbus_channel_msg_info* msg_info; hv_vmbus_channel_msg_header* request_header; @@ -610,8 +626,12 @@ * response and signal the requesting thread */ static void -vmbus_channel_on_gpadl_torndown(const hv_vmbus_channel_msg_header *hdr) +vmbus_channel_on_gpadl_torndown(struct vmbus_softc *sc, + const struct vmbus_message *msg) { + const hv_vmbus_channel_msg_header *hdr = + (const hv_vmbus_channel_msg_header *)msg->msg_data; + const hv_vmbus_channel_gpadl_torndown *gpadl_torndown; hv_vmbus_channel_msg_info* msg_info; hv_vmbus_channel_msg_header* requestHeader; @@ -647,39 +667,11 @@ mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); } -/** - * @brief Version response handler. - * - * This is invoked when we received a response - * to our initiate contact request. Find the matching request, copy th - * response and signal the requesting thread. - */ static void -vmbus_channel_on_version_response(const hv_vmbus_channel_msg_header *hdr) +vmbus_channel_on_version_response(struct vmbus_softc *sc, + const struct vmbus_message *msg) { - hv_vmbus_channel_msg_info* msg_info; - hv_vmbus_channel_msg_header* requestHeader; - hv_vmbus_channel_initiate_contact* initiate; - const hv_vmbus_channel_version_response *versionResponse; - - versionResponse = (const hv_vmbus_channel_version_response *)hdr; - - mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); - TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor, - msg_list_entry) { - requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg; - if (requestHeader->message_type - == HV_CHANNEL_MESSAGE_INITIATED_CONTACT) { - initiate = - (hv_vmbus_channel_initiate_contact*) requestHeader; - memcpy(&msg_info->response.version_response, - versionResponse, - sizeof(hv_vmbus_channel_version_response)); - sema_post(&msg_info->wait_sema); - } - } - mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); - + vmbus_msghc_wakeup(sc, msg); } /** @@ -865,5 +857,5 @@ entry = &g_channel_message_table[msg_type]; if (entry->messageHandler) - entry->messageHandler(hdr); + entry->messageHandler(sc, msg); } Index: head/sys/dev/hyperv/vmbus/hv_connection.c =================================================================== --- head/sys/dev/hyperv/vmbus/hv_connection.c +++ head/sys/dev/hyperv/vmbus/hv_connection.c @@ -39,6 +39,7 @@ #include #include +#include #include #include @@ -49,97 +50,7 @@ { .connect_state = HV_DISCONNECTED, .next_gpadl_handle = 0xE1E10, }; -uint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008; - -static uint32_t -hv_vmbus_get_next_version(uint32_t current_ver) -{ - switch (current_ver) { - case (HV_VMBUS_VERSION_WIN7): - return(HV_VMBUS_VERSION_WS2008); - - case (HV_VMBUS_VERSION_WIN8): - return(HV_VMBUS_VERSION_WIN7); - - case (HV_VMBUS_VERSION_WIN8_1): - return(HV_VMBUS_VERSION_WIN8); - - case (HV_VMBUS_VERSION_WS2008): - default: - return(HV_VMBUS_VERSION_INVALID); - } -} - -/** - * Negotiate the highest supported hypervisor version. - */ -static int -hv_vmbus_negotiate_version(struct vmbus_softc *sc, - hv_vmbus_channel_msg_info *msg_info, uint32_t version) -{ - int ret = 0; - hv_vmbus_channel_initiate_contact *msg; - - sema_init(&msg_info->wait_sema, 0, "Msg Info Sema"); - msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg; - - msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT; - msg->vmbus_version_requested = version; - - msg->interrupt_page = sc->vmbus_evtflags_dma.hv_paddr; - msg->monitor_page_1 = sc->vmbus_mnf1_dma.hv_paddr; - msg->monitor_page_2 = sc->vmbus_mnf2_dma.hv_paddr; - - /** - * Add to list before we send the request since we may receive the - * response before returning from this routine - */ - mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); - - TAILQ_INSERT_TAIL( - &hv_vmbus_g_connection.channel_msg_anchor, - msg_info, - msg_list_entry); - - mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); - - ret = hv_vmbus_post_message( - msg, - sizeof(hv_vmbus_channel_initiate_contact)); - - if (ret != 0) { - mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); - TAILQ_REMOVE( - &hv_vmbus_g_connection.channel_msg_anchor, - msg_info, - msg_list_entry); - mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); - return (ret); - } - - /** - * Wait for the connection response - */ - ret = sema_timedwait(&msg_info->wait_sema, 5 * hz); /* KYS 5 seconds */ - - mtx_lock(&hv_vmbus_g_connection.channel_msg_lock); - TAILQ_REMOVE( - &hv_vmbus_g_connection.channel_msg_anchor, - msg_info, - msg_list_entry); - mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock); - - /** - * Check if successful - */ - if (msg_info->response.version_response.version_supported) { - hv_vmbus_g_connection.connect_state = HV_CONNECTED; - } else { - ret = ECONNREFUSED; - } - - return (ret); -} +uint32_t hv_vmbus_protocal_version; /** * Send a connect request on the partition service connection @@ -147,10 +58,6 @@ int hv_vmbus_connect(struct vmbus_softc *sc) { - int ret = 0; - uint32_t version; - hv_vmbus_channel_msg_info* msg_info = NULL; - /** * Make sure we are not connecting or connected */ @@ -171,60 +78,12 @@ mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel", NULL, MTX_DEF); - msg_info = (hv_vmbus_channel_msg_info*) - malloc(sizeof(hv_vmbus_channel_msg_info) + - sizeof(hv_vmbus_channel_initiate_contact), - M_DEVBUF, M_WAITOK | M_ZERO); - hv_vmbus_g_connection.channels = malloc(sizeof(hv_vmbus_channel*) * VMBUS_CHAN_MAX, M_DEVBUF, M_WAITOK | M_ZERO); - /* - * Find the highest vmbus version number we can support. - */ - version = HV_VMBUS_VERSION_CURRENT; - - do { - ret = hv_vmbus_negotiate_version(sc, msg_info, version); - if (ret == EWOULDBLOCK) { - /* - * We timed out. - */ - goto cleanup; - } - - if (hv_vmbus_g_connection.connect_state == HV_CONNECTED) - break; - - version = hv_vmbus_get_next_version(version); - } while (version != HV_VMBUS_VERSION_INVALID); - - hv_vmbus_protocal_version = version; - if (bootverbose) - printf("VMBUS: Protocol Version: %d.%d\n", - version >> 16, version & 0xFFFF); - sema_destroy(&msg_info->wait_sema); - free(msg_info, M_DEVBUF); + hv_vmbus_g_connection.connect_state = HV_CONNECTED; return (0); - - /* - * Cleanup after failure! - */ - cleanup: - - hv_vmbus_g_connection.connect_state = HV_DISCONNECTED; - - mtx_destroy(&hv_vmbus_g_connection.channel_lock); - mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock); - - if (msg_info) { - sema_destroy(&msg_info->wait_sema); - free(msg_info, M_DEVBUF); - } - - free(hv_vmbus_g_connection.channels, M_DEVBUF); - return (ret); } /** @@ -330,7 +189,7 @@ */ for (retries = 0; retries < 20; retries++) { ret = hv_vmbus_post_msg_via_msg_ipc(connId, - VMBUS_MSGTYPE_CHANNEL, buffer, bufferLen); + HYPERV_MSGTYPE_CHANNEL, buffer, bufferLen); if (ret == HV_STATUS_SUCCESS) return (0); Index: head/sys/dev/hyperv/vmbus/hv_vmbus_priv.h =================================================================== --- head/sys/dev/hyperv/vmbus/hv_vmbus_priv.h +++ head/sys/dev/hyperv/vmbus/hv_vmbus_priv.h @@ -95,7 +95,6 @@ hv_vmbus_channel_open_result open_result; hv_vmbus_channel_gpadl_torndown gpadl_torndown; hv_vmbus_channel_gpadl_created gpadl_created; - hv_vmbus_channel_version_response version_response; } hv_vmbus_channel_msg_response; /* Index: head/sys/dev/hyperv/vmbus/hyperv.c =================================================================== --- head/sys/dev/hyperv/vmbus/hyperv.c +++ head/sys/dev/hyperv/vmbus/hyperv.c @@ -118,6 +118,13 @@ in_paddr, out_paddr); } +uint64_t +hypercall_post_message(bus_addr_t msg_paddr) +{ + return hypercall_md(hypercall_context.hc_addr, + HYPERCALL_POST_MESSAGE, msg_paddr, 0); +} + /** * @brief Post a message using the hypervisor message IPC. * (This involves a hypercall.) Index: head/sys/dev/hyperv/vmbus/hyperv_reg.h =================================================================== --- head/sys/dev/hyperv/vmbus/hyperv_reg.h +++ head/sys/dev/hyperv/vmbus/hyperv_reg.h @@ -29,6 +29,8 @@ #ifndef _HYPERV_REG_H_ #define _HYPERV_REG_H_ +#include + /* * Hyper-V Synthetic MSRs */ @@ -130,4 +132,41 @@ #define CPUID_LEAF_HV_LIMITS 0x40000005 #define CPUID_LEAF_HV_HWFEATURES 0x40000006 +/* + * Hyper-V message types + */ +#define HYPERV_MSGTYPE_NONE 0 +#define HYPERV_MSGTYPE_CHANNEL 1 +#define HYPERV_MSGTYPE_TIMER_EXPIRED 0x80000010 + +/* + * Hypercall status codes + */ +#define HYPERCALL_STATUS_SUCCESS 0x0000 + +/* + * Hypercall input values + */ +#define HYPERCALL_POST_MESSAGE 0x005c + +/* + * Hypercall input parameters + */ + +/* + * HYPERCALL_POST_MESSAGE + */ +#define HYPERCALL_POSTMSGIN_DSIZE_MAX 240 +#define HYPERCALL_POSTMSGIN_SIZE 256 +#define HYPERCALL_POSTMSGIN_ALIGN 8 + +struct hypercall_postmsg_in { + uint32_t hc_connid; + uint32_t hc_rsvd; + uint32_t hc_msgtype; /* HYPERV_MSGTYPE_ */ + uint32_t hc_dsize; + uint8_t hc_data[HYPERCALL_POSTMSGIN_DSIZE_MAX]; +} __packed; +CTASSERT(sizeof(struct hypercall_postmsg_in) == HYPERCALL_POSTMSGIN_SIZE); + #endif /* !_HYPERV_REG_H_ */ Index: head/sys/dev/hyperv/vmbus/hyperv_var.h =================================================================== --- head/sys/dev/hyperv/vmbus/hyperv_var.h +++ head/sys/dev/hyperv/vmbus/hyperv_var.h @@ -38,4 +38,6 @@ extern u_int hyperv_features; extern u_int hyperv_recommends; +uint64_t hypercall_post_message(bus_addr_t msg_paddr); + #endif /* !_HYPERV_VAR_H_ */ Index: head/sys/dev/hyperv/vmbus/vmbus.c =================================================================== --- head/sys/dev/hyperv/vmbus/vmbus.c +++ head/sys/dev/hyperv/vmbus/vmbus.c @@ -69,10 +69,354 @@ #include #include "acpi_if.h" +/* + * NOTE: DO NOT CHANGE THESE + */ +#define VMBUS_CONNID_MESSAGE 1 +#define VMBUS_CONNID_EVENT 2 + +struct vmbus_msghc { + struct hypercall_postmsg_in *mh_inprm; + struct hypercall_postmsg_in mh_inprm_save; + struct hyperv_dma mh_inprm_dma; + + struct vmbus_message *mh_resp; + struct vmbus_message mh_resp0; +}; + +struct vmbus_msghc_ctx { + struct vmbus_msghc *mhc_free; + struct mtx mhc_free_lock; + uint32_t mhc_flags; + + struct vmbus_msghc *mhc_active; + struct mtx mhc_active_lock; +}; + +#define VMBUS_MSGHC_CTXF_DESTROY 0x0001 + +static int vmbus_init(struct vmbus_softc *); +static int vmbus_init_contact(struct vmbus_softc *, + uint32_t); + +static struct vmbus_msghc_ctx *vmbus_msghc_ctx_create(bus_dma_tag_t); +static void vmbus_msghc_ctx_destroy( + struct vmbus_msghc_ctx *); +static void vmbus_msghc_ctx_free(struct vmbus_msghc_ctx *); +static struct vmbus_msghc *vmbus_msghc_alloc(bus_dma_tag_t); +static void vmbus_msghc_free(struct vmbus_msghc *); +static struct vmbus_msghc *vmbus_msghc_get1(struct vmbus_msghc_ctx *, + uint32_t); + struct vmbus_softc *vmbus_sc; extern inthand_t IDTVEC(vmbus_isr); +static const uint32_t vmbus_version[] = { + HV_VMBUS_VERSION_WIN8_1, + HV_VMBUS_VERSION_WIN8, + HV_VMBUS_VERSION_WIN7, + HV_VMBUS_VERSION_WS2008 +}; + +static struct vmbus_msghc * +vmbus_msghc_alloc(bus_dma_tag_t parent_dtag) +{ + struct vmbus_msghc *mh; + + mh = malloc(sizeof(*mh), M_DEVBUF, M_WAITOK | M_ZERO); + + mh->mh_inprm = hyperv_dmamem_alloc(parent_dtag, + HYPERCALL_POSTMSGIN_ALIGN, 0, HYPERCALL_POSTMSGIN_SIZE, + &mh->mh_inprm_dma, BUS_DMA_WAITOK); + if (mh->mh_inprm == NULL) { + free(mh, M_DEVBUF); + return NULL; + } + return mh; +} + +static void +vmbus_msghc_free(struct vmbus_msghc *mh) +{ + hyperv_dmamem_free(&mh->mh_inprm_dma, mh->mh_inprm); + free(mh, M_DEVBUF); +} + +static void +vmbus_msghc_ctx_free(struct vmbus_msghc_ctx *mhc) +{ + KASSERT(mhc->mhc_active == NULL, ("still have active msg hypercall")); + KASSERT(mhc->mhc_free == NULL, ("still have hypercall msg")); + + mtx_destroy(&mhc->mhc_free_lock); + mtx_destroy(&mhc->mhc_active_lock); + free(mhc, M_DEVBUF); +} + +static struct vmbus_msghc_ctx * +vmbus_msghc_ctx_create(bus_dma_tag_t parent_dtag) +{ + struct vmbus_msghc_ctx *mhc; + + mhc = malloc(sizeof(*mhc), M_DEVBUF, M_WAITOK | M_ZERO); + mtx_init(&mhc->mhc_free_lock, "vmbus msghc free", NULL, MTX_DEF); + mtx_init(&mhc->mhc_active_lock, "vmbus msghc act", NULL, MTX_DEF); + + mhc->mhc_free = vmbus_msghc_alloc(parent_dtag); + if (mhc->mhc_free == NULL) { + vmbus_msghc_ctx_free(mhc); + return NULL; + } + return mhc; +} + +static struct vmbus_msghc * +vmbus_msghc_get1(struct vmbus_msghc_ctx *mhc, uint32_t dtor_flag) +{ + struct vmbus_msghc *mh; + + mtx_lock(&mhc->mhc_free_lock); + + while ((mhc->mhc_flags & dtor_flag) == 0 && mhc->mhc_free == NULL) { + mtx_sleep(&mhc->mhc_free, &mhc->mhc_free_lock, 0, + "gmsghc", 0); + } + if (mhc->mhc_flags & dtor_flag) { + /* Being destroyed */ + mh = NULL; + } else { + mh = mhc->mhc_free; + KASSERT(mh != NULL, ("no free hypercall msg")); + KASSERT(mh->mh_resp == NULL, + ("hypercall msg has pending response")); + mhc->mhc_free = NULL; + } + + mtx_unlock(&mhc->mhc_free_lock); + + return mh; +} + +struct vmbus_msghc * +vmbus_msghc_get(struct vmbus_softc *sc, size_t dsize) +{ + struct hypercall_postmsg_in *inprm; + struct vmbus_msghc *mh; + + if (dsize > HYPERCALL_POSTMSGIN_DSIZE_MAX) + return NULL; + + mh = vmbus_msghc_get1(sc->vmbus_msg_hc, VMBUS_MSGHC_CTXF_DESTROY); + if (mh == NULL) + return NULL; + + inprm = mh->mh_inprm; + memset(inprm, 0, HYPERCALL_POSTMSGIN_SIZE); + inprm->hc_connid = VMBUS_CONNID_MESSAGE; + inprm->hc_msgtype = HYPERV_MSGTYPE_CHANNEL; + inprm->hc_dsize = dsize; + + return mh; +} + +void +vmbus_msghc_put(struct vmbus_softc *sc, struct vmbus_msghc *mh) +{ + struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc; + + KASSERT(mhc->mhc_active == NULL, ("msg hypercall is active")); + mh->mh_resp = NULL; + + mtx_lock(&mhc->mhc_free_lock); + KASSERT(mhc->mhc_free == NULL, ("has free hypercall msg")); + mhc->mhc_free = mh; + mtx_unlock(&mhc->mhc_free_lock); + wakeup(&mhc->mhc_free); +} + +void * +vmbus_msghc_dataptr(struct vmbus_msghc *mh) +{ + return mh->mh_inprm->hc_data; +} + +static void +vmbus_msghc_ctx_destroy(struct vmbus_msghc_ctx *mhc) +{ + struct vmbus_msghc *mh; + + mtx_lock(&mhc->mhc_free_lock); + mhc->mhc_flags |= VMBUS_MSGHC_CTXF_DESTROY; + mtx_unlock(&mhc->mhc_free_lock); + wakeup(&mhc->mhc_free); + + mh = vmbus_msghc_get1(mhc, 0); + if (mh == NULL) + panic("can't get msghc"); + + vmbus_msghc_free(mh); + vmbus_msghc_ctx_free(mhc); +} + +int +vmbus_msghc_exec_noresult(struct vmbus_msghc *mh) +{ + sbintime_t time = SBT_1MS; + int i; + + /* + * Save the input parameter so that we could restore the input + * parameter if the Hypercall failed. + * + * XXX + * Is this really necessary?! i.e. Will the Hypercall ever + * overwrite the input parameter? + */ + memcpy(&mh->mh_inprm_save, mh->mh_inprm, HYPERCALL_POSTMSGIN_SIZE); + + /* + * In order to cope with transient failures, e.g. insufficient + * resources on host side, we retry the post message Hypercall + * several times. 20 retries seem sufficient. + */ +#define HC_RETRY_MAX 20 + + for (i = 0; i < HC_RETRY_MAX; ++i) { + uint64_t status; + + status = hypercall_post_message(mh->mh_inprm_dma.hv_paddr); + if (status == HYPERCALL_STATUS_SUCCESS) + return 0; + + pause_sbt("hcpmsg", time, 0, C_HARDCLOCK); + if (time < SBT_1S * 2) + time *= 2; + + /* Restore input parameter and try again */ + memcpy(mh->mh_inprm, &mh->mh_inprm_save, + HYPERCALL_POSTMSGIN_SIZE); + } + +#undef HC_RETRY_MAX + + return EIO; +} + +int +vmbus_msghc_exec(struct vmbus_softc *sc, struct vmbus_msghc *mh) +{ + struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc; + int error; + + KASSERT(mh->mh_resp == NULL, ("hypercall msg has pending response")); + + mtx_lock(&mhc->mhc_active_lock); + KASSERT(mhc->mhc_active == NULL, ("pending active msg hypercall")); + mhc->mhc_active = mh; + mtx_unlock(&mhc->mhc_active_lock); + + error = vmbus_msghc_exec_noresult(mh); + if (error) { + mtx_lock(&mhc->mhc_active_lock); + KASSERT(mhc->mhc_active == mh, ("msghc mismatch")); + mhc->mhc_active = NULL; + mtx_unlock(&mhc->mhc_active_lock); + } + return error; +} + +const struct vmbus_message * +vmbus_msghc_wait_result(struct vmbus_softc *sc, struct vmbus_msghc *mh) +{ + struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc; + + mtx_lock(&mhc->mhc_active_lock); + + KASSERT(mhc->mhc_active == mh, ("msghc mismatch")); + while (mh->mh_resp == NULL) { + mtx_sleep(&mhc->mhc_active, &mhc->mhc_active_lock, 0, + "wmsghc", 0); + } + mhc->mhc_active = NULL; + + mtx_unlock(&mhc->mhc_active_lock); + + return mh->mh_resp; +} + +void +vmbus_msghc_wakeup(struct vmbus_softc *sc, const struct vmbus_message *msg) +{ + struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc; + struct vmbus_msghc *mh; + + mtx_lock(&mhc->mhc_active_lock); + + mh = mhc->mhc_active; + KASSERT(mh != NULL, ("no pending msg hypercall")); + memcpy(&mh->mh_resp0, msg, sizeof(mh->mh_resp0)); + mh->mh_resp = &mh->mh_resp0; + + mtx_unlock(&mhc->mhc_active_lock); + wakeup(&mhc->mhc_active); +} + +static int +vmbus_init_contact(struct vmbus_softc *sc, uint32_t version) +{ + struct vmbus_chanmsg_init_contact *req; + const struct vmbus_chanmsg_version_resp *resp; + const struct vmbus_message *msg; + struct vmbus_msghc *mh; + int error, supp = 0; + + mh = vmbus_msghc_get(sc, sizeof(*req)); + if (mh == NULL) + return ENXIO; + + req = vmbus_msghc_dataptr(mh); + req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_INIT_CONTACT; + req->chm_ver = version; + req->chm_evtflags = sc->vmbus_evtflags_dma.hv_paddr; + req->chm_mnf1 = sc->vmbus_mnf1_dma.hv_paddr; + req->chm_mnf2 = sc->vmbus_mnf2_dma.hv_paddr; + + error = vmbus_msghc_exec(sc, mh); + if (error) { + vmbus_msghc_put(sc, mh); + return error; + } + + msg = vmbus_msghc_wait_result(sc, mh); + resp = (const struct vmbus_chanmsg_version_resp *)msg->msg_data; + supp = resp->chm_supp; + + vmbus_msghc_put(sc, mh); + + return (supp ? 0 : EOPNOTSUPP); +} + +static int +vmbus_init(struct vmbus_softc *sc) +{ + int i; + + for (i = 0; i < nitems(vmbus_version); ++i) { + int error; + + error = vmbus_init_contact(sc, vmbus_version[i]); + if (!error) { + hv_vmbus_protocal_version = vmbus_version[i]; + device_printf(sc->vmbus_dev, "version %u.%u\n", + (hv_vmbus_protocal_version >> 16), + (hv_vmbus_protocal_version & 0xffff)); + return 0; + } + } + return ENXIO; +} + static void vmbus_msg_task(void *xsc, int pending __unused) { @@ -81,19 +425,19 @@ msg = VMBUS_PCPU_GET(sc, message, curcpu) + VMBUS_SINT_MESSAGE; for (;;) { - if (msg->msg_type == VMBUS_MSGTYPE_NONE) { + if (msg->msg_type == HYPERV_MSGTYPE_NONE) { /* No message */ break; - } else if (msg->msg_type == VMBUS_MSGTYPE_CHANNEL) { + } else if (msg->msg_type == HYPERV_MSGTYPE_CHANNEL) { /* Channel message */ vmbus_chan_msgproc(sc, __DEVOLATILE(const struct vmbus_message *, msg)); } - msg->msg_type = VMBUS_MSGTYPE_NONE; + msg->msg_type = HYPERV_MSGTYPE_NONE; /* * Make sure the write to msg_type (i.e. set to - * VMBUS_MSGTYPE_NONE) happens before we read the + * HYPERV_MSGTYPE_NONE) happens before we read the * msg_flags and EOMing. Otherwise, the EOMing will * not deliver any more messages since there is no * empty slot @@ -127,14 +471,14 @@ * TODO: move this to independent IDT vector. */ msg = msg_base + VMBUS_SINT_TIMER; - if (msg->msg_type == VMBUS_MSGTYPE_TIMER_EXPIRED) { - msg->msg_type = VMBUS_MSGTYPE_NONE; + if (msg->msg_type == HYPERV_MSGTYPE_TIMER_EXPIRED) { + msg->msg_type = HYPERV_MSGTYPE_NONE; vmbus_et_intr(frame); /* * Make sure the write to msg_type (i.e. set to - * VMBUS_MSGTYPE_NONE) happens before we read the + * HYPERV_MSGTYPE_NONE) happens before we read the * msg_flags and EOMing. Otherwise, the EOMing will * not deliver any more messages since there is no * empty slot @@ -166,7 +510,7 @@ * Check messages. Mainly management stuffs; ultra low rate. */ msg = msg_base + VMBUS_SINT_MESSAGE; - if (__predict_false(msg->msg_type != VMBUS_MSGTYPE_NONE)) { + if (__predict_false(msg->msg_type != HYPERV_MSGTYPE_NONE)) { taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu), VMBUS_PCPU_PTR(sc, message_task, cpu)); } @@ -620,6 +964,16 @@ sc->vmbus_flags |= VMBUS_FLAG_ATTACHED; /* + * Create context for "post message" Hypercalls + */ + sc->vmbus_msg_hc = vmbus_msghc_ctx_create( + bus_get_dma_tag(sc->vmbus_dev)); + if (sc->vmbus_msg_hc == NULL) { + ret = ENXIO; + goto cleanup; + } + + /* * Allocate DMA stuffs. */ ret = vmbus_dma_alloc(sc); @@ -648,6 +1002,10 @@ if (ret != 0) goto cleanup; + ret = vmbus_init(sc); + if (ret != 0) + goto cleanup; + if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 || hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7) sc->vmbus_event_proc = vmbus_event_proc_compat; @@ -665,6 +1023,10 @@ cleanup: vmbus_intr_teardown(sc); vmbus_dma_free(sc); + if (sc->vmbus_msg_hc != NULL) { + vmbus_msghc_ctx_destroy(sc->vmbus_msg_hc); + sc->vmbus_msg_hc = NULL; + } return (ret); } @@ -737,6 +1099,11 @@ vmbus_intr_teardown(sc); vmbus_dma_free(sc); + if (sc->vmbus_msg_hc != NULL) { + vmbus_msghc_ctx_destroy(sc->vmbus_msg_hc); + sc->vmbus_msg_hc = NULL; + } + return (0); } Index: head/sys/dev/hyperv/vmbus/vmbus_reg.h =================================================================== --- head/sys/dev/hyperv/vmbus/vmbus_reg.h +++ head/sys/dev/hyperv/vmbus/vmbus_reg.h @@ -39,7 +39,7 @@ #define VMBUS_MSG_SIZE 256 struct vmbus_message { - uint32_t msg_type; /* VMBUS_MSGTYPE_ */ + uint32_t msg_type; /* HYPERV_MSGTYPE_ */ uint8_t msg_dsize; /* data size */ uint8_t msg_flags; /* VMBUS_MSGFLAG_ */ uint16_t msg_rsvd; @@ -48,10 +48,6 @@ } __packed; CTASSERT(sizeof(struct vmbus_message) == VMBUS_MSG_SIZE); -#define VMBUS_MSGTYPE_NONE 0 -#define VMBUS_MSGTYPE_CHANNEL 1 -#define VMBUS_MSGTYPE_TIMER_EXPIRED 0x80000010 - #define VMBUS_MSGFLAG_PENDING 0x01 /* @@ -81,4 +77,34 @@ #define VMBUS_CHAN_MAX_COMPAT 256 #define VMBUS_CHAN_MAX (VMBUS_EVTFLAG_LEN * VMBUS_EVTFLAGS_MAX) +/* + * Channel messages + * - Embedded in vmbus_message.msg_data, e.g. response. + * - Embedded in hypercall_postmsg_in.hc_data, e.g. request. + */ + +#define VMBUS_CHANMSG_TYPE_INIT_CONTACT 14 /* REQ */ +#define VMBUS_CHANMSG_TYPE_VERSION_RESP 15 /* RESP */ + +struct vmbus_chanmsg_hdr { + uint32_t chm_type; /* VMBUS_CHANMSG_TYPE_ */ + uint32_t chm_rsvd; +} __packed; + +/* VMBUS_CHANMSG_TYPE_INIT_CONTACT */ +struct vmbus_chanmsg_init_contact { + struct vmbus_chanmsg_hdr chm_hdr; + uint32_t chm_ver; + uint32_t chm_rsvd; + uint64_t chm_evtflags; + uint64_t chm_mnf1; + uint64_t chm_mnf2; +} __packed; + +/* VMBUS_CHANMSG_TYPE_VERSION_RESP */ +struct vmbus_chanmsg_version_resp { + struct vmbus_chanmsg_hdr chm_hdr; + uint8_t chm_supp; +} __packed; + #endif /* !_VMBUS_REG_H_ */ Index: head/sys/dev/hyperv/vmbus/vmbus_var.h =================================================================== --- head/sys/dev/hyperv/vmbus/vmbus_var.h +++ head/sys/dev/hyperv/vmbus/vmbus_var.h @@ -69,6 +69,7 @@ u_long *vmbus_rx_evtflags; /* compat evtflgs from host */ + struct vmbus_msghc_ctx *vmbus_msg_hc; struct vmbus_pcpu_data vmbus_pcpu[MAXCPU]; /* Rarely used fields */ @@ -108,6 +109,7 @@ struct hv_vmbus_channel; struct trapframe; struct vmbus_message; +struct vmbus_msghc; void vmbus_on_channel_open(const struct hv_vmbus_channel *); void vmbus_event_proc(struct vmbus_softc *, int); @@ -118,4 +120,13 @@ void vmbus_chan_msgproc(struct vmbus_softc *, const struct vmbus_message *); +struct vmbus_msghc *vmbus_msghc_get(struct vmbus_softc *, size_t); +void vmbus_msghc_put(struct vmbus_softc *, struct vmbus_msghc *); +void *vmbus_msghc_dataptr(struct vmbus_msghc *); +int vmbus_msghc_exec_noresult(struct vmbus_msghc *); +int vmbus_msghc_exec(struct vmbus_softc *, struct vmbus_msghc *); +const struct vmbus_message *vmbus_msghc_wait_result(struct vmbus_softc *, + struct vmbus_msghc *); +void vmbus_msghc_wakeup(struct vmbus_softc *, const struct vmbus_message *); + #endif /* !_VMBUS_VAR_H_ */