Commit | Line | Data |
---|---|---|
773e8a04 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
a789aeba VK |
2 | #ifndef __KVM_X86_VMX_HYPERV_H |
3 | #define __KVM_X86_VMX_HYPERV_H | |
773e8a04 | 4 | |
75edce8a | 5 | #include <linux/jump_label.h> |
773e8a04 | 6 | |
75edce8a SC |
7 | #include <asm/hyperv-tlfs.h> |
8 | #include <asm/mshyperv.h> | |
9 | #include <asm/vmx.h> | |
10 | ||
a789aeba VK |
11 | #include "../hyperv.h" |
12 | ||
75edce8a SC |
13 | #include "capabilities.h" |
14 | #include "vmcs.h" | |
a8350231 | 15 | #include "vmcs12.h" |
75edce8a SC |
16 | |
17 | struct vmcs_config; | |
18 | ||
75edce8a SC |
19 | #define current_evmcs ((struct hv_enlightened_vmcs *)this_cpu_read(current_vmcs)) |
20 | ||
21 | #define KVM_EVMCS_VERSION 1 | |
22 | ||
773e8a04 VK |
23 | struct evmcs_field { |
24 | u16 offset; | |
25 | u16 clean_field; | |
26 | }; | |
27 | ||
75edce8a SC |
28 | extern const struct evmcs_field vmcs_field_to_evmcs_1[]; |
29 | extern const unsigned int nr_evmcs_1_fields; | |
30 | ||
892a42c1 VK |
31 | static __always_inline int evmcs_field_offset(unsigned long field, |
32 | u16 *clean_field) | |
773e8a04 VK |
33 | { |
34 | unsigned int index = ROL16(field, 6); | |
35 | const struct evmcs_field *evmcs_field; | |
36 | ||
892a42c1 | 37 | if (unlikely(index >= nr_evmcs_1_fields)) |
773e8a04 | 38 | return -ENOENT; |
773e8a04 VK |
39 | |
40 | evmcs_field = &vmcs_field_to_evmcs_1[index]; | |
41 | ||
892a42c1 VK |
42 | /* |
43 | * Use offset=0 to detect holes in eVMCS. This offset belongs to | |
44 | * 'revision_id' but this field has no encoding and is supposed to | |
45 | * be accessed directly. | |
46 | */ | |
47 | if (unlikely(!evmcs_field->offset)) | |
48 | return -ENOENT; | |
49 | ||
773e8a04 VK |
50 | if (clean_field) |
51 | *clean_field = evmcs_field->clean_field; | |
52 | ||
53 | return evmcs_field->offset; | |
54 | } | |
55 | ||
6cbbaab6 VK |
56 | static inline u64 evmcs_read_any(struct hv_enlightened_vmcs *evmcs, |
57 | unsigned long field, u16 offset) | |
58 | { | |
59 | /* | |
60 | * vmcs12_read_any() doesn't care whether the supplied structure | |
61 | * is 'struct vmcs12' or 'struct hv_enlightened_vmcs' as it takes | |
62 | * the exact offset of the required field, use it for convenience | |
63 | * here. | |
64 | */ | |
65 | return vmcs12_read_any((void *)evmcs, field, offset); | |
66 | } | |
67 | ||
892a42c1 VK |
68 | #if IS_ENABLED(CONFIG_HYPERV) |
69 | ||
fbc722aa | 70 | DECLARE_STATIC_KEY_FALSE(__kvm_is_using_evmcs); |
19f10315 SC |
71 | |
72 | static __always_inline bool kvm_is_using_evmcs(void) | |
73 | { | |
fbc722aa | 74 | return static_branch_unlikely(&__kvm_is_using_evmcs); |
19f10315 SC |
75 | } |
76 | ||
892a42c1 VK |
77 | static __always_inline int get_evmcs_offset(unsigned long field, |
78 | u16 *clean_field) | |
79 | { | |
80 | int offset = evmcs_field_offset(field, clean_field); | |
81 | ||
8d20bd63 | 82 | WARN_ONCE(offset < 0, "accessing unsupported EVMCS field %lx\n", field); |
892a42c1 VK |
83 | return offset; |
84 | } | |
85 | ||
010050a8 | 86 | static __always_inline void evmcs_write64(unsigned long field, u64 value) |
75edce8a SC |
87 | { |
88 | u16 clean_field; | |
89 | int offset = get_evmcs_offset(field, &clean_field); | |
90 | ||
91 | if (offset < 0) | |
92 | return; | |
93 | ||
94 | *(u64 *)((char *)current_evmcs + offset) = value; | |
95 | ||
96 | current_evmcs->hv_clean_fields &= ~clean_field; | |
97 | } | |
98 | ||
11633f69 | 99 | static __always_inline void evmcs_write32(unsigned long field, u32 value) |
75edce8a SC |
100 | { |
101 | u16 clean_field; | |
102 | int offset = get_evmcs_offset(field, &clean_field); | |
103 | ||
104 | if (offset < 0) | |
105 | return; | |
106 | ||
107 | *(u32 *)((char *)current_evmcs + offset) = value; | |
108 | current_evmcs->hv_clean_fields &= ~clean_field; | |
109 | } | |
110 | ||
11633f69 | 111 | static __always_inline void evmcs_write16(unsigned long field, u16 value) |
75edce8a SC |
112 | { |
113 | u16 clean_field; | |
114 | int offset = get_evmcs_offset(field, &clean_field); | |
115 | ||
116 | if (offset < 0) | |
117 | return; | |
118 | ||
119 | *(u16 *)((char *)current_evmcs + offset) = value; | |
120 | current_evmcs->hv_clean_fields &= ~clean_field; | |
121 | } | |
122 | ||
11633f69 | 123 | static __always_inline u64 evmcs_read64(unsigned long field) |
75edce8a SC |
124 | { |
125 | int offset = get_evmcs_offset(field, NULL); | |
126 | ||
127 | if (offset < 0) | |
128 | return 0; | |
129 | ||
130 | return *(u64 *)((char *)current_evmcs + offset); | |
131 | } | |
132 | ||
11633f69 | 133 | static __always_inline u32 evmcs_read32(unsigned long field) |
75edce8a SC |
134 | { |
135 | int offset = get_evmcs_offset(field, NULL); | |
136 | ||
137 | if (offset < 0) | |
138 | return 0; | |
139 | ||
140 | return *(u32 *)((char *)current_evmcs + offset); | |
141 | } | |
142 | ||
11633f69 | 143 | static __always_inline u16 evmcs_read16(unsigned long field) |
75edce8a SC |
144 | { |
145 | int offset = get_evmcs_offset(field, NULL); | |
146 | ||
147 | if (offset < 0) | |
148 | return 0; | |
149 | ||
150 | return *(u16 *)((char *)current_evmcs + offset); | |
151 | } | |
152 | ||
75edce8a SC |
153 | static inline void evmcs_load(u64 phys_addr) |
154 | { | |
155 | struct hv_vp_assist_page *vp_ap = | |
156 | hv_get_vp_assist_page(smp_processor_id()); | |
157 | ||
6f6a657c VK |
158 | if (current_evmcs->hv_enlightenments_control.nested_flush_hypercall) |
159 | vp_ap->nested_control.features.directhypercall = 1; | |
75edce8a SC |
160 | vp_ap->current_nested_vmcs = phys_addr; |
161 | vp_ap->enlighten_vmentry = 1; | |
162 | } | |
163 | ||
d83420c2 | 164 | void evmcs_sanitize_exec_ctrls(struct vmcs_config *vmcs_conf); |
75edce8a | 165 | #else /* !IS_ENABLED(CONFIG_HYPERV) */ |
19f10315 | 166 | static __always_inline bool kvm_is_using_evmcs(void) { return false; } |
010050a8 | 167 | static __always_inline void evmcs_write64(unsigned long field, u64 value) {} |
11633f69 SC |
168 | static __always_inline void evmcs_write32(unsigned long field, u32 value) {} |
169 | static __always_inline void evmcs_write16(unsigned long field, u16 value) {} | |
170 | static __always_inline u64 evmcs_read64(unsigned long field) { return 0; } | |
171 | static __always_inline u32 evmcs_read32(unsigned long field) { return 0; } | |
172 | static __always_inline u16 evmcs_read16(unsigned long field) { return 0; } | |
75edce8a | 173 | static inline void evmcs_load(u64 phys_addr) {} |
75edce8a SC |
174 | #endif /* IS_ENABLED(CONFIG_HYPERV) */ |
175 | ||
1e9dfbd7 | 176 | #define EVMPTR_INVALID (-1ULL) |
27849968 | 177 | #define EVMPTR_MAP_PENDING (-2ULL) |
1e9dfbd7 VK |
178 | |
179 | static inline bool evmptr_is_valid(u64 evmptr) | |
180 | { | |
27849968 | 181 | return evmptr != EVMPTR_INVALID && evmptr != EVMPTR_MAP_PENDING; |
1e9dfbd7 VK |
182 | } |
183 | ||
b6a0653a VK |
184 | enum nested_evmptrld_status { |
185 | EVMPTRLD_DISABLED, | |
186 | EVMPTRLD_SUCCEEDED, | |
187 | EVMPTRLD_VMFAIL, | |
188 | EVMPTRLD_ERROR, | |
189 | }; | |
190 | ||
046f5756 | 191 | u64 nested_get_evmptr(struct kvm_vcpu *vcpu); |
e2e871ab | 192 | uint16_t nested_get_evmcs_version(struct kvm_vcpu *vcpu); |
75edce8a SC |
193 | int nested_enable_evmcs(struct kvm_vcpu *vcpu, |
194 | uint16_t *vmcs_version); | |
4da77090 | 195 | void nested_evmcs_filter_control_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata); |
a8350231 | 196 | int nested_evmcs_check_controls(struct vmcs12 *vmcs12); |
c30e9bc8 | 197 | bool nested_evmcs_l2_tlb_flush_enabled(struct kvm_vcpu *vcpu); |
b0c9c25e | 198 | void vmx_hv_inject_synthetic_vmexit_post_tlb_flush(struct kvm_vcpu *vcpu); |
75edce8a | 199 | |
a789aeba | 200 | #endif /* __KVM_X86_VMX_HYPERV_H */ |