401 lines · c
1/*2 * Copyright(c) 2011-2016 Intel Corporation. All rights reserved.3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice (including the next12 * paragraph) shall be included in all copies or substantial portions of the13 * Software.14 *15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER19 * 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 THE21 * 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 38enum {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 one46 * byte) byte by byte in standard pci configuration space. (not the full47 * 256 bytes.)48 */49static 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 memory60 * @vgpu: target vgpu61 * @off: offset62 * @src: src ptr to write63 * @bytes: number of bytes64 *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 */69static 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, here84 * 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 read109 * @vgpu: target vgpu110 * @offset: offset111 * @p_data: return data ptr112 * @bytes: number of bytes to read113 *114 * Returns:115 * Zero on success, negative error code if failed.116 */117int 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 133static 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 139static 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 145static 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, offset, p_data, bytes);153 if (!(changed & PCI_COMMAND_MEMORY))154 return 0;155 156 if (old & PCI_COMMAND_MEMORY) {157 trap_gttmmio(vgpu, false);158 map_aperture(vgpu, false);159 } else {160 trap_gttmmio(vgpu, true);161 map_aperture(vgpu, true);162 }163 164 return 0;165}166 167static 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 else177 vgpu_pci_cfg_mem_write(vgpu, offset, p_data, bytes);178 return 0;179}180 181static 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 address193 * space the device requires by writing a value of194 * all 1's to the register and then reading the value195 * back. The device will return 0's in all don't-care196 * 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 size >> (lo ? 0 : 32), lo);205 /*206 * Untrap the BAR, since guest hasn't configured a207 * valid GPA208 */209 trap_gttmmio(vgpu, 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 size >> (lo ? 0 : 32), lo);216 map_aperture(vgpu, false);217 break;218 default:219 /* Unimplemented BARs */220 intel_vgpu_write_pci_bar(vgpu, offset, 0x0, 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 has228 * re-configured the BAR229 */230 trap_gttmmio(vgpu, false);231 intel_vgpu_write_pci_bar(vgpu, offset, new, lo);232 trap_gttmmio(vgpu, mmio_enabled);233 break;234 case PCI_BASE_ADDRESS_2:235 case PCI_BASE_ADDRESS_3:236 map_aperture(vgpu, false);237 intel_vgpu_write_pci_bar(vgpu, offset, new, lo);238 map_aperture(vgpu, mmio_enabled);239 break;240 default:241 intel_vgpu_write_pci_bar(vgpu, offset, new, lo);242 }243 }244}245 246/**247 * intel_vgpu_emulate_cfg_write - emulate vGPU configuration space write248 * @vgpu: target vgpu249 * @offset: offset250 * @p_data: write data ptr251 * @bytes: number of bytes to write252 *253 * Returns:254 * Zero on success, negative error code if failed.255 */256int 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, *(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 *(u32 *)p_data);300 if (ret)301 return ret;302 303 vgpu_pci_cfg_mem_write(vgpu, offset, p_data, bytes);304 break;305 default:306 vgpu_pci_cfg_mem_write(vgpu, offset, p_data, bytes);307 break;308 }309 return 0;310}311 312/**313 * intel_vgpu_init_cfg_space - init vGPU configuration space when create vGPU314 *315 * @vgpu: a vGPU316 * @primary: is the vGPU presented as primary317 *318 */319void 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), true);344 345 vgpu_cfg_space(vgpu)[PCI_COMMAND] &= ~(PCI_COMMAND_IO346 | PCI_COMMAND_MEMORY347 | PCI_COMMAND_MASTER);348 /*349 * Clear the bar upper 32bit and let guest to assign the new value350 */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 space379 *380 * @vgpu: a vGPU381 *382 */383void 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, false);391 map_aperture(vgpu, false);392 }393 394 /**395 * Currently we only do such reset when vGPU is not396 * owned by any VM, so we simply restore entire cfg397 * space to default value.398 */399 intel_vgpu_init_cfg_space(vgpu, primary);400}401