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