linux/drivers/gpu/drm/i915/gvt/firmware.c
<<
>>
Prefs
   1/*
   2 * Copyright(c) 2011-2016 Intel Corporation. All rights reserved.
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice (including the next
  12 * paragraph) shall be included in all copies or substantial portions of the
  13 * Software.
  14 *
  15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 * 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 THE
  21 * SOFTWARE.
  22 *
  23 * Authors:
  24 *    Zhi Wang <zhi.a.wang@intel.com>
  25 *
  26 * Contributors:
  27 *    Changbin Du <changbin.du@intel.com>
  28 *
  29 */
  30
  31#include <linux/firmware.h>
  32#include <linux/crc32.h>
  33
  34#include "i915_drv.h"
  35#include "gvt.h"
  36#include "i915_pvinfo.h"
  37
  38#define FIRMWARE_VERSION (0x0)
  39
  40struct gvt_firmware_header {
  41        u64 magic;
  42        u32 crc32;              /* protect the data after this field */
  43        u32 version;
  44        u64 cfg_space_size;
  45        u64 cfg_space_offset;   /* offset in the file */
  46        u64 mmio_size;
  47        u64 mmio_offset;        /* offset in the file */
  48        unsigned char data[1];
  49};
  50
  51#define dev_to_drm_minor(d) dev_get_drvdata((d))
  52
  53static ssize_t
  54gvt_firmware_read(struct file *filp, struct kobject *kobj,
  55             struct bin_attribute *attr, char *buf,
  56             loff_t offset, size_t count)
  57{
  58        memcpy(buf, attr->private + offset, count);
  59        return count;
  60}
  61
  62static struct bin_attribute firmware_attr = {
  63        .attr = {.name = "gvt_firmware", .mode = (S_IRUSR)},
  64        .read = gvt_firmware_read,
  65        .write = NULL,
  66        .mmap = NULL,
  67};
  68
  69static int mmio_snapshot_handler(struct intel_gvt *gvt, u32 offset, void *data)
  70{
  71        struct drm_i915_private *dev_priv = gvt->dev_priv;
  72
  73        *(u32 *)(data + offset) = I915_READ_NOTRACE(_MMIO(offset));
  74        return 0;
  75}
  76
  77static int expose_firmware_sysfs(struct intel_gvt *gvt)
  78{
  79        struct intel_gvt_device_info *info = &gvt->device_info;
  80        struct pci_dev *pdev = gvt->dev_priv->drm.pdev;
  81        struct gvt_firmware_header *h;
  82        void *firmware;
  83        void *p;
  84        unsigned long size, crc32_start;
  85        int i, ret;
  86
  87        size = sizeof(*h) + info->mmio_size + info->cfg_space_size;
  88        firmware = vzalloc(size);
  89        if (!firmware)
  90                return -ENOMEM;
  91
  92        h = firmware;
  93
  94        h->magic = VGT_MAGIC;
  95        h->version = FIRMWARE_VERSION;
  96        h->cfg_space_size = info->cfg_space_size;
  97        h->cfg_space_offset = offsetof(struct gvt_firmware_header, data);
  98        h->mmio_size = info->mmio_size;
  99        h->mmio_offset = h->cfg_space_offset + h->cfg_space_size;
 100
 101        p = firmware + h->cfg_space_offset;
 102
 103        for (i = 0; i < h->cfg_space_size; i += 4)
 104                pci_read_config_dword(pdev, i, p + i);
 105
 106        memcpy(gvt->firmware.cfg_space, p, info->cfg_space_size);
 107
 108        p = firmware + h->mmio_offset;
 109
 110        /* Take a snapshot of hw mmio registers. */
 111        intel_gvt_for_each_tracked_mmio(gvt, mmio_snapshot_handler, p);
 112
 113        memcpy(gvt->firmware.mmio, p, info->mmio_size);
 114
 115        crc32_start = offsetof(struct gvt_firmware_header, crc32) + 4;
 116        h->crc32 = crc32_le(0, firmware + crc32_start, size - crc32_start);
 117
 118        firmware_attr.size = size;
 119        firmware_attr.private = firmware;
 120
 121        ret = device_create_bin_file(&pdev->dev, &firmware_attr);
 122        if (ret) {
 123                vfree(firmware);
 124                return ret;
 125        }
 126        return 0;
 127}
 128
 129static void clean_firmware_sysfs(struct intel_gvt *gvt)
 130{
 131        struct pci_dev *pdev = gvt->dev_priv->drm.pdev;
 132
 133        device_remove_bin_file(&pdev->dev, &firmware_attr);
 134        vfree(firmware_attr.private);
 135}
 136
 137/**
 138 * intel_gvt_free_firmware - free GVT firmware
 139 * @gvt: intel gvt device
 140 *
 141 */
 142void intel_gvt_free_firmware(struct intel_gvt *gvt)
 143{
 144        if (!gvt->firmware.firmware_loaded)
 145                clean_firmware_sysfs(gvt);
 146
 147        kfree(gvt->firmware.cfg_space);
 148        kfree(gvt->firmware.mmio);
 149}
 150
 151static int verify_firmware(struct intel_gvt *gvt,
 152                           const struct firmware *fw)
 153{
 154        struct intel_gvt_device_info *info = &gvt->device_info;
 155        struct drm_i915_private *dev_priv = gvt->dev_priv;
 156        struct pci_dev *pdev = dev_priv->drm.pdev;
 157        struct gvt_firmware_header *h;
 158        unsigned long id, crc32_start;
 159        const void *mem;
 160        const char *item;
 161        u64 file, request;
 162
 163        h = (struct gvt_firmware_header *)fw->data;
 164
 165        crc32_start = offsetofend(struct gvt_firmware_header, crc32);
 166        mem = fw->data + crc32_start;
 167
 168#define VERIFY(s, a, b) do { \
 169        item = (s); file = (u64)(a); request = (u64)(b); \
 170        if ((a) != (b)) \
 171                goto invalid_firmware; \
 172} while (0)
 173
 174        VERIFY("magic number", h->magic, VGT_MAGIC);
 175        VERIFY("version", h->version, FIRMWARE_VERSION);
 176        VERIFY("crc32", h->crc32, crc32_le(0, mem, fw->size - crc32_start));
 177        VERIFY("cfg space size", h->cfg_space_size, info->cfg_space_size);
 178        VERIFY("mmio size", h->mmio_size, info->mmio_size);
 179
 180        mem = (fw->data + h->cfg_space_offset);
 181
 182        id = *(u16 *)(mem + PCI_VENDOR_ID);
 183        VERIFY("vender id", id, pdev->vendor);
 184
 185        id = *(u16 *)(mem + PCI_DEVICE_ID);
 186        VERIFY("device id", id, pdev->device);
 187
 188        id = *(u8 *)(mem + PCI_REVISION_ID);
 189        VERIFY("revision id", id, pdev->revision);
 190
 191#undef VERIFY
 192        return 0;
 193
 194invalid_firmware:
 195        gvt_dbg_core("Invalid firmware: %s [file] 0x%llx [request] 0x%llx\n",
 196                     item, file, request);
 197        return -EINVAL;
 198}
 199
 200#define GVT_FIRMWARE_PATH "i915/gvt"
 201
 202/**
 203 * intel_gvt_load_firmware - load GVT firmware
 204 * @gvt: intel gvt device
 205 *
 206 */
 207int intel_gvt_load_firmware(struct intel_gvt *gvt)
 208{
 209        struct intel_gvt_device_info *info = &gvt->device_info;
 210        struct drm_i915_private *dev_priv = gvt->dev_priv;
 211        struct pci_dev *pdev = dev_priv->drm.pdev;
 212        struct intel_gvt_firmware *firmware = &gvt->firmware;
 213        struct gvt_firmware_header *h;
 214        const struct firmware *fw;
 215        char *path;
 216        void *mem;
 217        int ret;
 218
 219        path = kmalloc(PATH_MAX, GFP_KERNEL);
 220        if (!path)
 221                return -ENOMEM;
 222
 223        mem = kmalloc(info->cfg_space_size, GFP_KERNEL);
 224        if (!mem) {
 225                kfree(path);
 226                return -ENOMEM;
 227        }
 228
 229        firmware->cfg_space = mem;
 230
 231        mem = kmalloc(info->mmio_size, GFP_KERNEL);
 232        if (!mem) {
 233                kfree(path);
 234                kfree(firmware->cfg_space);
 235                return -ENOMEM;
 236        }
 237
 238        firmware->mmio = mem;
 239
 240        sprintf(path, "%s/vid_0x%04x_did_0x%04x_rid_0x%02x.golden_hw_state",
 241                 GVT_FIRMWARE_PATH, pdev->vendor, pdev->device,
 242                 pdev->revision);
 243
 244        gvt_dbg_core("request hw state firmware %s...\n", path);
 245
 246        ret = request_firmware(&fw, path, &dev_priv->drm.pdev->dev);
 247        kfree(path);
 248
 249        if (ret)
 250                goto expose_firmware;
 251
 252        gvt_dbg_core("success.\n");
 253
 254        ret = verify_firmware(gvt, fw);
 255        if (ret)
 256                goto out_free_fw;
 257
 258        gvt_dbg_core("verified.\n");
 259
 260        h = (struct gvt_firmware_header *)fw->data;
 261
 262        memcpy(firmware->cfg_space, fw->data + h->cfg_space_offset,
 263               h->cfg_space_size);
 264        memcpy(firmware->mmio, fw->data + h->mmio_offset,
 265               h->mmio_size);
 266
 267        release_firmware(fw);
 268        firmware->firmware_loaded = true;
 269        return 0;
 270
 271out_free_fw:
 272        release_firmware(fw);
 273expose_firmware:
 274        expose_firmware_sysfs(gvt);
 275        return 0;
 276}
 277