Commit | Line | Data |
---|---|---|
b2441318 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
e08cae41 PA |
2 | #ifndef _ASM_X86_MSHYPER_H |
3 | #define _ASM_X86_MSHYPER_H | |
a2a47c6c | 4 | |
e08cae41 | 5 | #include <linux/types.h> |
26fcd952 | 6 | #include <linux/atomic.h> |
806c8927 | 7 | #include <linux/nmi.h> |
fc53662f | 8 | #include <asm/io.h> |
e08cae41 PA |
9 | #include <asm/hyperv.h> |
10 | ||
8de8af7e S |
11 | /* |
12 | * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent | |
13 | * is set by CPUID(HVCPUID_VERSION_FEATURES). | |
14 | */ | |
15 | enum hv_cpuid_function { | |
16 | HVCPUID_VERSION_FEATURES = 0x00000001, | |
17 | HVCPUID_VENDOR_MAXFUNCTION = 0x40000000, | |
18 | HVCPUID_INTERFACE = 0x40000001, | |
19 | ||
20 | /* | |
21 | * The remaining functions depend on the value of | |
22 | * HVCPUID_INTERFACE | |
23 | */ | |
24 | HVCPUID_VERSION = 0x40000002, | |
25 | HVCPUID_FEATURES = 0x40000003, | |
26 | HVCPUID_ENLIGHTENMENT_INFO = 0x40000004, | |
27 | HVCPUID_IMPLEMENTATION_LIMITS = 0x40000005, | |
28 | }; | |
29 | ||
e08cae41 PA |
30 | struct ms_hyperv_info { |
31 | u32 features; | |
cc2dd402 | 32 | u32 misc_features; |
e08cae41 | 33 | u32 hints; |
dd018597 VK |
34 | u32 max_vp_index; |
35 | u32 max_lp_index; | |
e08cae41 PA |
36 | }; |
37 | ||
38 | extern struct ms_hyperv_info ms_hyperv; | |
a2a47c6c | 39 | |
3f646ed7 S |
40 | /* |
41 | * Declare the MSR used to setup pages used to communicate with the hypervisor. | |
42 | */ | |
43 | union hv_x64_msr_hypercall_contents { | |
44 | u64 as_uint64; | |
45 | struct { | |
46 | u64 enable:1; | |
47 | u64 reserved:11; | |
48 | u64 guest_physical_address:52; | |
49 | }; | |
50 | }; | |
51 | ||
63ed4e0c S |
52 | /* |
53 | * TSC page layout. | |
54 | */ | |
55 | ||
56 | struct ms_hyperv_tsc_page { | |
57 | volatile u32 tsc_sequence; | |
58 | u32 reserved1; | |
59 | volatile u64 tsc_scale; | |
60 | volatile s64 tsc_offset; | |
61 | u64 reserved2[509]; | |
62 | }; | |
63 | ||
352c9624 S |
64 | /* |
65 | * The guest OS needs to register the guest ID with the hypervisor. | |
66 | * The guest ID is a 64 bit entity and the structure of this ID is | |
67 | * specified in the Hyper-V specification: | |
68 | * | |
69 | * msdn.microsoft.com/en-us/library/windows/hardware/ff542653%28v=vs.85%29.aspx | |
70 | * | |
71 | * While the current guideline does not specify how Linux guest ID(s) | |
72 | * need to be generated, our plan is to publish the guidelines for | |
73 | * Linux and other guest operating systems that currently are hosted | |
74 | * on Hyper-V. The implementation here conforms to this yet | |
75 | * unpublished guidelines. | |
76 | * | |
77 | * | |
78 | * Bit(s) | |
79 | * 63 - Indicates if the OS is Open Source or not; 1 is Open Source | |
80 | * 62:56 - Os Type; Linux is 0x100 | |
81 | * 55:48 - Distro specific identification | |
82 | * 47:16 - Linux kernel version number | |
83 | * 15:0 - Distro specific identification | |
84 | * | |
85 | * | |
86 | */ | |
87 | ||
9b06e101 | 88 | #define HV_LINUX_VENDOR_ID 0x8100 |
352c9624 S |
89 | |
90 | /* | |
91 | * Generate the guest ID based on the guideline described above. | |
92 | */ | |
93 | ||
94 | static inline __u64 generate_guest_id(__u64 d_info1, __u64 kernel_version, | |
95 | __u64 d_info2) | |
96 | { | |
97 | __u64 guest_id = 0; | |
98 | ||
9b06e101 | 99 | guest_id = (((__u64)HV_LINUX_VENDOR_ID) << 48); |
352c9624 S |
100 | guest_id |= (d_info1 << 48); |
101 | guest_id |= (kernel_version << 16); | |
102 | guest_id |= d_info2; | |
103 | ||
104 | return guest_id; | |
105 | } | |
106 | ||
e810e48c S |
107 | |
108 | /* Free the message slot and signal end-of-message if required */ | |
109 | static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type) | |
110 | { | |
111 | /* | |
112 | * On crash we're reading some other CPU's message page and we need | |
113 | * to be careful: this other CPU may already had cleared the header | |
114 | * and the host may already had delivered some other message there. | |
115 | * In case we blindly write msg->header.message_type we're going | |
116 | * to lose it. We can still lose a message of the same type but | |
117 | * we count on the fact that there can only be one | |
118 | * CHANNELMSG_UNLOAD_RESPONSE and we don't care about other messages | |
119 | * on crash. | |
120 | */ | |
121 | if (cmpxchg(&msg->header.message_type, old_msg_type, | |
122 | HVMSG_NONE) != old_msg_type) | |
123 | return; | |
124 | ||
125 | /* | |
126 | * Make sure the write to MessageType (ie set to | |
127 | * HVMSG_NONE) happens before we read the | |
128 | * MessagePending and EOMing. Otherwise, the EOMing | |
129 | * will not deliver any more messages since there is | |
130 | * no empty slot | |
131 | */ | |
132 | mb(); | |
133 | ||
134 | if (msg->header.message_flags.msg_pending) { | |
135 | /* | |
136 | * This will cause message queue rescan to | |
137 | * possibly deliver another msg from the | |
138 | * hypervisor | |
139 | */ | |
140 | wrmsrl(HV_X64_MSR_EOM, 0); | |
141 | } | |
142 | } | |
143 | ||
d5116b40 S |
144 | #define hv_init_timer(timer, tick) wrmsrl(timer, tick) |
145 | #define hv_init_timer_config(config, val) wrmsrl(config, val) | |
146 | ||
155e4a2f S |
147 | #define hv_get_simp(val) rdmsrl(HV_X64_MSR_SIMP, val) |
148 | #define hv_set_simp(val) wrmsrl(HV_X64_MSR_SIMP, val) | |
149 | ||
8e307bf8 S |
150 | #define hv_get_siefp(val) rdmsrl(HV_X64_MSR_SIEFP, val) |
151 | #define hv_set_siefp(val) wrmsrl(HV_X64_MSR_SIEFP, val) | |
152 | ||
06d1d98a S |
153 | #define hv_get_synic_state(val) rdmsrl(HV_X64_MSR_SCONTROL, val) |
154 | #define hv_set_synic_state(val) wrmsrl(HV_X64_MSR_SCONTROL, val) | |
155 | ||
7297ff0c S |
156 | #define hv_get_vp_index(index) rdmsrl(HV_X64_MSR_VP_INDEX, index) |
157 | ||
37e11d5c S |
158 | #define hv_get_synint_state(int_num, val) rdmsrl(int_num, val) |
159 | #define hv_set_synint_state(int_num, val) wrmsrl(int_num, val) | |
160 | ||
bc2b0331 | 161 | void hyperv_callback_vector(void); |
cf910e83 SA |
162 | #ifdef CONFIG_TRACING |
163 | #define trace_hyperv_callback_vector hyperv_callback_vector | |
164 | #endif | |
bc2b0331 | 165 | void hyperv_vector_handler(struct pt_regs *regs); |
76d388cd TG |
166 | void hv_setup_vmbus_irq(void (*handler)(void)); |
167 | void hv_remove_vmbus_irq(void); | |
bc2b0331 | 168 | |
2517281d VK |
169 | void hv_setup_kexec_handler(void (*handler)(void)); |
170 | void hv_remove_kexec_handler(void); | |
b4370df2 VK |
171 | void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs)); |
172 | void hv_remove_crash_handler(void); | |
8730046c S |
173 | |
174 | #if IS_ENABLED(CONFIG_HYPERV) | |
dee863b5 | 175 | extern struct clocksource *hyperv_cs; |
fc53662f VK |
176 | extern void *hv_hypercall_pg; |
177 | ||
178 | static inline u64 hv_do_hypercall(u64 control, void *input, void *output) | |
179 | { | |
180 | u64 input_address = input ? virt_to_phys(input) : 0; | |
181 | u64 output_address = output ? virt_to_phys(output) : 0; | |
182 | u64 hv_status; | |
fc53662f VK |
183 | |
184 | #ifdef CONFIG_X86_64 | |
185 | if (!hv_hypercall_pg) | |
186 | return U64_MAX; | |
187 | ||
188 | __asm__ __volatile__("mov %4, %%r8\n" | |
189 | "call *%5" | |
f5caf621 | 190 | : "=a" (hv_status), ASM_CALL_CONSTRAINT, |
fc53662f VK |
191 | "+c" (control), "+d" (input_address) |
192 | : "r" (output_address), "m" (hv_hypercall_pg) | |
193 | : "cc", "memory", "r8", "r9", "r10", "r11"); | |
194 | #else | |
195 | u32 input_address_hi = upper_32_bits(input_address); | |
196 | u32 input_address_lo = lower_32_bits(input_address); | |
197 | u32 output_address_hi = upper_32_bits(output_address); | |
198 | u32 output_address_lo = lower_32_bits(output_address); | |
199 | ||
200 | if (!hv_hypercall_pg) | |
201 | return U64_MAX; | |
202 | ||
203 | __asm__ __volatile__("call *%7" | |
204 | : "=A" (hv_status), | |
f5caf621 | 205 | "+c" (input_address_lo), ASM_CALL_CONSTRAINT |
fc53662f VK |
206 | : "A" (control), |
207 | "b" (input_address_hi), | |
208 | "D"(output_address_hi), "S"(output_address_lo), | |
209 | "m" (hv_hypercall_pg) | |
210 | : "cc", "memory"); | |
211 | #endif /* !x86_64 */ | |
212 | return hv_status; | |
213 | } | |
dee863b5 | 214 | |
806c8927 | 215 | #define HV_HYPERCALL_RESULT_MASK GENMASK_ULL(15, 0) |
6a8edbd0 | 216 | #define HV_HYPERCALL_FAST_BIT BIT(16) |
806c8927 VK |
217 | #define HV_HYPERCALL_VARHEAD_OFFSET 17 |
218 | #define HV_HYPERCALL_REP_COMP_OFFSET 32 | |
219 | #define HV_HYPERCALL_REP_COMP_MASK GENMASK_ULL(43, 32) | |
220 | #define HV_HYPERCALL_REP_START_OFFSET 48 | |
221 | #define HV_HYPERCALL_REP_START_MASK GENMASK_ULL(59, 48) | |
6a8edbd0 VK |
222 | |
223 | /* Fast hypercall with 8 bytes of input and no output */ | |
224 | static inline u64 hv_do_fast_hypercall8(u16 code, u64 input1) | |
225 | { | |
226 | u64 hv_status, control = (u64)code | HV_HYPERCALL_FAST_BIT; | |
6a8edbd0 VK |
227 | |
228 | #ifdef CONFIG_X86_64 | |
229 | { | |
230 | __asm__ __volatile__("call *%4" | |
f5caf621 | 231 | : "=a" (hv_status), ASM_CALL_CONSTRAINT, |
6a8edbd0 VK |
232 | "+c" (control), "+d" (input1) |
233 | : "m" (hv_hypercall_pg) | |
234 | : "cc", "r8", "r9", "r10", "r11"); | |
235 | } | |
236 | #else | |
237 | { | |
238 | u32 input1_hi = upper_32_bits(input1); | |
239 | u32 input1_lo = lower_32_bits(input1); | |
240 | ||
241 | __asm__ __volatile__ ("call *%5" | |
242 | : "=A"(hv_status), | |
243 | "+c"(input1_lo), | |
f5caf621 | 244 | ASM_CALL_CONSTRAINT |
6a8edbd0 VK |
245 | : "A" (control), |
246 | "b" (input1_hi), | |
247 | "m" (hv_hypercall_pg) | |
248 | : "cc", "edi", "esi"); | |
249 | } | |
250 | #endif | |
251 | return hv_status; | |
252 | } | |
253 | ||
806c8927 VK |
254 | /* |
255 | * Rep hypercalls. Callers of this functions are supposed to ensure that | |
256 | * rep_count and varhead_size comply with Hyper-V hypercall definition. | |
257 | */ | |
258 | static inline u64 hv_do_rep_hypercall(u16 code, u16 rep_count, u16 varhead_size, | |
259 | void *input, void *output) | |
260 | { | |
261 | u64 control = code; | |
262 | u64 status; | |
263 | u16 rep_comp; | |
264 | ||
265 | control |= (u64)varhead_size << HV_HYPERCALL_VARHEAD_OFFSET; | |
266 | control |= (u64)rep_count << HV_HYPERCALL_REP_COMP_OFFSET; | |
267 | ||
268 | do { | |
269 | status = hv_do_hypercall(control, input, output); | |
270 | if ((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS) | |
271 | return status; | |
272 | ||
273 | /* Bits 32-43 of status have 'Reps completed' data. */ | |
274 | rep_comp = (status & HV_HYPERCALL_REP_COMP_MASK) >> | |
275 | HV_HYPERCALL_REP_COMP_OFFSET; | |
276 | ||
277 | control &= ~HV_HYPERCALL_REP_START_MASK; | |
278 | control |= (u64)rep_comp << HV_HYPERCALL_REP_START_OFFSET; | |
279 | ||
280 | touch_nmi_watchdog(); | |
281 | } while (rep_comp < rep_count); | |
282 | ||
283 | return status; | |
284 | } | |
285 | ||
7415aea6 VK |
286 | /* |
287 | * Hypervisor's notion of virtual processor ID is different from | |
288 | * Linux' notion of CPU ID. This information can only be retrieved | |
289 | * in the context of the calling CPU. Setup a map for easy access | |
290 | * to this information. | |
291 | */ | |
292 | extern u32 *hv_vp_index; | |
a3b74243 | 293 | extern u32 hv_max_vp_index; |
7415aea6 VK |
294 | |
295 | /** | |
296 | * hv_cpu_number_to_vp_number() - Map CPU to VP. | |
297 | * @cpu_number: CPU number in Linux terms | |
298 | * | |
299 | * This function returns the mapping between the Linux processor | |
300 | * number and the hypervisor's virtual processor number, useful | |
301 | * in making hypercalls and such that talk about specific | |
302 | * processors. | |
303 | * | |
304 | * Return: Virtual processor number in Hyper-V terms | |
305 | */ | |
306 | static inline int hv_cpu_number_to_vp_number(int cpu_number) | |
307 | { | |
308 | return hv_vp_index[cpu_number]; | |
309 | } | |
dee863b5 | 310 | |
8730046c | 311 | void hyperv_init(void); |
2ffd9e33 VK |
312 | void hyperv_setup_mmu_ops(void); |
313 | void hyper_alloc_mmu(void); | |
d058fa7e | 314 | void hyperv_report_panic(struct pt_regs *regs); |
73638cdd | 315 | bool hv_is_hypercall_page_setup(void); |
d6f3609d | 316 | void hyperv_cleanup(void); |
79cadff2 VK |
317 | #else /* CONFIG_HYPERV */ |
318 | static inline void hyperv_init(void) {} | |
319 | static inline bool hv_is_hypercall_page_setup(void) { return false; } | |
320 | static inline void hyperv_cleanup(void) {} | |
2ffd9e33 | 321 | static inline void hyperv_setup_mmu_ops(void) {} |
79cadff2 VK |
322 | #endif /* CONFIG_HYPERV */ |
323 | ||
bd2a9ada VK |
324 | #ifdef CONFIG_HYPERV_TSCPAGE |
325 | struct ms_hyperv_tsc_page *hv_get_tsc_page(void); | |
0733379b VK |
326 | static inline u64 hv_read_tsc_page(const struct ms_hyperv_tsc_page *tsc_pg) |
327 | { | |
328 | u64 scale, offset, cur_tsc; | |
329 | u32 sequence; | |
330 | ||
331 | /* | |
332 | * The protocol for reading Hyper-V TSC page is specified in Hypervisor | |
333 | * Top-Level Functional Specification ver. 3.0 and above. To get the | |
334 | * reference time we must do the following: | |
335 | * - READ ReferenceTscSequence | |
336 | * A special '0' value indicates the time source is unreliable and we | |
337 | * need to use something else. The currently published specification | |
338 | * versions (up to 4.0b) contain a mistake and wrongly claim '-1' | |
339 | * instead of '0' as the special value, see commit c35b82ef0294. | |
340 | * - ReferenceTime = | |
341 | * ((RDTSC() * ReferenceTscScale) >> 64) + ReferenceTscOffset | |
342 | * - READ ReferenceTscSequence again. In case its value has changed | |
343 | * since our first reading we need to discard ReferenceTime and repeat | |
344 | * the whole sequence as the hypervisor was updating the page in | |
345 | * between. | |
346 | */ | |
347 | do { | |
348 | sequence = READ_ONCE(tsc_pg->tsc_sequence); | |
349 | if (!sequence) | |
350 | return U64_MAX; | |
351 | /* | |
352 | * Make sure we read sequence before we read other values from | |
353 | * TSC page. | |
354 | */ | |
355 | smp_rmb(); | |
356 | ||
357 | scale = READ_ONCE(tsc_pg->tsc_scale); | |
358 | offset = READ_ONCE(tsc_pg->tsc_offset); | |
359 | cur_tsc = rdtsc_ordered(); | |
360 | ||
361 | /* | |
362 | * Make sure we read sequence after we read all other values | |
363 | * from TSC page. | |
364 | */ | |
365 | smp_rmb(); | |
366 | ||
367 | } while (READ_ONCE(tsc_pg->tsc_sequence) != sequence); | |
368 | ||
369 | return mul_u64_u64_shr(cur_tsc, scale, 64) + offset; | |
370 | } | |
371 | ||
bd2a9ada VK |
372 | #else |
373 | static inline struct ms_hyperv_tsc_page *hv_get_tsc_page(void) | |
374 | { | |
375 | return NULL; | |
376 | } | |
377 | #endif | |
a2a47c6c | 378 | #endif |