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

source code of linux/drivers/gpu/drm/i915/gt/intel_wopcm.c