]>
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 | * Authors: | |
24 | * Eddie Dong <eddie.dong@intel.com> | |
25 | * Jike Song <jike.song@intel.com> | |
26 | * | |
27 | * Contributors: | |
28 | * Zhi Wang <zhi.a.wang@intel.com> | |
29 | * Min He <min.he@intel.com> | |
30 | * Bing Niu <bing.niu@intel.com> | |
31 | * | |
32 | */ | |
33 | ||
34 | #include "i915_drv.h" | |
feddf6e8 | 35 | #include "gvt.h" |
4d60c5fd ZW |
36 | |
37 | enum { | |
38 | INTEL_GVT_PCI_BAR_GTTMMIO = 0, | |
39 | INTEL_GVT_PCI_BAR_APERTURE, | |
40 | INTEL_GVT_PCI_BAR_PIO, | |
41 | INTEL_GVT_PCI_BAR_MAX, | |
42 | }; | |
43 | ||
c2e04fda CD |
44 | /* bitmap for writable bits (RW or RW1C bits, but cannot co-exist in one |
45 | * byte) byte by byte in standard pci configuration space. (not the full | |
46 | * 256 bytes.) | |
47 | */ | |
48 | static const u8 pci_cfg_space_rw_bmp[PCI_INTERRUPT_LINE + 4] = { | |
49 | [PCI_COMMAND] = 0xff, 0x07, | |
50 | [PCI_STATUS] = 0x00, 0xf9, /* the only one RW1C byte */ | |
51 | [PCI_CACHE_LINE_SIZE] = 0xff, | |
52 | [PCI_BASE_ADDRESS_0 ... PCI_CARDBUS_CIS - 1] = 0xff, | |
53 | [PCI_ROM_ADDRESS] = 0x01, 0xf8, 0xff, 0xff, | |
54 | [PCI_INTERRUPT_LINE] = 0xff, | |
55 | }; | |
56 | ||
57 | /** | |
58 | * vgpu_pci_cfg_mem_write - write virtual cfg space memory | |
59 | * | |
60 | * Use this function to write virtual cfg space memory. | |
61 | * For standard cfg space, only RW bits can be changed, | |
62 | * and we emulates the RW1C behavior of PCI_STATUS register. | |
63 | */ | |
64 | static void vgpu_pci_cfg_mem_write(struct intel_vgpu *vgpu, unsigned int off, | |
65 | u8 *src, unsigned int bytes) | |
66 | { | |
67 | u8 *cfg_base = vgpu_cfg_space(vgpu); | |
68 | u8 mask, new, old; | |
69 | int i = 0; | |
70 | ||
71 | for (; i < bytes && (off + i < sizeof(pci_cfg_space_rw_bmp)); i++) { | |
72 | mask = pci_cfg_space_rw_bmp[off + i]; | |
73 | old = cfg_base[off + i]; | |
74 | new = src[i] & mask; | |
75 | ||
76 | /** | |
77 | * The PCI_STATUS high byte has RW1C bits, here | |
78 | * emulates clear by writing 1 for these bits. | |
79 | * Writing a 0b to RW1C bits has no effect. | |
80 | */ | |
81 | if (off + i == PCI_STATUS + 1) | |
82 | new = (~new & old) & mask; | |
83 | ||
84 | cfg_base[off + i] = (old & ~mask) | new; | |
85 | } | |
86 | ||
87 | /* For other configuration space directly copy as it is. */ | |
88 | if (i < bytes) | |
89 | memcpy(cfg_base + off + i, src + i, bytes - i); | |
90 | } | |
91 | ||
4d60c5fd ZW |
92 | /** |
93 | * intel_vgpu_emulate_cfg_read - emulate vGPU configuration space read | |
94 | * | |
95 | * Returns: | |
96 | * Zero on success, negative error code if failed. | |
97 | */ | |
9ec1e66b | 98 | int intel_vgpu_emulate_cfg_read(struct intel_vgpu *vgpu, unsigned int offset, |
4d60c5fd ZW |
99 | void *p_data, unsigned int bytes) |
100 | { | |
4d60c5fd ZW |
101 | if (WARN_ON(bytes > 4)) |
102 | return -EINVAL; | |
103 | ||
02d578e5 | 104 | if (WARN_ON(offset + bytes > vgpu->gvt->device_info.cfg_space_size)) |
4d60c5fd ZW |
105 | return -EINVAL; |
106 | ||
107 | memcpy(p_data, vgpu_cfg_space(vgpu) + offset, bytes); | |
108 | return 0; | |
109 | } | |
110 | ||
111 | static int map_aperture(struct intel_vgpu *vgpu, bool map) | |
112 | { | |
f090a00d CD |
113 | phys_addr_t aperture_pa = vgpu_aperture_pa_base(vgpu); |
114 | unsigned long aperture_sz = vgpu_aperture_sz(vgpu); | |
115 | u64 first_gfn; | |
4d60c5fd ZW |
116 | u64 val; |
117 | int ret; | |
118 | ||
119 | if (map == vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_APERTURE].tracked) | |
120 | return 0; | |
121 | ||
f090a00d CD |
122 | if (map) { |
123 | vgpu->gm.aperture_va = memremap(aperture_pa, aperture_sz, | |
124 | MEMREMAP_WC); | |
125 | if (!vgpu->gm.aperture_va) | |
126 | return -ENOMEM; | |
127 | } else { | |
128 | memunmap(vgpu->gm.aperture_va); | |
129 | vgpu->gm.aperture_va = NULL; | |
130 | } | |
131 | ||
4d60c5fd ZW |
132 | val = vgpu_cfg_space(vgpu)[PCI_BASE_ADDRESS_2]; |
133 | if (val & PCI_BASE_ADDRESS_MEM_TYPE_64) | |
134 | val = *(u64 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_2); | |
135 | else | |
136 | val = *(u32 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_2); | |
137 | ||
138 | first_gfn = (val + vgpu_aperture_offset(vgpu)) >> PAGE_SHIFT; | |
4d60c5fd ZW |
139 | |
140 | ret = intel_gvt_hypervisor_map_gfn_to_mfn(vgpu, first_gfn, | |
f090a00d CD |
141 | aperture_pa >> PAGE_SHIFT, |
142 | aperture_sz >> PAGE_SHIFT, | |
143 | map); | |
144 | if (ret) { | |
145 | memunmap(vgpu->gm.aperture_va); | |
146 | vgpu->gm.aperture_va = NULL; | |
4d60c5fd | 147 | return ret; |
f090a00d | 148 | } |
4d60c5fd ZW |
149 | |
150 | vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_APERTURE].tracked = map; | |
151 | return 0; | |
152 | } | |
153 | ||
154 | static int trap_gttmmio(struct intel_vgpu *vgpu, bool trap) | |
155 | { | |
156 | u64 start, end; | |
157 | u64 val; | |
158 | int ret; | |
159 | ||
160 | if (trap == vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_GTTMMIO].tracked) | |
161 | return 0; | |
162 | ||
163 | val = vgpu_cfg_space(vgpu)[PCI_BASE_ADDRESS_0]; | |
164 | if (val & PCI_BASE_ADDRESS_MEM_TYPE_64) | |
165 | start = *(u64 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_0); | |
166 | else | |
167 | start = *(u32 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_0); | |
168 | ||
169 | start &= ~GENMASK(3, 0); | |
170 | end = start + vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_GTTMMIO].size - 1; | |
171 | ||
172 | ret = intel_gvt_hypervisor_set_trap_area(vgpu, start, end, trap); | |
173 | if (ret) | |
174 | return ret; | |
175 | ||
176 | vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_GTTMMIO].tracked = trap; | |
177 | return 0; | |
178 | } | |
179 | ||
180 | static int emulate_pci_command_write(struct intel_vgpu *vgpu, | |
181 | unsigned int offset, void *p_data, unsigned int bytes) | |
182 | { | |
183 | u8 old = vgpu_cfg_space(vgpu)[offset]; | |
184 | u8 new = *(u8 *)p_data; | |
185 | u8 changed = old ^ new; | |
186 | int ret; | |
187 | ||
c2e04fda | 188 | vgpu_pci_cfg_mem_write(vgpu, offset, p_data, bytes); |
4d60c5fd ZW |
189 | if (!(changed & PCI_COMMAND_MEMORY)) |
190 | return 0; | |
191 | ||
192 | if (old & PCI_COMMAND_MEMORY) { | |
193 | ret = trap_gttmmio(vgpu, false); | |
194 | if (ret) | |
195 | return ret; | |
196 | ret = map_aperture(vgpu, false); | |
197 | if (ret) | |
198 | return ret; | |
199 | } else { | |
200 | ret = trap_gttmmio(vgpu, true); | |
201 | if (ret) | |
202 | return ret; | |
203 | ret = map_aperture(vgpu, true); | |
204 | if (ret) | |
205 | return ret; | |
206 | } | |
207 | ||
4d60c5fd ZW |
208 | return 0; |
209 | } | |
210 | ||
add7e4fc CD |
211 | static int emulate_pci_rom_bar_write(struct intel_vgpu *vgpu, |
212 | unsigned int offset, void *p_data, unsigned int bytes) | |
213 | { | |
214 | u32 *pval = (u32 *)(vgpu_cfg_space(vgpu) + offset); | |
215 | u32 new = *(u32 *)(p_data); | |
216 | ||
217 | if ((new & PCI_ROM_ADDRESS_MASK) == PCI_ROM_ADDRESS_MASK) | |
218 | /* We don't have rom, return size of 0. */ | |
219 | *pval = 0; | |
220 | else | |
221 | vgpu_pci_cfg_mem_write(vgpu, offset, p_data, bytes); | |
222 | return 0; | |
223 | } | |
224 | ||
4d60c5fd ZW |
225 | static int emulate_pci_bar_write(struct intel_vgpu *vgpu, unsigned int offset, |
226 | void *p_data, unsigned int bytes) | |
227 | { | |
4d60c5fd ZW |
228 | u32 new = *(u32 *)(p_data); |
229 | bool lo = IS_ALIGNED(offset, 8); | |
230 | u64 size; | |
231 | int ret = 0; | |
232 | bool mmio_enabled = | |
233 | vgpu_cfg_space(vgpu)[PCI_COMMAND] & PCI_COMMAND_MEMORY; | |
f1751362 | 234 | struct intel_vgpu_pci_bar *bars = vgpu->cfg_space.bar; |
4d60c5fd | 235 | |
f1751362 CD |
236 | /* |
237 | * Power-up software can determine how much address | |
238 | * space the device requires by writing a value of | |
239 | * all 1's to the register and then reading the value | |
240 | * back. The device will return 0's in all don't-care | |
241 | * address bits. | |
242 | */ | |
4d60c5fd | 243 | if (new == 0xffffffff) { |
f1751362 CD |
244 | switch (offset) { |
245 | case PCI_BASE_ADDRESS_0: | |
246 | case PCI_BASE_ADDRESS_1: | |
247 | size = ~(bars[INTEL_GVT_PCI_BAR_GTTMMIO].size -1); | |
248 | intel_vgpu_write_pci_bar(vgpu, offset, | |
249 | size >> (lo ? 0 : 32), lo); | |
250 | /* | |
251 | * Untrap the BAR, since guest hasn't configured a | |
252 | * valid GPA | |
4d60c5fd | 253 | */ |
4d60c5fd ZW |
254 | ret = trap_gttmmio(vgpu, false); |
255 | break; | |
f1751362 CD |
256 | case PCI_BASE_ADDRESS_2: |
257 | case PCI_BASE_ADDRESS_3: | |
258 | size = ~(bars[INTEL_GVT_PCI_BAR_APERTURE].size -1); | |
259 | intel_vgpu_write_pci_bar(vgpu, offset, | |
260 | size >> (lo ? 0 : 32), lo); | |
4d60c5fd ZW |
261 | ret = map_aperture(vgpu, false); |
262 | break; | |
f1751362 CD |
263 | default: |
264 | /* Unimplemented BARs */ | |
265 | intel_vgpu_write_pci_bar(vgpu, offset, 0x0, false); | |
4d60c5fd | 266 | } |
4d60c5fd | 267 | } else { |
f1751362 CD |
268 | switch (offset) { |
269 | case PCI_BASE_ADDRESS_0: | |
270 | case PCI_BASE_ADDRESS_1: | |
271 | /* | |
272 | * Untrap the old BAR first, since guest has | |
273 | * re-configured the BAR | |
274 | */ | |
275 | trap_gttmmio(vgpu, false); | |
276 | intel_vgpu_write_pci_bar(vgpu, offset, new, lo); | |
277 | ret = trap_gttmmio(vgpu, mmio_enabled); | |
4d60c5fd | 278 | break; |
f1751362 CD |
279 | case PCI_BASE_ADDRESS_2: |
280 | case PCI_BASE_ADDRESS_3: | |
281 | map_aperture(vgpu, false); | |
282 | intel_vgpu_write_pci_bar(vgpu, offset, new, lo); | |
283 | ret = map_aperture(vgpu, mmio_enabled); | |
4d60c5fd | 284 | break; |
f1751362 CD |
285 | default: |
286 | intel_vgpu_write_pci_bar(vgpu, offset, new, lo); | |
4d60c5fd ZW |
287 | } |
288 | } | |
289 | return ret; | |
290 | } | |
291 | ||
292 | /** | |
293 | * intel_vgpu_emulate_cfg_read - emulate vGPU configuration space write | |
294 | * | |
295 | * Returns: | |
296 | * Zero on success, negative error code if failed. | |
297 | */ | |
9ec1e66b | 298 | int intel_vgpu_emulate_cfg_write(struct intel_vgpu *vgpu, unsigned int offset, |
4d60c5fd ZW |
299 | void *p_data, unsigned int bytes) |
300 | { | |
4d60c5fd ZW |
301 | int ret; |
302 | ||
303 | if (WARN_ON(bytes > 4)) | |
304 | return -EINVAL; | |
305 | ||
02d578e5 | 306 | if (WARN_ON(offset + bytes > vgpu->gvt->device_info.cfg_space_size)) |
4d60c5fd ZW |
307 | return -EINVAL; |
308 | ||
309 | /* First check if it's PCI_COMMAND */ | |
310 | if (IS_ALIGNED(offset, 2) && offset == PCI_COMMAND) { | |
311 | if (WARN_ON(bytes > 2)) | |
312 | return -EINVAL; | |
313 | return emulate_pci_command_write(vgpu, offset, p_data, bytes); | |
314 | } | |
315 | ||
316 | switch (rounddown(offset, 4)) { | |
add7e4fc CD |
317 | case PCI_ROM_ADDRESS: |
318 | if (WARN_ON(!IS_ALIGNED(offset, 4))) | |
319 | return -EINVAL; | |
320 | return emulate_pci_rom_bar_write(vgpu, offset, p_data, bytes); | |
321 | ||
f1751362 | 322 | case PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_5: |
4d60c5fd ZW |
323 | if (WARN_ON(!IS_ALIGNED(offset, 4))) |
324 | return -EINVAL; | |
325 | return emulate_pci_bar_write(vgpu, offset, p_data, bytes); | |
326 | ||
327 | case INTEL_GVT_PCI_SWSCI: | |
328 | if (WARN_ON(!IS_ALIGNED(offset, 4))) | |
329 | return -EINVAL; | |
330 | ret = intel_vgpu_emulate_opregion_request(vgpu, *(u32 *)p_data); | |
331 | if (ret) | |
332 | return ret; | |
333 | break; | |
334 | ||
335 | case INTEL_GVT_PCI_OPREGION: | |
336 | if (WARN_ON(!IS_ALIGNED(offset, 4))) | |
337 | return -EINVAL; | |
338 | ret = intel_vgpu_init_opregion(vgpu, *(u32 *)p_data); | |
339 | if (ret) | |
340 | return ret; | |
341 | ||
c2e04fda | 342 | vgpu_pci_cfg_mem_write(vgpu, offset, p_data, bytes); |
4d60c5fd ZW |
343 | break; |
344 | default: | |
c2e04fda | 345 | vgpu_pci_cfg_mem_write(vgpu, offset, p_data, bytes); |
4d60c5fd ZW |
346 | break; |
347 | } | |
348 | return 0; | |
349 | } | |
536fc234 CD |
350 | |
351 | /** | |
352 | * intel_vgpu_init_cfg_space - init vGPU configuration space when create vGPU | |
353 | * | |
354 | * @vgpu: a vGPU | |
355 | * @primary: is the vGPU presented as primary | |
356 | * | |
357 | */ | |
358 | void intel_vgpu_init_cfg_space(struct intel_vgpu *vgpu, | |
359 | bool primary) | |
360 | { | |
361 | struct intel_gvt *gvt = vgpu->gvt; | |
362 | const struct intel_gvt_device_info *info = &gvt->device_info; | |
363 | u16 *gmch_ctl; | |
536fc234 CD |
364 | |
365 | memcpy(vgpu_cfg_space(vgpu), gvt->firmware.cfg_space, | |
366 | info->cfg_space_size); | |
367 | ||
368 | if (!primary) { | |
369 | vgpu_cfg_space(vgpu)[PCI_CLASS_DEVICE] = | |
370 | INTEL_GVT_PCI_CLASS_VGA_OTHER; | |
371 | vgpu_cfg_space(vgpu)[PCI_CLASS_PROG] = | |
372 | INTEL_GVT_PCI_CLASS_VGA_OTHER; | |
373 | } | |
374 | ||
375 | /* Show guest that there isn't any stolen memory.*/ | |
376 | gmch_ctl = (u16 *)(vgpu_cfg_space(vgpu) + INTEL_GVT_PCI_GMCH_CONTROL); | |
377 | *gmch_ctl &= ~(BDW_GMCH_GMS_MASK << BDW_GMCH_GMS_SHIFT); | |
378 | ||
379 | intel_vgpu_write_pci_bar(vgpu, PCI_BASE_ADDRESS_2, | |
380 | gvt_aperture_pa_base(gvt), true); | |
381 | ||
382 | vgpu_cfg_space(vgpu)[PCI_COMMAND] &= ~(PCI_COMMAND_IO | |
383 | | PCI_COMMAND_MEMORY | |
384 | | PCI_COMMAND_MASTER); | |
385 | /* | |
386 | * Clear the bar upper 32bit and let guest to assign the new value | |
387 | */ | |
388 | memset(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_1, 0, 4); | |
389 | memset(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_3, 0, 4); | |
f1751362 | 390 | memset(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_4, 0, 8); |
536fc234 CD |
391 | memset(vgpu_cfg_space(vgpu) + INTEL_GVT_PCI_OPREGION, 0, 4); |
392 | ||
f1751362 CD |
393 | vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_GTTMMIO].size = |
394 | pci_resource_len(gvt->dev_priv->drm.pdev, 0); | |
395 | vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_APERTURE].size = | |
396 | pci_resource_len(gvt->dev_priv->drm.pdev, 2); | |
add7e4fc CD |
397 | |
398 | memset(vgpu_cfg_space(vgpu) + PCI_ROM_ADDRESS, 0, 4); | |
536fc234 | 399 | } |
c64ff6c7 CD |
400 | |
401 | /** | |
402 | * intel_vgpu_reset_cfg_space - reset vGPU configuration space | |
403 | * | |
404 | * @vgpu: a vGPU | |
405 | * | |
406 | */ | |
407 | void intel_vgpu_reset_cfg_space(struct intel_vgpu *vgpu) | |
408 | { | |
409 | u8 cmd = vgpu_cfg_space(vgpu)[PCI_COMMAND]; | |
410 | bool primary = vgpu_cfg_space(vgpu)[PCI_CLASS_DEVICE] != | |
411 | INTEL_GVT_PCI_CLASS_VGA_OTHER; | |
412 | ||
413 | if (cmd & PCI_COMMAND_MEMORY) { | |
414 | trap_gttmmio(vgpu, false); | |
415 | map_aperture(vgpu, false); | |
416 | } | |
417 | ||
418 | /** | |
419 | * Currently we only do such reset when vGPU is not | |
420 | * owned by any VM, so we simply restore entire cfg | |
421 | * space to default value. | |
422 | */ | |
423 | intel_vgpu_init_cfg_space(vgpu, primary); | |
424 | } |