drm/i915/gvt: golden virtual HW state management
authorZhi Wang <zhi.a.wang@intel.com>
Thu, 30 Jun 2016 16:45:34 +0000 (12:45 -0400)
committerZhenyu Wang <zhenyuw@linux.intel.com>
Fri, 14 Oct 2016 10:11:46 +0000 (18:11 +0800)
Each vGPU expects a golden virtual HW state, which is just the state after
system is freshly powered on. GVT-g will try to load the golden virtual HW
state via kernel firmware interface.

Signed-off-by: Zhi Wang <zhi.a.wang@intel.com>
Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
drivers/gpu/drm/i915/gvt/Makefile
drivers/gpu/drm/i915/gvt/firmware.c [new file with mode: 0644]
drivers/gpu/drm/i915/gvt/gvt.c
drivers/gpu/drm/i915/gvt/gvt.h

index 61b5b61f01f5bfae44463bcbf9fe11b80ae7b59d..ebbb90a6dca57a61515346f4d1e8b78d85051664 100644 (file)
@@ -1,5 +1,5 @@
 GVT_DIR := gvt
-GVT_SOURCE := gvt.o aperture_gm.o handlers.o
+GVT_SOURCE := gvt.o aperture_gm.o handlers.o firmware.o
 
 ccflags-y                      += -I$(src) -I$(src)/$(GVT_DIR) -Wall
 i915-y                        += $(addprefix $(GVT_DIR)/, $(GVT_SOURCE))
diff --git a/drivers/gpu/drm/i915/gvt/firmware.c b/drivers/gpu/drm/i915/gvt/firmware.c
new file mode 100644 (file)
index 0000000..4578a4d
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * Copyright(c) 2011-2016 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *    Zhi Wang <zhi.a.wang@intel.com>
+ *
+ * Contributors:
+ *    Changbin Du <changbin.du@intel.com>
+ *
+ */
+
+#include <linux/firmware.h>
+#include <linux/crc32.h>
+
+#include "i915_drv.h"
+
+#define FIRMWARE_VERSION (0x0)
+
+struct gvt_firmware_header {
+       u64 magic;
+       u32 crc32;              /* protect the data after this field */
+       u32 version;
+       u64 cfg_space_size;
+       u64 cfg_space_offset;   /* offset in the file */
+       u64 mmio_size;
+       u64 mmio_offset;        /* offset in the file */
+       unsigned char data[1];
+};
+
+#define RD(offset) (readl(mmio + offset.reg))
+#define WR(v, offset) (writel(v, mmio + offset.reg))
+
+static void bdw_forcewake_get(void *mmio)
+{
+       WR(_MASKED_BIT_DISABLE(0xffff), FORCEWAKE_MT);
+
+       RD(ECOBUS);
+
+       if (wait_for((RD(FORCEWAKE_ACK_HSW) & FORCEWAKE_KERNEL) == 0, 50))
+               gvt_err("fail to wait forcewake idle\n");
+
+       WR(_MASKED_BIT_ENABLE(FORCEWAKE_KERNEL), FORCEWAKE_MT);
+
+       if (wait_for((RD(FORCEWAKE_ACK_HSW) & FORCEWAKE_KERNEL), 50))
+               gvt_err("fail to wait forcewake ack\n");
+
+       if (wait_for((RD(GEN6_GT_THREAD_STATUS_REG) &
+                     GEN6_GT_THREAD_STATUS_CORE_MASK) == 0, 50))
+               gvt_err("fail to wait c0 wake up\n");
+}
+
+#undef RD
+#undef WR
+
+#define dev_to_drm_minor(d) dev_get_drvdata((d))
+
+static ssize_t
+gvt_firmware_read(struct file *filp, struct kobject *kobj,
+            struct bin_attribute *attr, char *buf,
+            loff_t offset, size_t count)
+{
+       memcpy(buf, attr->private + offset, count);
+       return count;
+}
+
+static struct bin_attribute firmware_attr = {
+       .attr = {.name = "gvt_firmware", .mode = (S_IRUSR)},
+       .read = gvt_firmware_read,
+       .write = NULL,
+       .mmap = NULL,
+};
+
+static int expose_firmware_sysfs(struct intel_gvt *gvt, void *mmio)
+{
+       struct intel_gvt_device_info *info = &gvt->device_info;
+       struct pci_dev *pdev = gvt->dev_priv->drm.pdev;
+       struct intel_gvt_mmio_info *e;
+       struct gvt_firmware_header *h;
+       void *firmware;
+       void *p;
+       unsigned long size;
+       int i;
+       int ret;
+
+       size = sizeof(*h) + info->mmio_size + info->cfg_space_size - 1;
+       firmware = vmalloc(size);
+       if (!firmware)
+               return -ENOMEM;
+
+       h = firmware;
+
+       h->magic = VGT_MAGIC;
+       h->version = FIRMWARE_VERSION;
+       h->cfg_space_size = info->cfg_space_size;
+       h->cfg_space_offset = offsetof(struct gvt_firmware_header, data);
+       h->mmio_size = info->mmio_size;
+       h->mmio_offset = h->cfg_space_offset + h->cfg_space_size;
+
+       p = firmware + h->cfg_space_offset;
+
+       for (i = 0; i < h->cfg_space_size; i += 4)
+               pci_read_config_dword(pdev, i, p + i);
+
+       memcpy(gvt->firmware.cfg_space, p, info->cfg_space_size);
+
+       p = firmware + h->mmio_offset;
+
+       hash_for_each(gvt->mmio.mmio_info_table, i, e, node) {
+               int j;
+
+               for (j = 0; j < e->length; j += 4)
+                       *(u32 *)(p + e->offset + j) =
+                               readl(mmio + e->offset + j);
+       }
+
+       memcpy(gvt->firmware.mmio, p, info->mmio_size);
+
+       firmware_attr.size = size;
+       firmware_attr.private = firmware;
+
+       ret = device_create_bin_file(&pdev->dev, &firmware_attr);
+       if (ret) {
+               vfree(firmware);
+               return ret;
+       }
+       return 0;
+}
+
+static void clean_firmware_sysfs(struct intel_gvt *gvt)
+{
+       struct pci_dev *pdev = gvt->dev_priv->drm.pdev;
+
+       device_remove_bin_file(&pdev->dev, &firmware_attr);
+       vfree(firmware_attr.private);
+}
+
+/**
+ * intel_gvt_free_firmware - free GVT firmware
+ * @gvt: intel gvt device
+ *
+ */
+void intel_gvt_free_firmware(struct intel_gvt *gvt)
+{
+       if (!gvt->firmware.firmware_loaded)
+               clean_firmware_sysfs(gvt);
+
+       kfree(gvt->firmware.cfg_space);
+       kfree(gvt->firmware.mmio);
+}
+
+static int verify_firmware(struct intel_gvt *gvt,
+                          const struct firmware *fw)
+{
+       struct intel_gvt_device_info *info = &gvt->device_info;
+       struct drm_i915_private *dev_priv = gvt->dev_priv;
+       struct pci_dev *pdev = dev_priv->drm.pdev;
+       struct gvt_firmware_header *h;
+       unsigned long id, crc32_start;
+       const void *mem;
+       const char *item;
+       u64 file, request;
+
+       h = (struct gvt_firmware_header *)fw->data;
+
+       crc32_start = offsetof(struct gvt_firmware_header, crc32) + 4;
+       mem = fw->data + crc32_start;
+
+#define VERIFY(s, a, b) do { \
+       item = (s); file = (u64)(a); request = (u64)(b); \
+       if ((a) != (b)) \
+               goto invalid_firmware; \
+} while (0)
+
+       VERIFY("magic number", h->magic, VGT_MAGIC);
+       VERIFY("version", h->version, FIRMWARE_VERSION);
+       VERIFY("crc32", h->crc32, crc32_le(0, mem, fw->size - crc32_start));
+       VERIFY("cfg space size", h->cfg_space_size, info->cfg_space_size);
+       VERIFY("mmio size", h->mmio_size, info->mmio_size);
+
+       mem = (fw->data + h->cfg_space_offset);
+
+       id = *(u16 *)(mem + PCI_VENDOR_ID);
+       VERIFY("vender id", id, pdev->vendor);
+
+       id = *(u16 *)(mem + PCI_DEVICE_ID);
+       VERIFY("device id", id, pdev->device);
+
+       id = *(u8 *)(mem + PCI_REVISION_ID);
+       VERIFY("revision id", id, pdev->revision);
+
+#undef VERIFY
+       return 0;
+
+invalid_firmware:
+       gvt_dbg_core("Invalid firmware: %s [file] 0x%llx [request] 0x%llx\n",
+                    item, file, request);
+       return -EINVAL;
+}
+
+#define GVT_FIRMWARE_PATH "i915/gvt"
+
+/**
+ * intel_gvt_load_firmware - load GVT firmware
+ * @gvt: intel gvt device
+ *
+ */
+int intel_gvt_load_firmware(struct intel_gvt *gvt)
+{
+       struct intel_gvt_device_info *info = &gvt->device_info;
+       struct drm_i915_private *dev_priv = gvt->dev_priv;
+       struct pci_dev *pdev = dev_priv->drm.pdev;
+       struct intel_gvt_firmware *firmware = &gvt->firmware;
+       struct gvt_firmware_header *h;
+       const struct firmware *fw;
+       char *path;
+       void *mmio, *mem;
+       int ret;
+
+       path = kmalloc(PATH_MAX, GFP_KERNEL);
+       if (!path)
+               return -ENOMEM;
+
+       mem = kmalloc(info->cfg_space_size, GFP_KERNEL);
+       if (!mem) {
+               kfree(path);
+               return -ENOMEM;
+       }
+
+       firmware->cfg_space = mem;
+
+       mem = kmalloc(info->mmio_size, GFP_KERNEL);
+       if (!mem) {
+               kfree(path);
+               kfree(firmware->cfg_space);
+               return -ENOMEM;
+       }
+
+       firmware->mmio = mem;
+
+       mmio = pci_iomap(pdev, info->mmio_bar, info->mmio_size);
+       if (!mmio) {
+               kfree(path);
+               kfree(firmware->cfg_space);
+               kfree(firmware->mmio);
+               return -EINVAL;
+       }
+
+       if (IS_BROADWELL(gvt->dev_priv) || IS_SKYLAKE(gvt->dev_priv))
+               bdw_forcewake_get(mmio);
+
+       sprintf(path, "%s/vid_0x%04x_did_0x%04x_rid_0x%04x.golden_hw_state",
+                GVT_FIRMWARE_PATH, pdev->vendor, pdev->device,
+                pdev->revision);
+
+       gvt_dbg_core("request hw state firmware %s...\n", path);
+
+       ret = request_firmware(&fw, path, &dev_priv->drm.pdev->dev);
+       kfree(path);
+
+       if (ret)
+               goto expose_firmware;
+
+       gvt_dbg_core("success.\n");
+
+       ret = verify_firmware(gvt, fw);
+       if (ret)
+               goto out_free_fw;
+
+       gvt_dbg_core("verified.\n");
+
+       h = (struct gvt_firmware_header *)fw->data;
+
+       memcpy(firmware->cfg_space, fw->data + h->cfg_space_offset,
+              h->cfg_space_size);
+       memcpy(firmware->mmio, fw->data + h->mmio_offset,
+              h->mmio_size);
+
+       release_firmware(fw);
+       firmware->firmware_loaded = true;
+       pci_iounmap(pdev, mmio);
+       return 0;
+
+out_free_fw:
+       release_firmware(fw);
+expose_firmware:
+       expose_firmware_sysfs(gvt, mmio);
+       pci_iounmap(pdev, mmio);
+       return 0;
+}
index 9f5c9bd83bc973837c1177d127568591e700f402..2c03dad287187543d61decc9f074adf3c2943199 100644 (file)
@@ -98,6 +98,8 @@ static void init_device_info(struct intel_gvt *gvt)
        if (IS_BROADWELL(gvt->dev_priv) || IS_SKYLAKE(gvt->dev_priv)) {
                info->max_support_vgpus = 8;
                info->mmio_size = 2 * 1024 * 1024;
+               info->cfg_space_size = 256;
+               info->mmio_bar = 0;
        }
 }
 
@@ -117,6 +119,7 @@ void intel_gvt_clean_device(struct drm_i915_private *dev_priv)
                return;
 
        intel_gvt_clean_mmio_info(gvt);
+       intel_gvt_free_firmware(gvt);
 
        gvt->initialized = false;
 }
@@ -158,7 +161,15 @@ int intel_gvt_init_device(struct drm_i915_private *dev_priv)
        if (ret)
                return ret;
 
+       ret = intel_gvt_load_firmware(gvt);
+       if (ret)
+               goto out_clean_mmio_info;
+
        gvt_dbg_core("gvt device creation is done\n");
        gvt->initialized = true;
        return 0;
+
+out_clean_mmio_info:
+       intel_gvt_clean_mmio_info(gvt);
+       return ret;
 }
index 024ad97eba12023c63b06a0b6da9c340cb0f2e3a..e8759680e9526fb61ec1db7023b4fa624ed7e7d9 100644 (file)
@@ -56,6 +56,8 @@ extern struct intel_gvt_host intel_gvt_host;
 struct intel_gvt_device_info {
        u32 max_support_vgpus;
        u32 mmio_size;
+       u32 cfg_space_size;
+       u32 mmio_bar;
 };
 
 /* GM resources owned by a vGPU */
@@ -100,6 +102,12 @@ struct intel_gvt_mmio {
        DECLARE_HASHTABLE(mmio_info_table, INTEL_GVT_MMIO_HASH_BITS);
 };
 
+struct intel_gvt_firmware {
+       void *cfg_space;
+       void *mmio;
+       bool firmware_loaded;
+};
+
 struct intel_gvt {
        struct mutex lock;
        bool initialized;
@@ -111,8 +119,12 @@ struct intel_gvt {
        struct intel_gvt_gm gm;
        struct intel_gvt_fence fence;
        struct intel_gvt_mmio mmio;
+       struct intel_gvt_firmware firmware;
 };
 
+void intel_gvt_free_firmware(struct intel_gvt *gvt);
+int intel_gvt_load_firmware(struct intel_gvt *gvt);
+
 /* Aperture/GM space definitions for GVT device */
 #define gvt_aperture_sz(gvt)     (gvt->dev_priv->ggtt.mappable_end)
 #define gvt_aperture_pa_base(gvt) (gvt->dev_priv->ggtt.mappable_base)