162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright(c) 2011-2016 Intel Corporation. All rights reserved.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
562306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
662306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
762306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
862306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
962306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the next
1262306a36Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
1362306a36Sopenharmony_ci * Software.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1662306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1762306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1862306a36Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1962306a36Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2062306a36Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2162306a36Sopenharmony_ci * SOFTWARE.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Authors:
2462306a36Sopenharmony_ci *    Zhi Wang <zhi.a.wang@intel.com>
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * Contributors:
2762306a36Sopenharmony_ci *    Changbin Du <changbin.du@intel.com>
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <linux/firmware.h>
3262306a36Sopenharmony_ci#include <linux/crc32.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include "i915_drv.h"
3562306a36Sopenharmony_ci#include "gvt.h"
3662306a36Sopenharmony_ci#include "i915_pvinfo.h"
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define FIRMWARE_VERSION (0x0)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct gvt_firmware_header {
4162306a36Sopenharmony_ci	u64 magic;
4262306a36Sopenharmony_ci	u32 crc32;		/* protect the data after this field */
4362306a36Sopenharmony_ci	u32 version;
4462306a36Sopenharmony_ci	u64 cfg_space_size;
4562306a36Sopenharmony_ci	u64 cfg_space_offset;	/* offset in the file */
4662306a36Sopenharmony_ci	u64 mmio_size;
4762306a36Sopenharmony_ci	u64 mmio_offset;	/* offset in the file */
4862306a36Sopenharmony_ci	unsigned char data[];
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define dev_to_drm_minor(d) dev_get_drvdata((d))
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic ssize_t
5462306a36Sopenharmony_cigvt_firmware_read(struct file *filp, struct kobject *kobj,
5562306a36Sopenharmony_ci	     struct bin_attribute *attr, char *buf,
5662306a36Sopenharmony_ci	     loff_t offset, size_t count)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	memcpy(buf, attr->private + offset, count);
5962306a36Sopenharmony_ci	return count;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic struct bin_attribute firmware_attr = {
6362306a36Sopenharmony_ci	.attr = {.name = "gvt_firmware", .mode = (S_IRUSR)},
6462306a36Sopenharmony_ci	.read = gvt_firmware_read,
6562306a36Sopenharmony_ci	.write = NULL,
6662306a36Sopenharmony_ci	.mmap = NULL,
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int expose_firmware_sysfs(struct intel_gvt *gvt)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct intel_gvt_device_info *info = &gvt->device_info;
7262306a36Sopenharmony_ci	struct drm_i915_private *i915 = gvt->gt->i915;
7362306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(i915->drm.dev);
7462306a36Sopenharmony_ci	struct gvt_firmware_header *h;
7562306a36Sopenharmony_ci	void *firmware;
7662306a36Sopenharmony_ci	void *p;
7762306a36Sopenharmony_ci	unsigned long size, crc32_start;
7862306a36Sopenharmony_ci	int ret;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	size = offsetof(struct gvt_firmware_header, data) + info->mmio_size + info->cfg_space_size;
8162306a36Sopenharmony_ci	firmware = vzalloc(size);
8262306a36Sopenharmony_ci	if (!firmware)
8362306a36Sopenharmony_ci		return -ENOMEM;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	h = firmware;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	h->magic = VGT_MAGIC;
8862306a36Sopenharmony_ci	h->version = FIRMWARE_VERSION;
8962306a36Sopenharmony_ci	h->cfg_space_size = info->cfg_space_size;
9062306a36Sopenharmony_ci	h->cfg_space_offset = offsetof(struct gvt_firmware_header, data);
9162306a36Sopenharmony_ci	h->mmio_size = info->mmio_size;
9262306a36Sopenharmony_ci	h->mmio_offset = h->cfg_space_offset + h->cfg_space_size;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	p = firmware + h->cfg_space_offset;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	memcpy(gvt->firmware.cfg_space, i915->vgpu.initial_cfg_space,
9762306a36Sopenharmony_ci	       info->cfg_space_size);
9862306a36Sopenharmony_ci	memcpy(p, gvt->firmware.cfg_space, info->cfg_space_size);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	p = firmware + h->mmio_offset;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	memcpy(gvt->firmware.mmio, i915->vgpu.initial_mmio,
10362306a36Sopenharmony_ci	       info->mmio_size);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	memcpy(p, gvt->firmware.mmio, info->mmio_size);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	crc32_start = offsetof(struct gvt_firmware_header, version);
10862306a36Sopenharmony_ci	h->crc32 = crc32_le(0, firmware + crc32_start, size - crc32_start);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	firmware_attr.size = size;
11162306a36Sopenharmony_ci	firmware_attr.private = firmware;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	ret = device_create_bin_file(&pdev->dev, &firmware_attr);
11462306a36Sopenharmony_ci	if (ret) {
11562306a36Sopenharmony_ci		vfree(firmware);
11662306a36Sopenharmony_ci		return ret;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci	return 0;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic void clean_firmware_sysfs(struct intel_gvt *gvt)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(gvt->gt->i915->drm.dev);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	device_remove_bin_file(&pdev->dev, &firmware_attr);
12662306a36Sopenharmony_ci	vfree(firmware_attr.private);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/**
13062306a36Sopenharmony_ci * intel_gvt_free_firmware - free GVT firmware
13162306a36Sopenharmony_ci * @gvt: intel gvt device
13262306a36Sopenharmony_ci *
13362306a36Sopenharmony_ci */
13462306a36Sopenharmony_civoid intel_gvt_free_firmware(struct intel_gvt *gvt)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	if (!gvt->firmware.firmware_loaded)
13762306a36Sopenharmony_ci		clean_firmware_sysfs(gvt);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	kfree(gvt->firmware.cfg_space);
14062306a36Sopenharmony_ci	vfree(gvt->firmware.mmio);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic int verify_firmware(struct intel_gvt *gvt,
14462306a36Sopenharmony_ci			   const struct firmware *fw)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct intel_gvt_device_info *info = &gvt->device_info;
14762306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(gvt->gt->i915->drm.dev);
14862306a36Sopenharmony_ci	struct gvt_firmware_header *h;
14962306a36Sopenharmony_ci	unsigned long id, crc32_start;
15062306a36Sopenharmony_ci	const void *mem;
15162306a36Sopenharmony_ci	const char *item;
15262306a36Sopenharmony_ci	u64 file, request;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	h = (struct gvt_firmware_header *)fw->data;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	crc32_start = offsetofend(struct gvt_firmware_header, crc32);
15762306a36Sopenharmony_ci	mem = fw->data + crc32_start;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci#define VERIFY(s, a, b) do { \
16062306a36Sopenharmony_ci	item = (s); file = (u64)(a); request = (u64)(b); \
16162306a36Sopenharmony_ci	if ((a) != (b)) \
16262306a36Sopenharmony_ci		goto invalid_firmware; \
16362306a36Sopenharmony_ci} while (0)
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	VERIFY("magic number", h->magic, VGT_MAGIC);
16662306a36Sopenharmony_ci	VERIFY("version", h->version, FIRMWARE_VERSION);
16762306a36Sopenharmony_ci	VERIFY("crc32", h->crc32, crc32_le(0, mem, fw->size - crc32_start));
16862306a36Sopenharmony_ci	VERIFY("cfg space size", h->cfg_space_size, info->cfg_space_size);
16962306a36Sopenharmony_ci	VERIFY("mmio size", h->mmio_size, info->mmio_size);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	mem = (fw->data + h->cfg_space_offset);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	id = *(u16 *)(mem + PCI_VENDOR_ID);
17462306a36Sopenharmony_ci	VERIFY("vendor id", id, pdev->vendor);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	id = *(u16 *)(mem + PCI_DEVICE_ID);
17762306a36Sopenharmony_ci	VERIFY("device id", id, pdev->device);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	id = *(u8 *)(mem + PCI_REVISION_ID);
18062306a36Sopenharmony_ci	VERIFY("revision id", id, pdev->revision);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci#undef VERIFY
18362306a36Sopenharmony_ci	return 0;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ciinvalid_firmware:
18662306a36Sopenharmony_ci	gvt_dbg_core("Invalid firmware: %s [file] 0x%llx [request] 0x%llx\n",
18762306a36Sopenharmony_ci		     item, file, request);
18862306a36Sopenharmony_ci	return -EINVAL;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci#define GVT_FIRMWARE_PATH "i915/gvt"
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci/**
19462306a36Sopenharmony_ci * intel_gvt_load_firmware - load GVT firmware
19562306a36Sopenharmony_ci * @gvt: intel gvt device
19662306a36Sopenharmony_ci *
19762306a36Sopenharmony_ci */
19862306a36Sopenharmony_ciint intel_gvt_load_firmware(struct intel_gvt *gvt)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct intel_gvt_device_info *info = &gvt->device_info;
20162306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(gvt->gt->i915->drm.dev);
20262306a36Sopenharmony_ci	struct intel_gvt_firmware *firmware = &gvt->firmware;
20362306a36Sopenharmony_ci	struct gvt_firmware_header *h;
20462306a36Sopenharmony_ci	const struct firmware *fw;
20562306a36Sopenharmony_ci	char *path;
20662306a36Sopenharmony_ci	void *mem;
20762306a36Sopenharmony_ci	int ret;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	path = kmalloc(PATH_MAX, GFP_KERNEL);
21062306a36Sopenharmony_ci	if (!path)
21162306a36Sopenharmony_ci		return -ENOMEM;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	mem = kmalloc(info->cfg_space_size, GFP_KERNEL);
21462306a36Sopenharmony_ci	if (!mem) {
21562306a36Sopenharmony_ci		kfree(path);
21662306a36Sopenharmony_ci		return -ENOMEM;
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	firmware->cfg_space = mem;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	mem = vmalloc(info->mmio_size);
22262306a36Sopenharmony_ci	if (!mem) {
22362306a36Sopenharmony_ci		kfree(path);
22462306a36Sopenharmony_ci		kfree(firmware->cfg_space);
22562306a36Sopenharmony_ci		return -ENOMEM;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	firmware->mmio = mem;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	sprintf(path, "%s/vid_0x%04x_did_0x%04x_rid_0x%02x.golden_hw_state",
23162306a36Sopenharmony_ci		 GVT_FIRMWARE_PATH, pdev->vendor, pdev->device,
23262306a36Sopenharmony_ci		 pdev->revision);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	gvt_dbg_core("request hw state firmware %s...\n", path);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	ret = request_firmware(&fw, path, gvt->gt->i915->drm.dev);
23762306a36Sopenharmony_ci	kfree(path);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (ret)
24062306a36Sopenharmony_ci		goto expose_firmware;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	gvt_dbg_core("success.\n");
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	ret = verify_firmware(gvt, fw);
24562306a36Sopenharmony_ci	if (ret)
24662306a36Sopenharmony_ci		goto out_free_fw;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	gvt_dbg_core("verified.\n");
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	h = (struct gvt_firmware_header *)fw->data;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	memcpy(firmware->cfg_space, fw->data + h->cfg_space_offset,
25362306a36Sopenharmony_ci	       h->cfg_space_size);
25462306a36Sopenharmony_ci	memcpy(firmware->mmio, fw->data + h->mmio_offset,
25562306a36Sopenharmony_ci	       h->mmio_size);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	release_firmware(fw);
25862306a36Sopenharmony_ci	firmware->firmware_loaded = true;
25962306a36Sopenharmony_ci	return 0;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ciout_free_fw:
26262306a36Sopenharmony_ci	release_firmware(fw);
26362306a36Sopenharmony_ciexpose_firmware:
26462306a36Sopenharmony_ci	expose_firmware_sysfs(gvt);
26562306a36Sopenharmony_ci	return 0;
26662306a36Sopenharmony_ci}
267