blob: 5b882cc0c0e9a96e1bd8beeec260ef657a439de4 [file] [log] [blame]
K. Y. Srinivasan87300462017-01-18 16:45:02 -07001/*
2 * X86 specific Hyper-V initialization code.
3 *
4 * Copyright (C) 2016, Microsoft, Inc.
5 *
6 * Author : K. Y. Srinivasan <kys@microsoft.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 2 as published
10 * by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
15 * NON INFRINGEMENT. See the GNU General Public License for more
16 * details.
17 *
18 */
19
20#include <linux/types.h>
21#include <asm/hypervisor.h>
22#include <asm/hyperv.h>
23#include <asm/mshyperv.h>
24#include <linux/version.h>
25#include <linux/vmalloc.h>
26#include <linux/mm.h>
K. Y. Srinivasan63ed4e02017-01-19 11:51:46 -070027#include <linux/clockchips.h>
Stephen Hemminger67071812017-03-04 18:27:11 -070028#include <linux/hyperv.h>
K. Y. Srinivasan63ed4e02017-01-19 11:51:46 -070029
Vitaly Kuznetsovbd2a9ad2017-03-03 14:21:40 +010030#ifdef CONFIG_HYPERV_TSCPAGE
K. Y. Srinivasan63ed4e02017-01-19 11:51:46 -070031
32static struct ms_hyperv_tsc_page *tsc_pg;
33
Vitaly Kuznetsovbd2a9ad2017-03-03 14:21:40 +010034struct ms_hyperv_tsc_page *hv_get_tsc_page(void)
35{
36 return tsc_pg;
37}
38
K. Y. Srinivasan63ed4e02017-01-19 11:51:46 -070039static u64 read_hv_clock_tsc(struct clocksource *arg)
40{
Vitaly Kuznetsov07333792017-03-03 14:21:41 +010041 u64 current_tick = hv_read_tsc_page(tsc_pg);
K. Y. Srinivasan63ed4e02017-01-19 11:51:46 -070042
Vitaly Kuznetsov07333792017-03-03 14:21:41 +010043 if (current_tick == U64_MAX)
44 rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick);
K. Y. Srinivasan63ed4e02017-01-19 11:51:46 -070045
K. Y. Srinivasan63ed4e02017-01-19 11:51:46 -070046 return current_tick;
47}
48
49static struct clocksource hyperv_cs_tsc = {
50 .name = "hyperv_clocksource_tsc_page",
51 .rating = 400,
52 .read = read_hv_clock_tsc,
53 .mask = CLOCKSOURCE_MASK(64),
54 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
55};
56#endif
57
58static u64 read_hv_clock_msr(struct clocksource *arg)
59{
60 u64 current_tick;
61 /*
62 * Read the partition counter to get the current tick count. This count
63 * is set to 0 when the partition is created and is incremented in
64 * 100 nanosecond units.
65 */
66 rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick);
67 return current_tick;
68}
69
70static struct clocksource hyperv_cs_msr = {
71 .name = "hyperv_clocksource_msr",
72 .rating = 400,
73 .read = read_hv_clock_msr,
74 .mask = CLOCKSOURCE_MASK(64),
75 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
76};
K. Y. Srinivasan87300462017-01-18 16:45:02 -070077
K. Y. Srinivasan6ab42a62017-01-18 16:45:03 -070078static void *hypercall_pg;
Vitaly Kuznetsovdee863b2017-02-04 09:57:13 -070079struct clocksource *hyperv_cs;
80EXPORT_SYMBOL_GPL(hyperv_cs);
81
K. Y. Srinivasan87300462017-01-18 16:45:02 -070082/*
83 * This function is to be invoked early in the boot sequence after the
84 * hypervisor has been detected.
85 *
86 * 1. Setup the hypercall page.
K. Y. Srinivasan63ed4e02017-01-19 11:51:46 -070087 * 2. Register Hyper-V specific clocksource.
K. Y. Srinivasan87300462017-01-18 16:45:02 -070088 */
89void hyperv_init(void)
90{
91 u64 guest_id;
92 union hv_x64_msr_hypercall_contents hypercall_msr;
93
94 if (x86_hyper != &x86_hyper_ms_hyperv)
95 return;
96
97 /*
98 * Setup the hypercall page and enable hypercalls.
99 * 1. Register the guest ID
100 * 2. Enable the hypercall and register the hypercall page
101 */
102 guest_id = generate_guest_id(0, LINUX_VERSION_CODE, 0);
103 wrmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id);
104
K. Y. Srinivasan372b1e92017-02-08 18:30:56 -0700105 hypercall_pg = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_RX);
K. Y. Srinivasan6ab42a62017-01-18 16:45:03 -0700106 if (hypercall_pg == NULL) {
K. Y. Srinivasan87300462017-01-18 16:45:02 -0700107 wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
108 return;
109 }
110
111 rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
112 hypercall_msr.enable = 1;
K. Y. Srinivasan6ab42a62017-01-18 16:45:03 -0700113 hypercall_msr.guest_physical_address = vmalloc_to_pfn(hypercall_pg);
K. Y. Srinivasan87300462017-01-18 16:45:02 -0700114 wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
K. Y. Srinivasan63ed4e02017-01-19 11:51:46 -0700115
116 /*
117 * Register Hyper-V specific clocksource.
118 */
Vitaly Kuznetsovbd2a9ad2017-03-03 14:21:40 +0100119#ifdef CONFIG_HYPERV_TSCPAGE
K. Y. Srinivasan63ed4e02017-01-19 11:51:46 -0700120 if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) {
121 union hv_x64_msr_hypercall_contents tsc_msr;
122
123 tsc_pg = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL);
Vitaly Kuznetsovdee863b2017-02-04 09:57:13 -0700124 if (!tsc_pg)
125 goto register_msr_cs;
126
127 hyperv_cs = &hyperv_cs_tsc;
K. Y. Srinivasan63ed4e02017-01-19 11:51:46 -0700128
129 rdmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64);
130
131 tsc_msr.enable = 1;
132 tsc_msr.guest_physical_address = vmalloc_to_pfn(tsc_pg);
133
134 wrmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64);
Vitaly Kuznetsov90b20432017-03-03 14:21:42 +0100135
136 hyperv_cs_tsc.archdata.vclock_mode = VCLOCK_HVCLOCK;
137
K. Y. Srinivasan63ed4e02017-01-19 11:51:46 -0700138 clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100);
139 return;
140 }
Arnd Bergmann73667e32017-02-14 22:17:17 +0100141register_msr_cs:
K. Y. Srinivasan63ed4e02017-01-19 11:51:46 -0700142#endif
143 /*
144 * For 32 bit guests just use the MSR based mechanism for reading
145 * the partition counter.
146 */
147
Vitaly Kuznetsovdee863b2017-02-04 09:57:13 -0700148 hyperv_cs = &hyperv_cs_msr;
K. Y. Srinivasan63ed4e02017-01-19 11:51:46 -0700149 if (ms_hyperv.features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE)
150 clocksource_register_hz(&hyperv_cs_msr, NSEC_PER_SEC/100);
K. Y. Srinivasan87300462017-01-18 16:45:02 -0700151}
K. Y. Srinivasan6ab42a62017-01-18 16:45:03 -0700152
153/*
Vitaly Kuznetsovd6f36092017-01-28 12:37:14 -0700154 * This routine is called before kexec/kdump, it does the required cleanup.
155 */
156void hyperv_cleanup(void)
157{
158 union hv_x64_msr_hypercall_contents hypercall_msr;
159
160 /* Reset our OS id */
161 wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
162
163 /* Reset the hypercall page */
164 hypercall_msr.as_uint64 = 0;
165 wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
Vitaly Kuznetsov5647dbf2017-01-28 12:37:15 -0700166
167 /* Reset the TSC page */
168 hypercall_msr.as_uint64 = 0;
169 wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64);
Vitaly Kuznetsovd6f36092017-01-28 12:37:14 -0700170}
171EXPORT_SYMBOL_GPL(hyperv_cleanup);
172
173/*
K. Y. Srinivasan6ab42a62017-01-18 16:45:03 -0700174 * hv_do_hypercall- Invoke the specified hypercall
175 */
176u64 hv_do_hypercall(u64 control, void *input, void *output)
177{
178 u64 input_address = (input) ? virt_to_phys(input) : 0;
179 u64 output_address = (output) ? virt_to_phys(output) : 0;
180#ifdef CONFIG_X86_64
181 u64 hv_status = 0;
182
183 if (!hypercall_pg)
184 return (u64)ULLONG_MAX;
185
186 __asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8");
187 __asm__ __volatile__("call *%3" : "=a" (hv_status) :
188 "c" (control), "d" (input_address),
189 "m" (hypercall_pg));
190
191 return hv_status;
192
193#else
194
195 u32 control_hi = control >> 32;
196 u32 control_lo = control & 0xFFFFFFFF;
197 u32 hv_status_hi = 1;
198 u32 hv_status_lo = 1;
199 u32 input_address_hi = input_address >> 32;
200 u32 input_address_lo = input_address & 0xFFFFFFFF;
201 u32 output_address_hi = output_address >> 32;
202 u32 output_address_lo = output_address & 0xFFFFFFFF;
203
204 if (!hypercall_pg)
205 return (u64)ULLONG_MAX;
206
207 __asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi),
208 "=a"(hv_status_lo) : "d" (control_hi),
209 "a" (control_lo), "b" (input_address_hi),
210 "c" (input_address_lo), "D"(output_address_hi),
211 "S"(output_address_lo), "m" (hypercall_pg));
212
213 return hv_status_lo | ((u64)hv_status_hi << 32);
214#endif /* !x86_64 */
215}
216EXPORT_SYMBOL_GPL(hv_do_hypercall);
K. Y. Srinivasand058fa72017-01-19 11:51:48 -0700217
218void hyperv_report_panic(struct pt_regs *regs)
219{
220 static bool panic_reported;
221
222 /*
223 * We prefer to report panic on 'die' chain as we have proper
224 * registers to report, but if we miss it (e.g. on BUG()) we need
225 * to report it on 'panic'.
226 */
227 if (panic_reported)
228 return;
229 panic_reported = true;
230
231 wrmsrl(HV_X64_MSR_CRASH_P0, regs->ip);
232 wrmsrl(HV_X64_MSR_CRASH_P1, regs->ax);
233 wrmsrl(HV_X64_MSR_CRASH_P2, regs->bx);
234 wrmsrl(HV_X64_MSR_CRASH_P3, regs->cx);
235 wrmsrl(HV_X64_MSR_CRASH_P4, regs->dx);
236
237 /*
238 * Let Hyper-V know there is crash data available
239 */
240 wrmsrl(HV_X64_MSR_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY);
241}
242EXPORT_SYMBOL_GPL(hyperv_report_panic);
K. Y. Srinivasan73638cd2017-01-19 11:51:49 -0700243
244bool hv_is_hypercall_page_setup(void)
245{
246 union hv_x64_msr_hypercall_contents hypercall_msr;
247
248 /* Check if the hypercall page is setup */
249 hypercall_msr.as_uint64 = 0;
250 rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
251
252 if (!hypercall_msr.enable)
253 return false;
254
255 return true;
256}
257EXPORT_SYMBOL_GPL(hv_is_hypercall_page_setup);