xref: /linux/drivers/gpu/drm/xe/xe_guc_klv_helpers.c (revision 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d)
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2024 Intel Corporation
4  */
5 
6 #include <linux/bitfield.h>
7 #include <kunit/static_stub.h>
8 #include <drm/drm_print.h>
9 
10 #include "abi/guc_klvs_abi.h"
11 #include "abi/xe_driver_klvs_abi.h"
12 #include "xe_guc_klv_helpers.h"
13 #include "xe_guc_klv_thresholds_set.h"
14 
15 #define make_u64(hi, lo) ((u64)((u64)(u32)(hi) << 32 | (u32)(lo)))
16 
17 static bool is_group_key(u16 key)
18 {
19 	KUNIT_STATIC_STUB_REDIRECT(is_group_key, key);
20 	return false;
21 }
22 
23 static bool is_reserved_key(u16 key)
24 {
25 	return in_range(key, GUC_KLV_RESERVED_RANGE_START, GUC_KLV_RESERVED_RANGE_LEN);
26 }
27 
28 /**
29  * xe_guc_klv_key_to_string - Convert KLV key into friendly name.
30  * @key: the `GuC KLV`_ key
31  *
32  * Return: name of the KLV key.
33  */
34 const char *xe_guc_klv_key_to_string(u16 key)
35 {
36 	switch (key) {
37 	/* GuC Global Config KLVs */
38 	case GUC_KLV_GLOBAL_CFG_GROUP_SCHEDULING_AVAILABLE_KEY:
39 		return "group_scheduling_available";
40 	case GUC_KLV_GLOBAL_CFG_NUM_PAGING_ENGINE_INSTANCES_KEY:
41 		return "num_paging_engine_instances";
42 	/* VGT POLICY keys */
43 	case GUC_KLV_VGT_POLICY_SCHED_IF_IDLE_KEY:
44 		return "sched_if_idle";
45 	case GUC_KLV_VGT_POLICY_ADVERSE_SAMPLE_PERIOD_KEY:
46 		return "sample_period";
47 	case GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG_KEY:
48 		return "engine_group_config";
49 	case GUC_KLV_VGT_POLICY_RESET_AFTER_VF_SWITCH_KEY:
50 		return "reset_engine";
51 	/* VF CFG keys */
52 	case GUC_KLV_VF_CFG_GGTT_START_KEY:
53 		return "ggtt_start";
54 	case GUC_KLV_VF_CFG_GGTT_SIZE_KEY:
55 		return "ggtt_size";
56 	case GUC_KLV_VF_CFG_LMEM_SIZE_KEY:
57 		return "lmem_size";
58 	case GUC_KLV_VF_CFG_NUM_CONTEXTS_KEY:
59 		return "num_contexts";
60 	case GUC_KLV_VF_CFG_TILE_MASK_KEY:
61 		return "tile_mask";
62 	case GUC_KLV_VF_CFG_NUM_DOORBELLS_KEY:
63 		return "num_doorbells";
64 	case GUC_KLV_VF_CFG_EXEC_QUANTUM_KEY:
65 		return "exec_quantum";
66 	case GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_KEY:
67 		return "preempt_timeout";
68 	case GUC_KLV_VF_CFG_BEGIN_DOORBELL_ID_KEY:
69 		return "begin_db_id";
70 	case GUC_KLV_VF_CFG_BEGIN_CONTEXT_ID_KEY:
71 		return "begin_ctx_id";
72 	case GUC_KLV_VF_CFG_SCHED_PRIORITY_KEY:
73 		return "sched_priority";
74 	case GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_KEY:
75 		return "sched_groups_exec_quantum";
76 	case GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_KEY:
77 		return "sched_groups_preempt_timeout";
78 
79 	/* VF CFG threshold keys */
80 #define define_threshold_key_to_string_case(TAG, NAME, ...)	\
81 								\
82 	case MAKE_GUC_KLV_VF_CFG_THRESHOLD_KEY(TAG):		\
83 		return #NAME;
84 
85 	/* private: auto-generated case statements */
86 	MAKE_XE_GUC_KLV_THRESHOLDS_SET(define_threshold_key_to_string_case)
87 #undef define_threshold_key_to_string_case
88 
89 	/* driver KLVs */
90 	case MIGRATION_KLV_DEVICE_DEVID_KEY:
91 		return "migration_devid";
92 	case MIGRATION_KLV_DEVICE_REVID_KEY:
93 		return "migration_revid";
94 
95 	default:
96 		if (is_reserved_key(key))
97 			return "(reserved)";
98 		return "(unknown)";
99 	}
100 }
101 
102 /**
103  * xe_guc_klv_print_one() - Print single `GuC KLV`_.
104  * @key: KLV key
105  * @len: KLV length (in u32 dwords) of the KLV @value
106  * @value: KLV value (as array of @len u32 dwords)
107  * @p: the &drm_printer
108  *
109  * The buffer may contain more than one KLV.
110  */
111 void xe_guc_klv_print_one(u16 key, u16 len, const u32 *value, struct drm_printer *p)
112 {
113 	const char *name = xe_guc_klv_key_to_string(key);
114 
115 	if (is_group_key(key)) {
116 		struct drm_printer gp = drm_line_printer(p, name, 0);
117 
118 		drm_printf(p, "{ key %#06x : group %u dwords } # %s\n",
119 			   key, len, name);
120 
121 		/* print group recursively */
122 		xe_guc_klv_print(value, len, &gp);
123 		return;
124 	}
125 
126 	switch (len) {
127 	case 0:
128 		drm_printf(p, "{ key %#06x : no value } # %s\n", key, name);
129 		break;
130 	case 1:
131 		drm_printf(p, "{ key %#06x : 32b value %u } # %s\n",
132 			   key, value[0], name);
133 		break;
134 	case 2:
135 		drm_printf(p, "{ key %#06x : 64b value %#llx } # %s\n",
136 			   key, make_u64(value[1], value[0]), name);
137 		break;
138 	default:
139 		drm_printf(p, "{ key %#06x : %zu bytes %*ph } # %s\n",
140 			   key, len * sizeof(u32), (int)(len * sizeof(u32)),
141 			   value, name);
142 		break;
143 	}
144 }
145 
146 /**
147  * xe_guc_klv_print - Print content of the buffer with `GuC KLV`_.
148  * @klvs: the buffer with KLVs
149  * @num_dwords: number of dwords (u32) available in the buffer
150  * @p: the &drm_printer
151  *
152  * The buffer may contain more than one KLV.
153  */
154 void xe_guc_klv_print(const u32 *klvs, u32 num_dwords, struct drm_printer *p)
155 {
156 	while (num_dwords >= GUC_KLV_LEN_MIN) {
157 		u32 key = FIELD_GET(GUC_KLV_0_KEY, klvs[0]);
158 		u32 len = FIELD_GET(GUC_KLV_0_LEN, klvs[0]);
159 
160 		klvs += GUC_KLV_LEN_MIN;
161 		num_dwords -= GUC_KLV_LEN_MIN;
162 
163 		if (num_dwords < len) {
164 			drm_printf(p, "{ key %#06x : truncated %zu of %zu bytes %*ph } # %s\n",
165 				   key, num_dwords * sizeof(u32), len * sizeof(u32),
166 				   (int)(num_dwords * sizeof(u32)), klvs,
167 				   xe_guc_klv_key_to_string(key));
168 			return;
169 		}
170 
171 		xe_guc_klv_print_one(key, len, klvs, p);
172 
173 		klvs += len;
174 		num_dwords -= len;
175 	}
176 
177 	/* we don't expect any leftovers, fix if KLV header is ever changed */
178 	BUILD_BUG_ON(GUC_KLV_LEN_MIN > 1);
179 }
180 
181 /**
182  * xe_guc_klv_count - Count KLVs present in the buffer.
183  * @klvs: the buffer with KLVs
184  * @num_dwords: number of dwords (u32) in the buffer
185  *
186  * Return: number of recognized KLVs or
187  *         a negative error code if KLV buffer is truncated.
188  */
189 int xe_guc_klv_count(const u32 *klvs, u32 num_dwords)
190 {
191 	int num_klvs = 0;
192 
193 	while (num_dwords >= GUC_KLV_LEN_MIN) {
194 		u32 len = FIELD_GET(GUC_KLV_0_LEN, klvs[0]);
195 
196 		if (num_dwords < len + GUC_KLV_LEN_MIN)
197 			break;
198 
199 		klvs += GUC_KLV_LEN_MIN + len;
200 		num_dwords -= GUC_KLV_LEN_MIN + len;
201 		num_klvs++;
202 	}
203 
204 	return num_dwords ? -ENODATA : num_klvs;
205 }
206 
207 static size_t to_num_bytes(u16 dwords)
208 {
209 	return dwords * sizeof(u32);
210 }
211 
212 static u16 to_num_dwords(size_t size)
213 {
214 	return round_up(size, sizeof(u32)) / sizeof(u32);
215 }
216 
217 /**
218  * xe_guc_klv_encode_u32() - Encode 32-bit value as KLV.
219  * @klvs: the buffer where to place KLV
220  * @avail: number of dwords (u32) available in the buffer
221  * @key: key to be used
222  * @value: value to be encoded
223  *
224  * Return: pointer to the buffer location past the encoded KLV or
225  *         an ERR_PTR if there was no space to encode the KLV.
226  */
227 u32 *xe_guc_klv_encode_u32(u32 *klvs, u32 avail, u16 key, u32 value)
228 {
229 	u16 len = to_num_dwords(sizeof(u32));
230 
231 	if (IS_ERR(klvs))
232 		return klvs;
233 
234 	if (avail < GUC_KLV_LEN_MIN + len)
235 		return ERR_PTR(-ENOSPC);
236 
237 	*klvs++ = PREP_GUC_KLV(key, len);
238 	*klvs++ = value;
239 	return klvs;
240 }
241 
242 /**
243  * xe_guc_klv_encode_u64() - Encode 64-bit value as KLV.
244  * @klvs: the buffer where to place KLV
245  * @avail: number of dwords (u32) available in the buffer
246  * @key: key to be used
247  * @value: value to be encoded
248  *
249  * Return: pointer to the buffer location past the encoded KLV or
250  *         an ERR_PTR if there was no space to encode the KLV.
251  */
252 u32 *xe_guc_klv_encode_u64(u32 *klvs, u32 avail, u16 key, u64 value)
253 {
254 	u16 len = to_num_dwords(sizeof(u64));
255 
256 	if (IS_ERR(klvs))
257 		return klvs;
258 
259 	if (avail < GUC_KLV_LEN_MIN + len)
260 		return ERR_PTR(-ENOSPC);
261 
262 	*klvs++ = PREP_GUC_KLV(key, len);
263 	*klvs++ = lower_32_bits(value);
264 	*klvs++ = upper_32_bits(value);
265 	return klvs;
266 }
267 
268 /**
269  * xe_guc_klv_encode_string() - Encode string as KLV.
270  * @klvs: the buffer where to place KLV
271  * @avail: number of dwords (u32) available in the buffer
272  * @key: key to be used
273  * @s: string to be encoded
274  *
275  * Return: pointer to the buffer location past the encoded KLV or
276  *         an ERR_PTR if there was no space to encode the KLV.
277  */
278 u32 *xe_guc_klv_encode_string(u32 *klvs, u32 avail, u16 key, const char *s)
279 {
280 	size_t longest = to_num_bytes(FIELD_MAX(GUC_KLV_0_LEN));
281 	size_t size = strnlen(s, longest) + 1; /* \0 */
282 	u16 len = to_num_dwords(size);
283 
284 	if (IS_ERR(klvs))
285 		return klvs;
286 
287 	if (size > longest)
288 		return ERR_PTR(-E2BIG);
289 
290 	if (avail < GUC_KLV_LEN_MIN + len)
291 		return ERR_PTR(-ENOSPC);
292 
293 	*klvs++ = PREP_GUC_KLV(key, len);
294 	strscpy_pad((void *)klvs, s, to_num_bytes(len));
295 	return klvs + len;
296 }
297 
298 /**
299  * xe_guc_klv_encode_object() - Encode object using custom encoder as single KLV.
300  * @klvs: the buffer where to place KLV
301  * @avail: number of dwords (u32) available in the buffer
302  * @key: key to be used
303  * @obj: opaque object pointer
304  * @encoder: function pointer to the custom encoder
305  *
306  * Return: pointer to the buffer location past the encoded KLV or
307  *         an ERR_PTR if there was no space to encode the KLV.
308  */
309 u32 *xe_guc_klv_encode_object(u32 *klvs, u32 avail, u16 key, const void *obj,
310 			      u32 *(*encoder)(u32 *klvs, u32 avail, const void *obj))
311 {
312 	u32 *end;
313 
314 	if (IS_ERR(klvs))
315 		return klvs;
316 
317 	if (avail < GUC_KLV_LEN_MIN)
318 		return ERR_PTR(-ENOSPC);
319 
320 	if (avail > GUC_KLV_LEN_MIN + FIELD_MAX(GUC_KLV_0_LEN))
321 		avail = GUC_KLV_LEN_MIN + FIELD_MAX(GUC_KLV_0_LEN);
322 
323 	end = encoder(klvs + GUC_KLV_LEN_MIN, avail - GUC_KLV_LEN_MIN, obj);
324 	if (IS_ERR(end))
325 		return end;
326 
327 	if (WARN_ON(end < klvs + GUC_KLV_LEN_MIN))
328 		return ERR_PTR(-EPIPE);
329 
330 	if (WARN_ON(end > klvs + avail))
331 		return ERR_PTR(-EFBIG);
332 
333 	*klvs = PREP_GUC_KLV(key, end - (klvs + GUC_KLV_LEN_MIN));
334 	return end;
335 }
336 
337 /**
338  * xe_guc_klv_parser() - Parse and decode stream of KLVs.
339  * @klvs: the buffer with KLVs
340  * @num_dwords: number of dwords (u32) available in the buffer
341  * @obj: opaque pointer to be used by the @decoder function
342  * @decoder: pointer to the decoder function
343  *
344  * Return: The sum of all results returned by the decoder or
345  *         an -errno on decoder or buffer failure.
346  */
347 int xe_guc_klv_parser(const u32 *klvs, u32 num_dwords, void *obj,
348 		      int (*decoder)(void *obj, u16 key, u16 len, const u32 *value))
349 {
350 	int total = 0;
351 	int ret;
352 
353 	while (num_dwords >= GUC_KLV_LEN_MIN) {
354 		u16 key = FIELD_GET(GUC_KLV_0_KEY, klvs[0]);
355 		u16 len = FIELD_GET(GUC_KLV_0_LEN, klvs[0]);
356 
357 		klvs += GUC_KLV_LEN_MIN;
358 		num_dwords -= GUC_KLV_LEN_MIN;
359 
360 		if (num_dwords < len)
361 			return -ENODATA;
362 
363 		ret = decoder(obj, key, len, klvs);
364 		if (ret < 0)
365 			return ret;
366 		total += ret;
367 
368 		klvs += len;
369 		num_dwords -= len;
370 	}
371 
372 	return total;
373 }
374 
375 #if IS_BUILTIN(CONFIG_DRM_XE_KUNIT_TEST)
376 #include "tests/xe_guc_klv_helpers_kunit.c"
377 #endif
378