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