|
@@ -39,6 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
39
|
39
|
#include <pic8259.h>
|
40
|
40
|
#include <ipxe/malloc.h>
|
41
|
41
|
#include <ipxe/device.h>
|
|
42
|
+#include <ipxe/timer.h>
|
42
|
43
|
#include <ipxe/cpuid.h>
|
43
|
44
|
#include <ipxe/msr.h>
|
44
|
45
|
#include <ipxe/hyperv.h>
|
|
@@ -51,6 +52,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
51
|
52
|
*/
|
52
|
53
|
#define HV_MESSAGE_MAX_WAIT_MS 1000
|
53
|
54
|
|
|
55
|
+/** Hyper-V timer frequency (fixed 10Mhz) */
|
|
56
|
+#define HV_TIMER_HZ 10000000
|
|
57
|
+
|
|
58
|
+/** Hyper-V timer scale factor (used to avoid 64-bit division) */
|
|
59
|
+#define HV_TIMER_SHIFT 18
|
|
60
|
+
|
54
|
61
|
/**
|
55
|
62
|
* Convert a Hyper-V status code to an iPXE status code
|
56
|
63
|
*
|
|
@@ -145,22 +152,19 @@ static void hv_free_message ( struct hv_hypervisor *hv ) {
|
145
|
152
|
/**
|
146
|
153
|
* Check whether or not we are running in Hyper-V
|
147
|
154
|
*
|
148
|
|
- * @v hv Hyper-V hypervisor
|
149
|
155
|
* @ret rc Return status code
|
150
|
156
|
*/
|
151
|
|
-static int hv_check_hv ( struct hv_hypervisor *hv ) {
|
|
157
|
+static int hv_check_hv ( void ) {
|
152
|
158
|
struct x86_features features;
|
153
|
159
|
uint32_t interface_id;
|
154
|
160
|
uint32_t discard_ebx;
|
155
|
161
|
uint32_t discard_ecx;
|
156
|
162
|
uint32_t discard_edx;
|
157
|
|
- uint32_t available;
|
158
|
|
- uint32_t permissions;
|
159
|
163
|
|
160
|
164
|
/* Check for presence of a hypervisor (not necessarily Hyper-V) */
|
161
|
165
|
x86_features ( &features );
|
162
|
166
|
if ( ! ( features.intel.ecx & CPUID_FEATURES_INTEL_ECX_HYPERVISOR ) ) {
|
163
|
|
- DBGC ( hv, "HV %p not running in a hypervisor\n", hv );
|
|
167
|
+ DBGC ( HV_INTERFACE_ID, "HV not running in a hypervisor\n" );
|
164
|
168
|
return -ENODEV;
|
165
|
169
|
}
|
166
|
170
|
|
|
@@ -168,11 +172,26 @@ static int hv_check_hv ( struct hv_hypervisor *hv ) {
|
168
|
172
|
cpuid ( HV_CPUID_INTERFACE_ID, &interface_id, &discard_ebx,
|
169
|
173
|
&discard_ecx, &discard_edx );
|
170
|
174
|
if ( interface_id != HV_INTERFACE_ID ) {
|
171
|
|
- DBGC ( hv, "HV %p not running in Hyper-V (interface ID "
|
172
|
|
- "%#08x)\n", hv, interface_id );
|
|
175
|
+ DBGC ( HV_INTERFACE_ID, "HV not running in Hyper-V (interface "
|
|
176
|
+ "ID %#08x)\n", interface_id );
|
173
|
177
|
return -ENODEV;
|
174
|
178
|
}
|
175
|
179
|
|
|
180
|
+ return 0;
|
|
181
|
+}
|
|
182
|
+
|
|
183
|
+/**
|
|
184
|
+ * Check required features
|
|
185
|
+ *
|
|
186
|
+ * @v hv Hyper-V hypervisor
|
|
187
|
+ * @ret rc Return status code
|
|
188
|
+ */
|
|
189
|
+static int hv_check_features ( struct hv_hypervisor *hv ) {
|
|
190
|
+ uint32_t available;
|
|
191
|
+ uint32_t permissions;
|
|
192
|
+ uint32_t discard_ecx;
|
|
193
|
+ uint32_t discard_edx;
|
|
194
|
+
|
176
|
195
|
/* Check that required features and privileges are available */
|
177
|
196
|
cpuid ( HV_CPUID_FEATURES, &available, &permissions, &discard_ecx,
|
178
|
197
|
&discard_edx );
|
|
@@ -509,6 +528,10 @@ static int hv_probe ( struct root_device *rootdev ) {
|
509
|
528
|
struct hv_hypervisor *hv;
|
510
|
529
|
int rc;
|
511
|
530
|
|
|
531
|
+ /* Check we are running in Hyper-V */
|
|
532
|
+ if ( ( rc = hv_check_hv() ) != 0 )
|
|
533
|
+ goto err_check_hv;
|
|
534
|
+
|
512
|
535
|
/* Allocate and initialise structure */
|
513
|
536
|
hv = zalloc ( sizeof ( *hv ) );
|
514
|
537
|
if ( ! hv ) {
|
|
@@ -516,9 +539,9 @@ static int hv_probe ( struct root_device *rootdev ) {
|
516
|
539
|
goto err_alloc;
|
517
|
540
|
}
|
518
|
541
|
|
519
|
|
- /* Check we are running in Hyper-V */
|
520
|
|
- if ( ( rc = hv_check_hv ( hv ) ) != 0 )
|
521
|
|
- goto err_check_hv;
|
|
542
|
+ /* Check features */
|
|
543
|
+ if ( ( rc = hv_check_features ( hv ) ) != 0 )
|
|
544
|
+ goto err_check_features;
|
522
|
545
|
|
523
|
546
|
/* Allocate pages */
|
524
|
547
|
if ( ( rc = hv_alloc_pages ( hv, &hv->hypercall, &hv->synic.message,
|
|
@@ -555,9 +578,10 @@ static int hv_probe ( struct root_device *rootdev ) {
|
555
|
578
|
hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
|
556
|
579
|
NULL );
|
557
|
580
|
err_alloc_pages:
|
558
|
|
- err_check_hv:
|
|
581
|
+ err_check_features:
|
559
|
582
|
free ( hv );
|
560
|
583
|
err_alloc:
|
|
584
|
+ err_check_hv:
|
561
|
585
|
return rc;
|
562
|
586
|
}
|
563
|
587
|
|
|
@@ -590,6 +614,73 @@ struct root_device hv_root_device __root_device = {
|
590
|
614
|
.driver = &hv_root_driver,
|
591
|
615
|
};
|
592
|
616
|
|
|
617
|
+/**
|
|
618
|
+ * Probe timer
|
|
619
|
+ *
|
|
620
|
+ * @ret rc Return status code
|
|
621
|
+ */
|
|
622
|
+static int hv_timer_probe ( void ) {
|
|
623
|
+ uint32_t available;
|
|
624
|
+ uint32_t discard_ebx;
|
|
625
|
+ uint32_t discard_ecx;
|
|
626
|
+ uint32_t discard_edx;
|
|
627
|
+ int rc;
|
|
628
|
+
|
|
629
|
+ /* Check we are running in Hyper-V */
|
|
630
|
+ if ( ( rc = hv_check_hv() ) != 0 )
|
|
631
|
+ return rc;
|
|
632
|
+
|
|
633
|
+ /* Check for available reference counter */
|
|
634
|
+ cpuid ( HV_CPUID_FEATURES, &available, &discard_ebx, &discard_ecx,
|
|
635
|
+ &discard_edx );
|
|
636
|
+ if ( ! ( available & HV_FEATURES_AVAIL_TIME_REF_COUNT_MSR ) ) {
|
|
637
|
+ DBGC ( HV_INTERFACE_ID, "HV has no time reference counter\n" );
|
|
638
|
+ return -ENODEV;
|
|
639
|
+ }
|
|
640
|
+
|
|
641
|
+ return 0;
|
|
642
|
+}
|
|
643
|
+
|
|
644
|
+/**
|
|
645
|
+ * Get current system time in ticks
|
|
646
|
+ *
|
|
647
|
+ * @ret ticks Current time, in ticks
|
|
648
|
+ */
|
|
649
|
+static unsigned long hv_currticks ( void ) {
|
|
650
|
+
|
|
651
|
+ /* Calculate time using a combination of bit shifts and
|
|
652
|
+ * multiplication (to avoid a 64-bit division).
|
|
653
|
+ */
|
|
654
|
+ return ( ( rdmsr ( HV_X64_MSR_TIME_REF_COUNT ) >> HV_TIMER_SHIFT ) *
|
|
655
|
+ ( TICKS_PER_SEC / ( HV_TIMER_HZ >> HV_TIMER_SHIFT ) ) );
|
|
656
|
+}
|
|
657
|
+
|
|
658
|
+/**
|
|
659
|
+ * Delay for a fixed number of microseconds
|
|
660
|
+ *
|
|
661
|
+ * @v usecs Number of microseconds for which to delay
|
|
662
|
+ */
|
|
663
|
+static void hv_udelay ( unsigned long usecs ) {
|
|
664
|
+ uint32_t start;
|
|
665
|
+ uint32_t elapsed;
|
|
666
|
+ uint32_t threshold;
|
|
667
|
+
|
|
668
|
+ /* Spin until specified number of 10MHz ticks have elapsed */
|
|
669
|
+ start = rdmsr ( HV_X64_MSR_TIME_REF_COUNT );
|
|
670
|
+ threshold = ( usecs * ( HV_TIMER_HZ / 1000000 ) );
|
|
671
|
+ do {
|
|
672
|
+ elapsed = ( rdmsr ( HV_X64_MSR_TIME_REF_COUNT ) - start );
|
|
673
|
+ } while ( elapsed < threshold );
|
|
674
|
+}
|
|
675
|
+
|
|
676
|
+/** Hyper-V timer */
|
|
677
|
+struct timer hv_timer __timer ( TIMER_PREFERRED ) = {
|
|
678
|
+ .name = "Hyper-V",
|
|
679
|
+ .probe = hv_timer_probe,
|
|
680
|
+ .currticks = hv_currticks,
|
|
681
|
+ .udelay = hv_udelay,
|
|
682
|
+};
|
|
683
|
+
|
593
|
684
|
/* Drag in objects via hv_root_device */
|
594
|
685
|
REQUIRING_SYMBOL ( hv_root_device );
|
595
|
686
|
|