xref: /linux/drivers/gpu/drm/xe/xe_uc_fw.c (revision ab93e0dd72c37d378dd936f031ffb83ff2bd87ce)
1dd08ebf6SMatthew Brost // SPDX-License-Identifier: MIT
2dd08ebf6SMatthew Brost /*
3dd08ebf6SMatthew Brost  * Copyright © 2022 Intel Corporation
4dd08ebf6SMatthew Brost  */
5dd08ebf6SMatthew Brost 
6dd08ebf6SMatthew Brost #include <linux/bitfield.h>
791b2c42cSFrancois Dugast #include <linux/fault-inject.h>
8dd08ebf6SMatthew Brost #include <linux/firmware.h>
9dd08ebf6SMatthew Brost 
10dd08ebf6SMatthew Brost #include <drm/drm_managed.h>
11dd08ebf6SMatthew Brost 
12a9b1a136SLucas De Marchi #include "regs/xe_guc_regs.h"
13dd08ebf6SMatthew Brost #include "xe_bo.h"
14dd08ebf6SMatthew Brost #include "xe_device_types.h"
15dd08ebf6SMatthew Brost #include "xe_force_wake.h"
16985d5a49SDaniele Ceraolo Spurio #include "xe_gsc.h"
17dd08ebf6SMatthew Brost #include "xe_gt.h"
1826a22952SMichal Wajdeczko #include "xe_gt_printk.h"
194eb0aab6SJulia Filipchuk #include "xe_gt_sriov_vf.h"
20dd08ebf6SMatthew Brost #include "xe_guc.h"
21dd08ebf6SMatthew Brost #include "xe_map.h"
22a455ed04SDaniele Ceraolo Spurio #include "xe_mmio.h"
2366cb3ca9SMichal Wajdeczko #include "xe_module.h"
24dd08ebf6SMatthew Brost #include "xe_sriov.h"
25dd08ebf6SMatthew Brost #include "xe_uc_fw.h"
26ad55ead7SLucas De Marchi 
27ad55ead7SLucas De Marchi /*
28ad55ead7SLucas De Marchi  * List of required GuC and HuC binaries per-platform. They must be ordered
29ad55ead7SLucas De Marchi  * based on platform, from newer to older.
30ad55ead7SLucas De Marchi  *
31ad55ead7SLucas De Marchi  * Versioning follows the guidelines from
32ad55ead7SLucas De Marchi  * Documentation/driver-api/firmware/firmware-usage-guidelines.rst. There is a
33ad55ead7SLucas De Marchi  * distinction for platforms being officially supported by the driver or not.
34ad55ead7SLucas De Marchi  * Platforms not available publicly or not yet officially supported by the
35ad55ead7SLucas De Marchi  * driver (under force-probe), use the mmp_ver(): the firmware autoselect logic
36ad55ead7SLucas De Marchi  * will select the firmware from disk with filename that matches the full
37ad55ead7SLucas De Marchi  * "mpp version", i.e. major.minor.patch. mmp_ver() should only be used for
38ad55ead7SLucas De Marchi  * this case.
39ad55ead7SLucas De Marchi  *
40ad55ead7SLucas De Marchi  * For platforms officially supported by the driver, the filename always only
41ad55ead7SLucas De Marchi  * ever contains the major version (GuC) or no version at all (HuC).
42ad55ead7SLucas De Marchi  *
43ad55ead7SLucas De Marchi  * After loading the file, the driver parses the versions embedded in the blob.
44ad55ead7SLucas De Marchi  * The major version needs to match a major version supported by the driver (if
45ad55ead7SLucas De Marchi  * any). The minor version is also checked and a notice emitted to the log if
46ad55ead7SLucas De Marchi  * the version found is smaller than the version wanted. This is done only for
47ad55ead7SLucas De Marchi  * informational purposes so users may have a chance to upgrade, but the driver
48ad55ead7SLucas De Marchi  * still loads and use the older firmware.
49ad55ead7SLucas De Marchi  *
50ad55ead7SLucas De Marchi  * Examples:
51ad55ead7SLucas De Marchi  *
52ad55ead7SLucas De Marchi  *	1) Platform officially supported by i915 - using Tigerlake as example.
53ad55ead7SLucas De Marchi  *	   Driver loads the following firmware blobs from disk:
54ad55ead7SLucas De Marchi  *
55ad55ead7SLucas De Marchi  *		- i915/tgl_guc_<major>.bin
56ad55ead7SLucas De Marchi  *		- i915/tgl_huc.bin
57ad55ead7SLucas De Marchi  *
58ad55ead7SLucas De Marchi  *	   <major> number for GuC is checked that it matches the version inside
59ad55ead7SLucas De Marchi  *	   the blob. <minor> version is checked and if smaller than the expected
60ad55ead7SLucas De Marchi  *	   an info message is emitted about that.
61ad55ead7SLucas De Marchi  *
62ad55ead7SLucas De Marchi  *	1) XE_<FUTUREINTELPLATFORM>, still under require_force_probe. Using
63ad55ead7SLucas De Marchi  *	   "wipplat" as a short-name. Driver loads the following firmware blobs
64ad55ead7SLucas De Marchi  *	   from disk:
65ad55ead7SLucas De Marchi  *
66ad55ead7SLucas De Marchi  *		- xe/wipplat_guc_<major>.<minor>.<patch>.bin
67ad55ead7SLucas De Marchi  *		- xe/wipplat_huc_<major>.<minor>.<patch>.bin
68ad55ead7SLucas De Marchi  *
69ad55ead7SLucas De Marchi  *	   <major> and <minor> are checked that they match the version inside
70ad55ead7SLucas De Marchi  *	   the blob. Both of them need to match exactly what the driver is
71ad55ead7SLucas De Marchi  *	   expecting, otherwise it fails.
72ad55ead7SLucas De Marchi  *
73ad55ead7SLucas De Marchi  *	3) Platform officially supported by xe and out of force-probe. Using
74ad55ead7SLucas De Marchi  *	   "plat" as a short-name. Except for the different directory, the
75ad55ead7SLucas De Marchi  *	   behavior is the same as (1). Driver loads the following firmware
76ad55ead7SLucas De Marchi  *	   blobs from disk:
77ad55ead7SLucas De Marchi  *
78ad55ead7SLucas De Marchi  *		- xe/plat_guc_<major>.bin
79ad55ead7SLucas De Marchi  *		- xe/plat_huc.bin
80ad55ead7SLucas De Marchi  *
81ad55ead7SLucas De Marchi  *	   <major> number for GuC is checked that it matches the version inside
82ad55ead7SLucas De Marchi  *	   the blob. <minor> version is checked and if smaller than the expected
83ad55ead7SLucas De Marchi  *	   an info message is emitted about that.
84ad55ead7SLucas De Marchi  *
85ad55ead7SLucas De Marchi  * For the platforms already released with a major version, they should never be
86ad55ead7SLucas De Marchi  * removed from the table. Instead new entries with newer versions may be added
87ad55ead7SLucas De Marchi  * before them, so they take precedence.
88ad55ead7SLucas De Marchi  *
89ad55ead7SLucas De Marchi  * TODO: Currently there's no fallback on major version. That's because xe
90ad55ead7SLucas De Marchi  * driver only supports the one major version of each firmware in the table.
91ad55ead7SLucas De Marchi  * This needs to be fixed when the major version of GuC is updated.
92ad55ead7SLucas De Marchi  */
93ad55ead7SLucas De Marchi 
94ad55ead7SLucas De Marchi struct uc_fw_entry {
95613256e6SLucas De Marchi 	enum xe_platform platform;
96613256e6SLucas De Marchi 	enum xe_gt_type gt_type;
97ad55ead7SLucas De Marchi 
98ad55ead7SLucas De Marchi 	struct {
99ad55ead7SLucas De Marchi 		const char *path;
100ad55ead7SLucas De Marchi 		u16 major;
1016650ad3eSJohn Harrison 		u16 minor;
102ad55ead7SLucas De Marchi 		u16 patch;
103ad55ead7SLucas De Marchi 		bool full_ver_required;
104ad55ead7SLucas De Marchi 	};
105ad55ead7SLucas De Marchi };
106ad55ead7SLucas De Marchi 
107ad55ead7SLucas De Marchi struct fw_blobs_by_type {
108ad55ead7SLucas De Marchi 	const struct uc_fw_entry *entries;
109ad55ead7SLucas De Marchi 	u32 count;
110ad55ead7SLucas De Marchi };
111613256e6SLucas De Marchi 
112613256e6SLucas De Marchi /*
113613256e6SLucas De Marchi  * Add an "ANY" define just to convey the meaning it's given here.
114613256e6SLucas De Marchi  */
115613256e6SLucas De Marchi #define XE_GT_TYPE_ANY XE_GT_TYPE_UNINITIALIZED
116ad55ead7SLucas De Marchi 
117*725648bcSJohn Harrison #define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver)					\
118*725648bcSJohn Harrison 	fw_def(PANTHERLAKE,	GT_TYPE_ANY,	major_ver(xe,	guc,	ptl,	70, 47, 0))	\
119*725648bcSJohn Harrison 	fw_def(BATTLEMAGE,	GT_TYPE_ANY,	major_ver(xe,	guc,	bmg,	70, 45, 2))	\
120*725648bcSJohn Harrison 	fw_def(LUNARLAKE,	GT_TYPE_ANY,	major_ver(xe,	guc,	lnl,	70, 45, 2))	\
121*725648bcSJohn Harrison 	fw_def(METEORLAKE,	GT_TYPE_ANY,	major_ver(i915,	guc,	mtl,	70, 44, 1))	\
122*725648bcSJohn Harrison 	fw_def(DG2,		GT_TYPE_ANY,	major_ver(i915,	guc,	dg2,	70, 45, 2))	\
123*725648bcSJohn Harrison 	fw_def(DG1,		GT_TYPE_ANY,	major_ver(i915,	guc,	dg1,	70, 44, 1))	\
124*725648bcSJohn Harrison 	fw_def(ALDERLAKE_N,	GT_TYPE_ANY,	major_ver(i915,	guc,	tgl,	70, 44, 1))	\
125*725648bcSJohn Harrison 	fw_def(ALDERLAKE_P,	GT_TYPE_ANY,	major_ver(i915,	guc,	adlp,	70, 44, 1))	\
126*725648bcSJohn Harrison 	fw_def(ALDERLAKE_S,	GT_TYPE_ANY,	major_ver(i915,	guc,	tgl,	70, 44, 1))	\
127ad55ead7SLucas De Marchi 	fw_def(ROCKETLAKE,	GT_TYPE_ANY,	major_ver(i915,	guc,	tgl,	70, 44, 1))	\
128ad55ead7SLucas De Marchi 	fw_def(TIGERLAKE,	GT_TYPE_ANY,	major_ver(i915,	guc,	tgl,	70, 44, 1))
129613256e6SLucas De Marchi 
130613256e6SLucas De Marchi #define XE_HUC_FIRMWARE_DEFS(fw_def, mmp_ver, no_ver)		\
131613256e6SLucas De Marchi 	fw_def(PANTHERLAKE,	GT_TYPE_ANY,	no_ver(xe,	huc,		ptl))		\
132613256e6SLucas De Marchi 	fw_def(BATTLEMAGE,	GT_TYPE_ANY,	no_ver(xe,	huc,		bmg))		\
133613256e6SLucas De Marchi 	fw_def(LUNARLAKE,	GT_TYPE_ANY,	no_ver(xe,	huc,		lnl))		\
134613256e6SLucas De Marchi 	fw_def(METEORLAKE,	GT_TYPE_ANY,	no_ver(i915,	huc_gsc,	mtl))		\
135613256e6SLucas De Marchi 	fw_def(DG1,		GT_TYPE_ANY,	no_ver(i915,	huc,		dg1))		\
136613256e6SLucas De Marchi 	fw_def(ALDERLAKE_P,	GT_TYPE_ANY,	no_ver(i915,	huc,		tgl))		\
137ad55ead7SLucas De Marchi 	fw_def(ALDERLAKE_S,	GT_TYPE_ANY,	no_ver(i915,	huc,		tgl))		\
1385152234eSDaniele Ceraolo Spurio 	fw_def(ROCKETLAKE,	GT_TYPE_ANY,	no_ver(i915,	huc,		tgl))		\
1395152234eSDaniele Ceraolo Spurio 	fw_def(TIGERLAKE,	GT_TYPE_ANY,	no_ver(i915,	huc,		tgl))
140613256e6SLucas De Marchi 
141613256e6SLucas De Marchi /* for the GSC FW we match the compatibility version and not the release one */
1425152234eSDaniele Ceraolo Spurio #define XE_GSC_FIRMWARE_DEFS(fw_def, major_ver)		\
143ad55ead7SLucas De Marchi 	fw_def(LUNARLAKE,	GT_TYPE_ANY,	major_ver(xe,	gsc,	lnl,	104, 1, 0))	\
144ad55ead7SLucas De Marchi 	fw_def(METEORLAKE,	GT_TYPE_ANY,	major_ver(i915,	gsc,	mtl,	102, 1, 0))
145ad55ead7SLucas De Marchi 
146ad55ead7SLucas De Marchi #define MAKE_FW_PATH(dir__, uc__, shortname__, version__)			\
147ad55ead7SLucas De Marchi 	__stringify(dir__) "/" __stringify(shortname__) "_" __stringify(uc__) version__ ".bin"
1486650ad3eSJohn Harrison 
149ad55ead7SLucas De Marchi #define fw_filename_mmp_ver(dir_, uc_, shortname_, a, b, c)			\
150ad55ead7SLucas De Marchi 	MAKE_FW_PATH(dir_, uc_, shortname_, "_" __stringify(a ## . ## b ## . ## c))
151ad55ead7SLucas De Marchi #define fw_filename_major_ver(dir_, uc_, shortname_, a, b, c)			\
152f7c2ea68SDaniele Ceraolo Spurio 	MAKE_FW_PATH(dir_, uc_, shortname_, "_" __stringify(a))
153f7c2ea68SDaniele Ceraolo Spurio #define fw_filename_no_ver(dir_, uc_, shortname_)				\
154ad55ead7SLucas De Marchi 	MAKE_FW_PATH(dir_, uc_, shortname_, "")
155ad55ead7SLucas De Marchi #define fw_filename_gsc(dir_, uc_, shortname_, a, b, c)				\
156ad55ead7SLucas De Marchi 	MAKE_FW_PATH(dir_, uc_, shortname_, "_" __stringify(b))
1576650ad3eSJohn Harrison 
1586650ad3eSJohn Harrison #define uc_fw_entry_mmp_ver(dir_, uc_, shortname_, a, b, c)			\
1596650ad3eSJohn Harrison 	{ fw_filename_mmp_ver(dir_, uc_, shortname_, a, b, c),			\
1606650ad3eSJohn Harrison 	  a, b, c, true }
161ad55ead7SLucas De Marchi #define uc_fw_entry_major_ver(dir_, uc_, shortname_, a, b, c)			\
162ad55ead7SLucas De Marchi 	{ fw_filename_major_ver(dir_, uc_, shortname_, a, b, c),		\
163ad55ead7SLucas De Marchi 	  a, b, c }
164f7c2ea68SDaniele Ceraolo Spurio #define uc_fw_entry_no_ver(dir_, uc_, shortname_)				\
165f7c2ea68SDaniele Ceraolo Spurio 	{ fw_filename_no_ver(dir_, uc_, shortname_),				\
166f7c2ea68SDaniele Ceraolo Spurio 	  0, 0 }
167ad55ead7SLucas De Marchi #define uc_fw_entry_gsc(dir_, uc_, shortname_, a, b, c)				\
168ad55ead7SLucas De Marchi 	{ fw_filename_gsc(dir_, uc_, shortname_, a, b, c),			\
169613256e6SLucas De Marchi 	  a, b, c }
170ad55ead7SLucas De Marchi 
171ad55ead7SLucas De Marchi /* All blobs need to be declared via MODULE_FIRMWARE() */
172613256e6SLucas De Marchi #define XE_UC_MODULE_FIRMWARE(platform__, gt_type__, fw_filename)		\
173ad55ead7SLucas De Marchi 	MODULE_FIRMWARE(fw_filename);
174ad55ead7SLucas De Marchi 
175613256e6SLucas De Marchi #define XE_UC_FW_ENTRY(platform__, gt_type__, entry__)				\
176ad55ead7SLucas De Marchi 	{									\
177ad55ead7SLucas De Marchi 		.platform = XE_ ## platform__,					\
178ad55ead7SLucas De Marchi 		.gt_type = XE_ ## gt_type__,					\
1793e8e7ee6SFrancois Dugast 		entry__,							\
180ad55ead7SLucas De Marchi 	},
1813e8e7ee6SFrancois Dugast 
XE_GUC_FIRMWARE_DEFS(XE_UC_MODULE_FIRMWARE,fw_filename_mmp_ver,fw_filename_major_ver)182ad55ead7SLucas De Marchi XE_GUC_FIRMWARE_DEFS(XE_UC_MODULE_FIRMWARE,
183f7c2ea68SDaniele Ceraolo Spurio 		     fw_filename_mmp_ver, fw_filename_major_ver)
184ad55ead7SLucas De Marchi XE_HUC_FIRMWARE_DEFS(XE_UC_MODULE_FIRMWARE,
185dd08ebf6SMatthew Brost 		     fw_filename_mmp_ver, fw_filename_no_ver)
186dd08ebf6SMatthew Brost XE_GSC_FIRMWARE_DEFS(XE_UC_MODULE_FIRMWARE, fw_filename_gsc)
187dd08ebf6SMatthew Brost 
1880d1caff4SDaniele Ceraolo Spurio static struct xe_gt *
189dd08ebf6SMatthew Brost __uc_fw_to_gt(struct xe_uc_fw *uc_fw, enum xe_uc_fw_type type)
1900d1caff4SDaniele Ceraolo Spurio {
1910d1caff4SDaniele Ceraolo Spurio 	XE_WARN_ON(type >= XE_UC_FW_NUM_TYPES);
1920d1caff4SDaniele Ceraolo Spurio 
1930d1caff4SDaniele Ceraolo Spurio 	switch (type) {
194dd08ebf6SMatthew Brost 	case XE_UC_FW_TYPE_GUC:
1950d1caff4SDaniele Ceraolo Spurio 		return container_of(uc_fw, struct xe_gt, uc.guc.fw);
1960d1caff4SDaniele Ceraolo Spurio 	case XE_UC_FW_TYPE_HUC:
1970d1caff4SDaniele Ceraolo Spurio 		return container_of(uc_fw, struct xe_gt, uc.huc.fw);
1980d1caff4SDaniele Ceraolo Spurio 	case XE_UC_FW_TYPE_GSC:
1990d1caff4SDaniele Ceraolo Spurio 		return container_of(uc_fw, struct xe_gt, uc.gsc.fw);
200dd08ebf6SMatthew Brost 	default:
201dd08ebf6SMatthew Brost 		return NULL;
202dd08ebf6SMatthew Brost 	}
203dd08ebf6SMatthew Brost }
204dd08ebf6SMatthew Brost 
uc_fw_to_gt(struct xe_uc_fw * uc_fw)205dd08ebf6SMatthew Brost static struct xe_gt *uc_fw_to_gt(struct xe_uc_fw *uc_fw)
206dd08ebf6SMatthew Brost {
207dd08ebf6SMatthew Brost 	return __uc_fw_to_gt(uc_fw, uc_fw->type);
208dd08ebf6SMatthew Brost }
209dd08ebf6SMatthew Brost 
uc_fw_to_xe(struct xe_uc_fw * uc_fw)210dd08ebf6SMatthew Brost static struct xe_device *uc_fw_to_xe(struct xe_uc_fw *uc_fw)
211dd08ebf6SMatthew Brost {
212dd08ebf6SMatthew Brost 	return gt_to_xe(uc_fw_to_gt(uc_fw));
213dd08ebf6SMatthew Brost }
214dd08ebf6SMatthew Brost 
215ad55ead7SLucas De Marchi static void
uc_fw_auto_select(struct xe_device * xe,struct xe_uc_fw * uc_fw)216ad55ead7SLucas De Marchi uc_fw_auto_select(struct xe_device *xe, struct xe_uc_fw *uc_fw)
217ad55ead7SLucas De Marchi {
218ad55ead7SLucas De Marchi 	static const struct uc_fw_entry entries_guc[] = {
219dd08ebf6SMatthew Brost 		XE_GUC_FIRMWARE_DEFS(XE_UC_FW_ENTRY,
220ad55ead7SLucas De Marchi 				     uc_fw_entry_mmp_ver,
221ad55ead7SLucas De Marchi 				     uc_fw_entry_major_ver)
222ad55ead7SLucas De Marchi 	};
223ad55ead7SLucas De Marchi 	static const struct uc_fw_entry entries_huc[] = {
224dd08ebf6SMatthew Brost 		XE_HUC_FIRMWARE_DEFS(XE_UC_FW_ENTRY,
2255152234eSDaniele Ceraolo Spurio 				     uc_fw_entry_mmp_ver,
226f7c2ea68SDaniele Ceraolo Spurio 				     uc_fw_entry_no_ver)
2275152234eSDaniele Ceraolo Spurio 	};
228dd08ebf6SMatthew Brost 	static const struct uc_fw_entry entries_gsc[] = {
229ad55ead7SLucas De Marchi 		XE_GSC_FIRMWARE_DEFS(XE_UC_FW_ENTRY, uc_fw_entry_gsc)
230ad55ead7SLucas De Marchi 	};
2315152234eSDaniele Ceraolo Spurio 	static const struct fw_blobs_by_type blobs_all[XE_UC_FW_NUM_TYPES] = {
232dd08ebf6SMatthew Brost 		[XE_UC_FW_TYPE_GUC] = { entries_guc, ARRAY_SIZE(entries_guc) },
233613256e6SLucas De Marchi 		[XE_UC_FW_TYPE_HUC] = { entries_huc, ARRAY_SIZE(entries_huc) },
234dd08ebf6SMatthew Brost 		[XE_UC_FW_TYPE_GSC] = { entries_gsc, ARRAY_SIZE(entries_gsc) },
23575584c82SLucas De Marchi 	};
236ad55ead7SLucas De Marchi 	struct xe_gt *gt = uc_fw_to_gt(uc_fw);
237dd08ebf6SMatthew Brost 	enum xe_platform p = xe->info.platform;
238dd08ebf6SMatthew Brost 	const struct uc_fw_entry *entries;
239613256e6SLucas De Marchi 	u32 count;
240613256e6SLucas De Marchi 	int i;
241613256e6SLucas De Marchi 
242ad55ead7SLucas De Marchi 	xe_gt_assert(gt, uc_fw->type < ARRAY_SIZE(blobs_all));
243ad55ead7SLucas De Marchi 	xe_gt_assert(gt, gt->info.type != XE_GT_TYPE_UNINITIALIZED);
244dd08ebf6SMatthew Brost 
245ad55ead7SLucas De Marchi 	entries = blobs_all[uc_fw->type].entries;
246613256e6SLucas De Marchi 	count = blobs_all[uc_fw->type].count;
247613256e6SLucas De Marchi 
248613256e6SLucas De Marchi 	for (i = 0; i < count && p <= entries[i].platform; i++) {
249613256e6SLucas De Marchi 		if (p != entries[i].platform)
250613256e6SLucas De Marchi 			continue;
251613256e6SLucas De Marchi 
252613256e6SLucas De Marchi 		if (entries[i].gt_type != XE_GT_TYPE_ANY &&
253ad55ead7SLucas De Marchi 		    entries[i].gt_type != gt->info.type)
2542e7227b4SDaniele Ceraolo Spurio 			continue;
2552e7227b4SDaniele Ceraolo Spurio 
2566650ad3eSJohn Harrison 		uc_fw->path = entries[i].path;
257ad55ead7SLucas De Marchi 		uc_fw->versions.wanted.major = entries[i].major;
2582e7227b4SDaniele Ceraolo Spurio 		uc_fw->versions.wanted.minor = entries[i].minor;
2590881cbe0SDaniele Ceraolo Spurio 		uc_fw->versions.wanted.patch = entries[i].patch;
2600881cbe0SDaniele Ceraolo Spurio 		uc_fw->full_ver_required = entries[i].full_ver_required;
2610881cbe0SDaniele Ceraolo Spurio 
2622e7227b4SDaniele Ceraolo Spurio 		if (uc_fw->type == XE_UC_FW_TYPE_GSC)
2630881cbe0SDaniele Ceraolo Spurio 			uc_fw->versions.wanted_type = XE_UC_FW_VER_COMPATIBILITY;
264dd08ebf6SMatthew Brost 		else
265dd08ebf6SMatthew Brost 			uc_fw->versions.wanted_type = XE_UC_FW_VER_RELEASE;
266dd08ebf6SMatthew Brost 
267dd08ebf6SMatthew Brost 		break;
268a455ed04SDaniele Ceraolo Spurio 	}
269a455ed04SDaniele Ceraolo Spurio }
270a455ed04SDaniele Ceraolo Spurio 
271a455ed04SDaniele Ceraolo Spurio static void
uc_fw_override(struct xe_uc_fw * uc_fw)272a455ed04SDaniele Ceraolo Spurio uc_fw_override(struct xe_uc_fw *uc_fw)
273a455ed04SDaniele Ceraolo Spurio {
274a455ed04SDaniele Ceraolo Spurio 	char *path_override = NULL;
275a455ed04SDaniele Ceraolo Spurio 
276adce1b39SBommithi Sakeena 	/* empty string disables, but it's not allowed for GuC */
277adce1b39SBommithi Sakeena 	switch (uc_fw->type) {
278a455ed04SDaniele Ceraolo Spurio 	case XE_UC_FW_TYPE_GUC:
279a455ed04SDaniele Ceraolo Spurio 		if (xe_modparam.guc_firmware_path && *xe_modparam.guc_firmware_path)
280adce1b39SBommithi Sakeena 			path_override = xe_modparam.guc_firmware_path;
281a455ed04SDaniele Ceraolo Spurio 		break;
2825152234eSDaniele Ceraolo Spurio 	case XE_UC_FW_TYPE_HUC:
2835152234eSDaniele Ceraolo Spurio 		path_override = xe_modparam.huc_firmware_path;
2845152234eSDaniele Ceraolo Spurio 		break;
285a455ed04SDaniele Ceraolo Spurio 	case XE_UC_FW_TYPE_GSC:
286a455ed04SDaniele Ceraolo Spurio 		path_override = xe_modparam.gsc_firmware_path;
287a455ed04SDaniele Ceraolo Spurio 		break;
288a455ed04SDaniele Ceraolo Spurio 	default:
289a455ed04SDaniele Ceraolo Spurio 		break;
290a455ed04SDaniele Ceraolo Spurio 	}
291a455ed04SDaniele Ceraolo Spurio 
292a455ed04SDaniele Ceraolo Spurio 	if (path_override) {
293a455ed04SDaniele Ceraolo Spurio 		uc_fw->path = path_override;
294a455ed04SDaniele Ceraolo Spurio 		uc_fw->user_overridden = true;
295dd08ebf6SMatthew Brost 	}
296dd08ebf6SMatthew Brost }
297dd08ebf6SMatthew Brost 
298dd08ebf6SMatthew Brost /**
299dd08ebf6SMatthew Brost  * xe_uc_fw_copy_rsa - copy fw RSA to buffer
300dd08ebf6SMatthew Brost  *
301dd08ebf6SMatthew Brost  * @uc_fw: uC firmware
302dd08ebf6SMatthew Brost  * @dst: dst buffer
303dd08ebf6SMatthew Brost  * @max_len: max number of bytes to copy
304dd08ebf6SMatthew Brost  *
305dd08ebf6SMatthew Brost  * Return: number of copied bytes.
306dd08ebf6SMatthew Brost  */
xe_uc_fw_copy_rsa(struct xe_uc_fw * uc_fw,void * dst,u32 max_len)307dd08ebf6SMatthew Brost size_t xe_uc_fw_copy_rsa(struct xe_uc_fw *uc_fw, void *dst, u32 max_len)
308dd08ebf6SMatthew Brost {
309c73acc1eSFrancois Dugast 	struct xe_device *xe = uc_fw_to_xe(uc_fw);
310c73acc1eSFrancois Dugast 	u32 size = min_t(u32, uc_fw->rsa_size, max_len);
311dd08ebf6SMatthew Brost 
312dd08ebf6SMatthew Brost 	xe_assert(xe, !(size % 4));
313dd08ebf6SMatthew Brost 	xe_assert(xe, xe_uc_fw_is_available(uc_fw));
314dd08ebf6SMatthew Brost 
315dd08ebf6SMatthew Brost 	xe_map_memcpy_from(xe, dst, &uc_fw->bo->vmap,
316dd08ebf6SMatthew Brost 			   xe_uc_fw_rsa_offset(uc_fw), size);
317dd08ebf6SMatthew Brost 
318dd08ebf6SMatthew Brost 	return size;
319dd08ebf6SMatthew Brost }
320dd08ebf6SMatthew Brost 
uc_fw_fini(struct drm_device * drm,void * arg)321dd08ebf6SMatthew Brost static void uc_fw_fini(struct drm_device *drm, void *arg)
322dd08ebf6SMatthew Brost {
323dd08ebf6SMatthew Brost 	struct xe_uc_fw *uc_fw = arg;
324dd08ebf6SMatthew Brost 
325dd08ebf6SMatthew Brost 	if (!xe_uc_fw_is_available(uc_fw))
326dd08ebf6SMatthew Brost 		return;
327dd08ebf6SMatthew Brost 
32843c4ff3cSDaniele Ceraolo Spurio 	xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_SELECTED);
32999c821b0SMatthew Brost }
33099c821b0SMatthew Brost 
guc_read_css_info(struct xe_uc_fw * uc_fw,struct uc_css_header * css)3312e7227b4SDaniele Ceraolo Spurio static int guc_read_css_info(struct xe_uc_fw *uc_fw, struct uc_css_header *css)
3322e7227b4SDaniele Ceraolo Spurio {
33399c821b0SMatthew Brost 	struct xe_gt *gt = uc_fw_to_gt(uc_fw);
334c73acc1eSFrancois Dugast 	struct xe_uc_fw_version *release = &uc_fw->versions.found[XE_UC_FW_VER_RELEASE];
33599c821b0SMatthew Brost 	struct xe_uc_fw_version *compatibility = &uc_fw->versions.found[XE_UC_FW_VER_COMPATIBILITY];
3364eb0aab6SJulia Filipchuk 
3374eb0aab6SJulia Filipchuk 	xe_gt_assert(gt, uc_fw->type == XE_UC_FW_TYPE_GUC);
3384eb0aab6SJulia Filipchuk 
3394eb0aab6SJulia Filipchuk 	/* We don't support GuC releases older than 70.29.2 */
34043c4ff3cSDaniele Ceraolo Spurio 	if (MAKE_GUC_VER_STRUCT(*release) < MAKE_GUC_VER(70, 29, 2)) {
34199c821b0SMatthew Brost 		xe_gt_err(gt, "Unsupported GuC v%u.%u.%u! v70.29.2 or newer is required\n",
34299c821b0SMatthew Brost 			  release->major, release->minor, release->patch);
34343c4ff3cSDaniele Ceraolo Spurio 		return -EINVAL;
34443c4ff3cSDaniele Ceraolo Spurio 	}
34543c4ff3cSDaniele Ceraolo Spurio 
34643c4ff3cSDaniele Ceraolo Spurio 	compatibility->major = FIELD_GET(CSS_SW_VERSION_UC_MAJOR, css->submission_version);
34799c821b0SMatthew Brost 	compatibility->minor = FIELD_GET(CSS_SW_VERSION_UC_MINOR, css->submission_version);
34843c4ff3cSDaniele Ceraolo Spurio 	compatibility->patch = FIELD_GET(CSS_SW_VERSION_UC_PATCH, css->submission_version);
34943c4ff3cSDaniele Ceraolo Spurio 
35099c821b0SMatthew Brost 	uc_fw->private_data_size = css->private_data_size;
35199c821b0SMatthew Brost 
3520881cbe0SDaniele Ceraolo Spurio 	return 0;
353ad55ead7SLucas De Marchi }
354ad55ead7SLucas De Marchi 
xe_uc_fw_check_version_requirements(struct xe_uc_fw * uc_fw)3552e7227b4SDaniele Ceraolo Spurio int xe_uc_fw_check_version_requirements(struct xe_uc_fw *uc_fw)
3562e7227b4SDaniele Ceraolo Spurio {
357ad55ead7SLucas De Marchi 	struct xe_device *xe = uc_fw_to_xe(uc_fw);
358ad55ead7SLucas De Marchi 	struct xe_uc_fw_version *wanted = &uc_fw->versions.wanted;
3592e7227b4SDaniele Ceraolo Spurio 	struct xe_uc_fw_version *found = &uc_fw->versions.found[uc_fw->versions.wanted_type];
360ad55ead7SLucas De Marchi 
361ad55ead7SLucas De Marchi 	/* Driver has no requirement on any version, any is good. */
362ad55ead7SLucas De Marchi 	if (!wanted->major)
363ad55ead7SLucas De Marchi 		return 0;
364ad55ead7SLucas De Marchi 
365ad55ead7SLucas De Marchi 	/*
3662e7227b4SDaniele Ceraolo Spurio 	 * If full version is required, both major and minor should match.
3676650ad3eSJohn Harrison 	 * Otherwise, at least the major version.
3686650ad3eSJohn Harrison 	 */
3696650ad3eSJohn Harrison 	if (wanted->major != found->major ||
3706650ad3eSJohn Harrison 	    (uc_fw->full_ver_required &&
371ad55ead7SLucas De Marchi 	     ((wanted->minor != found->minor) ||
3726650ad3eSJohn Harrison 	      (wanted->patch != found->patch)))) {
3736650ad3eSJohn Harrison 		drm_notice(&xe->drm, "%s firmware %s: unexpected version: %u.%u.%u != %u.%u.%u\n",
374ad55ead7SLucas De Marchi 			   xe_uc_fw_type_repr(uc_fw->type), uc_fw->path,
375ad55ead7SLucas De Marchi 			   found->major, found->minor, found->patch,
376ad55ead7SLucas De Marchi 			   wanted->major, wanted->minor, wanted->patch);
3776650ad3eSJohn Harrison 		goto fail;
3786650ad3eSJohn Harrison 	}
3796650ad3eSJohn Harrison 
380ad55ead7SLucas De Marchi 	if (wanted->minor > found->minor ||
3816650ad3eSJohn Harrison 	    (wanted->minor == found->minor && wanted->patch > found->patch)) {
3826650ad3eSJohn Harrison 		drm_notice(&xe->drm, "%s firmware (%u.%u.%u) is recommended, but only (%u.%u.%u) was found in %s\n",
383ad55ead7SLucas De Marchi 			   xe_uc_fw_type_repr(uc_fw->type),
384ad55ead7SLucas De Marchi 			   wanted->major, wanted->minor, wanted->patch,
385ad55ead7SLucas De Marchi 			   found->major, found->minor, found->patch,
386ad55ead7SLucas De Marchi 			   uc_fw->path);
387ad55ead7SLucas De Marchi 		drm_info(&xe->drm, "Consider updating your linux-firmware pkg or downloading from %s\n",
388ad55ead7SLucas De Marchi 			 XE_UC_FIRMWARE_URL);
389ad55ead7SLucas De Marchi 	}
390ad55ead7SLucas De Marchi 
391ad55ead7SLucas De Marchi 	return 0;
392ad55ead7SLucas De Marchi 
393ad55ead7SLucas De Marchi fail:
394ad55ead7SLucas De Marchi 	if (xe_uc_fw_is_overridden(uc_fw))
395ad55ead7SLucas De Marchi 		return 0;
396ad55ead7SLucas De Marchi 
397a9a95523SDaniele Ceraolo Spurio 	return -ENOEXEC;
398a9a95523SDaniele Ceraolo Spurio }
399a9a95523SDaniele Ceraolo Spurio 
400a9a95523SDaniele Ceraolo Spurio /* Refer to the "CSS-based Firmware Layout" documentation entry for details */
parse_css_header(struct xe_uc_fw * uc_fw,const void * fw_data,size_t fw_size)4012e7227b4SDaniele Ceraolo Spurio static int parse_css_header(struct xe_uc_fw *uc_fw, const void *fw_data, size_t fw_size)
402a9a95523SDaniele Ceraolo Spurio {
403a9a95523SDaniele Ceraolo Spurio 	struct xe_device *xe = uc_fw_to_xe(uc_fw);
404a9a95523SDaniele Ceraolo Spurio 	struct xe_uc_fw_version *release = &uc_fw->versions.found[XE_UC_FW_VER_RELEASE];
405a9a95523SDaniele Ceraolo Spurio 	struct uc_css_header *css;
406a9a95523SDaniele Ceraolo Spurio 	size_t size;
407a9a95523SDaniele Ceraolo Spurio 
408a9a95523SDaniele Ceraolo Spurio 	/* Check the size of the blob before examining buffer contents */
409a9a95523SDaniele Ceraolo Spurio 	if (unlikely(fw_size < sizeof(struct uc_css_header))) {
410a9a95523SDaniele Ceraolo Spurio 		drm_warn(&xe->drm, "%s firmware %s: invalid size: %zu < %zu\n",
411a9a95523SDaniele Ceraolo Spurio 			 xe_uc_fw_type_repr(uc_fw->type), uc_fw->path,
412a9a95523SDaniele Ceraolo Spurio 			 fw_size, sizeof(struct uc_css_header));
413a9a95523SDaniele Ceraolo Spurio 		return -ENODATA;
414a9a95523SDaniele Ceraolo Spurio 	}
415a9a95523SDaniele Ceraolo Spurio 
416a9a95523SDaniele Ceraolo Spurio 	css = (struct uc_css_header *)fw_data;
417a9a95523SDaniele Ceraolo Spurio 
418a9a95523SDaniele Ceraolo Spurio 	/* Check integrity of size values inside CSS header */
419a9a95523SDaniele Ceraolo Spurio 	size = (css->header_size_dw - css->key_size_dw - css->modulus_size_dw -
420a9a95523SDaniele Ceraolo Spurio 		css->exponent_size_dw) * sizeof(u32);
421a9a95523SDaniele Ceraolo Spurio 	if (unlikely(size != sizeof(struct uc_css_header))) {
422a9a95523SDaniele Ceraolo Spurio 		drm_warn(&xe->drm,
423a9a95523SDaniele Ceraolo Spurio 			 "%s firmware %s: unexpected header size: %zu != %zu\n",
424a9a95523SDaniele Ceraolo Spurio 			 xe_uc_fw_type_repr(uc_fw->type), uc_fw->path,
425a9a95523SDaniele Ceraolo Spurio 			 fw_size, sizeof(struct uc_css_header));
426a9a95523SDaniele Ceraolo Spurio 		return -EPROTO;
427a9a95523SDaniele Ceraolo Spurio 	}
428a9a95523SDaniele Ceraolo Spurio 
429a9a95523SDaniele Ceraolo Spurio 	/* uCode size must calculated from other sizes */
430a9a95523SDaniele Ceraolo Spurio 	uc_fw->ucode_size = (css->size_dw - css->header_size_dw) * sizeof(u32);
431a9a95523SDaniele Ceraolo Spurio 
432a9a95523SDaniele Ceraolo Spurio 	/* now RSA */
433a9a95523SDaniele Ceraolo Spurio 	uc_fw->rsa_size = css->key_size_dw * sizeof(u32);
434a9a95523SDaniele Ceraolo Spurio 
435a9a95523SDaniele Ceraolo Spurio 	/* At least, it should have header, uCode and RSA. Size of all three. */
436a9a95523SDaniele Ceraolo Spurio 	size = sizeof(struct uc_css_header) + uc_fw->ucode_size +
437a9a95523SDaniele Ceraolo Spurio 		uc_fw->rsa_size;
438a9a95523SDaniele Ceraolo Spurio 	if (unlikely(fw_size < size)) {
439a9a95523SDaniele Ceraolo Spurio 		drm_warn(&xe->drm, "%s firmware %s: invalid size: %zu < %zu\n",
440a9a95523SDaniele Ceraolo Spurio 			 xe_uc_fw_type_repr(uc_fw->type), uc_fw->path,
441a9a95523SDaniele Ceraolo Spurio 			 fw_size, size);
442a9a95523SDaniele Ceraolo Spurio 		return -ENOEXEC;
4432e7227b4SDaniele Ceraolo Spurio 	}
4442e7227b4SDaniele Ceraolo Spurio 
4452e7227b4SDaniele Ceraolo Spurio 	/* Get version numbers from the CSS header */
446a9a95523SDaniele Ceraolo Spurio 	release->major = FIELD_GET(CSS_SW_VERSION_UC_MAJOR, css->sw_version);
447a9a95523SDaniele Ceraolo Spurio 	release->minor = FIELD_GET(CSS_SW_VERSION_UC_MINOR, css->sw_version);
44843c4ff3cSDaniele Ceraolo Spurio 	release->patch = FIELD_GET(CSS_SW_VERSION_UC_PATCH, css->sw_version);
449a9a95523SDaniele Ceraolo Spurio 
450a9a95523SDaniele Ceraolo Spurio 	if (uc_fw->type == XE_UC_FW_TYPE_GUC)
451a9a95523SDaniele Ceraolo Spurio 		return guc_read_css_info(uc_fw, css);
452a9a95523SDaniele Ceraolo Spurio 
453484ecffaSDaniele Ceraolo Spurio 	return 0;
454484ecffaSDaniele Ceraolo Spurio }
455484ecffaSDaniele Ceraolo Spurio 
is_cpd_header(const void * data)456484ecffaSDaniele Ceraolo Spurio static bool is_cpd_header(const void *data)
457484ecffaSDaniele Ceraolo Spurio {
458484ecffaSDaniele Ceraolo Spurio 	const u32 *marker = data;
459484ecffaSDaniele Ceraolo Spurio 
460484ecffaSDaniele Ceraolo Spurio 	return *marker == GSC_CPD_HEADER_MARKER;
461484ecffaSDaniele Ceraolo Spurio }
462484ecffaSDaniele Ceraolo Spurio 
entry_offset(const struct gsc_cpd_header_v2 * header,const char * name)463484ecffaSDaniele Ceraolo Spurio static u32 entry_offset(const struct gsc_cpd_header_v2 *header, const char *name)
464484ecffaSDaniele Ceraolo Spurio {
465484ecffaSDaniele Ceraolo Spurio 	const struct gsc_cpd_entry *entry;
466484ecffaSDaniele Ceraolo Spurio 	int i;
467484ecffaSDaniele Ceraolo Spurio 
468484ecffaSDaniele Ceraolo Spurio 	entry = (void *)header + header->header_length;
469484ecffaSDaniele Ceraolo Spurio 
470484ecffaSDaniele Ceraolo Spurio 	for (i = 0; i < header->num_of_entries; i++, entry++)
471484ecffaSDaniele Ceraolo Spurio 		if (strcmp(entry->name, name) == 0)
472484ecffaSDaniele Ceraolo Spurio 			return entry->offset & GSC_CPD_ENTRY_OFFSET_MASK;
473484ecffaSDaniele Ceraolo Spurio 
474484ecffaSDaniele Ceraolo Spurio 	return 0;
475484ecffaSDaniele Ceraolo Spurio }
476484ecffaSDaniele Ceraolo Spurio 
477484ecffaSDaniele Ceraolo Spurio /* Refer to the "GSC-based Firmware Layout" documentation entry for details */
parse_cpd_header(struct xe_uc_fw * uc_fw,const void * data,size_t size,const char * manifest_entry,const char * css_entry)478484ecffaSDaniele Ceraolo Spurio static int parse_cpd_header(struct xe_uc_fw *uc_fw, const void *data, size_t size,
479484ecffaSDaniele Ceraolo Spurio 			    const char *manifest_entry, const char *css_entry)
480484ecffaSDaniele Ceraolo Spurio {
4812e7227b4SDaniele Ceraolo Spurio 	struct xe_gt *gt = uc_fw_to_gt(uc_fw);
482484ecffaSDaniele Ceraolo Spurio 	struct xe_device *xe = gt_to_xe(gt);
483484ecffaSDaniele Ceraolo Spurio 	const struct gsc_cpd_header_v2 *header = data;
484484ecffaSDaniele Ceraolo Spurio 	struct xe_uc_fw_version *release = &uc_fw->versions.found[XE_UC_FW_VER_RELEASE];
485484ecffaSDaniele Ceraolo Spurio 	const struct gsc_manifest_header *manifest;
486484ecffaSDaniele Ceraolo Spurio 	size_t min_size = sizeof(*header);
487484ecffaSDaniele Ceraolo Spurio 	u32 offset;
488484ecffaSDaniele Ceraolo Spurio 
489484ecffaSDaniele Ceraolo Spurio 	/* manifest_entry is mandatory, css_entry is optional */
490484ecffaSDaniele Ceraolo Spurio 	xe_assert(xe, manifest_entry);
491484ecffaSDaniele Ceraolo Spurio 
492484ecffaSDaniele Ceraolo Spurio 	if (size < min_size || !is_cpd_header(header))
493484ecffaSDaniele Ceraolo Spurio 		return -ENOENT;
494484ecffaSDaniele Ceraolo Spurio 
495484ecffaSDaniele Ceraolo Spurio 	if (header->header_length < sizeof(struct gsc_cpd_header_v2)) {
496484ecffaSDaniele Ceraolo Spurio 		xe_gt_err(gt, "invalid CPD header length %u!\n", header->header_length);
497484ecffaSDaniele Ceraolo Spurio 		return -EINVAL;
498484ecffaSDaniele Ceraolo Spurio 	}
499484ecffaSDaniele Ceraolo Spurio 
500484ecffaSDaniele Ceraolo Spurio 	min_size = header->header_length + sizeof(struct gsc_cpd_entry) * header->num_of_entries;
501484ecffaSDaniele Ceraolo Spurio 	if (size < min_size) {
502484ecffaSDaniele Ceraolo Spurio 		xe_gt_err(gt, "FW too small! %zu < %zu\n", size, min_size);
503484ecffaSDaniele Ceraolo Spurio 		return -ENODATA;
504484ecffaSDaniele Ceraolo Spurio 	}
505484ecffaSDaniele Ceraolo Spurio 
506484ecffaSDaniele Ceraolo Spurio 	/* Look for the manifest first */
507484ecffaSDaniele Ceraolo Spurio 	offset = entry_offset(header, manifest_entry);
508484ecffaSDaniele Ceraolo Spurio 	if (!offset) {
509484ecffaSDaniele Ceraolo Spurio 		xe_gt_err(gt, "Failed to find %s manifest!\n",
510484ecffaSDaniele Ceraolo Spurio 			  xe_uc_fw_type_repr(uc_fw->type));
511484ecffaSDaniele Ceraolo Spurio 		return -ENODATA;
512484ecffaSDaniele Ceraolo Spurio 	}
513484ecffaSDaniele Ceraolo Spurio 
514484ecffaSDaniele Ceraolo Spurio 	min_size = offset + sizeof(struct gsc_manifest_header);
515484ecffaSDaniele Ceraolo Spurio 	if (size < min_size) {
516484ecffaSDaniele Ceraolo Spurio 		xe_gt_err(gt, "FW too small! %zu < %zu\n", size, min_size);
517484ecffaSDaniele Ceraolo Spurio 		return -ENODATA;
518484ecffaSDaniele Ceraolo Spurio 	}
5192e7227b4SDaniele Ceraolo Spurio 
5202e7227b4SDaniele Ceraolo Spurio 	manifest = data + offset;
5212e7227b4SDaniele Ceraolo Spurio 
522484ecffaSDaniele Ceraolo Spurio 	release->major = manifest->fw_version.major;
523985d5a49SDaniele Ceraolo Spurio 	release->minor = manifest->fw_version.minor;
524985d5a49SDaniele Ceraolo Spurio 	release->patch = manifest->fw_version.hotfix;
525985d5a49SDaniele Ceraolo Spurio 
526985d5a49SDaniele Ceraolo Spurio 	if (uc_fw->type == XE_UC_FW_TYPE_GSC) {
527985d5a49SDaniele Ceraolo Spurio 		struct xe_gsc *gsc = container_of(uc_fw, struct xe_gsc, fw);
528985d5a49SDaniele Ceraolo Spurio 
529985d5a49SDaniele Ceraolo Spurio 		release->build = manifest->fw_version.build;
530484ecffaSDaniele Ceraolo Spurio 		gsc->security_version = manifest->security_version;
531484ecffaSDaniele Ceraolo Spurio 	}
532484ecffaSDaniele Ceraolo Spurio 
533484ecffaSDaniele Ceraolo Spurio 	/* then optionally look for the css header */
534484ecffaSDaniele Ceraolo Spurio 	if (css_entry) {
535484ecffaSDaniele Ceraolo Spurio 		int ret;
536484ecffaSDaniele Ceraolo Spurio 
537484ecffaSDaniele Ceraolo Spurio 		/*
538484ecffaSDaniele Ceraolo Spurio 		 * This section does not contain a CSS entry on DG2. We
539484ecffaSDaniele Ceraolo Spurio 		 * don't support DG2 HuC right now, so no need to handle
540484ecffaSDaniele Ceraolo Spurio 		 * it, just add a reminder in case that changes.
541484ecffaSDaniele Ceraolo Spurio 		 */
542484ecffaSDaniele Ceraolo Spurio 		xe_assert(xe, xe->info.platform != XE_DG2);
543484ecffaSDaniele Ceraolo Spurio 
544484ecffaSDaniele Ceraolo Spurio 		offset = entry_offset(header, css_entry);
545484ecffaSDaniele Ceraolo Spurio 
546484ecffaSDaniele Ceraolo Spurio 		/* the CSS header parser will check that the CSS header fits */
547484ecffaSDaniele Ceraolo Spurio 		if (offset > size) {
548484ecffaSDaniele Ceraolo Spurio 			xe_gt_err(gt, "FW too small! %zu < %u\n", size, offset);
549484ecffaSDaniele Ceraolo Spurio 			return -ENODATA;
550484ecffaSDaniele Ceraolo Spurio 		}
551484ecffaSDaniele Ceraolo Spurio 
552484ecffaSDaniele Ceraolo Spurio 		ret = parse_css_header(uc_fw, data + offset, size - offset);
553484ecffaSDaniele Ceraolo Spurio 		if (ret)
554484ecffaSDaniele Ceraolo Spurio 			return ret;
555484ecffaSDaniele Ceraolo Spurio 
556d8b15713SDaniele Ceraolo Spurio 		uc_fw->css_offset = offset;
557d8b15713SDaniele Ceraolo Spurio 	}
558484ecffaSDaniele Ceraolo Spurio 
559484ecffaSDaniele Ceraolo Spurio 	uc_fw->has_gsc_headers = true;
560484ecffaSDaniele Ceraolo Spurio 
561985d5a49SDaniele Ceraolo Spurio 	return 0;
562985d5a49SDaniele Ceraolo Spurio }
563985d5a49SDaniele Ceraolo Spurio 
parse_gsc_layout(struct xe_uc_fw * uc_fw,const void * data,size_t size)564985d5a49SDaniele Ceraolo Spurio static int parse_gsc_layout(struct xe_uc_fw *uc_fw, const void *data, size_t size)
565985d5a49SDaniele Ceraolo Spurio {
566985d5a49SDaniele Ceraolo Spurio 	struct xe_gt *gt = uc_fw_to_gt(uc_fw);
567985d5a49SDaniele Ceraolo Spurio 	const struct gsc_layout_pointers *layout = data;
568985d5a49SDaniele Ceraolo Spurio 	const struct gsc_bpdt_header *bpdt_header = NULL;
569985d5a49SDaniele Ceraolo Spurio 	const struct gsc_bpdt_entry *bpdt_entry = NULL;
570985d5a49SDaniele Ceraolo Spurio 	size_t min_size = sizeof(*layout);
571985d5a49SDaniele Ceraolo Spurio 	int i;
572985d5a49SDaniele Ceraolo Spurio 
573985d5a49SDaniele Ceraolo Spurio 	if (size < min_size) {
574985d5a49SDaniele Ceraolo Spurio 		xe_gt_err(gt, "GSC FW too small! %zu < %zu\n", size, min_size);
575985d5a49SDaniele Ceraolo Spurio 		return -ENODATA;
576985d5a49SDaniele Ceraolo Spurio 	}
577985d5a49SDaniele Ceraolo Spurio 
578985d5a49SDaniele Ceraolo Spurio 	min_size = layout->boot1.offset + layout->boot1.size;
579985d5a49SDaniele Ceraolo Spurio 	if (size < min_size) {
580985d5a49SDaniele Ceraolo Spurio 		xe_gt_err(gt, "GSC FW too small for boot section! %zu < %zu\n",
581985d5a49SDaniele Ceraolo Spurio 			  size, min_size);
582985d5a49SDaniele Ceraolo Spurio 		return -ENODATA;
583985d5a49SDaniele Ceraolo Spurio 	}
584985d5a49SDaniele Ceraolo Spurio 
585985d5a49SDaniele Ceraolo Spurio 	min_size = sizeof(*bpdt_header);
586985d5a49SDaniele Ceraolo Spurio 	if (layout->boot1.size < min_size) {
587985d5a49SDaniele Ceraolo Spurio 		xe_gt_err(gt, "GSC FW boot section too small for BPDT header: %u < %zu\n",
588985d5a49SDaniele Ceraolo Spurio 			  layout->boot1.size, min_size);
589985d5a49SDaniele Ceraolo Spurio 		return -ENODATA;
590985d5a49SDaniele Ceraolo Spurio 	}
591985d5a49SDaniele Ceraolo Spurio 
592985d5a49SDaniele Ceraolo Spurio 	bpdt_header = data + layout->boot1.offset;
593985d5a49SDaniele Ceraolo Spurio 	if (bpdt_header->signature != GSC_BPDT_HEADER_SIGNATURE) {
594985d5a49SDaniele Ceraolo Spurio 		xe_gt_err(gt, "invalid signature for BPDT header: 0x%08x!\n",
595985d5a49SDaniele Ceraolo Spurio 			  bpdt_header->signature);
596985d5a49SDaniele Ceraolo Spurio 		return -EINVAL;
597985d5a49SDaniele Ceraolo Spurio 	}
598985d5a49SDaniele Ceraolo Spurio 
599985d5a49SDaniele Ceraolo Spurio 	min_size += sizeof(*bpdt_entry) * bpdt_header->descriptor_count;
600985d5a49SDaniele Ceraolo Spurio 	if (layout->boot1.size < min_size) {
601985d5a49SDaniele Ceraolo Spurio 		xe_gt_err(gt, "GSC FW boot section too small for BPDT entries: %u < %zu\n",
602985d5a49SDaniele Ceraolo Spurio 			  layout->boot1.size, min_size);
603985d5a49SDaniele Ceraolo Spurio 		return -ENODATA;
604985d5a49SDaniele Ceraolo Spurio 	}
605985d5a49SDaniele Ceraolo Spurio 
606985d5a49SDaniele Ceraolo Spurio 	bpdt_entry = (void *)bpdt_header + sizeof(*bpdt_header);
607985d5a49SDaniele Ceraolo Spurio 	for (i = 0; i < bpdt_header->descriptor_count; i++, bpdt_entry++) {
608985d5a49SDaniele Ceraolo Spurio 		if ((bpdt_entry->type & GSC_BPDT_ENTRY_TYPE_MASK) !=
609985d5a49SDaniele Ceraolo Spurio 		    GSC_BPDT_ENTRY_TYPE_GSC_RBE)
610985d5a49SDaniele Ceraolo Spurio 			continue;
611985d5a49SDaniele Ceraolo Spurio 
612985d5a49SDaniele Ceraolo Spurio 		min_size = bpdt_entry->sub_partition_offset;
613985d5a49SDaniele Ceraolo Spurio 
614985d5a49SDaniele Ceraolo Spurio 		/* the CPD header parser will check that the CPD header fits */
615985d5a49SDaniele Ceraolo Spurio 		if (layout->boot1.size < min_size) {
616985d5a49SDaniele Ceraolo Spurio 			xe_gt_err(gt, "GSC FW boot section too small for CPD offset: %u < %zu\n",
617985d5a49SDaniele Ceraolo Spurio 				  layout->boot1.size, min_size);
618985d5a49SDaniele Ceraolo Spurio 			return -ENODATA;
619985d5a49SDaniele Ceraolo Spurio 		}
620985d5a49SDaniele Ceraolo Spurio 
621985d5a49SDaniele Ceraolo Spurio 		return parse_cpd_header(uc_fw,
622985d5a49SDaniele Ceraolo Spurio 					(void *)bpdt_header + min_size,
623985d5a49SDaniele Ceraolo Spurio 					layout->boot1.size - min_size,
624985d5a49SDaniele Ceraolo Spurio 					"RBEP.man", NULL);
625985d5a49SDaniele Ceraolo Spurio 	}
626985d5a49SDaniele Ceraolo Spurio 
627985d5a49SDaniele Ceraolo Spurio 	xe_gt_err(gt, "couldn't find CPD header in GSC binary!\n");
628a9a95523SDaniele Ceraolo Spurio 	return -ENODATA;
629a9a95523SDaniele Ceraolo Spurio }
630484ecffaSDaniele Ceraolo Spurio 
parse_headers(struct xe_uc_fw * uc_fw,const struct firmware * fw)631484ecffaSDaniele Ceraolo Spurio static int parse_headers(struct xe_uc_fw *uc_fw, const struct firmware *fw)
632484ecffaSDaniele Ceraolo Spurio {
633484ecffaSDaniele Ceraolo Spurio 	int ret;
634484ecffaSDaniele Ceraolo Spurio 
635484ecffaSDaniele Ceraolo Spurio 	/*
636484ecffaSDaniele Ceraolo Spurio 	 * All GuC releases and older HuC ones use CSS headers, while newer HuC
637985d5a49SDaniele Ceraolo Spurio 	 * releases use GSC CPD headers.
638985d5a49SDaniele Ceraolo Spurio 	 */
639484ecffaSDaniele Ceraolo Spurio 	switch (uc_fw->type) {
640484ecffaSDaniele Ceraolo Spurio 	case XE_UC_FW_TYPE_GSC:
641484ecffaSDaniele Ceraolo Spurio 		return parse_gsc_layout(uc_fw, fw->data, fw->size);
642484ecffaSDaniele Ceraolo Spurio 	case XE_UC_FW_TYPE_HUC:
643484ecffaSDaniele Ceraolo Spurio 		ret = parse_cpd_header(uc_fw, fw->data, fw->size, "HUCP.man", "huc_fw");
644484ecffaSDaniele Ceraolo Spurio 		if (!ret || ret != -ENOENT)
645a9a95523SDaniele Ceraolo Spurio 			return ret;
646484ecffaSDaniele Ceraolo Spurio 		fallthrough;
647484ecffaSDaniele Ceraolo Spurio 	case XE_UC_FW_TYPE_GUC:
648484ecffaSDaniele Ceraolo Spurio 		return parse_css_header(uc_fw, fw->data, fw->size);
649484ecffaSDaniele Ceraolo Spurio 	default:
650484ecffaSDaniele Ceraolo Spurio 		return -EINVAL;
651a9a95523SDaniele Ceraolo Spurio 	}
652a9a95523SDaniele Ceraolo Spurio 
6532e7227b4SDaniele Ceraolo Spurio 	return 0;
6542e7227b4SDaniele Ceraolo Spurio }
6552e7227b4SDaniele Ceraolo Spurio 
6562e7227b4SDaniele Ceraolo Spurio #define print_uc_fw_version(p_, version_, prefix_, ...) \
6572e7227b4SDaniele Ceraolo Spurio do { \
6582e7227b4SDaniele Ceraolo Spurio 	struct xe_uc_fw_version *ver_ = (version_); \
6592e7227b4SDaniele Ceraolo Spurio 	if (ver_->build) \
6602e7227b4SDaniele Ceraolo Spurio 		drm_printf(p_, prefix_ " version %u.%u.%u.%u\n", ##__VA_ARGS__, \
6612e7227b4SDaniele Ceraolo Spurio 			   ver_->major, ver_->minor, \
6622e7227b4SDaniele Ceraolo Spurio 			   ver_->patch, ver_->build); \
6632e7227b4SDaniele Ceraolo Spurio 	else \
6642e7227b4SDaniele Ceraolo Spurio 		drm_printf(p_, prefix_ " version %u.%u.%u\n", ##__VA_ARGS__, \
665c93ea051SMichał Winiarski 			  ver_->major, ver_->minor, ver_->patch); \
666dd08ebf6SMatthew Brost } while (0)
667dd08ebf6SMatthew Brost 
uc_fw_vf_override(struct xe_uc_fw * uc_fw)668dd08ebf6SMatthew Brost static void uc_fw_vf_override(struct xe_uc_fw *uc_fw)
6692e7227b4SDaniele Ceraolo Spurio {
670dd08ebf6SMatthew Brost 	struct xe_uc_fw_version *compat = &uc_fw->versions.found[XE_UC_FW_VER_COMPATIBILITY];
671dd08ebf6SMatthew Brost 	struct xe_uc_fw_version *wanted = &uc_fw->versions.wanted;
672dd08ebf6SMatthew Brost 
673dd08ebf6SMatthew Brost 	/* Only GuC/HuC are supported */
674dd08ebf6SMatthew Brost 	if (uc_fw->type != XE_UC_FW_TYPE_GUC && uc_fw->type != XE_UC_FW_TYPE_HUC)
675dd08ebf6SMatthew Brost 		uc_fw->path = NULL;
676dd08ebf6SMatthew Brost 
677dd08ebf6SMatthew Brost 	/* VF will support only firmwares that driver can autoselect */
678c73acc1eSFrancois Dugast 	xe_uc_fw_change_status(uc_fw, uc_fw->path ?
679c73acc1eSFrancois Dugast 			       XE_UC_FIRMWARE_PRELOADED :
680dd08ebf6SMatthew Brost 			       XE_UC_FIRMWARE_NOT_SUPPORTED);
681dd08ebf6SMatthew Brost 
68266cb3ca9SMichal Wajdeczko 	if (!xe_uc_fw_is_supported(uc_fw))
68366cb3ca9SMichal Wajdeczko 		return;
68405e49e0cSMichal Wajdeczko 
68505e49e0cSMichal Wajdeczko 	/* PF is doing the loading, so we don't need a path on the VF */
68605e49e0cSMichal Wajdeczko 	uc_fw->path = "Loaded by PF";
68705e49e0cSMichal Wajdeczko 
68866cb3ca9SMichal Wajdeczko 	/* The GuC versions are set up during the VF bootstrap */
68966cb3ca9SMichal Wajdeczko 	if (uc_fw->type == XE_UC_FW_TYPE_GUC) {
69066cb3ca9SMichal Wajdeczko 		uc_fw->versions.wanted_type = XE_UC_FW_VER_COMPATIBILITY;
69166cb3ca9SMichal Wajdeczko 		xe_gt_sriov_vf_guc_versions(uc_fw_to_gt(uc_fw), wanted, compat);
69266cb3ca9SMichal Wajdeczko 	}
69366cb3ca9SMichal Wajdeczko }
69466cb3ca9SMichal Wajdeczko 
uc_fw_request(struct xe_uc_fw * uc_fw,const struct firmware ** firmware_p)69545883418SLucas De Marchi static int uc_fw_request(struct xe_uc_fw *uc_fw, const struct firmware **firmware_p)
69666cb3ca9SMichal Wajdeczko {
69775730847SDaniele Ceraolo Spurio 	struct xe_device *xe = uc_fw_to_xe(uc_fw);
698dd08ebf6SMatthew Brost 	struct xe_gt *gt = uc_fw_to_gt(uc_fw);
699dd08ebf6SMatthew Brost 	struct drm_printer p = xe_gt_info_printer(gt);
700dd08ebf6SMatthew Brost 	struct device *dev = xe->drm.dev;
7016badfc46SLucas De Marchi 	const struct firmware *fw = NULL;
7026badfc46SLucas De Marchi 	int err;
7036badfc46SLucas De Marchi 
7046badfc46SLucas De Marchi 	/*
7056badfc46SLucas De Marchi 	 * we use FIRMWARE_UNINITIALIZED to detect checks against uc_fw->status
70675730847SDaniele Ceraolo Spurio 	 * before we're looked at the HW caps to see if we have uc support
7076badfc46SLucas De Marchi 	 */
70875730847SDaniele Ceraolo Spurio 	BUILD_BUG_ON(XE_UC_FIRMWARE_UNINITIALIZED);
709a455ed04SDaniele Ceraolo Spurio 	xe_gt_assert(gt, !uc_fw->status);
710a455ed04SDaniele Ceraolo Spurio 	xe_gt_assert(gt, !uc_fw->path);
711dd08ebf6SMatthew Brost 
71275730847SDaniele Ceraolo Spurio 	uc_fw_auto_select(xe, uc_fw);
71375730847SDaniele Ceraolo Spurio 
714dd08ebf6SMatthew Brost 	if (IS_SRIOV_VF(xe)) {
71575730847SDaniele Ceraolo Spurio 		uc_fw_vf_override(uc_fw);
716dd08ebf6SMatthew Brost 		return 0;
717dd08ebf6SMatthew Brost 	}
718dd08ebf6SMatthew Brost 
719dd08ebf6SMatthew Brost 	uc_fw_override(uc_fw);
720a9a95523SDaniele Ceraolo Spurio 
721a9a95523SDaniele Ceraolo Spurio 	xe_uc_fw_change_status(uc_fw, uc_fw->path ?
722dd08ebf6SMatthew Brost 			       XE_UC_FIRMWARE_SELECTED :
723dd08ebf6SMatthew Brost 			       XE_UC_FIRMWARE_NOT_SUPPORTED);
7242e7227b4SDaniele Ceraolo Spurio 
7252e7227b4SDaniele Ceraolo Spurio 	if (!xe_uc_fw_is_supported(uc_fw)) {
7262e7227b4SDaniele Ceraolo Spurio 		if (uc_fw->type == XE_UC_FW_TYPE_GUC) {
7272e7227b4SDaniele Ceraolo Spurio 			xe_gt_err(gt, "No GuC firmware defined for platform\n");
72861e72e77SLucas De Marchi 			return -ENOENT;
7290881cbe0SDaniele Ceraolo Spurio 		}
7300881cbe0SDaniele Ceraolo Spurio 		return 0;
7310881cbe0SDaniele Ceraolo Spurio 	}
732ad55ead7SLucas De Marchi 
733dd08ebf6SMatthew Brost 	/* an empty path means the firmware is disabled */
7340881cbe0SDaniele Ceraolo Spurio 	if (!xe_device_uc_enabled(xe) || !(*uc_fw->path)) {
735dd08ebf6SMatthew Brost 		xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_DISABLED);
736c93ea051SMichał Winiarski 		xe_gt_dbg(gt, "%s disabled\n", xe_uc_fw_type_repr(uc_fw->type));
737dd08ebf6SMatthew Brost 		return 0;
738dd08ebf6SMatthew Brost 	}
739dd08ebf6SMatthew Brost 
740dd08ebf6SMatthew Brost 	err = request_firmware(&fw, uc_fw->path, dev);
741dd08ebf6SMatthew Brost 	if (err)
742dd08ebf6SMatthew Brost 		goto fail;
743dd08ebf6SMatthew Brost 
744dd08ebf6SMatthew Brost 	err = parse_headers(uc_fw, fw);
745dd08ebf6SMatthew Brost 	if (err)
746dd08ebf6SMatthew Brost 		goto fail;
747dd08ebf6SMatthew Brost 
748dd08ebf6SMatthew Brost 	print_uc_fw_version(&p,
749dd08ebf6SMatthew Brost 			    &uc_fw->versions.found[XE_UC_FW_VER_RELEASE],
750dd08ebf6SMatthew Brost 			    "Using %s firmware from %s",
7510e1a47fcSMichał Winiarski 			    xe_uc_fw_type_repr(uc_fw->type), uc_fw->path);
752dd08ebf6SMatthew Brost 
753dd08ebf6SMatthew Brost 	/* for GSC FW we want the compatibility version, which we query after load */
754dd08ebf6SMatthew Brost 	if (uc_fw->type != XE_UC_FW_TYPE_GSC) {
755c93ea051SMichał Winiarski 		err = xe_uc_fw_check_version_requirements(uc_fw);
756c93ea051SMichał Winiarski 		if (err)
757c93ea051SMichał Winiarski 			goto fail;
758c93ea051SMichał Winiarski 	}
759c93ea051SMichał Winiarski 
760c93ea051SMichał Winiarski 	*firmware_p = fw;
761c93ea051SMichał Winiarski 
762c93ea051SMichał Winiarski 	return 0;
763c93ea051SMichał Winiarski 
764c93ea051SMichał Winiarski fail:
765c93ea051SMichał Winiarski 	xe_uc_fw_change_status(uc_fw, err == -ENOENT ?
766c93ea051SMichał Winiarski 			       XE_UC_FIRMWARE_MISSING :
767c93ea051SMichał Winiarski 			       XE_UC_FIRMWARE_ERROR);
768c93ea051SMichał Winiarski 
769c93ea051SMichał Winiarski 	xe_gt_notice(gt, "%s firmware %s: fetch failed with error %pe\n",
770c93ea051SMichał Winiarski 		     xe_uc_fw_type_repr(uc_fw->type), uc_fw->path, ERR_PTR(err));
771c93ea051SMichał Winiarski 	xe_gt_info(gt, "%s firmware(s) can be downloaded from %s\n",
772c93ea051SMichał Winiarski 		   xe_uc_fw_type_repr(uc_fw->type), XE_UC_FIRMWARE_URL);
773c93ea051SMichał Winiarski 
774c93ea051SMichał Winiarski 	release_firmware(fw);		/* OK even if fw is NULL */
775c93ea051SMichał Winiarski 
776c93ea051SMichał Winiarski 	return err;
777c93ea051SMichał Winiarski }
778c93ea051SMichał Winiarski 
uc_fw_release(const struct firmware * fw)779c93ea051SMichał Winiarski static void uc_fw_release(const struct firmware *fw)
780c93ea051SMichał Winiarski {
781c93ea051SMichał Winiarski 	release_firmware(fw);
782c93ea051SMichał Winiarski }
783c93ea051SMichał Winiarski 
uc_fw_copy(struct xe_uc_fw * uc_fw,const void * data,size_t size,u32 flags)784c93ea051SMichał Winiarski static int uc_fw_copy(struct xe_uc_fw *uc_fw, const void *data, size_t size, u32 flags)
785c93ea051SMichał Winiarski {
786c93ea051SMichał Winiarski 	struct xe_device *xe = uc_fw_to_xe(uc_fw);
787c93ea051SMichał Winiarski 	struct xe_gt *gt = uc_fw_to_gt(uc_fw);
788c93ea051SMichał Winiarski 	struct xe_tile *tile = gt_to_tile(gt);
789c93ea051SMichał Winiarski 	struct xe_bo *obj;
790c93ea051SMichał Winiarski 	int err;
791c93ea051SMichał Winiarski 
792c93ea051SMichał Winiarski 	obj = xe_managed_bo_create_from_data(xe, tile, data, size, flags);
793c93ea051SMichał Winiarski 	if (IS_ERR(obj)) {
794c93ea051SMichał Winiarski 		drm_notice(&xe->drm, "%s firmware %s: failed to create / populate bo",
795c93ea051SMichał Winiarski 			   xe_uc_fw_type_repr(uc_fw->type), uc_fw->path);
796c93ea051SMichał Winiarski 		err = PTR_ERR(obj);
797c93ea051SMichał Winiarski 		goto fail;
798c93ea051SMichał Winiarski 	}
799c93ea051SMichał Winiarski 
800c93ea051SMichał Winiarski 	uc_fw->bo = obj;
801c93ea051SMichał Winiarski 	uc_fw->size = size;
802c93ea051SMichał Winiarski 
803c93ea051SMichał Winiarski 	xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_AVAILABLE);
804c93ea051SMichał Winiarski 
805c93ea051SMichał Winiarski 	err = drmm_add_action_or_reset(&xe->drm, uc_fw_fini, uc_fw);
806c93ea051SMichał Winiarski 	if (err)
807c93ea051SMichał Winiarski 		goto fail;
808c93ea051SMichał Winiarski 
80962742d12SLucas De Marchi 	return 0;
81062742d12SLucas De Marchi 
811c93ea051SMichał Winiarski fail:
812c93ea051SMichał Winiarski 	xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_ERROR);
813c93ea051SMichał Winiarski 	drm_notice(&xe->drm, "%s firmware %s: copy failed with error %d\n",
814c93ea051SMichał Winiarski 		   xe_uc_fw_type_repr(uc_fw->type), uc_fw->path, err);
815c93ea051SMichał Winiarski 
81691b2c42cSFrancois Dugast 	return err;
817c93ea051SMichał Winiarski }
818dd08ebf6SMatthew Brost 
xe_uc_fw_init(struct xe_uc_fw * uc_fw)819dd08ebf6SMatthew Brost int xe_uc_fw_init(struct xe_uc_fw *uc_fw)
820dd08ebf6SMatthew Brost {
821dd08ebf6SMatthew Brost 	const struct firmware *fw = NULL;
822dd08ebf6SMatthew Brost 	int err;
823dd08ebf6SMatthew Brost 
824dd08ebf6SMatthew Brost 	err = uc_fw_request(uc_fw, &fw);
825dd08ebf6SMatthew Brost 	if (err)
826dd08ebf6SMatthew Brost 		return err;
8272dd21a9bSMatt Roper 
8284c15a6dcSDaniele Ceraolo Spurio 	/* no error and no firmware means nothing to copy */
8294c15a6dcSDaniele Ceraolo Spurio 	if (!fw)
830dd08ebf6SMatthew Brost 		return 0;
831dd08ebf6SMatthew Brost 
832dd08ebf6SMatthew Brost 	err = uc_fw_copy(uc_fw, fw->data, fw->size,
833dd08ebf6SMatthew Brost 			 XE_BO_FLAG_SYSTEM | XE_BO_FLAG_GGTT |
834dd08ebf6SMatthew Brost 			 XE_BO_FLAG_GGTT_INVALIDATE);
835484ecffaSDaniele Ceraolo Spurio 
8362dd21a9bSMatt Roper 	uc_fw_release(fw);
8372dd21a9bSMatt Roper 
838473b6276SFei Yang 	return err;
839dd08ebf6SMatthew Brost }
840dd08ebf6SMatthew Brost ALLOW_ERROR_INJECTION(xe_uc_fw_init, ERRNO); /* See xe_pci_probe() */
8412dd21a9bSMatt Roper 
uc_fw_ggtt_offset(struct xe_uc_fw * uc_fw)8422dd21a9bSMatt Roper static u32 uc_fw_ggtt_offset(struct xe_uc_fw *uc_fw)
843dd08ebf6SMatthew Brost {
844dd08ebf6SMatthew Brost 	return xe_bo_ggtt_addr(uc_fw->bo);
845dd08ebf6SMatthew Brost }
846dd08ebf6SMatthew Brost 
uc_fw_xfer(struct xe_uc_fw * uc_fw,u32 offset,u32 dma_flags)847dd08ebf6SMatthew Brost static int uc_fw_xfer(struct xe_uc_fw *uc_fw, u32 offset, u32 dma_flags)
8482dd21a9bSMatt Roper {
849dd08ebf6SMatthew Brost 	struct xe_device *xe = uc_fw_to_xe(uc_fw);
850dd08ebf6SMatthew Brost 	struct xe_gt *gt = uc_fw_to_gt(uc_fw);
851dd08ebf6SMatthew Brost 	struct xe_mmio *mmio = &gt->mmio;
8522dd21a9bSMatt Roper 	u64 src_offset;
853dd08ebf6SMatthew Brost 	u32 dma_ctrl;
854dd08ebf6SMatthew Brost 	int ret;
855dd08ebf6SMatthew Brost 
8562dd21a9bSMatt Roper 	xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GT);
8577dc9b92dSRodrigo Vivi 
858dd08ebf6SMatthew Brost 	/* Set the source address for the uCode */
859dd08ebf6SMatthew Brost 	src_offset = uc_fw_ggtt_offset(uc_fw) + uc_fw->css_offset;
8607aaec3a6SRodrigo Vivi 	xe_mmio_write32(mmio, DMA_ADDR_0_LOW, lower_32_bits(src_offset));
861dd08ebf6SMatthew Brost 	xe_mmio_write32(mmio, DMA_ADDR_0_HIGH,
862dd08ebf6SMatthew Brost 			upper_32_bits(src_offset) | DMA_ADDRESS_SPACE_GGTT);
8632dd21a9bSMatt Roper 
864dd08ebf6SMatthew Brost 	/* Set the DMA destination */
865dd08ebf6SMatthew Brost 	xe_mmio_write32(mmio, DMA_ADDR_1_LOW, offset);
866dd08ebf6SMatthew Brost 	xe_mmio_write32(mmio, DMA_ADDR_1_HIGH, DMA_ADDRESS_SPACE_WOPCM);
867dd08ebf6SMatthew Brost 
868dd08ebf6SMatthew Brost 	/*
869dd08ebf6SMatthew Brost 	 * Set the transfer size. The header plus uCode will be copied to WOPCM
870dd08ebf6SMatthew Brost 	 * via DMA, excluding any other components
871dd08ebf6SMatthew Brost 	 */
872dd08ebf6SMatthew Brost 	xe_mmio_write32(mmio, DMA_COPY_SIZE,
873dd08ebf6SMatthew Brost 			sizeof(struct uc_css_header) + uc_fw->ucode_size);
874c73acc1eSFrancois Dugast 
875dd08ebf6SMatthew Brost 	/* Start the DMA */
876dd08ebf6SMatthew Brost 	xe_mmio_write32(mmio, DMA_CTRL,
877dd08ebf6SMatthew Brost 			_MASKED_BIT_ENABLE(dma_flags | START_DMA));
878dd08ebf6SMatthew Brost 
879dd08ebf6SMatthew Brost 	/* Wait for DMA to finish */
880dd08ebf6SMatthew Brost 	ret = xe_mmio_wait32(mmio, DMA_CTRL, START_DMA, 0, 100000, &dma_ctrl,
881dd08ebf6SMatthew Brost 			     false);
882dd08ebf6SMatthew Brost 	if (ret)
883dd08ebf6SMatthew Brost 		drm_err(&xe->drm, "DMA for %s fw failed, DMA_CTRL=%u\n",
884dd08ebf6SMatthew Brost 			xe_uc_fw_type_repr(uc_fw->type), dma_ctrl);
885dd08ebf6SMatthew Brost 
886dd08ebf6SMatthew Brost 	/* Disable the bits once DMA is over */
887dd08ebf6SMatthew Brost 	xe_mmio_write32(mmio, DMA_CTRL, _MASKED_BIT_DISABLE(dma_flags));
888dd08ebf6SMatthew Brost 
889dd08ebf6SMatthew Brost 	return ret;
890dd08ebf6SMatthew Brost }
891dd08ebf6SMatthew Brost 
xe_uc_fw_upload(struct xe_uc_fw * uc_fw,u32 offset,u32 dma_flags)892dd08ebf6SMatthew Brost int xe_uc_fw_upload(struct xe_uc_fw *uc_fw, u32 offset, u32 dma_flags)
893dd08ebf6SMatthew Brost {
894dd08ebf6SMatthew Brost 	struct xe_device *xe = uc_fw_to_xe(uc_fw);
8952e7227b4SDaniele Ceraolo Spurio 	int err;
8962e7227b4SDaniele Ceraolo Spurio 
8972e7227b4SDaniele Ceraolo Spurio 	/* make sure the status was cleared the last time we reset the uc */
8982e7227b4SDaniele Ceraolo Spurio 	xe_assert(xe, !xe_uc_fw_is_loaded(uc_fw));
8992e7227b4SDaniele Ceraolo Spurio 
9002e7227b4SDaniele Ceraolo Spurio 	if (!xe_uc_fw_is_loadable(uc_fw))
9012e7227b4SDaniele Ceraolo Spurio 		return -ENOEXEC;
9022e7227b4SDaniele Ceraolo Spurio 
9032e7227b4SDaniele Ceraolo Spurio 	/* Call custom loader */
9042e7227b4SDaniele Ceraolo Spurio 	err = uc_fw_xfer(uc_fw, offset, dma_flags);
9052e7227b4SDaniele Ceraolo Spurio 	if (err)
906dd08ebf6SMatthew Brost 		goto fail;
907dd08ebf6SMatthew Brost 
908dd08ebf6SMatthew Brost 	xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_TRANSFERRED);
9092e7227b4SDaniele Ceraolo Spurio 	return 0;
9102e7227b4SDaniele Ceraolo Spurio 
911dd08ebf6SMatthew Brost fail:
912dd08ebf6SMatthew Brost 	drm_err(&xe->drm, "Failed to load %s firmware %s (%d)\n",
913dd08ebf6SMatthew Brost 		xe_uc_fw_type_repr(uc_fw->type), uc_fw->path,
914dd08ebf6SMatthew Brost 		err);
91599c821b0SMatthew Brost 	xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_LOAD_FAIL);
9162e7227b4SDaniele Ceraolo Spurio 	return err;
9172e7227b4SDaniele Ceraolo Spurio }
91899c821b0SMatthew Brost 
version_type_repr(enum xe_uc_fw_version_types type)9192e7227b4SDaniele Ceraolo Spurio static const char *version_type_repr(enum xe_uc_fw_version_types type)
9202e7227b4SDaniele Ceraolo Spurio {
9212e7227b4SDaniele Ceraolo Spurio 	switch (type) {
9222e7227b4SDaniele Ceraolo Spurio 	case XE_UC_FW_VER_RELEASE:
9232e7227b4SDaniele Ceraolo Spurio 		return "release";
9242e7227b4SDaniele Ceraolo Spurio 	case XE_UC_FW_VER_COMPATIBILITY:
92599c821b0SMatthew Brost 		return "compatibility";
9262e7227b4SDaniele Ceraolo Spurio 	default:
9272e7227b4SDaniele Ceraolo Spurio 		return "Unknown version type";
9282e7227b4SDaniele Ceraolo Spurio 	}
9292e7227b4SDaniele Ceraolo Spurio }
9302e7227b4SDaniele Ceraolo Spurio 
xe_uc_fw_print(struct xe_uc_fw * uc_fw,struct drm_printer * p)931dd08ebf6SMatthew Brost void xe_uc_fw_print(struct xe_uc_fw *uc_fw, struct drm_printer *p)
932 {
933 	int i;
934 
935 	drm_printf(p, "%s firmware: %s\n",
936 		   xe_uc_fw_type_repr(uc_fw->type), uc_fw->path);
937 	drm_printf(p, "\tstatus: %s\n",
938 		   xe_uc_fw_status_repr(uc_fw->status));
939 
940 	print_uc_fw_version(p, &uc_fw->versions.wanted, "\twanted %s",
941 			    version_type_repr(uc_fw->versions.wanted_type));
942 
943 	for (i = 0; i < XE_UC_FW_VER_TYPE_COUNT; i++) {
944 		struct xe_uc_fw_version *ver = &uc_fw->versions.found[i];
945 
946 		if (ver->major)
947 			print_uc_fw_version(p, ver, "\tfound %s",
948 					    version_type_repr(i));
949 	}
950 
951 	if (uc_fw->ucode_size)
952 		drm_printf(p, "\tuCode: %u bytes\n", uc_fw->ucode_size);
953 	if (uc_fw->rsa_size)
954 		drm_printf(p, "\tRSA: %u bytes\n", uc_fw->rsa_size);
955 }
956