iPXE
Defines | Functions | Variables
hyperv.c File Reference

Hyper-V driver. More...

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <byteswap.h>
#include <pic8259.h>
#include <ipxe/malloc.h>
#include <ipxe/device.h>
#include <ipxe/timer.h>
#include <ipxe/quiesce.h>
#include <ipxe/cpuid.h>
#include <ipxe/msr.h>
#include <ipxe/hyperv.h>
#include <ipxe/vmbus.h>
#include "hyperv.h"

Go to the source code of this file.

Defines

#define HV_MESSAGE_MAX_WAIT_MS   1000
 Maximum time to wait for a message response.
#define HV_TIMER_HZ   10000000
 Hyper-V timer frequency (fixed 10Mhz)
#define HV_TIMER_SHIFT   18
 Hyper-V timer scale factor (used to avoid 64-bit division)
#define EHV(status)   EPLATFORM ( EINFO_EPLATFORM, (status) )
 Convert a Hyper-V status code to an iPXE status code.

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
int hv_alloc_pages (struct hv_hypervisor *hv,...)
 Allocate zeroed pages.
void hv_free_pages (struct hv_hypervisor *hv,...)
 Free pages.
static int hv_alloc_message (struct hv_hypervisor *hv)
 Allocate message buffer.
static void hv_free_message (struct hv_hypervisor *hv)
 Free message buffer.
static int hv_check_hv (void)
 Check whether or not we are running in Hyper-V.
static int hv_check_features (struct hv_hypervisor *hv)
 Check required features.
static int hv_check_uefi (struct hv_hypervisor *hv)
 Check that Gen 2 UEFI firmware is not running.
static void hv_map_hypercall (struct hv_hypervisor *hv)
 Map hypercall page.
static void hv_unmap_hypercall (struct hv_hypervisor *hv)
 Unmap hypercall page.
static void hv_map_synic (struct hv_hypervisor *hv)
 Map synthetic interrupt controller.
static void hv_unmap_synic_no_scontrol (struct hv_hypervisor *hv)
 Unmap synthetic interrupt controller, leaving SCONTROL untouched.
static void hv_unmap_synic (struct hv_hypervisor *hv)
 Unmap synthetic interrupt controller.
void hv_enable_sint (struct hv_hypervisor *hv, unsigned int sintx)
 Enable synthetic interrupt.
void hv_disable_sint (struct hv_hypervisor *hv, unsigned int sintx)
 Disable synthetic interrupt.
int hv_post_message (struct hv_hypervisor *hv, unsigned int id, unsigned int type, const void *data, size_t len)
 Post message.
int hv_wait_for_message (struct hv_hypervisor *hv, unsigned int sintx)
 Wait for received message.
int hv_signal_event (struct hv_hypervisor *hv, unsigned int id, unsigned int flag)
 Signal event.
static int hv_probe (struct root_device *rootdev)
 Probe root device.
static void hv_remove (struct root_device *rootdev)
 Remove root device.
static void hv_quiesce (void)
 Quiesce system.
static void hv_unquiesce (void)
 Unquiesce system.
static int hv_timer_probe (void)
 Probe timer.
static unsigned long hv_currticks (void)
 Get current system time in ticks.
static void hv_udelay (unsigned long usecs)
 Delay for a fixed number of microseconds.
struct timer hv_timer __timer (TIMER_PREFERRED)
 Hyper-V timer.
 REQUIRING_SYMBOL (hv_root_device)
 REQUIRE_OBJECT (netvsc)

Variables

static struct root_driver hv_root_driver
 Hyper-V root device driver.
struct root_device hv_root_device __root_device
 Hyper-V root device.
struct quiescer hv_quiescer __quiescer
 Hyper-V quiescer.

Detailed Description

Hyper-V driver.

Definition in file hyperv.c.


Define Documentation

#define HV_MESSAGE_MAX_WAIT_MS   1000

Maximum time to wait for a message response.

This is a policy decision.

Definition at line 54 of file hyperv.c.

Referenced by hv_wait_for_message().

#define HV_TIMER_HZ   10000000

Hyper-V timer frequency (fixed 10Mhz)

Definition at line 57 of file hyperv.c.

Referenced by hv_currticks(), and hv_udelay().

#define HV_TIMER_SHIFT   18

Hyper-V timer scale factor (used to avoid 64-bit division)

Definition at line 60 of file hyperv.c.

Referenced by hv_currticks().

#define EHV (   status)    EPLATFORM ( EINFO_EPLATFORM, (status) )

Convert a Hyper-V status code to an iPXE status code.

Parameters:
statusHyper-V status code
Return values:
rciPXE status code (before negation)

Definition at line 68 of file hyperv.c.

Referenced by hv_post_message(), and hv_signal_event().


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
int hv_alloc_pages ( struct hv_hypervisor hv,
  ... 
)

Allocate zeroed pages.

Parameters:
hvHyper-V hypervisor
...Page addresses to fill in, terminated by NULL
Return values:
rcReturn status code

Definition at line 78 of file hyperv.c.

References ENOMEM, free_dma(), malloc_dma(), memset(), NULL, PAGE_SIZE, va_arg, va_end, and va_start.

Referenced by hv_probe(), and vmbus_probe().

                                                 {
        va_list args;
        void **page;
        int i;

        /* Allocate and zero pages */
        va_start ( args, hv );
        for ( i = 0 ; ( ( page = va_arg ( args, void ** ) ) != NULL ); i++ ) {
                *page = malloc_dma ( PAGE_SIZE, PAGE_SIZE );
                if ( ! *page )
                        goto err_alloc;
                memset ( *page, 0, PAGE_SIZE );
        }
        va_end ( args );

        return 0;

 err_alloc:
        va_end ( args );
        va_start ( args, hv );
        for ( ; i >= 0 ; i-- ) {
                page = va_arg ( args, void ** );
                free_dma ( *page, PAGE_SIZE );
        }
        va_end ( args );
        return -ENOMEM;
}
void hv_free_pages ( struct hv_hypervisor hv,
  ... 
)

Free pages.

Parameters:
hvHyper-V hypervisor
...Page addresses, terminated by NULL

Definition at line 113 of file hyperv.c.

References free_dma(), NULL, PAGE_SIZE, va_arg, va_end, and va_start.

Referenced by hv_probe(), hv_remove(), vmbus_probe(), and vmbus_remove().

                                                {
        va_list args;
        void *page;

        va_start ( args, hv );
        while ( ( page = va_arg ( args, void * ) ) != NULL )
                free_dma ( page, PAGE_SIZE );
        va_end ( args );
}
static int hv_alloc_message ( struct hv_hypervisor hv) [static]

Allocate message buffer.

Parameters:
hvHyper-V hypervisor
Return values:
rcReturn status code

Definition at line 129 of file hyperv.c.

References ENOMEM, malloc_dma(), and hv_hypervisor::message.

Referenced by hv_probe().

                                                         {

        /* Allocate buffer.  Must be aligned to at least 8 bytes and
         * must not cross a page boundary, so align on its own size.
         */
        hv->message = malloc_dma ( sizeof ( *hv->message ),
                                   sizeof ( *hv->message ) );
        if ( ! hv->message )
                return -ENOMEM;

        return 0;
}
static void hv_free_message ( struct hv_hypervisor hv) [static]

Free message buffer.

Parameters:
hvHyper-V hypervisor

Definition at line 147 of file hyperv.c.

References free_dma(), and hv_hypervisor::message.

Referenced by hv_probe(), and hv_remove().

                                                         {

        /* Free buffer */
        free_dma ( hv->message, sizeof ( *hv->message ) );
}
static int hv_check_hv ( void  ) [static]

Check whether or not we are running in Hyper-V.

Return values:
rcReturn status code

Definition at line 158 of file hyperv.c.

References CPUID_FEATURES_INTEL_ECX_HYPERVISOR, DBGC, x86_feature_registers::ecx, ENODEV, HV_CPUID_INTERFACE_ID, HV_INTERFACE_ID, x86_features::intel, and x86_features().

Referenced by hv_probe(), and hv_timer_probe().

                                {
        struct x86_features features;
        uint32_t interface_id;
        uint32_t discard_ebx;
        uint32_t discard_ecx;
        uint32_t discard_edx;

        /* Check for presence of a hypervisor (not necessarily Hyper-V) */
        x86_features ( &features );
        if ( ! ( features.intel.ecx & CPUID_FEATURES_INTEL_ECX_HYPERVISOR ) ) {
                DBGC ( HV_INTERFACE_ID, "HV not running in a hypervisor\n" );
                return -ENODEV;
        }

        /* Check that hypervisor is Hyper-V */
        cpuid ( HV_CPUID_INTERFACE_ID, 0, &interface_id, &discard_ebx,
                &discard_ecx, &discard_edx );
        if ( interface_id != HV_INTERFACE_ID ) {
                DBGC ( HV_INTERFACE_ID, "HV not running in Hyper-V (interface "
                       "ID %#08x)\n", interface_id );
                return -ENODEV;
        }

        return 0;
}
static int hv_check_features ( struct hv_hypervisor hv) [static]

Check required features.

Parameters:
hvHyper-V hypervisor
Return values:
rcReturn status code

Definition at line 190 of file hyperv.c.

References DBGC, EACCES, ENODEV, HV_CPUID_FEATURES, HV_FEATURES_AVAIL_HYPERCALL_MSR, HV_FEATURES_AVAIL_SYNIC_MSR, HV_FEATURES_PERM_POST_MESSAGES, and HV_FEATURES_PERM_SIGNAL_EVENTS.

Referenced by hv_probe().

                                                          {
        uint32_t available;
        uint32_t permissions;
        uint32_t discard_ecx;
        uint32_t discard_edx;

        /* Check that required features and privileges are available */
        cpuid ( HV_CPUID_FEATURES, 0, &available, &permissions, &discard_ecx,
                &discard_edx );
        if ( ! ( available & HV_FEATURES_AVAIL_HYPERCALL_MSR ) ) {
                DBGC ( hv, "HV %p has no hypercall MSRs (features %08x:%08x)\n",
                       hv, available, permissions );
                return -ENODEV;
        }
        if ( ! ( available & HV_FEATURES_AVAIL_SYNIC_MSR ) ) {
                DBGC ( hv, "HV %p has no SynIC MSRs (features %08x:%08x)\n",
                       hv, available, permissions );
                return -ENODEV;
        }
        if ( ! ( permissions & HV_FEATURES_PERM_POST_MESSAGES ) ) {
                DBGC ( hv, "HV %p cannot post messages (features %08x:%08x)\n",
                       hv, available, permissions );
                return -EACCES;
        }
        if ( ! ( permissions & HV_FEATURES_PERM_SIGNAL_EVENTS ) ) {
                DBGC ( hv, "HV %p cannot signal events (features %08x:%08x)",
                       hv, available, permissions );
                return -EACCES;
        }

        return 0;
}
static int hv_check_uefi ( struct hv_hypervisor hv) [static]

Check that Gen 2 UEFI firmware is not running.

Parameters:
hvHyper-V hypervisor
Return values:
rcReturn status code

We must not steal ownership from the Gen 2 UEFI firmware, since doing so will cause an immediate crash. Avoid this by checking for the guest OS identity known to be used by the Gen 2 UEFI firmware.

Definition at line 233 of file hyperv.c.

References DBGC, ENOTSUP, HV_GUEST_OS_ID_UEFI, and HV_X64_MSR_GUEST_OS_ID.

Referenced by hv_probe().

                                                      {
        uint64_t guest_os_id;

        /* Check for UEFI firmware's guest OS identity */
        guest_os_id = rdmsr ( HV_X64_MSR_GUEST_OS_ID );
        if ( guest_os_id == HV_GUEST_OS_ID_UEFI ) {
                DBGC ( hv, "HV %p is owned by UEFI firmware\n", hv );
                return -ENOTSUP;
        }

        return 0;
}
static void hv_map_hypercall ( struct hv_hypervisor hv) [static]

Map hypercall page.

Parameters:
hvHyper-V hypervisor

Definition at line 251 of file hyperv.c.

References __attribute__, build, DBGC, DBGC2, ebx, ecx, edx, HV_CPUID_HYPERVISOR_ID, HV_CPUID_VENDOR_ID, HV_GUEST_OS_ID_IPXE, HV_HYPERCALL_ENABLE, HV_X64_MSR_GUEST_OS_ID, HV_X64_MSR_HYPERCALL, hypercall, hv_hypervisor::hypercall, PAGE_SIZE, vendor_id, version, and virt_to_phys().

Referenced by hv_probe(), and hv_unquiesce().

                                                          {
        union {
                struct {
                        uint32_t ebx;
                        uint32_t ecx;
                        uint32_t edx;
                } __attribute__ (( packed ));
                char text[ 13 /* "bbbbccccdddd" + NUL */ ];
        } vendor_id;
        uint32_t build;
        uint32_t version;
        uint32_t discard_eax;
        uint32_t discard_ecx;
        uint32_t discard_edx;
        uint64_t guest_os_id;
        uint64_t hypercall;

        /* Report guest OS identity */
        guest_os_id = rdmsr ( HV_X64_MSR_GUEST_OS_ID );
        if ( guest_os_id != 0 ) {
                DBGC ( hv, "HV %p guest OS ID MSR was %#08llx\n",
                       hv, guest_os_id );
        }
        guest_os_id = HV_GUEST_OS_ID_IPXE;
        DBGC2 ( hv, "HV %p guest OS ID MSR is %#08llx\n", hv, guest_os_id );
        wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id );

        /* Get hypervisor system identity (for debugging) */
        cpuid ( HV_CPUID_VENDOR_ID, 0, &discard_eax, &vendor_id.ebx,
                &vendor_id.ecx, &vendor_id.edx );
        vendor_id.text[ sizeof ( vendor_id.text ) - 1 ] = '\0';
        cpuid ( HV_CPUID_HYPERVISOR_ID, 0, &build, &version, &discard_ecx,
                &discard_edx );
        DBGC ( hv, "HV %p detected \"%s\" version %d.%d build %d\n", hv,
               vendor_id.text, ( version >> 16 ), ( version & 0xffff ), build );

        /* Map hypercall page */
        hypercall = rdmsr ( HV_X64_MSR_HYPERCALL );
        hypercall &= ( PAGE_SIZE - 1 );
        hypercall |= ( virt_to_phys ( hv->hypercall ) | HV_HYPERCALL_ENABLE );
        DBGC2 ( hv, "HV %p hypercall MSR is %#08llx\n", hv, hypercall );
        wrmsr ( HV_X64_MSR_HYPERCALL, hypercall );
}
static void hv_unmap_hypercall ( struct hv_hypervisor hv) [static]

Unmap hypercall page.

Parameters:
hvHyper-V hypervisor

Definition at line 300 of file hyperv.c.

References DBGC2, HV_HYPERCALL_ENABLE, HV_X64_MSR_GUEST_OS_ID, HV_X64_MSR_HYPERCALL, hypercall, and PAGE_SIZE.

Referenced by hv_probe(), hv_quiesce(), and hv_remove().

                                                            {
        uint64_t hypercall;
        uint64_t guest_os_id;

        /* Unmap the hypercall page */
        hypercall = rdmsr ( HV_X64_MSR_HYPERCALL );
        hypercall &= ( ( PAGE_SIZE - 1 ) & ~HV_HYPERCALL_ENABLE );
        DBGC2 ( hv, "HV %p hypercall MSR is %#08llx\n", hv, hypercall );
        wrmsr ( HV_X64_MSR_HYPERCALL, hypercall );

        /* Reset the guest OS identity */
        guest_os_id = 0;
        DBGC2 ( hv, "HV %p guest OS ID MSR is %#08llx\n", hv, guest_os_id );
        wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id );
}
static void hv_map_synic ( struct hv_hypervisor hv) [static]

Map synthetic interrupt controller.

Parameters:
hvHyper-V hypervisor

Definition at line 321 of file hyperv.c.

References DBGC2, hv_synic::event, HV_SCONTROL_ENABLE, HV_SIEFP_ENABLE, HV_SIMP_ENABLE, HV_X64_MSR_SCONTROL, HV_X64_MSR_SIEFP, HV_X64_MSR_SIMP, memset(), hv_synic::message, PAGE_SIZE, hv_hypervisor::synic, and virt_to_phys().

Referenced by hv_probe(), and hv_unquiesce().

                                                      {
        uint64_t simp;
        uint64_t siefp;
        uint64_t scontrol;

        /* Zero SynIC message and event pages */
        memset ( hv->synic.message, 0, PAGE_SIZE );
        memset ( hv->synic.event, 0, PAGE_SIZE );

        /* Map SynIC message page */
        simp = rdmsr ( HV_X64_MSR_SIMP );
        simp &= ( PAGE_SIZE - 1 );
        simp |= ( virt_to_phys ( hv->synic.message ) | HV_SIMP_ENABLE );
        DBGC2 ( hv, "HV %p SIMP MSR is %#08llx\n", hv, simp );
        wrmsr ( HV_X64_MSR_SIMP, simp );

        /* Map SynIC event page */
        siefp = rdmsr ( HV_X64_MSR_SIEFP );
        siefp &= ( PAGE_SIZE - 1 );
        siefp |= ( virt_to_phys ( hv->synic.event ) | HV_SIEFP_ENABLE );
        DBGC2 ( hv, "HV %p SIEFP MSR is %#08llx\n", hv, siefp );
        wrmsr ( HV_X64_MSR_SIEFP, siefp );

        /* Enable SynIC */
        scontrol = rdmsr ( HV_X64_MSR_SCONTROL );
        scontrol |= HV_SCONTROL_ENABLE;
        DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol );
        wrmsr ( HV_X64_MSR_SCONTROL, scontrol );
}
static void hv_unmap_synic_no_scontrol ( struct hv_hypervisor hv) [static]

Unmap synthetic interrupt controller, leaving SCONTROL untouched.

Parameters:
hvHyper-V hypervisor

Definition at line 356 of file hyperv.c.

References DBGC2, HV_SIEFP_ENABLE, HV_SIMP_ENABLE, HV_X64_MSR_SIEFP, HV_X64_MSR_SIMP, and PAGE_SIZE.

Referenced by hv_quiesce(), and hv_unmap_synic().

                                                                    {
        uint64_t siefp;
        uint64_t simp;

        /* Unmap SynIC event page */
        siefp = rdmsr ( HV_X64_MSR_SIEFP );
        siefp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIEFP_ENABLE );
        DBGC2 ( hv, "HV %p SIEFP MSR is %#08llx\n", hv, siefp );
        wrmsr ( HV_X64_MSR_SIEFP, siefp );

        /* Unmap SynIC message page */
        simp = rdmsr ( HV_X64_MSR_SIMP );
        simp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIMP_ENABLE );
        DBGC2 ( hv, "HV %p SIMP MSR is %#08llx\n", hv, simp );
        wrmsr ( HV_X64_MSR_SIMP, simp );
}
static void hv_unmap_synic ( struct hv_hypervisor hv) [static]

Unmap synthetic interrupt controller.

Parameters:
hvHyper-V hypervisor

Definition at line 378 of file hyperv.c.

References DBGC2, HV_SCONTROL_ENABLE, hv_unmap_synic_no_scontrol(), and HV_X64_MSR_SCONTROL.

Referenced by hv_probe(), and hv_remove().

                                                        {
        uint64_t scontrol;

        /* Disable SynIC */
        scontrol = rdmsr ( HV_X64_MSR_SCONTROL );
        scontrol &= ~HV_SCONTROL_ENABLE;
        DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol );
        wrmsr ( HV_X64_MSR_SCONTROL, scontrol );

        /* Unmap SynIC event and message pages */
        hv_unmap_synic_no_scontrol ( hv );
}
void hv_enable_sint ( struct hv_hypervisor hv,
unsigned int  sintx 
)

Enable synthetic interrupt.

Parameters:
hvHyper-V hypervisor
sintxSynthetic interrupt number

Definition at line 397 of file hyperv.c.

References DBGC2, HV_SINT_AUTO_EOI, HV_SINT_MASKED, HV_SINT_VECTOR, HV_SINT_VECTOR_MASK, and HV_X64_MSR_SINT.

Referenced by vmbus_probe(), and vmbus_reset().

                                                                     {
        unsigned long msr = HV_X64_MSR_SINT ( sintx );
        uint64_t sint;

        /* Enable synthetic interrupt
         *
         * We have to enable the interrupt, otherwise messages will
         * not be delivered (even though the documentation implies
         * that polling for messages is possible).  We enable AutoEOI
         * and hook the interrupt to the obsolete IRQ13 (FPU
         * exception) vector, which will be implemented as a no-op.
         */
        sint = rdmsr ( msr );
        sint &= ~( HV_SINT_MASKED | HV_SINT_VECTOR_MASK );
        sint |= ( HV_SINT_AUTO_EOI |
                  HV_SINT_VECTOR ( IRQ_INT ( 13 /* See comment above */ ) ) );
        DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint );
        wrmsr ( msr, sint );
}
void hv_disable_sint ( struct hv_hypervisor hv,
unsigned int  sintx 
)

Disable synthetic interrupt.

Parameters:
hvHyper-V hypervisor
sintxSynthetic interrupt number

Definition at line 423 of file hyperv.c.

References DBGC2, HV_SINT_AUTO_EOI, HV_SINT_MASKED, and HV_X64_MSR_SINT.

Referenced by hv_quiesce(), vmbus_probe(), and vmbus_remove().

                                                                      {
        unsigned long msr = HV_X64_MSR_SINT ( sintx );
        uint64_t sint;

        /* Do nothing if interrupt is already disabled */
        sint = rdmsr ( msr );
        if ( sint & HV_SINT_MASKED )
                return;

        /* Disable synthetic interrupt */
        sint &= ~HV_SINT_AUTO_EOI;
        sint |= HV_SINT_MASKED;
        DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint );
        wrmsr ( msr, sint );
}
int hv_post_message ( struct hv_hypervisor hv,
unsigned int  id,
unsigned int  type,
const void *  data,
size_t  len 
)

Post message.

Parameters:
hvHyper-V hypervisor
idConnection ID
typeMessage type
dataMessage
lenLength of message
Return values:
rcReturn status code

Definition at line 449 of file hyperv.c.

References assert, cpu_to_le32, hv_post_message::data, DBGC, DBGC2, DBGC2_HDA, EHV, HV_POST_MESSAGE, hv_post_message::id, hv_post_message::len, memcpy(), memset(), hv_hypervisor::message, msg(), NULL, hv_message_buffer::posted, rc, status, strerror(), and hv_post_message::type.

                                                                        {
        struct hv_post_message *msg = &hv->message->posted;
        int status;
        int rc;

        /* Sanity check */
        assert ( len <= sizeof ( msg->data ) );

        /* Construct message */
        memset ( msg, 0, sizeof ( *msg ) );
        msg->id = cpu_to_le32 ( id );
        msg->type = cpu_to_le32 ( type );
        msg->len = cpu_to_le32 ( len );
        memcpy ( msg->data, data, len );
        DBGC2 ( hv, "HV %p connection %d posting message type %#08x:\n",
                hv, id, type );
        DBGC2_HDA ( hv, 0, msg->data, len );

        /* Post message */
        if ( ( status = hv_call ( hv, HV_POST_MESSAGE, msg, NULL ) ) != 0 ) {
                rc = -EHV ( status );
                DBGC ( hv, "HV %p could not post message to %#08x: %s\n",
                       hv, id, strerror ( rc ) );
                return rc;
        }

        return 0;
}
int hv_wait_for_message ( struct hv_hypervisor hv,
unsigned int  sintx 
)

Wait for received message.

Parameters:
hvHyper-V hypervisor
sintxSynthetic interrupt number
Return values:
rcReturn status code

Definition at line 486 of file hyperv.c.

References assert, hv_message::data, data, DBGC, DBGC2, DBGC2_HDA, ETIMEDOUT, HV_MESSAGE_MAX_WAIT_MS, HV_X64_MSR_EOM, le32_to_cpu, hv_message::len, len, mdelay(), memcpy(), memset(), hv_synic::message, hv_hypervisor::message, msg(), offsetof, hv_message_buffer::received, src, hv_hypervisor::synic, and hv_message::type.

Referenced by vmbus_wait_for_any_message().

                                                                         {
        struct hv_message *msg = &hv->message->received;
        struct hv_message *src = &hv->synic.message[sintx];
        unsigned int retries;
        size_t len;

        /* Wait for message to arrive */
        for ( retries = 0 ; retries < HV_MESSAGE_MAX_WAIT_MS ; retries++ ) {

                /* Check for message */
                if ( src->type ) {

                        /* Copy message */
                        memset ( msg, 0, sizeof ( *msg ) );
                        len = src->len;
                        assert ( len <= sizeof ( *msg ) );
                        memcpy ( msg, src,
                                 ( offsetof ( typeof ( *msg ), data ) + len ) );
                        DBGC2 ( hv, "HV %p SINT%d received message type "
                                "%#08x:\n", hv, sintx,
                                le32_to_cpu ( msg->type ) );
                        DBGC2_HDA ( hv, 0, msg->data, len );

                        /* Consume message */
                        src->type = 0;

                        return 0;
                }

                /* Trigger message delivery */
                wrmsr ( HV_X64_MSR_EOM, 0 );

                /* Delay */
                mdelay ( 1 );
        }

        DBGC ( hv, "HV %p SINT%d timed out waiting for message\n",
               hv, sintx );
        return -ETIMEDOUT;
}
int hv_signal_event ( struct hv_hypervisor hv,
unsigned int  id,
unsigned int  flag 
)

Signal event.

Parameters:
hvHyper-V hypervisor
idConnection ID
flagFlag number
Return values:
rcReturn status code

Definition at line 535 of file hyperv.c.

References cpu_to_le16, cpu_to_le32, DBGC, EHV, HV_SIGNAL_EVENT, memset(), hv_hypervisor::message, NULL, rc, hv_message_buffer::signalled, status, and strerror().

                                          {
        struct hv_signal_event *event = &hv->message->signalled;
        int status;
        int rc;

        /* Construct event */
        memset ( event, 0, sizeof ( *event ) );
        event->id = cpu_to_le32 ( id );
        event->flag = cpu_to_le16 ( flag );

        /* Signal event */
        if ( ( status = hv_call ( hv, HV_SIGNAL_EVENT, event, NULL ) ) != 0 ) {
                rc = -EHV ( status );
                DBGC ( hv, "HV %p could not signal event to %#08x: %s\n",
                       hv, id, strerror ( rc ) );
                return rc;
        }

        return 0;
}
static int hv_probe ( struct root_device rootdev) [static]

Probe root device.

Parameters:
rootdevRoot device
Return values:
rcReturn status code

Definition at line 563 of file hyperv.c.

References root_device::dev, ENOMEM, hv_synic::event, free, hv_alloc_message(), hv_alloc_pages(), hv_check_features(), hv_check_hv(), hv_check_uefi(), hv_free_message(), hv_free_pages(), hv_map_hypercall(), hv_map_synic(), hv_unmap_hypercall(), hv_unmap_synic(), hv_hypervisor::hypercall, hv_synic::message, NULL, rc, rootdev_set_drvdata(), hv_hypervisor::synic, vmbus_probe(), vmbus_remove(), and zalloc().

                                                    {
        struct hv_hypervisor *hv;
        int rc;

        /* Check we are running in Hyper-V */
        if ( ( rc = hv_check_hv() ) != 0 )
                goto err_check_hv;

        /* Allocate and initialise structure */
        hv = zalloc ( sizeof ( *hv ) );
        if ( ! hv ) {
                rc = -ENOMEM;
                goto err_alloc;
        }

        /* Check features */
        if ( ( rc = hv_check_features ( hv ) ) != 0 )
                goto err_check_features;

        /* Check that Gen 2 UEFI firmware is not running */
        if ( ( rc = hv_check_uefi ( hv ) ) != 0 )
                goto err_check_uefi;

        /* Allocate pages */
        if ( ( rc = hv_alloc_pages ( hv, &hv->hypercall, &hv->synic.message,
                                     &hv->synic.event, NULL ) ) != 0 )
                goto err_alloc_pages;

        /* Allocate message buffer */
        if ( ( rc = hv_alloc_message ( hv ) ) != 0 )
                goto err_alloc_message;

        /* Map hypercall page */
        hv_map_hypercall ( hv );

        /* Map synthetic interrupt controller */
        hv_map_synic ( hv );

        /* Probe Hyper-V devices */
        if ( ( rc = vmbus_probe ( hv, &rootdev->dev ) ) != 0 )
                goto err_vmbus_probe;

        rootdev_set_drvdata ( rootdev, hv );
        return 0;

        vmbus_remove ( hv, &rootdev->dev );
 err_vmbus_probe:
        hv_unmap_synic ( hv );
        hv_unmap_hypercall ( hv );
        hv_free_message ( hv );
 err_alloc_message:
        hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
                        NULL );
 err_alloc_pages:
 err_check_uefi:
 err_check_features:
        free ( hv );
 err_alloc:
 err_check_hv:
        return rc;
}
static void hv_remove ( struct root_device rootdev) [static]
static void hv_quiesce ( void  ) [static]

Quiesce system.

Definition at line 659 of file hyperv.c.

References DBGC, hv_disable_sint(), HV_SINT_MAX, hv_unmap_hypercall(), hv_unmap_synic_no_scontrol(), and rootdev_get_drvdata().

                                {
        struct hv_hypervisor *hv = rootdev_get_drvdata ( &hv_root_device );
        unsigned int i;

        /* Do nothing if we are not running in Hyper-V */
        if ( ! hv )
                return;

        /* The "enlightened" portions of the Windows Server 2016 boot
         * process will not cleanly take ownership of an active
         * Hyper-V connection.  Experimentation shows that the minimum
         * requirement is that we disable the SynIC message page
         * (i.e. zero the SIMP MSR).
         *
         * We cannot perform a full shutdown of the Hyper-V
         * connection.  Experimentation shows that if we disable the
         * SynIC (i.e. zero the SCONTROL MSR) then Windows Server 2016
         * will enter an indefinite wait loop.
         *
         * Attempt to create a safe handover environment by resetting
         * all MSRs except for SCONTROL.
         *
         * Note that we do not shut down our VMBus devices, since we
         * may need to unquiesce the system and continue operation.
         */

        /* Disable all synthetic interrupts */
        for ( i = 0 ; i <= HV_SINT_MAX ; i++ )
                hv_disable_sint ( hv, i );

        /* Unmap synthetic interrupt controller, leaving SCONTROL
         * enabled (see above).
         */
        hv_unmap_synic_no_scontrol ( hv );

        /* Unmap hypercall page */
        hv_unmap_hypercall ( hv );

        DBGC ( hv, "HV %p quiesced\n", hv );
}
static void hv_unquiesce ( void  ) [static]

Unquiesce system.

Definition at line 704 of file hyperv.c.

References DBGC, hv_map_hypercall(), hv_map_synic(), HV_SIMP_ENABLE, HV_X64_MSR_SIMP, rc, rootdev_get_drvdata(), strerror(), and vmbus_reset().

                                  {
        struct hv_hypervisor *hv = rootdev_get_drvdata ( &hv_root_device );
        uint64_t simp;
        int rc;

        /* Do nothing if we are not running in Hyper-V */
        if ( ! hv )
                return;

        /* Experimentation shows that the "enlightened" portions of
         * Windows Server 2016 will break our Hyper-V connection at
         * some point during a SAN boot.  Surprisingly it does not
         * change the guest OS ID MSR, but it does leave the SynIC
         * message page disabled.
         *
         * Our own explicit quiescing procedure will also disable the
         * SynIC message page.  We can therefore use the SynIC message
         * page enable bit as a heuristic to determine when we need to
         * reestablish our Hyper-V connection.
         */
        simp = rdmsr ( HV_X64_MSR_SIMP );
        if ( simp & HV_SIMP_ENABLE )
                return;

        /* Remap hypercall page */
        hv_map_hypercall ( hv );

        /* Remap synthetic interrupt controller */
        hv_map_synic ( hv );

        /* Reset Hyper-V devices */
        if ( ( rc = vmbus_reset ( hv, &hv_root_device.dev ) ) != 0 ) {
                DBGC ( hv, "HV %p could not unquiesce: %s\n",
                       hv, strerror ( rc ) );
                /* Nothing we can do */
                return;
        }
}
static int hv_timer_probe ( void  ) [static]

Probe timer.

Return values:
rcReturn status code

Definition at line 754 of file hyperv.c.

References DBGC, ENODEV, hv_check_hv(), HV_CPUID_FEATURES, HV_FEATURES_AVAIL_TIME_REF_COUNT_MSR, HV_INTERFACE_ID, and rc.

                                   {
        uint32_t available;
        uint32_t discard_ebx;
        uint32_t discard_ecx;
        uint32_t discard_edx;
        int rc;

        /* Check we are running in Hyper-V */
        if ( ( rc = hv_check_hv() ) != 0 )
                return rc;

        /* Check for available reference counter */
        cpuid ( HV_CPUID_FEATURES, 0, &available, &discard_ebx, &discard_ecx,
                &discard_edx );
        if ( ! ( available & HV_FEATURES_AVAIL_TIME_REF_COUNT_MSR ) ) {
                DBGC ( HV_INTERFACE_ID, "HV has no time reference counter\n" );
                return -ENODEV;
        }

        return 0;
}
static unsigned long hv_currticks ( void  ) [static]

Get current system time in ticks.

Return values:
ticksCurrent time, in ticks

Definition at line 781 of file hyperv.c.

References HV_TIMER_HZ, HV_TIMER_SHIFT, HV_X64_MSR_TIME_REF_COUNT, and TICKS_PER_SEC.

                                           {

        /* Calculate time using a combination of bit shifts and
         * multiplication (to avoid a 64-bit division).
         */
        return ( ( rdmsr ( HV_X64_MSR_TIME_REF_COUNT ) >> HV_TIMER_SHIFT ) *
                 ( TICKS_PER_SEC / ( HV_TIMER_HZ >> HV_TIMER_SHIFT ) ) );
}
static void hv_udelay ( unsigned long  usecs) [static]

Delay for a fixed number of microseconds.

Parameters:
usecsNumber of microseconds for which to delay

Definition at line 795 of file hyperv.c.

References HV_TIMER_HZ, HV_X64_MSR_TIME_REF_COUNT, and start.

                                              {
        uint32_t start;
        uint32_t elapsed;
        uint32_t threshold;

        /* Spin until specified number of 10MHz ticks have elapsed */
        start = rdmsr ( HV_X64_MSR_TIME_REF_COUNT );
        threshold = ( usecs * ( HV_TIMER_HZ / 1000000 ) );
        do {
                elapsed = ( rdmsr ( HV_X64_MSR_TIME_REF_COUNT ) - start );
        } while ( elapsed < threshold );
}
struct timer hv_timer __timer ( TIMER_PREFERRED  ) [read]

Hyper-V timer.

REQUIRING_SYMBOL ( hv_root_device  )
REQUIRE_OBJECT ( netvsc  )

Variable Documentation

struct root_driver hv_root_driver [static]
Initial value:
 {
        .probe = hv_probe,
        .remove = hv_remove,
}

Hyper-V root device driver.

Definition at line 644 of file hyperv.c.

struct root_device hv_root_device __root_device
Initial value:
 {
        .dev = { .name = "Hyper-V" },
        .driver = &hv_root_driver,
}

Hyper-V root device.

Definition at line 650 of file hyperv.c.

struct quiescer hv_quiescer __quiescer
Initial value:
 {
        .quiesce = hv_quiesce,
        .unquiesce = hv_unquiesce,
}

Hyper-V quiescer.

Definition at line 744 of file hyperv.c.