brintos

brintos / linux-shallow public Read only

0
0
Text · 11.3 KiB · 9bafac1 Raw
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