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