]>
Commit | Line | Data |
---|---|---|
4d60c5fd ZW |
1 | /* |
2 | * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice (including the next | |
12 | * paragraph) shall be included in all copies or substantial portions of the | |
13 | * Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
21 | * SOFTWARE. | |
22 | */ | |
23 | ||
24 | #include <linux/acpi.h> | |
25 | #include "i915_drv.h" | |
feddf6e8 | 26 | #include "gvt.h" |
4d60c5fd ZW |
27 | |
28 | static int init_vgpu_opregion(struct intel_vgpu *vgpu, u32 gpa) | |
29 | { | |
321927db | 30 | void __iomem *host_va = vgpu->gvt->opregion.opregion_va; |
4d60c5fd ZW |
31 | u8 *buf; |
32 | int i; | |
33 | ||
34 | if (WARN((vgpu_opregion(vgpu)->va), | |
35 | "vgpu%d: opregion has been initialized already.\n", | |
36 | vgpu->id)) | |
37 | return -EINVAL; | |
38 | ||
39 | vgpu_opregion(vgpu)->va = (void *)__get_free_pages(GFP_ATOMIC | | |
40 | GFP_DMA32 | __GFP_ZERO, | |
41 | INTEL_GVT_OPREGION_PORDER); | |
42 | ||
43 | if (!vgpu_opregion(vgpu)->va) | |
44 | return -ENOMEM; | |
45 | ||
46 | memcpy_fromio(vgpu_opregion(vgpu)->va, host_va, | |
47 | INTEL_GVT_OPREGION_SIZE); | |
48 | ||
49 | for (i = 0; i < INTEL_GVT_OPREGION_PAGES; i++) | |
50 | vgpu_opregion(vgpu)->gfn[i] = (gpa >> PAGE_SHIFT) + i; | |
51 | ||
52 | /* for unknown reason, the value in LID field is incorrect | |
53 | * which block the windows guest, so workaround it by force | |
54 | * setting it to "OPEN" | |
55 | */ | |
56 | buf = (u8 *)vgpu_opregion(vgpu)->va; | |
57 | buf[INTEL_GVT_OPREGION_CLID] = 0x3; | |
58 | ||
59 | return 0; | |
60 | } | |
61 | ||
62 | static int map_vgpu_opregion(struct intel_vgpu *vgpu, bool map) | |
63 | { | |
64 | u64 mfn; | |
65 | int i, ret; | |
66 | ||
67 | for (i = 0; i < INTEL_GVT_OPREGION_PAGES; i++) { | |
b8395cc7 | 68 | mfn = intel_gvt_hypervisor_virt_to_mfn(vgpu_opregion(vgpu)->va |
4d60c5fd ZW |
69 | + i * PAGE_SIZE); |
70 | if (mfn == INTEL_GVT_INVALID_ADDR) { | |
71 | gvt_err("fail to get MFN from VA\n"); | |
72 | return -EINVAL; | |
73 | } | |
74 | ret = intel_gvt_hypervisor_map_gfn_to_mfn(vgpu, | |
75 | vgpu_opregion(vgpu)->gfn[i], | |
8f89743b | 76 | mfn, 1, map); |
4d60c5fd ZW |
77 | if (ret) { |
78 | gvt_err("fail to map GFN to MFN, errno: %d\n", ret); | |
79 | return ret; | |
80 | } | |
81 | } | |
82 | return 0; | |
83 | } | |
84 | ||
85 | /** | |
86 | * intel_vgpu_clean_opregion - clean the stuff used to emulate opregion | |
87 | * @vgpu: a vGPU | |
88 | * | |
89 | */ | |
90 | void intel_vgpu_clean_opregion(struct intel_vgpu *vgpu) | |
91 | { | |
4d60c5fd ZW |
92 | gvt_dbg_core("vgpu%d: clean vgpu opregion\n", vgpu->id); |
93 | ||
94 | if (!vgpu_opregion(vgpu)->va) | |
95 | return; | |
96 | ||
8f89743b | 97 | if (intel_gvt_host.hypervisor_type == INTEL_GVT_HYPERVISOR_XEN) { |
4d60c5fd ZW |
98 | map_vgpu_opregion(vgpu, false); |
99 | free_pages((unsigned long)vgpu_opregion(vgpu)->va, | |
100 | INTEL_GVT_OPREGION_PORDER); | |
4d60c5fd | 101 | |
8f89743b JS |
102 | vgpu_opregion(vgpu)->va = NULL; |
103 | } | |
4d60c5fd ZW |
104 | } |
105 | ||
106 | /** | |
107 | * intel_vgpu_init_opregion - initialize the stuff used to emulate opregion | |
108 | * @vgpu: a vGPU | |
109 | * @gpa: guest physical address of opregion | |
110 | * | |
111 | * Returns: | |
112 | * Zero on success, negative error code if failed. | |
113 | */ | |
114 | int intel_vgpu_init_opregion(struct intel_vgpu *vgpu, u32 gpa) | |
115 | { | |
116 | int ret; | |
117 | ||
118 | gvt_dbg_core("vgpu%d: init vgpu opregion\n", vgpu->id); | |
119 | ||
120 | if (intel_gvt_host.hypervisor_type == INTEL_GVT_HYPERVISOR_XEN) { | |
121 | gvt_dbg_core("emulate opregion from kernel\n"); | |
122 | ||
123 | ret = init_vgpu_opregion(vgpu, gpa); | |
124 | if (ret) | |
125 | return ret; | |
126 | ||
127 | ret = map_vgpu_opregion(vgpu, true); | |
128 | if (ret) | |
129 | return ret; | |
4d60c5fd | 130 | } |
8f89743b | 131 | |
4d60c5fd ZW |
132 | return 0; |
133 | } | |
134 | ||
135 | /** | |
136 | * intel_gvt_clean_opregion - clean host opergion related stuffs | |
137 | * @gvt: a GVT device | |
138 | * | |
139 | */ | |
140 | void intel_gvt_clean_opregion(struct intel_gvt *gvt) | |
141 | { | |
d1bbef6d | 142 | memunmap(gvt->opregion.opregion_va); |
4d60c5fd ZW |
143 | gvt->opregion.opregion_va = NULL; |
144 | } | |
145 | ||
146 | /** | |
147 | * intel_gvt_init_opregion - initialize host opergion related stuffs | |
148 | * @gvt: a GVT device | |
149 | * | |
150 | * Returns: | |
151 | * Zero on success, negative error code if failed. | |
152 | */ | |
153 | int intel_gvt_init_opregion(struct intel_gvt *gvt) | |
154 | { | |
155 | gvt_dbg_core("init host opregion\n"); | |
156 | ||
157 | pci_read_config_dword(gvt->dev_priv->drm.pdev, INTEL_GVT_PCI_OPREGION, | |
158 | &gvt->opregion.opregion_pa); | |
159 | ||
d1bbef6d ZW |
160 | gvt->opregion.opregion_va = memremap(gvt->opregion.opregion_pa, |
161 | INTEL_GVT_OPREGION_SIZE, MEMREMAP_WB); | |
4d60c5fd ZW |
162 | if (!gvt->opregion.opregion_va) { |
163 | gvt_err("fail to map host opregion\n"); | |
164 | return -EFAULT; | |
165 | } | |
166 | return 0; | |
167 | } | |
168 | ||
169 | #define GVT_OPREGION_FUNC(scic) \ | |
170 | ({ \ | |
171 | u32 __ret; \ | |
172 | __ret = (scic & OPREGION_SCIC_FUNC_MASK) >> \ | |
173 | OPREGION_SCIC_FUNC_SHIFT; \ | |
174 | __ret; \ | |
175 | }) | |
176 | ||
177 | #define GVT_OPREGION_SUBFUNC(scic) \ | |
178 | ({ \ | |
179 | u32 __ret; \ | |
180 | __ret = (scic & OPREGION_SCIC_SUBFUNC_MASK) >> \ | |
181 | OPREGION_SCIC_SUBFUNC_SHIFT; \ | |
182 | __ret; \ | |
183 | }) | |
184 | ||
185 | static const char *opregion_func_name(u32 func) | |
186 | { | |
187 | const char *name = NULL; | |
188 | ||
189 | switch (func) { | |
190 | case 0 ... 3: | |
191 | case 5: | |
192 | case 7 ... 15: | |
193 | name = "Reserved"; | |
194 | break; | |
195 | ||
196 | case 4: | |
197 | name = "Get BIOS Data"; | |
198 | break; | |
199 | ||
200 | case 6: | |
201 | name = "System BIOS Callbacks"; | |
202 | break; | |
203 | ||
204 | default: | |
205 | name = "Unknown"; | |
206 | break; | |
207 | } | |
208 | return name; | |
209 | } | |
210 | ||
211 | static const char *opregion_subfunc_name(u32 subfunc) | |
212 | { | |
213 | const char *name = NULL; | |
214 | ||
215 | switch (subfunc) { | |
216 | case 0: | |
217 | name = "Supported Calls"; | |
218 | break; | |
219 | ||
220 | case 1: | |
221 | name = "Requested Callbacks"; | |
222 | break; | |
223 | ||
224 | case 2 ... 3: | |
225 | case 8 ... 9: | |
226 | name = "Reserved"; | |
227 | break; | |
228 | ||
229 | case 5: | |
230 | name = "Boot Display"; | |
231 | break; | |
232 | ||
233 | case 6: | |
234 | name = "TV-Standard/Video-Connector"; | |
235 | break; | |
236 | ||
237 | case 7: | |
238 | name = "Internal Graphics"; | |
239 | break; | |
240 | ||
241 | case 10: | |
242 | name = "Spread Spectrum Clocks"; | |
243 | break; | |
244 | ||
245 | case 11: | |
246 | name = "Get AKSV"; | |
247 | break; | |
248 | ||
249 | default: | |
250 | name = "Unknown"; | |
251 | break; | |
252 | } | |
253 | return name; | |
254 | }; | |
255 | ||
256 | static bool querying_capabilities(u32 scic) | |
257 | { | |
258 | u32 func, subfunc; | |
259 | ||
260 | func = GVT_OPREGION_FUNC(scic); | |
261 | subfunc = GVT_OPREGION_SUBFUNC(scic); | |
262 | ||
263 | if ((func == INTEL_GVT_OPREGION_SCIC_F_GETBIOSDATA && | |
264 | subfunc == INTEL_GVT_OPREGION_SCIC_SF_SUPPRTEDCALLS) | |
265 | || (func == INTEL_GVT_OPREGION_SCIC_F_GETBIOSDATA && | |
266 | subfunc == INTEL_GVT_OPREGION_SCIC_SF_REQEUSTEDCALLBACKS) | |
267 | || (func == INTEL_GVT_OPREGION_SCIC_F_GETBIOSCALLBACKS && | |
268 | subfunc == INTEL_GVT_OPREGION_SCIC_SF_SUPPRTEDCALLS)) { | |
269 | return true; | |
270 | } | |
271 | return false; | |
272 | } | |
273 | ||
274 | /** | |
275 | * intel_vgpu_emulate_opregion_request - emulating OpRegion request | |
276 | * @vgpu: a vGPU | |
277 | * @swsci: SWSCI request | |
278 | * | |
279 | * Returns: | |
280 | * Zero on success, negative error code if failed | |
281 | */ | |
282 | int intel_vgpu_emulate_opregion_request(struct intel_vgpu *vgpu, u32 swsci) | |
283 | { | |
284 | u32 *scic, *parm; | |
285 | u32 func, subfunc; | |
286 | ||
287 | scic = vgpu_opregion(vgpu)->va + INTEL_GVT_OPREGION_SCIC; | |
288 | parm = vgpu_opregion(vgpu)->va + INTEL_GVT_OPREGION_PARM; | |
289 | ||
290 | if (!(swsci & SWSCI_SCI_SELECT)) { | |
291 | gvt_err("vgpu%d: requesting SMI service\n", vgpu->id); | |
292 | return 0; | |
293 | } | |
294 | /* ignore non 0->1 trasitions */ | |
295 | if ((vgpu_cfg_space(vgpu)[INTEL_GVT_PCI_SWSCI] | |
296 | & SWSCI_SCI_TRIGGER) || | |
297 | !(swsci & SWSCI_SCI_TRIGGER)) { | |
298 | return 0; | |
299 | } | |
300 | ||
301 | func = GVT_OPREGION_FUNC(*scic); | |
302 | subfunc = GVT_OPREGION_SUBFUNC(*scic); | |
303 | if (!querying_capabilities(*scic)) { | |
304 | gvt_err("vgpu%d: requesting runtime service: func \"%s\"," | |
305 | " subfunc \"%s\"\n", | |
306 | vgpu->id, | |
307 | opregion_func_name(func), | |
308 | opregion_subfunc_name(subfunc)); | |
309 | /* | |
310 | * emulate exit status of function call, '0' means | |
311 | * "failure, generic, unsupported or unknown cause" | |
312 | */ | |
313 | *scic &= ~OPREGION_SCIC_EXIT_MASK; | |
314 | return 0; | |
315 | } | |
316 | ||
317 | *scic = 0; | |
318 | *parm = 0; | |
319 | return 0; | |
320 | } |