18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright(c) 2011-2016 Intel Corporation. All rights reserved.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next
128c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
138c2ecf20Sopenharmony_ci * Software.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
168c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
178c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
188c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
198c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
208c2ecf20Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
218c2ecf20Sopenharmony_ci * SOFTWARE.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * Authors:
248c2ecf20Sopenharmony_ci *    Zhi Wang <zhi.a.wang@intel.com>
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci * Contributors:
278c2ecf20Sopenharmony_ci *    Changbin Du <changbin.du@intel.com>
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <linux/firmware.h>
328c2ecf20Sopenharmony_ci#include <linux/crc32.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include "i915_drv.h"
358c2ecf20Sopenharmony_ci#include "gvt.h"
368c2ecf20Sopenharmony_ci#include "i915_pvinfo.h"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define FIRMWARE_VERSION (0x0)
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistruct gvt_firmware_header {
418c2ecf20Sopenharmony_ci	u64 magic;
428c2ecf20Sopenharmony_ci	u32 crc32;		/* protect the data after this field */
438c2ecf20Sopenharmony_ci	u32 version;
448c2ecf20Sopenharmony_ci	u64 cfg_space_size;
458c2ecf20Sopenharmony_ci	u64 cfg_space_offset;	/* offset in the file */
468c2ecf20Sopenharmony_ci	u64 mmio_size;
478c2ecf20Sopenharmony_ci	u64 mmio_offset;	/* offset in the file */
488c2ecf20Sopenharmony_ci	unsigned char data[1];
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define dev_to_drm_minor(d) dev_get_drvdata((d))
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic ssize_t
548c2ecf20Sopenharmony_cigvt_firmware_read(struct file *filp, struct kobject *kobj,
558c2ecf20Sopenharmony_ci	     struct bin_attribute *attr, char *buf,
568c2ecf20Sopenharmony_ci	     loff_t offset, size_t count)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	memcpy(buf, attr->private + offset, count);
598c2ecf20Sopenharmony_ci	return count;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic struct bin_attribute firmware_attr = {
638c2ecf20Sopenharmony_ci	.attr = {.name = "gvt_firmware", .mode = (S_IRUSR)},
648c2ecf20Sopenharmony_ci	.read = gvt_firmware_read,
658c2ecf20Sopenharmony_ci	.write = NULL,
668c2ecf20Sopenharmony_ci	.mmap = NULL,
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic int mmio_snapshot_handler(struct intel_gvt *gvt, u32 offset, void *data)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	*(u32 *)(data + offset) = intel_uncore_read_notrace(gvt->gt->uncore,
728c2ecf20Sopenharmony_ci							    _MMIO(offset));
738c2ecf20Sopenharmony_ci	return 0;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int expose_firmware_sysfs(struct intel_gvt *gvt)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct intel_gvt_device_info *info = &gvt->device_info;
798c2ecf20Sopenharmony_ci	struct pci_dev *pdev = gvt->gt->i915->drm.pdev;
808c2ecf20Sopenharmony_ci	struct gvt_firmware_header *h;
818c2ecf20Sopenharmony_ci	void *firmware;
828c2ecf20Sopenharmony_ci	void *p;
838c2ecf20Sopenharmony_ci	unsigned long size, crc32_start;
848c2ecf20Sopenharmony_ci	int i, ret;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	size = sizeof(*h) + info->mmio_size + info->cfg_space_size;
878c2ecf20Sopenharmony_ci	firmware = vzalloc(size);
888c2ecf20Sopenharmony_ci	if (!firmware)
898c2ecf20Sopenharmony_ci		return -ENOMEM;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	h = firmware;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	h->magic = VGT_MAGIC;
948c2ecf20Sopenharmony_ci	h->version = FIRMWARE_VERSION;
958c2ecf20Sopenharmony_ci	h->cfg_space_size = info->cfg_space_size;
968c2ecf20Sopenharmony_ci	h->cfg_space_offset = offsetof(struct gvt_firmware_header, data);
978c2ecf20Sopenharmony_ci	h->mmio_size = info->mmio_size;
988c2ecf20Sopenharmony_ci	h->mmio_offset = h->cfg_space_offset + h->cfg_space_size;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	p = firmware + h->cfg_space_offset;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	for (i = 0; i < h->cfg_space_size; i += 4)
1038c2ecf20Sopenharmony_ci		pci_read_config_dword(pdev, i, p + i);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	memcpy(gvt->firmware.cfg_space, p, info->cfg_space_size);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	p = firmware + h->mmio_offset;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/* Take a snapshot of hw mmio registers. */
1108c2ecf20Sopenharmony_ci	intel_gvt_for_each_tracked_mmio(gvt, mmio_snapshot_handler, p);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	memcpy(gvt->firmware.mmio, p, info->mmio_size);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	crc32_start = offsetof(struct gvt_firmware_header, crc32) + 4;
1158c2ecf20Sopenharmony_ci	h->crc32 = crc32_le(0, firmware + crc32_start, size - crc32_start);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	firmware_attr.size = size;
1188c2ecf20Sopenharmony_ci	firmware_attr.private = firmware;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	ret = device_create_bin_file(&pdev->dev, &firmware_attr);
1218c2ecf20Sopenharmony_ci	if (ret) {
1228c2ecf20Sopenharmony_ci		vfree(firmware);
1238c2ecf20Sopenharmony_ci		return ret;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci	return 0;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic void clean_firmware_sysfs(struct intel_gvt *gvt)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	struct pci_dev *pdev = gvt->gt->i915->drm.pdev;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	device_remove_bin_file(&pdev->dev, &firmware_attr);
1338c2ecf20Sopenharmony_ci	vfree(firmware_attr.private);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/**
1378c2ecf20Sopenharmony_ci * intel_gvt_free_firmware - free GVT firmware
1388c2ecf20Sopenharmony_ci * @gvt: intel gvt device
1398c2ecf20Sopenharmony_ci *
1408c2ecf20Sopenharmony_ci */
1418c2ecf20Sopenharmony_civoid intel_gvt_free_firmware(struct intel_gvt *gvt)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	if (!gvt->firmware.firmware_loaded)
1448c2ecf20Sopenharmony_ci		clean_firmware_sysfs(gvt);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	kfree(gvt->firmware.cfg_space);
1478c2ecf20Sopenharmony_ci	vfree(gvt->firmware.mmio);
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int verify_firmware(struct intel_gvt *gvt,
1518c2ecf20Sopenharmony_ci			   const struct firmware *fw)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct intel_gvt_device_info *info = &gvt->device_info;
1548c2ecf20Sopenharmony_ci	struct pci_dev *pdev = gvt->gt->i915->drm.pdev;
1558c2ecf20Sopenharmony_ci	struct gvt_firmware_header *h;
1568c2ecf20Sopenharmony_ci	unsigned long id, crc32_start;
1578c2ecf20Sopenharmony_ci	const void *mem;
1588c2ecf20Sopenharmony_ci	const char *item;
1598c2ecf20Sopenharmony_ci	u64 file, request;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	h = (struct gvt_firmware_header *)fw->data;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	crc32_start = offsetofend(struct gvt_firmware_header, crc32);
1648c2ecf20Sopenharmony_ci	mem = fw->data + crc32_start;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci#define VERIFY(s, a, b) do { \
1678c2ecf20Sopenharmony_ci	item = (s); file = (u64)(a); request = (u64)(b); \
1688c2ecf20Sopenharmony_ci	if ((a) != (b)) \
1698c2ecf20Sopenharmony_ci		goto invalid_firmware; \
1708c2ecf20Sopenharmony_ci} while (0)
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	VERIFY("magic number", h->magic, VGT_MAGIC);
1738c2ecf20Sopenharmony_ci	VERIFY("version", h->version, FIRMWARE_VERSION);
1748c2ecf20Sopenharmony_ci	VERIFY("crc32", h->crc32, crc32_le(0, mem, fw->size - crc32_start));
1758c2ecf20Sopenharmony_ci	VERIFY("cfg space size", h->cfg_space_size, info->cfg_space_size);
1768c2ecf20Sopenharmony_ci	VERIFY("mmio size", h->mmio_size, info->mmio_size);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	mem = (fw->data + h->cfg_space_offset);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	id = *(u16 *)(mem + PCI_VENDOR_ID);
1818c2ecf20Sopenharmony_ci	VERIFY("vender id", id, pdev->vendor);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	id = *(u16 *)(mem + PCI_DEVICE_ID);
1848c2ecf20Sopenharmony_ci	VERIFY("device id", id, pdev->device);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	id = *(u8 *)(mem + PCI_REVISION_ID);
1878c2ecf20Sopenharmony_ci	VERIFY("revision id", id, pdev->revision);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci#undef VERIFY
1908c2ecf20Sopenharmony_ci	return 0;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ciinvalid_firmware:
1938c2ecf20Sopenharmony_ci	gvt_dbg_core("Invalid firmware: %s [file] 0x%llx [request] 0x%llx\n",
1948c2ecf20Sopenharmony_ci		     item, file, request);
1958c2ecf20Sopenharmony_ci	return -EINVAL;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci#define GVT_FIRMWARE_PATH "i915/gvt"
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci/**
2018c2ecf20Sopenharmony_ci * intel_gvt_load_firmware - load GVT firmware
2028c2ecf20Sopenharmony_ci * @gvt: intel gvt device
2038c2ecf20Sopenharmony_ci *
2048c2ecf20Sopenharmony_ci */
2058c2ecf20Sopenharmony_ciint intel_gvt_load_firmware(struct intel_gvt *gvt)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct intel_gvt_device_info *info = &gvt->device_info;
2088c2ecf20Sopenharmony_ci	struct pci_dev *pdev = gvt->gt->i915->drm.pdev;
2098c2ecf20Sopenharmony_ci	struct intel_gvt_firmware *firmware = &gvt->firmware;
2108c2ecf20Sopenharmony_ci	struct gvt_firmware_header *h;
2118c2ecf20Sopenharmony_ci	const struct firmware *fw;
2128c2ecf20Sopenharmony_ci	char *path;
2138c2ecf20Sopenharmony_ci	void *mem;
2148c2ecf20Sopenharmony_ci	int ret;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	path = kmalloc(PATH_MAX, GFP_KERNEL);
2178c2ecf20Sopenharmony_ci	if (!path)
2188c2ecf20Sopenharmony_ci		return -ENOMEM;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	mem = kmalloc(info->cfg_space_size, GFP_KERNEL);
2218c2ecf20Sopenharmony_ci	if (!mem) {
2228c2ecf20Sopenharmony_ci		kfree(path);
2238c2ecf20Sopenharmony_ci		return -ENOMEM;
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	firmware->cfg_space = mem;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	mem = vmalloc(info->mmio_size);
2298c2ecf20Sopenharmony_ci	if (!mem) {
2308c2ecf20Sopenharmony_ci		kfree(path);
2318c2ecf20Sopenharmony_ci		kfree(firmware->cfg_space);
2328c2ecf20Sopenharmony_ci		return -ENOMEM;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	firmware->mmio = mem;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	sprintf(path, "%s/vid_0x%04x_did_0x%04x_rid_0x%02x.golden_hw_state",
2388c2ecf20Sopenharmony_ci		 GVT_FIRMWARE_PATH, pdev->vendor, pdev->device,
2398c2ecf20Sopenharmony_ci		 pdev->revision);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	gvt_dbg_core("request hw state firmware %s...\n", path);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	ret = request_firmware(&fw, path, &gvt->gt->i915->drm.pdev->dev);
2448c2ecf20Sopenharmony_ci	kfree(path);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (ret)
2478c2ecf20Sopenharmony_ci		goto expose_firmware;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	gvt_dbg_core("success.\n");
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	ret = verify_firmware(gvt, fw);
2528c2ecf20Sopenharmony_ci	if (ret)
2538c2ecf20Sopenharmony_ci		goto out_free_fw;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	gvt_dbg_core("verified.\n");
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	h = (struct gvt_firmware_header *)fw->data;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	memcpy(firmware->cfg_space, fw->data + h->cfg_space_offset,
2608c2ecf20Sopenharmony_ci	       h->cfg_space_size);
2618c2ecf20Sopenharmony_ci	memcpy(firmware->mmio, fw->data + h->mmio_offset,
2628c2ecf20Sopenharmony_ci	       h->mmio_size);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	release_firmware(fw);
2658c2ecf20Sopenharmony_ci	firmware->firmware_loaded = true;
2668c2ecf20Sopenharmony_ci	return 0;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ciout_free_fw:
2698c2ecf20Sopenharmony_ci	release_firmware(fw);
2708c2ecf20Sopenharmony_ciexpose_firmware:
2718c2ecf20Sopenharmony_ci	expose_firmware_sysfs(gvt);
2728c2ecf20Sopenharmony_ci	return 0;
2738c2ecf20Sopenharmony_ci}
274