mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-17 16:36:52 +07:00
28e0e8ac27
If GuC firmware is not available on the system and we load i915 with enable
GuC, then we hit this null pointer dereference issue:
[ 71.098873] BUG: unable to handle kernel NULL pointer dereference at 0000000000000008
[ 71.098938] IP: intel_uc_fw_upload+0x1f/0x360 [i915]
[ 71.098947] PGD 0 P4D 0
[ 71.098956] Oops: 0000 [#1] PREEMPT SMP PTI
[ 71.098965] Modules linked in: i915(O+) netconsole x86_pkg_temp_thermal intel_powerclamp coretemp crct10dif_pclmul crc32_pclmul ghash_clmulni_intel mei_me i2c_i801 prime_numbers mei [last unloaded: i915]
[ 71.099005] CPU: 2 PID: 1167 Comm: insmod Tainted: G U W O 4.16.0-rc1+ #337
[ 71.099018] Hardware name: /NUC6i5SYB, BIOS SYSKLi35.86A.0065.2018.0103.1000 01/03/2018
[ 71.099077] RIP: 0010:intel_uc_fw_upload+0x1f/0x360 [i915]
[ 71.099087] RSP: 0018:ffffc90000417aa0 EFLAGS: 00010282
[ 71.099097] RAX: 0000000000000000 RBX: ffff88084cad12f8 RCX: ffffffffa03e9357
[ 71.099108] RDX: 0000000000000002 RSI: ffffffffa034dba0 RDI: ffff88084cad12f8
[ 71.099118] RBP: 0000000000000002 R08: ffff88085344ca90 R09: 0000000000000001
[ 71.099128] R10: 0000000000000000 R11: 0000000000000000 R12: ffff88084cad0000
[ 71.099139] R13: ffffffffa034dba0 R14: 00000000fffffff5 R15: ffff88084cad12b0
[ 71.099151] FS: 00007f7f24ae2740(0000) GS:ffff88085e200000(0000) knlGS:0000000000000000
[ 71.099162] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 71.099171] CR2: 0000000000000008 CR3: 0000000855f48001 CR4: 00000000003606e0
[ 71.099182] Call Trace:
[ 71.099246] intel_uc_init_hw+0xc8/0x520 [i915]
[ 71.099303] i915_gem_init_hw+0x11f/0x2d0 [i915]
[ 71.099364] i915_gem_init+0x2b9/0x640 [i915]
[ 71.099413] i915_driver_load+0xb74/0x1110 [i915]
[ 71.099462] i915_pci_probe+0x2e/0x90 [i915]
[ 71.099476] pci_device_probe+0xa1/0x130
[ 71.099488] driver_probe_device+0x302/0x470
[ 71.099502] __driver_attach+0xb9/0xe0
[ 71.099513] ? driver_probe_device+0x470/0x470
[ 71.099525] ? driver_probe_device+0x470/0x470
[ 71.099538] bus_for_each_dev+0x64/0x90
[ 71.099550] bus_add_driver+0x164/0x260
[ 71.099561] ? 0xffffffffa04d6000
[ 71.099572] driver_register+0x57/0xc0
[ 71.099582] ? 0xffffffffa04d6000
[ 71.099593] do_one_initcall+0x3b/0x160
[ 71.099606] ? kmem_cache_alloc_trace+0x1c3/0x2a0
[ 71.099621] do_init_module+0x5b/0x1f9
[ 71.099635] load_module+0x2467/0x2a70
[ 71.099654] ? SyS_finit_module+0xbd/0xe0
[ 71.099668] SyS_finit_module+0xbd/0xe0
[ 71.099682] do_syscall_64+0x73/0x1c0
[ 71.099694] entry_SYSCALL_64_after_hwframe+0x26/0x9b
[ 71.099706] RIP: 0033:0x7f7f23fb40d9
[ 71.099717] RSP: 002b:00007ffda7d67ed8 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 71.099734] RAX: ffffffffffffffda RBX: 000055f96e2a8870 RCX: 00007f7f23fb40d9
[ 71.099748] RDX: 0000000000000000 RSI: 000055f96e2a8260 RDI: 0000000000000003
[ 71.099763] RBP: 000055f96e2a8260 R08: 0000000000000000 R09: 00007ffda7d68088
[ 71.099777] R10: 0000000000000003 R11: 0000000000000246 R12: 0000000000000000
[ 71.099791] R13: 000055f96e2a8830 R14: 0000000000000000 R15: 000055f96e2a8260
[ 71.099810] Code: 00 00 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 41 55 41 54 49 89 f5 55 53 48 c7 c1 57 93 3e a0 48 8b 47 10 48 89 fb 4c 8b 07 <48> 8b 68 08 8b 47 28 85 c0 74 15 83 f8 01 48 c7 c1 5b 93 3e a0
[ 71.100004] RIP: intel_uc_fw_upload+0x1f/0x360 [i915] RSP: ffffc90000417aa0
[ 71.100020] CR2: 0000000000000008
[ 71.100031] ---[ end trace d8ac93c30ceff5b2 ]--
Fixes: 6b0478fb72
("drm/i915: Implement dynamic GuC WOPCM offset and size calculation")
v2: don't assume it is always GuC FW (Michal)
v3: added a new variable to avoid exceeding the number of characters in the
line (Michal)
Signed-off-by: Piotr Piórkowski <piotr.piorkowski@intel.com>
Reported-by: Radoslaw Szwichtenberg <radoslaw.szwichtenberg@intel.com>
Cc: Michał Winiarski <michal.winiarski@intel.com>
Cc: Michal Wajdeczko <michal.wajdeczko@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Cc: Jackie Li <yaodong.li@intel.com>
Cc: Radoslaw Szwichtenberg <radoslaw.szwichtenberg@intel.com>
Reviewed-by: Michal Wajdeczko <michal.wajdeczko@intel.com>
Reviewed-by: Jackie Li <yaodong.li@intel.com>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Link: https://patchwork.freedesktop.org/patch/msgid/20180323112319.16293-1-piotr.piorkowski@intel.com
313 lines
9.4 KiB
C
313 lines
9.4 KiB
C
/*
|
|
* Copyright © 2016-2017 Intel Corporation
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include <linux/firmware.h>
|
|
#include <drm/drm_print.h>
|
|
|
|
#include "intel_uc_fw.h"
|
|
#include "i915_drv.h"
|
|
|
|
/**
|
|
* intel_uc_fw_fetch - fetch uC firmware
|
|
*
|
|
* @dev_priv: device private
|
|
* @uc_fw: uC firmware
|
|
*
|
|
* Fetch uC firmware into GEM obj.
|
|
*/
|
|
void intel_uc_fw_fetch(struct drm_i915_private *dev_priv,
|
|
struct intel_uc_fw *uc_fw)
|
|
{
|
|
struct pci_dev *pdev = dev_priv->drm.pdev;
|
|
struct drm_i915_gem_object *obj;
|
|
const struct firmware *fw = NULL;
|
|
struct uc_css_header *css;
|
|
size_t size;
|
|
int err;
|
|
|
|
DRM_DEBUG_DRIVER("%s fw fetch %s\n",
|
|
intel_uc_fw_type_repr(uc_fw->type), uc_fw->path);
|
|
|
|
if (!uc_fw->path)
|
|
return;
|
|
|
|
uc_fw->fetch_status = INTEL_UC_FIRMWARE_PENDING;
|
|
DRM_DEBUG_DRIVER("%s fw fetch %s\n",
|
|
intel_uc_fw_type_repr(uc_fw->type),
|
|
intel_uc_fw_status_repr(uc_fw->fetch_status));
|
|
|
|
err = request_firmware(&fw, uc_fw->path, &pdev->dev);
|
|
if (err) {
|
|
DRM_DEBUG_DRIVER("%s fw request_firmware err=%d\n",
|
|
intel_uc_fw_type_repr(uc_fw->type), err);
|
|
goto fail;
|
|
}
|
|
|
|
DRM_DEBUG_DRIVER("%s fw size %zu ptr %p\n",
|
|
intel_uc_fw_type_repr(uc_fw->type), fw->size, fw);
|
|
|
|
/* Check the size of the blob before examining buffer contents */
|
|
if (fw->size < sizeof(struct uc_css_header)) {
|
|
DRM_WARN("%s: Unexpected firmware size (%zu, min %zu)\n",
|
|
intel_uc_fw_type_repr(uc_fw->type),
|
|
fw->size, sizeof(struct uc_css_header));
|
|
err = -ENODATA;
|
|
goto fail;
|
|
}
|
|
|
|
css = (struct uc_css_header *)fw->data;
|
|
|
|
/* Firmware bits always start from header */
|
|
uc_fw->header_offset = 0;
|
|
uc_fw->header_size = (css->header_size_dw - css->modulus_size_dw -
|
|
css->key_size_dw - css->exponent_size_dw) *
|
|
sizeof(u32);
|
|
|
|
if (uc_fw->header_size != sizeof(struct uc_css_header)) {
|
|
DRM_WARN("%s: Mismatched firmware header definition\n",
|
|
intel_uc_fw_type_repr(uc_fw->type));
|
|
err = -ENOEXEC;
|
|
goto fail;
|
|
}
|
|
|
|
/* then, uCode */
|
|
uc_fw->ucode_offset = uc_fw->header_offset + uc_fw->header_size;
|
|
uc_fw->ucode_size = (css->size_dw - css->header_size_dw) * sizeof(u32);
|
|
|
|
/* now RSA */
|
|
if (css->key_size_dw != UOS_RSA_SCRATCH_COUNT) {
|
|
DRM_WARN("%s: Mismatched firmware RSA key size (%u)\n",
|
|
intel_uc_fw_type_repr(uc_fw->type), css->key_size_dw);
|
|
err = -ENOEXEC;
|
|
goto fail;
|
|
}
|
|
uc_fw->rsa_offset = uc_fw->ucode_offset + uc_fw->ucode_size;
|
|
uc_fw->rsa_size = css->key_size_dw * sizeof(u32);
|
|
|
|
/* At least, it should have header, uCode and RSA. Size of all three. */
|
|
size = uc_fw->header_size + uc_fw->ucode_size + uc_fw->rsa_size;
|
|
if (fw->size < size) {
|
|
DRM_WARN("%s: Truncated firmware (%zu, expected %zu)\n",
|
|
intel_uc_fw_type_repr(uc_fw->type), fw->size, size);
|
|
err = -ENOEXEC;
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* The GuC firmware image has the version number embedded at a
|
|
* well-known offset within the firmware blob; note that major / minor
|
|
* version are TWO bytes each (i.e. u16), although all pointers and
|
|
* offsets are defined in terms of bytes (u8).
|
|
*/
|
|
switch (uc_fw->type) {
|
|
case INTEL_UC_FW_TYPE_GUC:
|
|
uc_fw->major_ver_found = css->guc.sw_version >> 16;
|
|
uc_fw->minor_ver_found = css->guc.sw_version & 0xFFFF;
|
|
break;
|
|
|
|
case INTEL_UC_FW_TYPE_HUC:
|
|
uc_fw->major_ver_found = css->huc.sw_version >> 16;
|
|
uc_fw->minor_ver_found = css->huc.sw_version & 0xFFFF;
|
|
break;
|
|
|
|
default:
|
|
MISSING_CASE(uc_fw->type);
|
|
break;
|
|
}
|
|
|
|
DRM_DEBUG_DRIVER("%s fw version %u.%u (wanted %u.%u)\n",
|
|
intel_uc_fw_type_repr(uc_fw->type),
|
|
uc_fw->major_ver_found, uc_fw->minor_ver_found,
|
|
uc_fw->major_ver_wanted, uc_fw->minor_ver_wanted);
|
|
|
|
if (uc_fw->major_ver_wanted == 0 && uc_fw->minor_ver_wanted == 0) {
|
|
DRM_NOTE("%s: Skipping firmware version check\n",
|
|
intel_uc_fw_type_repr(uc_fw->type));
|
|
} else if (uc_fw->major_ver_found != uc_fw->major_ver_wanted ||
|
|
uc_fw->minor_ver_found < uc_fw->minor_ver_wanted) {
|
|
DRM_NOTE("%s: Wrong firmware version (%u.%u, required %u.%u)\n",
|
|
intel_uc_fw_type_repr(uc_fw->type),
|
|
uc_fw->major_ver_found, uc_fw->minor_ver_found,
|
|
uc_fw->major_ver_wanted, uc_fw->minor_ver_wanted);
|
|
err = -ENOEXEC;
|
|
goto fail;
|
|
}
|
|
|
|
obj = i915_gem_object_create_from_data(dev_priv, fw->data, fw->size);
|
|
if (IS_ERR(obj)) {
|
|
err = PTR_ERR(obj);
|
|
DRM_DEBUG_DRIVER("%s fw object_create err=%d\n",
|
|
intel_uc_fw_type_repr(uc_fw->type), err);
|
|
goto fail;
|
|
}
|
|
|
|
uc_fw->obj = obj;
|
|
uc_fw->size = fw->size;
|
|
uc_fw->fetch_status = INTEL_UC_FIRMWARE_SUCCESS;
|
|
DRM_DEBUG_DRIVER("%s fw fetch %s\n",
|
|
intel_uc_fw_type_repr(uc_fw->type),
|
|
intel_uc_fw_status_repr(uc_fw->fetch_status));
|
|
|
|
release_firmware(fw);
|
|
return;
|
|
|
|
fail:
|
|
uc_fw->fetch_status = INTEL_UC_FIRMWARE_FAIL;
|
|
DRM_DEBUG_DRIVER("%s fw fetch %s\n",
|
|
intel_uc_fw_type_repr(uc_fw->type),
|
|
intel_uc_fw_status_repr(uc_fw->fetch_status));
|
|
|
|
DRM_WARN("%s: Failed to fetch firmware %s (error %d)\n",
|
|
intel_uc_fw_type_repr(uc_fw->type), uc_fw->path, err);
|
|
DRM_INFO("%s: Firmware can be downloaded from %s\n",
|
|
intel_uc_fw_type_repr(uc_fw->type), INTEL_UC_FIRMWARE_URL);
|
|
|
|
release_firmware(fw); /* OK even if fw is NULL */
|
|
}
|
|
|
|
/**
|
|
* intel_uc_fw_upload - load uC firmware using custom loader
|
|
* @uc_fw: uC firmware
|
|
* @xfer: custom uC firmware loader function
|
|
*
|
|
* Loads uC firmware using custom loader and updates internal flags.
|
|
*
|
|
* Return: 0 on success, non-zero on failure.
|
|
*/
|
|
int intel_uc_fw_upload(struct intel_uc_fw *uc_fw,
|
|
int (*xfer)(struct intel_uc_fw *uc_fw,
|
|
struct i915_vma *vma))
|
|
{
|
|
struct i915_vma *vma;
|
|
u32 ggtt_pin_bias;
|
|
int err;
|
|
|
|
DRM_DEBUG_DRIVER("%s fw load %s\n",
|
|
intel_uc_fw_type_repr(uc_fw->type), uc_fw->path);
|
|
|
|
if (uc_fw->fetch_status != INTEL_UC_FIRMWARE_SUCCESS)
|
|
return -ENOEXEC;
|
|
|
|
uc_fw->load_status = INTEL_UC_FIRMWARE_PENDING;
|
|
DRM_DEBUG_DRIVER("%s fw load %s\n",
|
|
intel_uc_fw_type_repr(uc_fw->type),
|
|
intel_uc_fw_status_repr(uc_fw->load_status));
|
|
|
|
/* Pin object with firmware */
|
|
err = i915_gem_object_set_to_gtt_domain(uc_fw->obj, false);
|
|
if (err) {
|
|
DRM_DEBUG_DRIVER("%s fw set-domain err=%d\n",
|
|
intel_uc_fw_type_repr(uc_fw->type), err);
|
|
goto fail;
|
|
}
|
|
|
|
ggtt_pin_bias = to_i915(uc_fw->obj->base.dev)->guc.ggtt_pin_bias;
|
|
vma = i915_gem_object_ggtt_pin(uc_fw->obj, NULL, 0, 0,
|
|
PIN_OFFSET_BIAS | ggtt_pin_bias);
|
|
if (IS_ERR(vma)) {
|
|
err = PTR_ERR(vma);
|
|
DRM_DEBUG_DRIVER("%s fw ggtt-pin err=%d\n",
|
|
intel_uc_fw_type_repr(uc_fw->type), err);
|
|
goto fail;
|
|
}
|
|
|
|
/* Call custom loader */
|
|
err = xfer(uc_fw, vma);
|
|
|
|
/*
|
|
* We keep the object pages for reuse during resume. But we can unpin it
|
|
* now that DMA has completed, so it doesn't continue to take up space.
|
|
*/
|
|
i915_vma_unpin(vma);
|
|
|
|
if (err)
|
|
goto fail;
|
|
|
|
uc_fw->load_status = INTEL_UC_FIRMWARE_SUCCESS;
|
|
DRM_DEBUG_DRIVER("%s fw load %s\n",
|
|
intel_uc_fw_type_repr(uc_fw->type),
|
|
intel_uc_fw_status_repr(uc_fw->load_status));
|
|
|
|
DRM_INFO("%s: Loaded firmware %s (version %u.%u)\n",
|
|
intel_uc_fw_type_repr(uc_fw->type),
|
|
uc_fw->path,
|
|
uc_fw->major_ver_found, uc_fw->minor_ver_found);
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
uc_fw->load_status = INTEL_UC_FIRMWARE_FAIL;
|
|
DRM_DEBUG_DRIVER("%s fw load %s\n",
|
|
intel_uc_fw_type_repr(uc_fw->type),
|
|
intel_uc_fw_status_repr(uc_fw->load_status));
|
|
|
|
DRM_WARN("%s: Failed to load firmware %s (error %d)\n",
|
|
intel_uc_fw_type_repr(uc_fw->type), uc_fw->path, err);
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* intel_uc_fw_fini - cleanup uC firmware
|
|
*
|
|
* @uc_fw: uC firmware
|
|
*
|
|
* Cleans up uC firmware by releasing the firmware GEM obj.
|
|
*/
|
|
void intel_uc_fw_fini(struct intel_uc_fw *uc_fw)
|
|
{
|
|
struct drm_i915_gem_object *obj;
|
|
|
|
obj = fetch_and_zero(&uc_fw->obj);
|
|
if (obj)
|
|
i915_gem_object_put(obj);
|
|
|
|
uc_fw->fetch_status = INTEL_UC_FIRMWARE_NONE;
|
|
}
|
|
|
|
/**
|
|
* intel_uc_fw_dump - dump information about uC firmware
|
|
* @uc_fw: uC firmware
|
|
* @p: the &drm_printer
|
|
*
|
|
* Pretty printer for uC firmware.
|
|
*/
|
|
void intel_uc_fw_dump(const struct intel_uc_fw *uc_fw, struct drm_printer *p)
|
|
{
|
|
drm_printf(p, "%s firmware: %s\n",
|
|
intel_uc_fw_type_repr(uc_fw->type), uc_fw->path);
|
|
drm_printf(p, "\tstatus: fetch %s, load %s\n",
|
|
intel_uc_fw_status_repr(uc_fw->fetch_status),
|
|
intel_uc_fw_status_repr(uc_fw->load_status));
|
|
drm_printf(p, "\tversion: wanted %u.%u, found %u.%u\n",
|
|
uc_fw->major_ver_wanted, uc_fw->minor_ver_wanted,
|
|
uc_fw->major_ver_found, uc_fw->minor_ver_found);
|
|
drm_printf(p, "\theader: offset %u, size %u\n",
|
|
uc_fw->header_offset, uc_fw->header_size);
|
|
drm_printf(p, "\tuCode: offset %u, size %u\n",
|
|
uc_fw->ucode_offset, uc_fw->ucode_size);
|
|
drm_printf(p, "\tRSA: offset %u, size %u\n",
|
|
uc_fw->rsa_offset, uc_fw->rsa_size);
|
|
}
|