xref: /openbmc/linux/drivers/gpu/drm/i915/gvt/cfg_space.c (revision feddf6e8)
14d60c5fdSZhi Wang /*
24d60c5fdSZhi Wang  * Copyright(c) 2011-2016 Intel Corporation. All rights reserved.
34d60c5fdSZhi Wang  *
44d60c5fdSZhi Wang  * Permission is hereby granted, free of charge, to any person obtaining a
54d60c5fdSZhi Wang  * copy of this software and associated documentation files (the "Software"),
64d60c5fdSZhi Wang  * to deal in the Software without restriction, including without limitation
74d60c5fdSZhi Wang  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
84d60c5fdSZhi Wang  * and/or sell copies of the Software, and to permit persons to whom the
94d60c5fdSZhi Wang  * Software is furnished to do so, subject to the following conditions:
104d60c5fdSZhi Wang  *
114d60c5fdSZhi Wang  * The above copyright notice and this permission notice (including the next
124d60c5fdSZhi Wang  * paragraph) shall be included in all copies or substantial portions of the
134d60c5fdSZhi Wang  * Software.
144d60c5fdSZhi Wang  *
154d60c5fdSZhi Wang  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
164d60c5fdSZhi Wang  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
174d60c5fdSZhi Wang  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
184d60c5fdSZhi Wang  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
194d60c5fdSZhi Wang  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
204d60c5fdSZhi Wang  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
214d60c5fdSZhi Wang  * SOFTWARE.
224d60c5fdSZhi Wang  *
234d60c5fdSZhi Wang  * Authors:
244d60c5fdSZhi Wang  *    Eddie Dong <eddie.dong@intel.com>
254d60c5fdSZhi Wang  *    Jike Song <jike.song@intel.com>
264d60c5fdSZhi Wang  *
274d60c5fdSZhi Wang  * Contributors:
284d60c5fdSZhi Wang  *    Zhi Wang <zhi.a.wang@intel.com>
294d60c5fdSZhi Wang  *    Min He <min.he@intel.com>
304d60c5fdSZhi Wang  *    Bing Niu <bing.niu@intel.com>
314d60c5fdSZhi Wang  *
324d60c5fdSZhi Wang  */
334d60c5fdSZhi Wang 
344d60c5fdSZhi Wang #include "i915_drv.h"
35feddf6e8SZhenyu Wang #include "gvt.h"
364d60c5fdSZhi Wang 
374d60c5fdSZhi Wang enum {
384d60c5fdSZhi Wang 	INTEL_GVT_PCI_BAR_GTTMMIO = 0,
394d60c5fdSZhi Wang 	INTEL_GVT_PCI_BAR_APERTURE,
404d60c5fdSZhi Wang 	INTEL_GVT_PCI_BAR_PIO,
414d60c5fdSZhi Wang 	INTEL_GVT_PCI_BAR_MAX,
424d60c5fdSZhi Wang };
434d60c5fdSZhi Wang 
444d60c5fdSZhi Wang /**
454d60c5fdSZhi Wang  * intel_vgpu_emulate_cfg_read - emulate vGPU configuration space read
464d60c5fdSZhi Wang  *
474d60c5fdSZhi Wang  * Returns:
484d60c5fdSZhi Wang  * Zero on success, negative error code if failed.
494d60c5fdSZhi Wang  */
504d60c5fdSZhi Wang int intel_vgpu_emulate_cfg_read(void *__vgpu, unsigned int offset,
514d60c5fdSZhi Wang 	void *p_data, unsigned int bytes)
524d60c5fdSZhi Wang {
534d60c5fdSZhi Wang 	struct intel_vgpu *vgpu = __vgpu;
544d60c5fdSZhi Wang 
554d60c5fdSZhi Wang 	if (WARN_ON(bytes > 4))
564d60c5fdSZhi Wang 		return -EINVAL;
574d60c5fdSZhi Wang 
584d60c5fdSZhi Wang 	if (WARN_ON(offset + bytes > INTEL_GVT_MAX_CFG_SPACE_SZ))
594d60c5fdSZhi Wang 		return -EINVAL;
604d60c5fdSZhi Wang 
614d60c5fdSZhi Wang 	memcpy(p_data, vgpu_cfg_space(vgpu) + offset, bytes);
624d60c5fdSZhi Wang 	return 0;
634d60c5fdSZhi Wang }
644d60c5fdSZhi Wang 
654d60c5fdSZhi Wang static int map_aperture(struct intel_vgpu *vgpu, bool map)
664d60c5fdSZhi Wang {
674d60c5fdSZhi Wang 	u64 first_gfn, first_mfn;
684d60c5fdSZhi Wang 	u64 val;
694d60c5fdSZhi Wang 	int ret;
704d60c5fdSZhi Wang 
714d60c5fdSZhi Wang 	if (map == vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_APERTURE].tracked)
724d60c5fdSZhi Wang 		return 0;
734d60c5fdSZhi Wang 
744d60c5fdSZhi Wang 	val = vgpu_cfg_space(vgpu)[PCI_BASE_ADDRESS_2];
754d60c5fdSZhi Wang 	if (val & PCI_BASE_ADDRESS_MEM_TYPE_64)
764d60c5fdSZhi Wang 		val = *(u64 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_2);
774d60c5fdSZhi Wang 	else
784d60c5fdSZhi Wang 		val = *(u32 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_2);
794d60c5fdSZhi Wang 
804d60c5fdSZhi Wang 	first_gfn = (val + vgpu_aperture_offset(vgpu)) >> PAGE_SHIFT;
814d60c5fdSZhi Wang 	first_mfn = vgpu_aperture_pa_base(vgpu) >> PAGE_SHIFT;
824d60c5fdSZhi Wang 
834d60c5fdSZhi Wang 	ret = intel_gvt_hypervisor_map_gfn_to_mfn(vgpu, first_gfn,
844d60c5fdSZhi Wang 						  first_mfn,
854d60c5fdSZhi Wang 						  vgpu_aperture_sz(vgpu)
864d60c5fdSZhi Wang 						  >> PAGE_SHIFT, map,
874d60c5fdSZhi Wang 						  GVT_MAP_APERTURE);
884d60c5fdSZhi Wang 	if (ret)
894d60c5fdSZhi Wang 		return ret;
904d60c5fdSZhi Wang 
914d60c5fdSZhi Wang 	vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_APERTURE].tracked = map;
924d60c5fdSZhi Wang 	return 0;
934d60c5fdSZhi Wang }
944d60c5fdSZhi Wang 
954d60c5fdSZhi Wang static int trap_gttmmio(struct intel_vgpu *vgpu, bool trap)
964d60c5fdSZhi Wang {
974d60c5fdSZhi Wang 	u64 start, end;
984d60c5fdSZhi Wang 	u64 val;
994d60c5fdSZhi Wang 	int ret;
1004d60c5fdSZhi Wang 
1014d60c5fdSZhi Wang 	if (trap == vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_GTTMMIO].tracked)
1024d60c5fdSZhi Wang 		return 0;
1034d60c5fdSZhi Wang 
1044d60c5fdSZhi Wang 	val = vgpu_cfg_space(vgpu)[PCI_BASE_ADDRESS_0];
1054d60c5fdSZhi Wang 	if (val & PCI_BASE_ADDRESS_MEM_TYPE_64)
1064d60c5fdSZhi Wang 		start = *(u64 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_0);
1074d60c5fdSZhi Wang 	else
1084d60c5fdSZhi Wang 		start = *(u32 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_0);
1094d60c5fdSZhi Wang 
1104d60c5fdSZhi Wang 	start &= ~GENMASK(3, 0);
1114d60c5fdSZhi Wang 	end = start + vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_GTTMMIO].size - 1;
1124d60c5fdSZhi Wang 
1134d60c5fdSZhi Wang 	ret = intel_gvt_hypervisor_set_trap_area(vgpu, start, end, trap);
1144d60c5fdSZhi Wang 	if (ret)
1154d60c5fdSZhi Wang 		return ret;
1164d60c5fdSZhi Wang 
1174d60c5fdSZhi Wang 	vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_GTTMMIO].tracked = trap;
1184d60c5fdSZhi Wang 	return 0;
1194d60c5fdSZhi Wang }
1204d60c5fdSZhi Wang 
1214d60c5fdSZhi Wang static int emulate_pci_command_write(struct intel_vgpu *vgpu,
1224d60c5fdSZhi Wang 	unsigned int offset, void *p_data, unsigned int bytes)
1234d60c5fdSZhi Wang {
1244d60c5fdSZhi Wang 	u8 old = vgpu_cfg_space(vgpu)[offset];
1254d60c5fdSZhi Wang 	u8 new = *(u8 *)p_data;
1264d60c5fdSZhi Wang 	u8 changed = old ^ new;
1274d60c5fdSZhi Wang 	int ret;
1284d60c5fdSZhi Wang 
1294d60c5fdSZhi Wang 	if (!(changed & PCI_COMMAND_MEMORY))
1304d60c5fdSZhi Wang 		return 0;
1314d60c5fdSZhi Wang 
1324d60c5fdSZhi Wang 	if (old & PCI_COMMAND_MEMORY) {
1334d60c5fdSZhi Wang 		ret = trap_gttmmio(vgpu, false);
1344d60c5fdSZhi Wang 		if (ret)
1354d60c5fdSZhi Wang 			return ret;
1364d60c5fdSZhi Wang 		ret = map_aperture(vgpu, false);
1374d60c5fdSZhi Wang 		if (ret)
1384d60c5fdSZhi Wang 			return ret;
1394d60c5fdSZhi Wang 	} else {
1404d60c5fdSZhi Wang 		ret = trap_gttmmio(vgpu, true);
1414d60c5fdSZhi Wang 		if (ret)
1424d60c5fdSZhi Wang 			return ret;
1434d60c5fdSZhi Wang 		ret = map_aperture(vgpu, true);
1444d60c5fdSZhi Wang 		if (ret)
1454d60c5fdSZhi Wang 			return ret;
1464d60c5fdSZhi Wang 	}
1474d60c5fdSZhi Wang 
1484d60c5fdSZhi Wang 	memcpy(vgpu_cfg_space(vgpu) + offset, p_data, bytes);
1494d60c5fdSZhi Wang 	return 0;
1504d60c5fdSZhi Wang }
1514d60c5fdSZhi Wang 
1524d60c5fdSZhi Wang static int emulate_pci_bar_write(struct intel_vgpu *vgpu, unsigned int offset,
1534d60c5fdSZhi Wang 	void *p_data, unsigned int bytes)
1544d60c5fdSZhi Wang {
1554d60c5fdSZhi Wang 	unsigned int bar_index =
1564d60c5fdSZhi Wang 		(rounddown(offset, 8) % PCI_BASE_ADDRESS_0) / 8;
1574d60c5fdSZhi Wang 	u32 new = *(u32 *)(p_data);
1584d60c5fdSZhi Wang 	bool lo = IS_ALIGNED(offset, 8);
1594d60c5fdSZhi Wang 	u64 size;
1604d60c5fdSZhi Wang 	int ret = 0;
1614d60c5fdSZhi Wang 	bool mmio_enabled =
1624d60c5fdSZhi Wang 		vgpu_cfg_space(vgpu)[PCI_COMMAND] & PCI_COMMAND_MEMORY;
1634d60c5fdSZhi Wang 
1644d60c5fdSZhi Wang 	if (WARN_ON(bar_index >= INTEL_GVT_PCI_BAR_MAX))
1654d60c5fdSZhi Wang 		return -EINVAL;
1664d60c5fdSZhi Wang 
1674d60c5fdSZhi Wang 	if (new == 0xffffffff) {
1684d60c5fdSZhi Wang 		/*
1694d60c5fdSZhi Wang 		 * Power-up software can determine how much address
1704d60c5fdSZhi Wang 		 * space the device requires by writing a value of
1714d60c5fdSZhi Wang 		 * all 1's to the register and then reading the value
1724d60c5fdSZhi Wang 		 * back. The device will return 0's in all don't-care
1734d60c5fdSZhi Wang 		 * address bits.
1744d60c5fdSZhi Wang 		 */
1754d60c5fdSZhi Wang 		size = vgpu->cfg_space.bar[bar_index].size;
1764d60c5fdSZhi Wang 		if (lo) {
1774d60c5fdSZhi Wang 			new = rounddown(new, size);
1784d60c5fdSZhi Wang 		} else {
1794d60c5fdSZhi Wang 			u32 val = vgpu_cfg_space(vgpu)[rounddown(offset, 8)];
1804d60c5fdSZhi Wang 			/* for 32bit mode bar it returns all-0 in upper 32
1814d60c5fdSZhi Wang 			 * bit, for 64bit mode bar it will calculate the
1824d60c5fdSZhi Wang 			 * size with lower 32bit and return the corresponding
1834d60c5fdSZhi Wang 			 * value
1844d60c5fdSZhi Wang 			 */
1854d60c5fdSZhi Wang 			if (val & PCI_BASE_ADDRESS_MEM_TYPE_64)
1864d60c5fdSZhi Wang 				new &= (~(size-1)) >> 32;
1874d60c5fdSZhi Wang 			else
1884d60c5fdSZhi Wang 				new = 0;
1894d60c5fdSZhi Wang 		}
1904d60c5fdSZhi Wang 		/*
1914d60c5fdSZhi Wang 		 * Unmapp & untrap the BAR, since guest hasn't configured a
1924d60c5fdSZhi Wang 		 * valid GPA
1934d60c5fdSZhi Wang 		 */
1944d60c5fdSZhi Wang 		switch (bar_index) {
1954d60c5fdSZhi Wang 		case INTEL_GVT_PCI_BAR_GTTMMIO:
1964d60c5fdSZhi Wang 			ret = trap_gttmmio(vgpu, false);
1974d60c5fdSZhi Wang 			break;
1984d60c5fdSZhi Wang 		case INTEL_GVT_PCI_BAR_APERTURE:
1994d60c5fdSZhi Wang 			ret = map_aperture(vgpu, false);
2004d60c5fdSZhi Wang 			break;
2014d60c5fdSZhi Wang 		}
2024d60c5fdSZhi Wang 		intel_vgpu_write_pci_bar(vgpu, offset, new, lo);
2034d60c5fdSZhi Wang 	} else {
2044d60c5fdSZhi Wang 		/*
2054d60c5fdSZhi Wang 		 * Unmapp & untrap the old BAR first, since guest has
2064d60c5fdSZhi Wang 		 * re-configured the BAR
2074d60c5fdSZhi Wang 		 */
2084d60c5fdSZhi Wang 		switch (bar_index) {
2094d60c5fdSZhi Wang 		case INTEL_GVT_PCI_BAR_GTTMMIO:
2104d60c5fdSZhi Wang 			ret = trap_gttmmio(vgpu, false);
2114d60c5fdSZhi Wang 			break;
2124d60c5fdSZhi Wang 		case INTEL_GVT_PCI_BAR_APERTURE:
2134d60c5fdSZhi Wang 			ret = map_aperture(vgpu, false);
2144d60c5fdSZhi Wang 			break;
2154d60c5fdSZhi Wang 		}
2164d60c5fdSZhi Wang 		intel_vgpu_write_pci_bar(vgpu, offset, new, lo);
2174d60c5fdSZhi Wang 		/* Track the new BAR */
2184d60c5fdSZhi Wang 		if (mmio_enabled) {
2194d60c5fdSZhi Wang 			switch (bar_index) {
2204d60c5fdSZhi Wang 			case INTEL_GVT_PCI_BAR_GTTMMIO:
2214d60c5fdSZhi Wang 				ret = trap_gttmmio(vgpu, true);
2224d60c5fdSZhi Wang 				break;
2234d60c5fdSZhi Wang 			case INTEL_GVT_PCI_BAR_APERTURE:
2244d60c5fdSZhi Wang 				ret = map_aperture(vgpu, true);
2254d60c5fdSZhi Wang 				break;
2264d60c5fdSZhi Wang 			}
2274d60c5fdSZhi Wang 		}
2284d60c5fdSZhi Wang 	}
2294d60c5fdSZhi Wang 	return ret;
2304d60c5fdSZhi Wang }
2314d60c5fdSZhi Wang 
2324d60c5fdSZhi Wang /**
2334d60c5fdSZhi Wang  * intel_vgpu_emulate_cfg_read - emulate vGPU configuration space write
2344d60c5fdSZhi Wang  *
2354d60c5fdSZhi Wang  * Returns:
2364d60c5fdSZhi Wang  * Zero on success, negative error code if failed.
2374d60c5fdSZhi Wang  */
2384d60c5fdSZhi Wang int intel_vgpu_emulate_cfg_write(void *__vgpu, unsigned int offset,
2394d60c5fdSZhi Wang 	void *p_data, unsigned int bytes)
2404d60c5fdSZhi Wang {
2414d60c5fdSZhi Wang 	struct intel_vgpu *vgpu = __vgpu;
2424d60c5fdSZhi Wang 	int ret;
2434d60c5fdSZhi Wang 
2444d60c5fdSZhi Wang 	if (WARN_ON(bytes > 4))
2454d60c5fdSZhi Wang 		return -EINVAL;
2464d60c5fdSZhi Wang 
2474d60c5fdSZhi Wang 	if (WARN_ON(offset + bytes >= INTEL_GVT_MAX_CFG_SPACE_SZ))
2484d60c5fdSZhi Wang 		return -EINVAL;
2494d60c5fdSZhi Wang 
2504d60c5fdSZhi Wang 	/* First check if it's PCI_COMMAND */
2514d60c5fdSZhi Wang 	if (IS_ALIGNED(offset, 2) && offset == PCI_COMMAND) {
2524d60c5fdSZhi Wang 		if (WARN_ON(bytes > 2))
2534d60c5fdSZhi Wang 			return -EINVAL;
2544d60c5fdSZhi Wang 		return emulate_pci_command_write(vgpu, offset, p_data, bytes);
2554d60c5fdSZhi Wang 	}
2564d60c5fdSZhi Wang 
2574d60c5fdSZhi Wang 	switch (rounddown(offset, 4)) {
2584d60c5fdSZhi Wang 	case PCI_BASE_ADDRESS_0:
2594d60c5fdSZhi Wang 	case PCI_BASE_ADDRESS_1:
2604d60c5fdSZhi Wang 	case PCI_BASE_ADDRESS_2:
2614d60c5fdSZhi Wang 	case PCI_BASE_ADDRESS_3:
2624d60c5fdSZhi Wang 		if (WARN_ON(!IS_ALIGNED(offset, 4)))
2634d60c5fdSZhi Wang 			return -EINVAL;
2644d60c5fdSZhi Wang 		return emulate_pci_bar_write(vgpu, offset, p_data, bytes);
2654d60c5fdSZhi Wang 
2664d60c5fdSZhi Wang 	case INTEL_GVT_PCI_SWSCI:
2674d60c5fdSZhi Wang 		if (WARN_ON(!IS_ALIGNED(offset, 4)))
2684d60c5fdSZhi Wang 			return -EINVAL;
2694d60c5fdSZhi Wang 		ret = intel_vgpu_emulate_opregion_request(vgpu, *(u32 *)p_data);
2704d60c5fdSZhi Wang 		if (ret)
2714d60c5fdSZhi Wang 			return ret;
2724d60c5fdSZhi Wang 		break;
2734d60c5fdSZhi Wang 
2744d60c5fdSZhi Wang 	case INTEL_GVT_PCI_OPREGION:
2754d60c5fdSZhi Wang 		if (WARN_ON(!IS_ALIGNED(offset, 4)))
2764d60c5fdSZhi Wang 			return -EINVAL;
2774d60c5fdSZhi Wang 		ret = intel_vgpu_init_opregion(vgpu, *(u32 *)p_data);
2784d60c5fdSZhi Wang 		if (ret)
2794d60c5fdSZhi Wang 			return ret;
2804d60c5fdSZhi Wang 
2814d60c5fdSZhi Wang 		memcpy(vgpu_cfg_space(vgpu) + offset, p_data, bytes);
2824d60c5fdSZhi Wang 		break;
2834d60c5fdSZhi Wang 	default:
2844d60c5fdSZhi Wang 		memcpy(vgpu_cfg_space(vgpu) + offset, p_data, bytes);
2854d60c5fdSZhi Wang 		break;
2864d60c5fdSZhi Wang 	}
2874d60c5fdSZhi Wang 	return 0;
2884d60c5fdSZhi Wang }
289