Browse Source

[hyperv] Provide timer based on the 10MHz time reference count MSR

When running on AMD platforms, the legacy hardware emulation is
extremely unreliable.  In particular, the IRQ0 timer interrupt is
likely to simply stop working, resulting in a total failure of any
code that relies on timers (such as DHCP retransmission attempts).

Work around this by using the 10MHz time counter provided by Hyper-V
via an MSR.  (This timer can be tested in KVM via the command-line
option "-cpu host,hv_time".)

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 8 years ago
parent
commit
f3ba0fb5fd
2 changed files with 108 additions and 11 deletions
  1. 102
    11
      src/arch/x86/drivers/hyperv/hyperv.c
  2. 6
    0
      src/arch/x86/drivers/hyperv/hyperv.h

+ 102
- 11
src/arch/x86/drivers/hyperv/hyperv.c View File

39
 #include <pic8259.h>
39
 #include <pic8259.h>
40
 #include <ipxe/malloc.h>
40
 #include <ipxe/malloc.h>
41
 #include <ipxe/device.h>
41
 #include <ipxe/device.h>
42
+#include <ipxe/timer.h>
42
 #include <ipxe/cpuid.h>
43
 #include <ipxe/cpuid.h>
43
 #include <ipxe/msr.h>
44
 #include <ipxe/msr.h>
44
 #include <ipxe/hyperv.h>
45
 #include <ipxe/hyperv.h>
51
  */
52
  */
52
 #define HV_MESSAGE_MAX_WAIT_MS 1000
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
  * Convert a Hyper-V status code to an iPXE status code
62
  * Convert a Hyper-V status code to an iPXE status code
56
  *
63
  *
145
 /**
152
 /**
146
  * Check whether or not we are running in Hyper-V
153
  * Check whether or not we are running in Hyper-V
147
  *
154
  *
148
- * @v hv		Hyper-V hypervisor
149
  * @ret rc		Return status code
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
 	struct x86_features features;
158
 	struct x86_features features;
153
 	uint32_t interface_id;
159
 	uint32_t interface_id;
154
 	uint32_t discard_ebx;
160
 	uint32_t discard_ebx;
155
 	uint32_t discard_ecx;
161
 	uint32_t discard_ecx;
156
 	uint32_t discard_edx;
162
 	uint32_t discard_edx;
157
-	uint32_t available;
158
-	uint32_t permissions;
159
 
163
 
160
 	/* Check for presence of a hypervisor (not necessarily Hyper-V) */
164
 	/* Check for presence of a hypervisor (not necessarily Hyper-V) */
161
 	x86_features ( &features );
165
 	x86_features ( &features );
162
 	if ( ! ( features.intel.ecx & CPUID_FEATURES_INTEL_ECX_HYPERVISOR ) ) {
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
 		return -ENODEV;
168
 		return -ENODEV;
165
 	}
169
 	}
166
 
170
 
168
 	cpuid ( HV_CPUID_INTERFACE_ID, &interface_id, &discard_ebx,
172
 	cpuid ( HV_CPUID_INTERFACE_ID, &interface_id, &discard_ebx,
169
 		&discard_ecx, &discard_edx );
173
 		&discard_ecx, &discard_edx );
170
 	if ( interface_id != HV_INTERFACE_ID ) {
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
 		return -ENODEV;
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
 	/* Check that required features and privileges are available */
195
 	/* Check that required features and privileges are available */
177
 	cpuid ( HV_CPUID_FEATURES, &available, &permissions, &discard_ecx,
196
 	cpuid ( HV_CPUID_FEATURES, &available, &permissions, &discard_ecx,
178
 		&discard_edx );
197
 		&discard_edx );
509
 	struct hv_hypervisor *hv;
528
 	struct hv_hypervisor *hv;
510
 	int rc;
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
 	/* Allocate and initialise structure */
535
 	/* Allocate and initialise structure */
513
 	hv = zalloc ( sizeof ( *hv ) );
536
 	hv = zalloc ( sizeof ( *hv ) );
514
 	if ( ! hv ) {
537
 	if ( ! hv ) {
516
 		goto err_alloc;
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
 	/* Allocate pages */
546
 	/* Allocate pages */
524
 	if ( ( rc = hv_alloc_pages ( hv, &hv->hypercall, &hv->synic.message,
547
 	if ( ( rc = hv_alloc_pages ( hv, &hv->hypercall, &hv->synic.message,
555
 	hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
578
 	hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
556
 			NULL );
579
 			NULL );
557
  err_alloc_pages:
580
  err_alloc_pages:
558
- err_check_hv:
581
+ err_check_features:
559
 	free ( hv );
582
 	free ( hv );
560
  err_alloc:
583
  err_alloc:
584
+ err_check_hv:
561
 	return rc;
585
 	return rc;
562
 }
586
 }
563
 
587
 
590
 	.driver = &hv_root_driver,
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
 /* Drag in objects via hv_root_device */
684
 /* Drag in objects via hv_root_device */
594
 REQUIRING_SYMBOL ( hv_root_device );
685
 REQUIRING_SYMBOL ( hv_root_device );
595
 
686
 

+ 6
- 0
src/arch/x86/drivers/hyperv/hyperv.h View File

21
 /** Get hypervisor features */
21
 /** Get hypervisor features */
22
 #define HV_CPUID_FEATURES 0x40000003UL
22
 #define HV_CPUID_FEATURES 0x40000003UL
23
 
23
 
24
+/** Time reference counter MSR is available */
25
+#define HV_FEATURES_AVAIL_TIME_REF_COUNT_MSR 0x00000002UL
26
+
24
 /** SynIC MSRs are available */
27
 /** SynIC MSRs are available */
25
 #define HV_FEATURES_AVAIL_SYNIC_MSR 0x00000004UL
28
 #define HV_FEATURES_AVAIL_SYNIC_MSR 0x00000004UL
26
 
29
 
39
 /** Hypercall page MSR */
42
 /** Hypercall page MSR */
40
 #define HV_X64_MSR_HYPERCALL 0x40000001UL
43
 #define HV_X64_MSR_HYPERCALL 0x40000001UL
41
 
44
 
45
+/** Time reference MSR */
46
+#define HV_X64_MSR_TIME_REF_COUNT 0x40000020UL
47
+
42
 /** SynIC control MSR */
48
 /** SynIC control MSR */
43
 #define HV_X64_MSR_SCONTROL 0x40000080UL
49
 #define HV_X64_MSR_SCONTROL 0x40000080UL
44
 
50
 

Loading…
Cancel
Save