Some of the newer HW will use bigger RSA keys to authenticate the GuC binary. On those platforms the HW will read the key from memory instead of the RSA registers, so we need to copy it in a dedicated vma, like we do for the HuC. The address of the key is provided to the HW via the first RSA register. v2: clarify that the RSA behavior is hardcoded in the bootrom (Matt) Signed-off-by: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com> Cc: Michal Wajdeczko <michal.wajdeczko@intel.com> Cc: John Harrison <John.C.Harrison@Intel.com> Cc: Matthew Brost <matthew.brost@intel.com> Reviewed-by: Matthew Brost <matthew.brost@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20211211000756.1698923-4-daniele.ceraolospurio@intel.com
193 lines
5.4 KiB
C
193 lines
5.4 KiB
C
// SPDX-License-Identifier: MIT
|
|
/*
|
|
* Copyright © 2014-2019 Intel Corporation
|
|
*
|
|
* Authors:
|
|
* Vinit Azad <vinit.azad@intel.com>
|
|
* Ben Widawsky <ben@bwidawsk.net>
|
|
* Dave Gordon <david.s.gordon@intel.com>
|
|
* Alex Dai <yu.dai@intel.com>
|
|
*/
|
|
|
|
#include "gt/intel_gt.h"
|
|
#include "intel_guc_fw.h"
|
|
#include "i915_drv.h"
|
|
|
|
static void guc_prepare_xfer(struct intel_uncore *uncore)
|
|
{
|
|
u32 shim_flags = GUC_DISABLE_SRAM_INIT_TO_ZEROES |
|
|
GUC_ENABLE_READ_CACHE_LOGIC |
|
|
GUC_ENABLE_MIA_CACHING |
|
|
GUC_ENABLE_READ_CACHE_FOR_SRAM_DATA |
|
|
GUC_ENABLE_READ_CACHE_FOR_WOPCM_DATA |
|
|
GUC_ENABLE_MIA_CLOCK_GATING;
|
|
|
|
/* Must program this register before loading the ucode with DMA */
|
|
intel_uncore_write(uncore, GUC_SHIM_CONTROL, shim_flags);
|
|
|
|
if (IS_GEN9_LP(uncore->i915))
|
|
intel_uncore_write(uncore, GEN9LP_GT_PM_CONFIG, GT_DOORBELL_ENABLE);
|
|
else
|
|
intel_uncore_write(uncore, GEN9_GT_PM_CONFIG, GT_DOORBELL_ENABLE);
|
|
|
|
if (GRAPHICS_VER(uncore->i915) == 9) {
|
|
/* DOP Clock Gating Enable for GuC clocks */
|
|
intel_uncore_rmw(uncore, GEN7_MISCCPCTL,
|
|
0, GEN8_DOP_CLOCK_GATE_GUC_ENABLE);
|
|
|
|
/* allows for 5us (in 10ns units) before GT can go to RC6 */
|
|
intel_uncore_write(uncore, GUC_ARAT_C6DIS, 0x1FF);
|
|
}
|
|
}
|
|
|
|
static int guc_xfer_rsa_mmio(struct intel_uc_fw *guc_fw,
|
|
struct intel_uncore *uncore)
|
|
{
|
|
u32 rsa[UOS_RSA_SCRATCH_COUNT];
|
|
size_t copied;
|
|
int i;
|
|
|
|
copied = intel_uc_fw_copy_rsa(guc_fw, rsa, sizeof(rsa));
|
|
if (copied < sizeof(rsa))
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < UOS_RSA_SCRATCH_COUNT; i++)
|
|
intel_uncore_write(uncore, UOS_RSA_SCRATCH(i), rsa[i]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int guc_xfer_rsa_vma(struct intel_uc_fw *guc_fw,
|
|
struct intel_uncore *uncore)
|
|
{
|
|
struct intel_guc *guc = container_of(guc_fw, struct intel_guc, fw);
|
|
|
|
intel_uncore_write(uncore, UOS_RSA_SCRATCH(0),
|
|
intel_guc_ggtt_offset(guc, guc_fw->rsa_data));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Copy RSA signature from the fw image to HW for verification */
|
|
static int guc_xfer_rsa(struct intel_uc_fw *guc_fw,
|
|
struct intel_uncore *uncore)
|
|
{
|
|
if (guc_fw->rsa_data)
|
|
return guc_xfer_rsa_vma(guc_fw, uncore);
|
|
else
|
|
return guc_xfer_rsa_mmio(guc_fw, uncore);
|
|
}
|
|
|
|
/*
|
|
* Read the GuC status register (GUC_STATUS) and store it in the
|
|
* specified location; then return a boolean indicating whether
|
|
* the value matches either of two values representing completion
|
|
* of the GuC boot process.
|
|
*
|
|
* This is used for polling the GuC status in a wait_for()
|
|
* loop below.
|
|
*/
|
|
static inline bool guc_ready(struct intel_uncore *uncore, u32 *status)
|
|
{
|
|
u32 val = intel_uncore_read(uncore, GUC_STATUS);
|
|
u32 uk_val = val & GS_UKERNEL_MASK;
|
|
|
|
*status = val;
|
|
return (uk_val == GS_UKERNEL_READY) ||
|
|
((val & GS_MIA_CORE_STATE) && (uk_val == GS_UKERNEL_LAPIC_DONE));
|
|
}
|
|
|
|
static int guc_wait_ucode(struct intel_uncore *uncore)
|
|
{
|
|
u32 status;
|
|
int ret;
|
|
|
|
/*
|
|
* Wait for the GuC to start up.
|
|
* NB: Docs recommend not using the interrupt for completion.
|
|
* Measurements indicate this should take no more than 20ms, so a
|
|
* timeout here indicates that the GuC has failed and is unusable.
|
|
* (Higher levels of the driver may decide to reset the GuC and
|
|
* attempt the ucode load again if this happens.)
|
|
*/
|
|
ret = wait_for(guc_ready(uncore, &status), 100);
|
|
if (ret) {
|
|
struct drm_device *drm = &uncore->i915->drm;
|
|
|
|
drm_dbg(drm, "GuC load failed: status = 0x%08X\n", status);
|
|
drm_dbg(drm, "GuC load failed: status: Reset = %d, "
|
|
"BootROM = 0x%02X, UKernel = 0x%02X, "
|
|
"MIA = 0x%02X, Auth = 0x%02X\n",
|
|
REG_FIELD_GET(GS_MIA_IN_RESET, status),
|
|
REG_FIELD_GET(GS_BOOTROM_MASK, status),
|
|
REG_FIELD_GET(GS_UKERNEL_MASK, status),
|
|
REG_FIELD_GET(GS_MIA_MASK, status),
|
|
REG_FIELD_GET(GS_AUTH_STATUS_MASK, status));
|
|
|
|
if ((status & GS_BOOTROM_MASK) == GS_BOOTROM_RSA_FAILED) {
|
|
drm_dbg(drm, "GuC firmware signature verification failed\n");
|
|
ret = -ENOEXEC;
|
|
}
|
|
|
|
if ((status & GS_UKERNEL_MASK) == GS_UKERNEL_EXCEPTION) {
|
|
drm_dbg(drm, "GuC firmware exception. EIP: %#x\n",
|
|
intel_uncore_read(uncore, SOFT_SCRATCH(13)));
|
|
ret = -ENXIO;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* intel_guc_fw_upload() - load GuC uCode to device
|
|
* @guc: intel_guc structure
|
|
*
|
|
* Called from intel_uc_init_hw() during driver load, resume from sleep and
|
|
* after a GPU reset.
|
|
*
|
|
* The firmware image should have already been fetched into memory, so only
|
|
* check that fetch succeeded, and then transfer the image to the h/w.
|
|
*
|
|
* Return: non-zero code on error
|
|
*/
|
|
int intel_guc_fw_upload(struct intel_guc *guc)
|
|
{
|
|
struct intel_gt *gt = guc_to_gt(guc);
|
|
struct intel_uncore *uncore = gt->uncore;
|
|
int ret;
|
|
|
|
guc_prepare_xfer(uncore);
|
|
|
|
/*
|
|
* Note that GuC needs the CSS header plus uKernel code to be copied
|
|
* by the DMA engine in one operation, whereas the RSA signature is
|
|
* loaded separately, either by copying it to the UOS_RSA_SCRATCH
|
|
* register (if key size <= 256) or through a ggtt-pinned vma (if key
|
|
* size > 256). The RSA size and therefore the way we provide it to the
|
|
* HW is fixed for each platform and hard-coded in the bootrom.
|
|
*/
|
|
ret = guc_xfer_rsa(&guc->fw, uncore);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/*
|
|
* Current uCode expects the code to be loaded at 8k; locations below
|
|
* this are used for the stack.
|
|
*/
|
|
ret = intel_uc_fw_upload(&guc->fw, 0x2000, UOS_MOVE);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = guc_wait_ucode(uncore);
|
|
if (ret)
|
|
goto out;
|
|
|
|
intel_uc_fw_change_status(&guc->fw, INTEL_UC_FIRMWARE_RUNNING);
|
|
return 0;
|
|
|
|
out:
|
|
intel_uc_fw_change_status(&guc->fw, INTEL_UC_FIRMWARE_LOAD_FAIL);
|
|
return ret;
|
|
}
|