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        *(u32 *)(data + offset) = intel_uncore_read_notrace(gvt->gt->uncore,
  72                                                            _MMIO(offset));
  73        return 0;
  74}
  75
  76static int expose_firmware_sysfs(struct intel_gvt *gvt)
  77{
  78        struct intel_gvt_device_info *info = &gvt->device_info;
  79        struct pci_dev *pdev = to_pci_dev(gvt->gt->i915->drm.dev);
  80        struct gvt_firmware_header *h;
  81        void *firmware;
  82        void *p;
  83        unsigned long size, crc32_start;
  84        int i, ret;
  85
  86        size = sizeof(*h) + info->mmio_size + info->cfg_space_size;
  87        firmware = vzalloc(size);
  88        if (!firmware)
  89                return -ENOMEM;
  90
  91        h = firmware;
  92
  93        h->magic = VGT_MAGIC;
  94        h->version = FIRMWARE_VERSION;
  95        h->cfg_space_size = info->cfg_space_size;
  96        h->cfg_space_offset = offsetof(struct gvt_firmware_header, data);
  97        h->mmio_size = info->mmio_size;
  98        h->mmio_offset = h->cfg_space_offset + h->cfg_space_size;
  99
 100        p = firmware + h->cfg_space_offset;
 101
 102        for (i = 0; i < h->cfg_space_size; i += 4)
 103                pci_read_config_dword(pdev, i, p + i);
 104
 105        memcpy(gvt->firmware.cfg_space, p, info->cfg_space_size);
 106
 107        p = firmware + h->mmio_offset;
 108
 109        /* Take a snapshot of hw mmio registers. */
 110        intel_gvt_for_each_tracked_mmio(gvt, mmio_snapshot_handler, p);
 111
 112        memcpy(gvt->firmware.mmio, p, info->mmio_size);
 113
 114        crc32_start = offsetof(struct gvt_firmware_header, crc32) + 4;
 115        h->crc32 = crc32_le(0, firmware + crc32_start, size - crc32_start);
 116
 117        firmware_attr.size = size;
 118        firmware_attr.private = firmware;
 119
 120        ret = device_create_bin_file(&pdev->dev, &firmware_attr);
 121        if (ret) {
 122                vfree(firmware);
 123                return ret;
 124        }
 125        return 0;
 126}
 127
 128static void clean_firmware_sysfs(struct intel_gvt *gvt)
 129{
 130        struct pci_dev *pdev = to_pci_dev(gvt->gt->i915->drm.dev);
 131
 132        device_remove_bin_file(&pdev->dev, &firmware_attr);
 133        vfree(firmware_attr.private);
 134}
 135
 136/**
 137 * intel_gvt_free_firmware - free GVT firmware
 138 * @gvt: intel gvt device
 139 *
 140 */
 141void intel_gvt_free_firmware(struct intel_gvt *gvt)
 142{
 143        if (!gvt->firmware.firmware_loaded)
 144                clean_firmware_sysfs(gvt);
 145
 146        kfree(gvt->firmware.cfg_space);
 147        vfree(gvt->firmware.mmio);
 148}
 149
 150static int verify_firmware(struct intel_gvt *gvt,
 151                           const struct firmware *fw)
 152{
 153        struct intel_gvt_device_info *info = &gvt->device_info;
 154        struct pci_dev *pdev = to_pci_dev(gvt->gt->i915->drm.dev);
 155        struct gvt_firmware_header *h;
 156        unsigned long id, crc32_start;
 157        const void *mem;
 158        const char *item;
 159        u64 file, request;
 160
 161        h = (struct gvt_firmware_header *)fw->data;
 162
 163        crc32_start = offsetofend(struct gvt_firmware_header, crc32);
 164        mem = fw->data + crc32_start;
 165
 166#define VERIFY(s, a, b) do { \
 167        item = (s); file = (u64)(a); request = (u64)(b); \
 168        if ((a) != (b)) \
 169                goto invalid_firmware; \
 170} while (0)
 171
 172        VERIFY("magic number", h->magic, VGT_MAGIC);
 173        VERIFY("version", h->version, FIRMWARE_VERSION);
 174        VERIFY("crc32", h->crc32, crc32_le(0, mem, fw->size - crc32_start));
 175        VERIFY("cfg space size", h->cfg_space_size, info->cfg_space_size);
 176        VERIFY("mmio size", h->mmio_size, info->mmio_size);
 177
 178        mem = (fw->data + h->cfg_space_offset);
 179
 180        id = *(u16 *)(mem + PCI_VENDOR_ID);
 181        VERIFY("vender id", id, pdev->vendor);
 182
 183        id = *(u16 *)(mem + PCI_DEVICE_ID);
 184        VERIFY("device id", id, pdev->device);
 185
 186        id = *(u8 *)(mem + PCI_REVISION_ID);
 187        VERIFY("revision id", id, pdev->revision);
 188
 189#undef VERIFY
 190        return 0;
 191
 192invalid_firmware:
 193        gvt_dbg_core("Invalid firmware: %s [file] 0x%llx [request] 0x%llx\n",
 194                     item, file, request);
 195        return -EINVAL;
 196}
 197
 198#define GVT_FIRMWARE_PATH "i915/gvt"
 199
 200/**
 201 * intel_gvt_load_firmware - load GVT firmware
 202 * @gvt: intel gvt device
 203 *
 204 */
 205int intel_gvt_load_firmware(struct intel_gvt *gvt)
 206{
 207        struct intel_gvt_device_info *info = &gvt->device_info;
 208        struct pci_dev *pdev = to_pci_dev(gvt->gt->i915->drm.dev);
 209        struct intel_gvt_firmware *firmware = &gvt->firmware;
 210        struct gvt_firmware_header *h;
 211        const struct firmware *fw;
 212        char *path;
 213        void *mem;
 214        int ret;
 215
 216        path = kmalloc(PATH_MAX, GFP_KERNEL);
 217        if (!path)
 218                return -ENOMEM;
 219
 220        mem = kmalloc(info->cfg_space_size, GFP_KERNEL);
 221        if (!mem) {
 222                kfree(path);
 223                return -ENOMEM;
 224        }
 225
 226        firmware->cfg_space = mem;
 227
 228        mem = vmalloc(info->mmio_size);
 229        if (!mem) {
 230                kfree(path);
 231                kfree(firmware->cfg_space);
 232                return -ENOMEM;
 233        }
 234
 235        firmware->mmio = mem;
 236
 237        sprintf(path, "%s/vid_0x%04x_did_0x%04x_rid_0x%02x.golden_hw_state",
 238                 GVT_FIRMWARE_PATH, pdev->vendor, pdev->device,
 239                 pdev->revision);
 240
 241        gvt_dbg_core("request hw state firmware %s...\n", path);
 242
 243        ret = request_firmware(&fw, path, gvt->gt->i915->drm.dev);
 244        kfree(path);
 245
 246        if (ret)
 247                goto expose_firmware;
 248
 249        gvt_dbg_core("success.\n");
 250
 251        ret = verify_firmware(gvt, fw);
 252        if (ret)
 253                goto out_free_fw;
 254
 255        gvt_dbg_core("verified.\n");
 256
 257        h = (struct gvt_firmware_header *)fw->data;
 258
 259        memcpy(firmware->cfg_space, fw->data + h->cfg_space_offset,
 260               h->cfg_space_size);
 261        memcpy(firmware->mmio, fw->data + h->mmio_offset,
 262               h->mmio_size);
 263
 264        release_firmware(fw);
 265        firmware->firmware_loaded = true;
 266        return 0;
 267
 268out_free_fw:
 269        release_firmware(fw);
 270expose_firmware:
 271        expose_firmware_sysfs(gvt);
 272        return 0;
 273}
 274