|
24 | 24 | #include <linux/version.h>
|
25 | 25 | #include <linux/vmalloc.h>
|
26 | 26 | #include <linux/mm.h>
|
| 27 | +#include <linux/clockchips.h> |
| 28 | + |
| 29 | + |
| 30 | +#ifdef CONFIG_X86_64 |
| 31 | + |
| 32 | +static struct ms_hyperv_tsc_page *tsc_pg; |
| 33 | + |
| 34 | +static u64 read_hv_clock_tsc(struct clocksource *arg) |
| 35 | +{ |
| 36 | + u64 current_tick; |
| 37 | + |
| 38 | + if (tsc_pg->tsc_sequence != 0) { |
| 39 | + /* |
| 40 | + * Use the tsc page to compute the value. |
| 41 | + */ |
| 42 | + |
| 43 | + while (1) { |
| 44 | + u64 tmp; |
| 45 | + u32 sequence = tsc_pg->tsc_sequence; |
| 46 | + u64 cur_tsc; |
| 47 | + u64 scale = tsc_pg->tsc_scale; |
| 48 | + s64 offset = tsc_pg->tsc_offset; |
| 49 | + |
| 50 | + rdtscll(cur_tsc); |
| 51 | + /* current_tick = ((cur_tsc *scale) >> 64) + offset */ |
| 52 | + asm("mulq %3" |
| 53 | + : "=d" (current_tick), "=a" (tmp) |
| 54 | + : "a" (cur_tsc), "r" (scale)); |
| 55 | + |
| 56 | + current_tick += offset; |
| 57 | + if (tsc_pg->tsc_sequence == sequence) |
| 58 | + return current_tick; |
| 59 | + |
| 60 | + if (tsc_pg->tsc_sequence != 0) |
| 61 | + continue; |
| 62 | + /* |
| 63 | + * Fallback using MSR method. |
| 64 | + */ |
| 65 | + break; |
| 66 | + } |
| 67 | + } |
| 68 | + rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick); |
| 69 | + return current_tick; |
| 70 | +} |
| 71 | + |
| 72 | +static struct clocksource hyperv_cs_tsc = { |
| 73 | + .name = "hyperv_clocksource_tsc_page", |
| 74 | + .rating = 400, |
| 75 | + .read = read_hv_clock_tsc, |
| 76 | + .mask = CLOCKSOURCE_MASK(64), |
| 77 | + .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
| 78 | +}; |
| 79 | +#endif |
| 80 | + |
| 81 | +static u64 read_hv_clock_msr(struct clocksource *arg) |
| 82 | +{ |
| 83 | + u64 current_tick; |
| 84 | + /* |
| 85 | + * Read the partition counter to get the current tick count. This count |
| 86 | + * is set to 0 when the partition is created and is incremented in |
| 87 | + * 100 nanosecond units. |
| 88 | + */ |
| 89 | + rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick); |
| 90 | + return current_tick; |
| 91 | +} |
| 92 | + |
| 93 | +static struct clocksource hyperv_cs_msr = { |
| 94 | + .name = "hyperv_clocksource_msr", |
| 95 | + .rating = 400, |
| 96 | + .read = read_hv_clock_msr, |
| 97 | + .mask = CLOCKSOURCE_MASK(64), |
| 98 | + .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
| 99 | +}; |
27 | 100 |
|
28 | 101 | static void *hypercall_pg;
|
29 | 102 | /*
|
30 | 103 | * This function is to be invoked early in the boot sequence after the
|
31 | 104 | * hypervisor has been detected.
|
32 | 105 | *
|
33 | 106 | * 1. Setup the hypercall page.
|
| 107 | + * 2. Register Hyper-V specific clocksource. |
34 | 108 | */
|
35 | 109 | void hyperv_init(void)
|
36 | 110 | {
|
@@ -58,6 +132,37 @@ void hyperv_init(void)
|
58 | 132 | hypercall_msr.enable = 1;
|
59 | 133 | hypercall_msr.guest_physical_address = vmalloc_to_pfn(hypercall_pg);
|
60 | 134 | wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
|
| 135 | + |
| 136 | + /* |
| 137 | + * Register Hyper-V specific clocksource. |
| 138 | + */ |
| 139 | +#ifdef CONFIG_X86_64 |
| 140 | + if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) { |
| 141 | + union hv_x64_msr_hypercall_contents tsc_msr; |
| 142 | + |
| 143 | + tsc_pg = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL); |
| 144 | + if (!tsc_pg) { |
| 145 | + clocksource_register_hz(&hyperv_cs_msr, NSEC_PER_SEC/100); |
| 146 | + return; |
| 147 | + } |
| 148 | + |
| 149 | + rdmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64); |
| 150 | + |
| 151 | + tsc_msr.enable = 1; |
| 152 | + tsc_msr.guest_physical_address = vmalloc_to_pfn(tsc_pg); |
| 153 | + |
| 154 | + wrmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64); |
| 155 | + clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100); |
| 156 | + return; |
| 157 | + } |
| 158 | +#endif |
| 159 | + /* |
| 160 | + * For 32 bit guests just use the MSR based mechanism for reading |
| 161 | + * the partition counter. |
| 162 | + */ |
| 163 | + |
| 164 | + if (ms_hyperv.features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE) |
| 165 | + clocksource_register_hz(&hyperv_cs_msr, NSEC_PER_SEC/100); |
61 | 166 | }
|
62 | 167 |
|
63 | 168 | /*
|
|
0 commit comments