cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

intel_wopcm.c (9930B)


      1// SPDX-License-Identifier: MIT
      2/*
      3 * Copyright © 2017-2019 Intel Corporation
      4 */
      5
      6#include "intel_wopcm.h"
      7#include "i915_drv.h"
      8
      9/**
     10 * DOC: WOPCM Layout
     11 *
     12 * The layout of the WOPCM will be fixed after writing to GuC WOPCM size and
     13 * offset registers whose values are calculated and determined by HuC/GuC
     14 * firmware size and set of hardware requirements/restrictions as shown below:
     15 *
     16 * ::
     17 *
     18 *    +=========> +====================+ <== WOPCM Top
     19 *    ^           |  HW contexts RSVD  |
     20 *    |     +===> +====================+ <== GuC WOPCM Top
     21 *    |     ^     |                    |
     22 *    |     |     |                    |
     23 *    |     |     |                    |
     24 *    |    GuC    |                    |
     25 *    |   WOPCM   |                    |
     26 *    |    Size   +--------------------+
     27 *  WOPCM   |     |    GuC FW RSVD     |
     28 *    |     |     +--------------------+
     29 *    |     |     |   GuC Stack RSVD   |
     30 *    |     |     +------------------- +
     31 *    |     v     |   GuC WOPCM RSVD   |
     32 *    |     +===> +====================+ <== GuC WOPCM base
     33 *    |           |     WOPCM RSVD     |
     34 *    |           +------------------- + <== HuC Firmware Top
     35 *    v           |      HuC FW        |
     36 *    +=========> +====================+ <== WOPCM Base
     37 *
     38 * GuC accessible WOPCM starts at GuC WOPCM base and ends at GuC WOPCM top.
     39 * The top part of the WOPCM is reserved for hardware contexts (e.g. RC6
     40 * context).
     41 */
     42
     43/* Default WOPCM size is 2MB from Gen11, 1MB on previous platforms */
     44#define GEN11_WOPCM_SIZE		SZ_2M
     45#define GEN9_WOPCM_SIZE			SZ_1M
     46#define MAX_WOPCM_SIZE			SZ_8M
     47/* 16KB WOPCM (RSVD WOPCM) is reserved from HuC firmware top. */
     48#define WOPCM_RESERVED_SIZE		SZ_16K
     49
     50/* 16KB reserved at the beginning of GuC WOPCM. */
     51#define GUC_WOPCM_RESERVED		SZ_16K
     52/* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */
     53#define GUC_WOPCM_STACK_RESERVED	SZ_8K
     54
     55/* GuC WOPCM Offset value needs to be aligned to 16KB. */
     56#define GUC_WOPCM_OFFSET_ALIGNMENT	(1UL << GUC_WOPCM_OFFSET_SHIFT)
     57
     58/* 24KB at the end of WOPCM is reserved for RC6 CTX on BXT. */
     59#define BXT_WOPCM_RC6_CTX_RESERVED	(SZ_16K + SZ_8K)
     60/* 36KB WOPCM reserved at the end of WOPCM on ICL. */
     61#define ICL_WOPCM_HW_CTX_RESERVED	(SZ_32K + SZ_4K)
     62
     63/* 128KB from GUC_WOPCM_RESERVED is reserved for FW on Gen9. */
     64#define GEN9_GUC_FW_RESERVED	SZ_128K
     65#define GEN9_GUC_WOPCM_OFFSET	(GUC_WOPCM_RESERVED + GEN9_GUC_FW_RESERVED)
     66
     67static inline struct drm_i915_private *wopcm_to_i915(struct intel_wopcm *wopcm)
     68{
     69	return container_of(wopcm, struct drm_i915_private, wopcm);
     70}
     71
     72/**
     73 * intel_wopcm_init_early() - Early initialization of the WOPCM.
     74 * @wopcm: pointer to intel_wopcm.
     75 *
     76 * Setup the size of WOPCM which will be used by later on WOPCM partitioning.
     77 */
     78void intel_wopcm_init_early(struct intel_wopcm *wopcm)
     79{
     80	struct drm_i915_private *i915 = wopcm_to_i915(wopcm);
     81
     82	if (!HAS_GT_UC(i915))
     83		return;
     84
     85	if (GRAPHICS_VER(i915) >= 11)
     86		wopcm->size = GEN11_WOPCM_SIZE;
     87	else
     88		wopcm->size = GEN9_WOPCM_SIZE;
     89
     90	drm_dbg(&i915->drm, "WOPCM: %uK\n", wopcm->size / 1024);
     91}
     92
     93static u32 context_reserved_size(struct drm_i915_private *i915)
     94{
     95	if (IS_GEN9_LP(i915))
     96		return BXT_WOPCM_RC6_CTX_RESERVED;
     97	else if (GRAPHICS_VER(i915) >= 11)
     98		return ICL_WOPCM_HW_CTX_RESERVED;
     99	else
    100		return 0;
    101}
    102
    103static bool gen9_check_dword_gap(struct drm_i915_private *i915,
    104				 u32 guc_wopcm_base, u32 guc_wopcm_size)
    105{
    106	u32 offset;
    107
    108	/*
    109	 * GuC WOPCM size shall be at least a dword larger than the offset from
    110	 * WOPCM base (GuC WOPCM offset from WOPCM base + GEN9_GUC_WOPCM_OFFSET)
    111	 * due to hardware limitation on Gen9.
    112	 */
    113	offset = guc_wopcm_base + GEN9_GUC_WOPCM_OFFSET;
    114	if (offset > guc_wopcm_size ||
    115	    (guc_wopcm_size - offset) < sizeof(u32)) {
    116		drm_err(&i915->drm,
    117			"WOPCM: invalid GuC region size: %uK < %uK\n",
    118			guc_wopcm_size / SZ_1K,
    119			(u32)(offset + sizeof(u32)) / SZ_1K);
    120		return false;
    121	}
    122
    123	return true;
    124}
    125
    126static bool gen9_check_huc_fw_fits(struct drm_i915_private *i915,
    127				   u32 guc_wopcm_size, u32 huc_fw_size)
    128{
    129	/*
    130	 * On Gen9, hardware requires the total available GuC WOPCM
    131	 * size to be larger than or equal to HuC firmware size. Otherwise,
    132	 * firmware uploading would fail.
    133	 */
    134	if (huc_fw_size > guc_wopcm_size - GUC_WOPCM_RESERVED) {
    135		drm_err(&i915->drm, "WOPCM: no space for %s: %uK < %uK\n",
    136			intel_uc_fw_type_repr(INTEL_UC_FW_TYPE_HUC),
    137			(guc_wopcm_size - GUC_WOPCM_RESERVED) / SZ_1K,
    138			huc_fw_size / 1024);
    139		return false;
    140	}
    141
    142	return true;
    143}
    144
    145static bool check_hw_restrictions(struct drm_i915_private *i915,
    146				  u32 guc_wopcm_base, u32 guc_wopcm_size,
    147				  u32 huc_fw_size)
    148{
    149	if (GRAPHICS_VER(i915) == 9 && !gen9_check_dword_gap(i915, guc_wopcm_base,
    150							     guc_wopcm_size))
    151		return false;
    152
    153	if (GRAPHICS_VER(i915) == 9 &&
    154	    !gen9_check_huc_fw_fits(i915, guc_wopcm_size, huc_fw_size))
    155		return false;
    156
    157	return true;
    158}
    159
    160static bool __check_layout(struct drm_i915_private *i915, u32 wopcm_size,
    161			   u32 guc_wopcm_base, u32 guc_wopcm_size,
    162			   u32 guc_fw_size, u32 huc_fw_size)
    163{
    164	const u32 ctx_rsvd = context_reserved_size(i915);
    165	u32 size;
    166
    167	size = wopcm_size - ctx_rsvd;
    168	if (unlikely(range_overflows(guc_wopcm_base, guc_wopcm_size, size))) {
    169		drm_err(&i915->drm,
    170			"WOPCM: invalid GuC region layout: %uK + %uK > %uK\n",
    171			guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K,
    172			size / SZ_1K);
    173		return false;
    174	}
    175
    176	size = guc_fw_size + GUC_WOPCM_RESERVED + GUC_WOPCM_STACK_RESERVED;
    177	if (unlikely(guc_wopcm_size < size)) {
    178		drm_err(&i915->drm, "WOPCM: no space for %s: %uK < %uK\n",
    179			intel_uc_fw_type_repr(INTEL_UC_FW_TYPE_GUC),
    180			guc_wopcm_size / SZ_1K, size / SZ_1K);
    181		return false;
    182	}
    183
    184	size = huc_fw_size + WOPCM_RESERVED_SIZE;
    185	if (unlikely(guc_wopcm_base < size)) {
    186		drm_err(&i915->drm, "WOPCM: no space for %s: %uK < %uK\n",
    187			intel_uc_fw_type_repr(INTEL_UC_FW_TYPE_HUC),
    188			guc_wopcm_base / SZ_1K, size / SZ_1K);
    189		return false;
    190	}
    191
    192	return check_hw_restrictions(i915, guc_wopcm_base, guc_wopcm_size,
    193				     huc_fw_size);
    194}
    195
    196static bool __wopcm_regs_locked(struct intel_uncore *uncore,
    197				u32 *guc_wopcm_base, u32 *guc_wopcm_size)
    198{
    199	u32 reg_base = intel_uncore_read(uncore, DMA_GUC_WOPCM_OFFSET);
    200	u32 reg_size = intel_uncore_read(uncore, GUC_WOPCM_SIZE);
    201
    202	if (!(reg_size & GUC_WOPCM_SIZE_LOCKED) ||
    203	    !(reg_base & GUC_WOPCM_OFFSET_VALID))
    204		return false;
    205
    206	*guc_wopcm_base = reg_base & GUC_WOPCM_OFFSET_MASK;
    207	*guc_wopcm_size = reg_size & GUC_WOPCM_SIZE_MASK;
    208	return true;
    209}
    210
    211static bool __wopcm_regs_writable(struct intel_uncore *uncore)
    212{
    213	if (!HAS_GUC_DEPRIVILEGE(uncore->i915))
    214		return true;
    215
    216	return intel_uncore_read(uncore, GUC_SHIM_CONTROL2) & GUC_IS_PRIVILEGED;
    217}
    218
    219/**
    220 * intel_wopcm_init() - Initialize the WOPCM structure.
    221 * @wopcm: pointer to intel_wopcm.
    222 *
    223 * This function will partition WOPCM space based on GuC and HuC firmware sizes
    224 * and will allocate max remaining for use by GuC. This function will also
    225 * enforce platform dependent hardware restrictions on GuC WOPCM offset and
    226 * size. It will fail the WOPCM init if any of these checks fail, so that the
    227 * following WOPCM registers setup and GuC firmware uploading would be aborted.
    228 */
    229void intel_wopcm_init(struct intel_wopcm *wopcm)
    230{
    231	struct drm_i915_private *i915 = wopcm_to_i915(wopcm);
    232	struct intel_gt *gt = to_gt(i915);
    233	u32 guc_fw_size = intel_uc_fw_get_upload_size(&gt->uc.guc.fw);
    234	u32 huc_fw_size = intel_uc_fw_get_upload_size(&gt->uc.huc.fw);
    235	u32 ctx_rsvd = context_reserved_size(i915);
    236	u32 wopcm_size = wopcm->size;
    237	u32 guc_wopcm_base;
    238	u32 guc_wopcm_size;
    239
    240	if (!guc_fw_size)
    241		return;
    242
    243	GEM_BUG_ON(!wopcm_size);
    244	GEM_BUG_ON(wopcm->guc.base);
    245	GEM_BUG_ON(wopcm->guc.size);
    246	GEM_BUG_ON(guc_fw_size >= wopcm_size);
    247	GEM_BUG_ON(huc_fw_size >= wopcm_size);
    248	GEM_BUG_ON(ctx_rsvd + WOPCM_RESERVED_SIZE >= wopcm_size);
    249
    250	if (i915_inject_probe_failure(i915))
    251		return;
    252
    253	if (__wopcm_regs_locked(gt->uncore, &guc_wopcm_base, &guc_wopcm_size)) {
    254		drm_dbg(&i915->drm, "GuC WOPCM is already locked [%uK, %uK)\n",
    255			guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K);
    256		/*
    257		 * Note that to keep things simple (i.e. avoid different
    258		 * defines per platform) our WOPCM math doesn't always use the
    259		 * actual WOPCM size, but a value that is less or equal to it.
    260		 * This is perfectly fine when i915 programs the registers, but
    261		 * on platforms with GuC deprivilege the registers are not
    262		 * writable from i915 and are instead pre-programmed by the
    263		 * bios/IFWI, so there might be a mismatch of sizes.
    264		 * Instead of handling the size difference, we trust that the
    265		 * programmed values make sense and disable the relevant check
    266		 * by using the maximum possible WOPCM size in the verification
    267		 * math. In the extremely unlikely case that the registers
    268		 * were pre-programmed with an invalid value, we will still
    269		 * gracefully fail later during the GuC/HuC dma.
    270		 */
    271		if (!__wopcm_regs_writable(gt->uncore))
    272			wopcm_size = MAX_WOPCM_SIZE;
    273
    274		goto check;
    275	}
    276
    277	/*
    278	 * Aligned value of guc_wopcm_base will determine available WOPCM space
    279	 * for HuC firmware and mandatory reserved area.
    280	 */
    281	guc_wopcm_base = huc_fw_size + WOPCM_RESERVED_SIZE;
    282	guc_wopcm_base = ALIGN(guc_wopcm_base, GUC_WOPCM_OFFSET_ALIGNMENT);
    283
    284	/*
    285	 * Need to clamp guc_wopcm_base now to make sure the following math is
    286	 * correct. Formal check of whole WOPCM layout will be done below.
    287	 */
    288	guc_wopcm_base = min(guc_wopcm_base, wopcm_size - ctx_rsvd);
    289
    290	/* Aligned remainings of usable WOPCM space can be assigned to GuC. */
    291	guc_wopcm_size = wopcm_size - ctx_rsvd - guc_wopcm_base;
    292	guc_wopcm_size &= GUC_WOPCM_SIZE_MASK;
    293
    294	drm_dbg(&i915->drm, "Calculated GuC WOPCM [%uK, %uK)\n",
    295		guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K);
    296
    297check:
    298	if (__check_layout(i915, wopcm_size, guc_wopcm_base, guc_wopcm_size,
    299			   guc_fw_size, huc_fw_size)) {
    300		wopcm->guc.base = guc_wopcm_base;
    301		wopcm->guc.size = guc_wopcm_size;
    302		GEM_BUG_ON(!wopcm->guc.base);
    303		GEM_BUG_ON(!wopcm->guc.size);
    304	}
    305}