1 // SPDX-License-Identifier: GPL-2.0-only
2 //
3 // KUnit tests for cs_dsp.
4 //
5 // Copyright (C) 2024 Cirrus Logic, Inc. and
6 //                    Cirrus Logic International Semiconductor Ltd.
7 //
8 
9 #include <kunit/device.h>
10 #include <kunit/resource.h>
11 #include <kunit/test.h>
12 #include <linux/build_bug.h>
13 #include <linux/firmware/cirrus/cs_dsp.h>
14 #include <linux/firmware/cirrus/cs_dsp_test_utils.h>
15 #include <linux/firmware/cirrus/wmfw.h>
16 #include <linux/random.h>
17 #include <linux/regmap.h>
18 #include <linux/string.h>
19 #include <linux/vmalloc.h>
20 
21 KUNIT_DEFINE_ACTION_WRAPPER(_put_device_wrapper, put_device, struct device *);
22 KUNIT_DEFINE_ACTION_WRAPPER(_cs_dsp_remove_wrapper, cs_dsp_remove, struct cs_dsp *);
23 
24 struct cs_dsp_test_local {
25 	struct cs_dsp_mock_xm_header *xm_header;
26 	struct cs_dsp_mock_wmfw_builder *wmfw_builder;
27 	int wmfw_version;
28 };
29 
30 struct cs_dsp_wmfw_test_param {
31 	int block_type;
32 };
33 
34 static const struct cs_dsp_mock_alg_def cs_dsp_wmfw_err_test_mock_algs[] = {
35 	{
36 		.id = 0xfafa,
37 		.ver = 0x100000,
38 		.xm_size_words = 164,
39 		.ym_size_words = 164,
40 		.zm_size_words = 164,
41 	},
42 };
43 
44 static const struct cs_dsp_mock_coeff_def mock_coeff_template = {
45 	.shortname = "Dummy Coeff",
46 	.type = WMFW_CTL_TYPE_BYTES,
47 	.mem_type = WMFW_ADSP2_YM,
48 	.flags = WMFW_CTL_FLAG_VOLATILE,
49 	.length_bytes = 4,
50 };
51 
52 /* Load a wmfw containing unknown blocks. They should be skipped. */
wmfw_load_with_unknown_blocks(struct kunit * test)53 static void wmfw_load_with_unknown_blocks(struct kunit *test)
54 {
55 	struct cs_dsp_test *priv = test->priv;
56 	struct cs_dsp_test_local *local = priv->local;
57 	struct firmware *wmfw;
58 	unsigned int reg_addr;
59 	u8 *payload_data, *readback;
60 	u8 random_data[8];
61 	const unsigned int payload_size_bytes = 64;
62 
63 	/* Add dummy XM header payload to wmfw */
64 	cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
65 					WMFW_ADSP2_XM, 0,
66 					local->xm_header->blob_data,
67 					local->xm_header->blob_size_bytes);
68 
69 	payload_data = kunit_kmalloc(test, payload_size_bytes, GFP_KERNEL);
70 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload_data);
71 	get_random_bytes(payload_data, payload_size_bytes);
72 
73 	readback = kunit_kzalloc(test, payload_size_bytes, GFP_KERNEL);
74 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);
75 
76 	/* Add some unknown blocks at the start of the wmfw */
77 	get_random_bytes(random_data, sizeof(random_data));
78 	cs_dsp_mock_wmfw_add_raw_block(local->wmfw_builder, 0xf5, 0,
79 				       random_data, sizeof(random_data));
80 	cs_dsp_mock_wmfw_add_raw_block(local->wmfw_builder, 0xc0, 0, random_data,
81 				       sizeof(random_data));
82 	cs_dsp_mock_wmfw_add_raw_block(local->wmfw_builder, 0x33, 0, NULL, 0);
83 
84 	/* Add a single payload to be written to DSP memory */
85 	cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
86 					WMFW_ADSP2_YM, 0,
87 					payload_data, payload_size_bytes);
88 
89 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
90 
91 	/* Sanity-check that the good wmfw loads ok */
92 	KUNIT_EXPECT_EQ(test,
93 			cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
94 			0);
95 	cs_dsp_power_down(priv->dsp);
96 
97 	KUNIT_EXPECT_EQ(test,
98 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
99 			0);
100 
101 	/* Check that the payload was written to memory */
102 	reg_addr = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_YM);
103 	KUNIT_EXPECT_EQ(test,
104 			regmap_raw_read(priv->dsp->regmap, reg_addr, readback, payload_size_bytes),
105 			0);
106 	KUNIT_EXPECT_MEMEQ(test, readback, payload_data, payload_size_bytes);
107 }
108 
109 /* Load a wmfw that doesn't have a valid magic marker. */
wmfw_err_wrong_magic(struct kunit * test)110 static void wmfw_err_wrong_magic(struct kunit *test)
111 {
112 	struct cs_dsp_test *priv = test->priv;
113 	struct cs_dsp_test_local *local = priv->local;
114 	struct firmware *wmfw;
115 
116 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
117 
118 	/* Sanity-check that the good wmfw loads ok */
119 	KUNIT_EXPECT_EQ(test,
120 			cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
121 			0);
122 	cs_dsp_power_down(priv->dsp);
123 
124 	memcpy((void *)wmfw->data, "WMDR", 4);
125 	KUNIT_EXPECT_LT(test,
126 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
127 			0);
128 
129 	memcpy((void *)wmfw->data, "xMFW", 4);
130 	KUNIT_EXPECT_LT(test,
131 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
132 			0);
133 
134 	memcpy((void *)wmfw->data, "WxFW", 4);
135 	KUNIT_EXPECT_LT(test,
136 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
137 			0);
138 
139 	memcpy((void *)wmfw->data, "WMxW", 4);
140 	KUNIT_EXPECT_LT(test,
141 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
142 			0);
143 
144 	memcpy((void *)wmfw->data, "WMFx", 4);
145 	KUNIT_EXPECT_LT(test,
146 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
147 			0);
148 
149 	memset((void *)wmfw->data, 0, 4);
150 	KUNIT_EXPECT_LT(test,
151 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
152 			0);
153 }
154 
155 /* Load a wmfw that is too short for a valid header. */
wmfw_err_too_short_for_header(struct kunit * test)156 static void wmfw_err_too_short_for_header(struct kunit *test)
157 {
158 	struct cs_dsp_test *priv = test->priv;
159 	struct cs_dsp_test_local *local = priv->local;
160 	struct firmware *wmfw;
161 
162 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
163 
164 	/* Sanity-check that the good wmfw loads ok */
165 	KUNIT_EXPECT_EQ(test,
166 			cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
167 			0);
168 	cs_dsp_power_down(priv->dsp);
169 
170 	do {
171 		wmfw->size--;
172 
173 		KUNIT_EXPECT_EQ(test,
174 				cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
175 				-EOVERFLOW);
176 	} while (wmfw->size > 0);
177 }
178 
179 /* Header length field isn't a valid header length. */
wmfw_err_bad_header_length(struct kunit * test)180 static void wmfw_err_bad_header_length(struct kunit *test)
181 {
182 	struct cs_dsp_test *priv = test->priv;
183 	struct cs_dsp_test_local *local = priv->local;
184 	struct firmware *wmfw;
185 	struct wmfw_header *header;
186 	unsigned int real_len, len;
187 
188 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
189 
190 	/* Sanity-check that the good wmfw loads ok */
191 	KUNIT_EXPECT_EQ(test,
192 			cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
193 			0);
194 	cs_dsp_power_down(priv->dsp);
195 
196 	header = (struct wmfw_header *)wmfw->data;
197 	real_len = le32_to_cpu(header->len);
198 
199 	for (len = 0; len < real_len; len++) {
200 		header->len = cpu_to_le32(len);
201 		KUNIT_EXPECT_EQ(test,
202 				cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
203 				-EOVERFLOW);
204 	}
205 
206 	for (len = real_len + 1; len < real_len + 7; len++) {
207 		header->len = cpu_to_le32(len);
208 		KUNIT_EXPECT_EQ(test,
209 				cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
210 				-EOVERFLOW);
211 	}
212 
213 	header->len = cpu_to_le32(0xffffffff);
214 	KUNIT_EXPECT_EQ(test,
215 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
216 			-EOVERFLOW);
217 
218 	header->len = cpu_to_le32(0x80000000);
219 	KUNIT_EXPECT_EQ(test,
220 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
221 			-EOVERFLOW);
222 
223 	header->len = cpu_to_le32(0x7fffffff);
224 	KUNIT_EXPECT_EQ(test,
225 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
226 			-EOVERFLOW);
227 }
228 
229 /* Wrong core type in header. */
wmfw_err_bad_core_type(struct kunit * test)230 static void wmfw_err_bad_core_type(struct kunit *test)
231 {
232 	struct cs_dsp_test *priv = test->priv;
233 	struct cs_dsp_test_local *local = priv->local;
234 	struct firmware *wmfw;
235 	struct wmfw_header *header;
236 
237 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
238 
239 	/* Sanity-check that the good wmfw loads ok */
240 	KUNIT_EXPECT_EQ(test,
241 			cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
242 			0);
243 	cs_dsp_power_down(priv->dsp);
244 
245 	header = (struct wmfw_header *)wmfw->data;
246 
247 	header->core = 0;
248 	KUNIT_EXPECT_LT(test,
249 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
250 			0);
251 
252 	header->core = 1;
253 	KUNIT_EXPECT_LT(test,
254 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
255 			0);
256 
257 	header->core = priv->dsp->type + 1;
258 	KUNIT_EXPECT_LT(test,
259 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
260 			0);
261 
262 	header->core = 0xff;
263 	KUNIT_EXPECT_LT(test,
264 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
265 			0);
266 }
267 
268 /* File too short to contain a full block header */
wmfw_too_short_for_block_header(struct kunit * test)269 static void wmfw_too_short_for_block_header(struct kunit *test)
270 {
271 	const struct cs_dsp_wmfw_test_param *param = test->param_value;
272 	struct cs_dsp_test *priv = test->priv;
273 	struct cs_dsp_test_local *local = priv->local;
274 	struct firmware *wmfw;
275 	unsigned int header_length;
276 	u32 dummy_payload = 0;
277 
278 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
279 	header_length = wmfw->size;
280 	kunit_kfree(test, wmfw);
281 
282 	/* Add the block. A block must have at least 4 bytes of payload */
283 	cs_dsp_mock_wmfw_add_raw_block(local->wmfw_builder, param->block_type, 0,
284 				       &dummy_payload, sizeof(dummy_payload));
285 
286 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
287 	KUNIT_ASSERT_GT(test, wmfw->size, header_length);
288 
289 	/* Sanity-check that the good wmfw loads ok */
290 	KUNIT_EXPECT_EQ(test,
291 			cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
292 			0);
293 	cs_dsp_power_down(priv->dsp);
294 
295 	for (wmfw->size--; wmfw->size > header_length; wmfw->size--) {
296 		KUNIT_EXPECT_EQ(test,
297 				cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
298 				-EOVERFLOW);
299 	}
300 }
301 
302 /* File too short to contain the block payload */
wmfw_too_short_for_block_payload(struct kunit * test)303 static void wmfw_too_short_for_block_payload(struct kunit *test)
304 {
305 	const struct cs_dsp_wmfw_test_param *param = test->param_value;
306 	struct cs_dsp_test *priv = test->priv;
307 	struct cs_dsp_test_local *local = priv->local;
308 	struct firmware *wmfw;
309 	static const u8 payload[256] = { };
310 	int i;
311 
312 	cs_dsp_mock_wmfw_add_raw_block(local->wmfw_builder, param->block_type, 0,
313 				       payload, sizeof(payload));
314 
315 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
316 
317 	/* Sanity-check that the good wmfw loads ok */
318 	KUNIT_EXPECT_EQ(test,
319 			cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
320 			0);
321 	cs_dsp_power_down(priv->dsp);
322 
323 	for (i = 0; i < sizeof(payload); i++) {
324 		wmfw->size--;
325 		KUNIT_EXPECT_EQ(test,
326 				cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
327 				-EOVERFLOW);
328 	}
329 }
330 
331 /* Block payload length is a garbage value */
wmfw_block_payload_len_garbage(struct kunit * test)332 static void wmfw_block_payload_len_garbage(struct kunit *test)
333 {
334 	const struct cs_dsp_wmfw_test_param *param = test->param_value;
335 	struct cs_dsp_test *priv = test->priv;
336 	struct cs_dsp_test_local *local = priv->local;
337 	struct firmware *wmfw;
338 	struct wmfw_header *header;
339 	struct wmfw_region *region;
340 	u32 payload = 0;
341 
342 
343 	cs_dsp_mock_wmfw_add_raw_block(local->wmfw_builder, param->block_type, 0,
344 				       &payload, sizeof(payload));
345 
346 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
347 
348 	/* Sanity-check that the good wmfw loads ok */
349 	KUNIT_EXPECT_EQ(test,
350 			cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
351 			0);
352 	cs_dsp_power_down(priv->dsp);
353 
354 	header = (struct wmfw_header *)wmfw->data;
355 	region = (struct wmfw_region *)&wmfw->data[le32_to_cpu(header->len)];
356 
357 	/* Sanity check that we're looking at the correct part of the wmfw */
358 	KUNIT_ASSERT_EQ(test, le32_to_cpu(region->offset) >> 24, param->block_type);
359 	KUNIT_ASSERT_EQ(test, le32_to_cpu(region->len), sizeof(payload));
360 
361 	region->len = cpu_to_le32(0x8000);
362 	KUNIT_EXPECT_EQ(test,
363 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
364 			-EOVERFLOW);
365 
366 	region->len = cpu_to_le32(0xffff);
367 	KUNIT_EXPECT_EQ(test,
368 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
369 			-EOVERFLOW);
370 
371 	region->len = cpu_to_le32(0x7fffffff);
372 	KUNIT_EXPECT_EQ(test,
373 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
374 			-EOVERFLOW);
375 
376 	region->len = cpu_to_le32(0x80000000);
377 	KUNIT_EXPECT_EQ(test,
378 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
379 			-EOVERFLOW);
380 
381 	region->len = cpu_to_le32(0xffffffff);
382 	KUNIT_EXPECT_EQ(test,
383 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
384 			-EOVERFLOW);
385 }
386 
387 /* File too short to contain an algorithm header */
wmfw_too_short_for_alg_header(struct kunit * test)388 static void wmfw_too_short_for_alg_header(struct kunit *test)
389 {
390 	struct cs_dsp_test *priv = test->priv;
391 	struct cs_dsp_test_local *local = priv->local;
392 	struct firmware *wmfw;
393 	unsigned int header_length;
394 
395 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
396 	header_length = wmfw->size;
397 	kunit_kfree(test, wmfw);
398 
399 	cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
400 					      cs_dsp_wmfw_err_test_mock_algs[0].id,
401 					      NULL, NULL);
402 	cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
403 
404 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
405 	KUNIT_ASSERT_GT(test, wmfw->size, header_length);
406 
407 	/* Sanity-check that the good wmfw loads ok */
408 	KUNIT_EXPECT_EQ(test,
409 			cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
410 			0);
411 	cs_dsp_power_down(priv->dsp);
412 
413 	for (wmfw->size--; wmfw->size > header_length; wmfw->size--) {
414 		KUNIT_EXPECT_EQ(test,
415 				cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
416 				-EOVERFLOW);
417 	}
418 }
419 
420 /* V1 algorithm name does not have NUL terminator */
wmfw_v1_alg_name_unterminated(struct kunit * test)421 static void wmfw_v1_alg_name_unterminated(struct kunit *test)
422 {
423 	struct cs_dsp_test *priv = test->priv;
424 	struct cs_dsp_test_local *local = priv->local;
425 	struct firmware *wmfw;
426 	struct wmfw_header *header;
427 	struct wmfw_region *region;
428 	struct wmfw_adsp_alg_data *alg_data;
429 	struct cs_dsp_coeff_ctl *ctl;
430 
431 	/* Create alg info block with a coefficient */
432 	cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
433 					      cs_dsp_wmfw_err_test_mock_algs[0].id,
434 					      "abc", "de");
435 	cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &mock_coeff_template);
436 	cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
437 
438 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
439 
440 	header = (struct wmfw_header *)wmfw->data;
441 	region = (struct wmfw_region *)&wmfw->data[le32_to_cpu(header->len)];
442 	alg_data = (struct wmfw_adsp_alg_data *)region->data;
443 
444 	/* Sanity check we're pointing at the alg header */
445 	KUNIT_ASSERT_EQ(test, le32_to_cpu(alg_data->id), cs_dsp_wmfw_err_test_mock_algs[0].id);
446 
447 	/* Write a string to the alg name that overflows the array */
448 	memset(alg_data->descr, 0, sizeof(alg_data->descr));
449 	memset(alg_data->name, 'A', sizeof(alg_data->name));
450 	memset(alg_data->descr, 'A', sizeof(alg_data->descr) - 1);
451 
452 	/*
453 	 * Sanity-check that a strlen would overflow alg_data->name.
454 	 * FORTIFY_STRING obstructs testing what strlen() would actually
455 	 * return, so instead verify that a strnlen() returns
456 	 * sizeof(alg_data->name[]), therefore it doesn't have a NUL.
457 	 */
458 	KUNIT_ASSERT_EQ(test, strnlen(alg_data->name, sizeof(alg_data->name)),
459 			sizeof(alg_data->name));
460 
461 	/*
462 	 * The alg name isn't stored, but cs_dsp parses the name field.
463 	 * It should load the file successfully and create the control.
464 	 * If FORTIFY_STRING is enabled it will detect a buffer overflow
465 	 * if cs_dsp string length walks past end of alg name array.
466 	 */
467 	KUNIT_EXPECT_EQ(test,
468 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
469 			0);
470 	ctl = list_first_entry_or_null(&priv->dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
471 	KUNIT_ASSERT_NOT_NULL(test, ctl);
472 	KUNIT_EXPECT_EQ(test, ctl->subname_len, 0);
473 }
474 
475 /* V2+ algorithm name exceeds length of containing block */
wmfw_v2_alg_name_exceeds_block(struct kunit * test)476 static void wmfw_v2_alg_name_exceeds_block(struct kunit *test)
477 {
478 	struct cs_dsp_test *priv = test->priv;
479 	struct cs_dsp_test_local *local = priv->local;
480 	struct firmware *wmfw;
481 	struct wmfw_header *header;
482 	struct wmfw_region *region;
483 	__le32 *alg_data;
484 
485 	cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
486 					      cs_dsp_wmfw_err_test_mock_algs[0].id,
487 					      "abc", NULL);
488 	cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
489 
490 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
491 
492 	/* Sanity-check that the good wmfw loads ok */
493 	KUNIT_EXPECT_EQ(test,
494 			cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
495 			0);
496 	cs_dsp_power_down(priv->dsp);
497 
498 	header = (struct wmfw_header *)wmfw->data;
499 	region = (struct wmfw_region *)&wmfw->data[le32_to_cpu(header->len)];
500 	alg_data = (__force __le32 *)region->data;
501 
502 	/*
503 	 * Sanity check we're pointing at the alg header of
504 	 *   [     alg_id       ][name_len]abc
505 	 */
506 	KUNIT_ASSERT_EQ(test, le32_to_cpu(alg_data[0]), cs_dsp_wmfw_err_test_mock_algs[0].id);
507 	KUNIT_ASSERT_EQ(test, le32_to_cpu(alg_data[1]), 3 | ('a' << 8) | ('b' << 16) | ('c' << 24));
508 	KUNIT_ASSERT_EQ(test, *(u8 *)&alg_data[1], 3);
509 
510 	/* Set name string length longer than available space */
511 	*(u8 *)&alg_data[1] = 4;
512 	KUNIT_EXPECT_EQ(test,
513 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
514 			-EOVERFLOW);
515 
516 	*(u8 *)&alg_data[1] = 7;
517 	KUNIT_EXPECT_EQ(test,
518 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
519 			-EOVERFLOW);
520 
521 	*(u8 *)&alg_data[1] = 0x80;
522 	KUNIT_EXPECT_EQ(test,
523 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
524 			-EOVERFLOW);
525 
526 	*(u8 *)&alg_data[1] = 0xff;
527 	KUNIT_EXPECT_EQ(test,
528 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
529 			-EOVERFLOW);
530 }
531 
532 /* V2+ algorithm description exceeds length of containing block */
wmfw_v2_alg_description_exceeds_block(struct kunit * test)533 static void wmfw_v2_alg_description_exceeds_block(struct kunit *test)
534 {
535 	struct cs_dsp_test *priv = test->priv;
536 	struct cs_dsp_test_local *local = priv->local;
537 	struct firmware *wmfw;
538 	struct wmfw_header *header;
539 	struct wmfw_region *region;
540 	__le32 *alg_data;
541 
542 	cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
543 					      cs_dsp_wmfw_err_test_mock_algs[0].id,
544 					      "abc", "de");
545 	cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
546 
547 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
548 
549 	/* Sanity-check that the good wmfw loads ok */
550 	KUNIT_EXPECT_EQ(test,
551 			cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
552 			0);
553 	cs_dsp_power_down(priv->dsp);
554 
555 	header = (struct wmfw_header *)wmfw->data;
556 	region = (struct wmfw_region *)&wmfw->data[le32_to_cpu(header->len)];
557 	alg_data = (__force __le32 *)region->data;
558 
559 	/*
560 	 * Sanity check we're pointing at the alg header of
561 	 *   [     alg_id       ][name_len]abc[desc_len]de
562 	 */
563 	KUNIT_ASSERT_EQ(test, le32_to_cpu(alg_data[0]), cs_dsp_wmfw_err_test_mock_algs[0].id);
564 	KUNIT_ASSERT_EQ(test, le32_to_cpu(alg_data[2]), 2 | ('d' << 16) | ('e' << 24));
565 	KUNIT_ASSERT_EQ(test, le16_to_cpu(*(__le16 *)&alg_data[2]), 2);
566 
567 	/* Set name string length longer than available space */
568 	*(__le16 *)&alg_data[2] = cpu_to_le16(4);
569 	KUNIT_EXPECT_EQ(test,
570 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
571 			-EOVERFLOW);
572 
573 	*(__le16 *)&alg_data[2] = cpu_to_le16(7);
574 	KUNIT_EXPECT_EQ(test,
575 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
576 			-EOVERFLOW);
577 
578 	*(__le16 *)&alg_data[2] = cpu_to_le16(0x80);
579 	KUNIT_EXPECT_EQ(test,
580 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
581 			-EOVERFLOW);
582 
583 	*(__le16 *)&alg_data[2] = cpu_to_le16(0xff);
584 	KUNIT_EXPECT_EQ(test,
585 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
586 			-EOVERFLOW);
587 
588 	*(__le16 *)&alg_data[2] = cpu_to_le16(0x8000);
589 	KUNIT_EXPECT_EQ(test,
590 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
591 			-EOVERFLOW);
592 
593 	*(__le16 *)&alg_data[2] = cpu_to_le16(0xffff);
594 	KUNIT_EXPECT_EQ(test,
595 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
596 			-EOVERFLOW);
597 }
598 
599 /* V1 coefficient count exceeds length of containing block */
wmfw_v1_coeff_count_exceeds_block(struct kunit * test)600 static void wmfw_v1_coeff_count_exceeds_block(struct kunit *test)
601 {
602 	struct cs_dsp_test *priv = test->priv;
603 	struct cs_dsp_test_local *local = priv->local;
604 	struct firmware *wmfw;
605 	struct wmfw_header *header;
606 	struct wmfw_region *region;
607 	struct wmfw_adsp_alg_data *alg_data;
608 
609 	/* Create alg info block with a coefficient */
610 	cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
611 					      cs_dsp_wmfw_err_test_mock_algs[0].id,
612 					      "abc", "de");
613 	cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &mock_coeff_template);
614 	cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
615 
616 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
617 
618 	/* Sanity-check that the good wmfw loads ok */
619 	KUNIT_EXPECT_EQ(test,
620 			cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
621 			0);
622 	cs_dsp_power_down(priv->dsp);
623 
624 	header = (struct wmfw_header *)wmfw->data;
625 	region = (struct wmfw_region *)&wmfw->data[le32_to_cpu(header->len)];
626 	alg_data = (struct wmfw_adsp_alg_data *)region->data;
627 
628 	/* Sanity check we're pointing at the alg header */
629 	KUNIT_ASSERT_EQ(test, le32_to_cpu(alg_data->id), cs_dsp_wmfw_err_test_mock_algs[0].id);
630 
631 	/* Add one to the coefficient count */
632 	alg_data->ncoeff = cpu_to_le32(le32_to_cpu(alg_data->ncoeff) + 1);
633 	KUNIT_EXPECT_EQ(test,
634 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
635 			-EOVERFLOW);
636 
637 	/* Make the coefficient count garbage */
638 	alg_data->ncoeff = cpu_to_le32(0xffffffff);
639 	KUNIT_EXPECT_EQ(test,
640 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
641 			-EOVERFLOW);
642 
643 	alg_data->ncoeff = cpu_to_le32(0x7fffffff);
644 	KUNIT_EXPECT_EQ(test,
645 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
646 			-EOVERFLOW);
647 
648 	alg_data->ncoeff = cpu_to_le32(0x80000000);
649 	KUNIT_EXPECT_EQ(test,
650 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
651 			-EOVERFLOW);
652 }
653 
654 /* V2+ coefficient count exceeds length of containing block */
wmfw_v2_coeff_count_exceeds_block(struct kunit * test)655 static void wmfw_v2_coeff_count_exceeds_block(struct kunit *test)
656 {
657 	struct cs_dsp_test *priv = test->priv;
658 	struct cs_dsp_test_local *local = priv->local;
659 	struct firmware *wmfw;
660 	struct wmfw_header *header;
661 	struct wmfw_region *region;
662 	__le32 *alg_data, *ncoeff;
663 
664 	/* Create alg info block with a coefficient */
665 	cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
666 					      cs_dsp_wmfw_err_test_mock_algs[0].id,
667 					      "abc", "de");
668 	cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &mock_coeff_template);
669 	cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
670 
671 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
672 
673 	/* Sanity-check that the good wmfw loads ok */
674 	KUNIT_EXPECT_EQ(test,
675 			cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
676 			0);
677 	cs_dsp_power_down(priv->dsp);
678 
679 	header = (struct wmfw_header *)wmfw->data;
680 	region = (struct wmfw_region *)&wmfw->data[le32_to_cpu(header->len)];
681 	alg_data = (__force __le32 *)region->data;
682 
683 	/* Sanity check we're pointing at the alg header */
684 	KUNIT_ASSERT_EQ(test, le32_to_cpu(alg_data[0]), cs_dsp_wmfw_err_test_mock_algs[0].id);
685 
686 	ncoeff = (__force __le32 *)&alg_data[3];
687 	KUNIT_ASSERT_EQ(test, le32_to_cpu(*ncoeff), 1);
688 
689 	/* Add one to the coefficient count */
690 	*ncoeff = cpu_to_le32(2);
691 	KUNIT_EXPECT_EQ(test,
692 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
693 			-EOVERFLOW);
694 
695 	/* Make the coefficient count garbage */
696 	*ncoeff = cpu_to_le32(0xffffffff);
697 	KUNIT_EXPECT_EQ(test,
698 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
699 			-EOVERFLOW);
700 
701 	*ncoeff = cpu_to_le32(0x7fffffff);
702 	KUNIT_EXPECT_EQ(test,
703 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
704 			-EOVERFLOW);
705 
706 	*ncoeff = cpu_to_le32(0x80000000);
707 	KUNIT_EXPECT_EQ(test,
708 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
709 			-EOVERFLOW);
710 }
711 
712 /* V2+ coefficient block size exceeds length of containing block */
wmfw_v2_coeff_block_size_exceeds_block(struct kunit * test)713 static void wmfw_v2_coeff_block_size_exceeds_block(struct kunit *test)
714 {
715 	struct cs_dsp_test *priv = test->priv;
716 	struct cs_dsp_test_local *local = priv->local;
717 	struct firmware *wmfw;
718 	struct wmfw_header *header;
719 	struct wmfw_region *region;
720 	__le32 *alg_data, *coeff;
721 
722 	/* Create alg info block with a coefficient */
723 	cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
724 					      cs_dsp_wmfw_err_test_mock_algs[0].id,
725 					      "abc", "de");
726 	cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &mock_coeff_template);
727 	cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
728 
729 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
730 
731 	/* Sanity-check that the good wmfw loads ok */
732 	KUNIT_EXPECT_EQ(test,
733 			cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
734 			0);
735 	cs_dsp_power_down(priv->dsp);
736 
737 	header = (struct wmfw_header *)wmfw->data;
738 	region = (struct wmfw_region *)&wmfw->data[le32_to_cpu(header->len)];
739 	alg_data = (__force __le32 *)region->data;
740 
741 	/* Sanity check we're pointing at the alg header */
742 	KUNIT_ASSERT_EQ(test, le32_to_cpu(alg_data[0]), cs_dsp_wmfw_err_test_mock_algs[0].id);
743 
744 	/* Sanity check we're pointing at the coeff block */
745 	coeff = (__force __le32 *)&alg_data[4];
746 	KUNIT_ASSERT_EQ(test, le32_to_cpu(coeff[0]), mock_coeff_template.mem_type << 16);
747 
748 	/* Add one to the block size */
749 	coeff[1] = cpu_to_le32(le32_to_cpu(coeff[1]) + 1);
750 	KUNIT_EXPECT_EQ(test,
751 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
752 			-EOVERFLOW);
753 
754 	/* Make the block size garbage */
755 	coeff[1] = cpu_to_le32(0xffffffff);
756 	KUNIT_EXPECT_EQ(test,
757 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
758 			-EOVERFLOW);
759 
760 	coeff[1] = cpu_to_le32(0x7fffffff);
761 	KUNIT_EXPECT_EQ(test,
762 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
763 			-EOVERFLOW);
764 
765 	coeff[1] = cpu_to_le32(0x80000000);
766 	KUNIT_EXPECT_EQ(test,
767 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
768 			-EOVERFLOW);
769 }
770 
771 /* V1 coeff name does not have NUL terminator */
wmfw_v1_coeff_name_unterminated(struct kunit * test)772 static void wmfw_v1_coeff_name_unterminated(struct kunit *test)
773 {
774 	struct cs_dsp_test *priv = test->priv;
775 	struct cs_dsp_test_local *local = priv->local;
776 	struct firmware *wmfw;
777 	struct wmfw_header *header;
778 	struct wmfw_region *region;
779 	struct wmfw_adsp_alg_data *alg_data;
780 	struct wmfw_adsp_coeff_data *coeff;
781 	struct cs_dsp_coeff_ctl *ctl;
782 
783 	/* Create alg info block with a coefficient */
784 	cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
785 					      cs_dsp_wmfw_err_test_mock_algs[0].id,
786 					      "abc", "de");
787 	cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &mock_coeff_template);
788 	cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
789 
790 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
791 
792 	header = (struct wmfw_header *)wmfw->data;
793 	region = (struct wmfw_region *)&wmfw->data[le32_to_cpu(header->len)];
794 	alg_data = (struct wmfw_adsp_alg_data *)region->data;
795 
796 	/* Sanity check we're pointing at the alg header */
797 	KUNIT_ASSERT_EQ(test, le32_to_cpu(alg_data->id), cs_dsp_wmfw_err_test_mock_algs[0].id);
798 	KUNIT_ASSERT_EQ(test, le32_to_cpu(alg_data->ncoeff), 1);
799 
800 	coeff = (void *)alg_data->data;
801 
802 	/* Write a string to the coeff name that overflows the array */
803 	memset(coeff->descr, 0, sizeof(coeff->descr));
804 	memset(coeff->name, 'A', sizeof(coeff->name));
805 	memset(coeff->descr, 'A', sizeof(coeff->descr) - 1);
806 
807 	/*
808 	 * Sanity-check that a strlen would overflow coeff->name.
809 	 * FORTIFY_STRING obstructs testing what strlen() would actually
810 	 * return, so instead verify that a strnlen() returns
811 	 * sizeof(coeff->name[]), therefore it doesn't have a NUL.
812 	 */
813 	KUNIT_ASSERT_EQ(test, strnlen(coeff->name, sizeof(coeff->name)),
814 			sizeof(coeff->name));
815 
816 	/*
817 	 * V1 controls do not have names, but cs_dsp parses the name
818 	 * field. It should load the file successfully and create the
819 	 * control.
820 	 * If FORTIFY_STRING is enabled it will detect a buffer overflow
821 	 * if cs_dsp string length walks past end of coeff name array.
822 	 */
823 	KUNIT_EXPECT_EQ(test,
824 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
825 			0);
826 	ctl = list_first_entry_or_null(&priv->dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
827 	KUNIT_ASSERT_NOT_NULL(test, ctl);
828 	KUNIT_EXPECT_EQ(test, ctl->subname_len, 0);
829 }
830 
831 /* V2+ coefficient shortname exceeds length of coeff block */
wmfw_v2_coeff_shortname_exceeds_block(struct kunit * test)832 static void wmfw_v2_coeff_shortname_exceeds_block(struct kunit *test)
833 {
834 	struct cs_dsp_test *priv = test->priv;
835 	struct cs_dsp_test_local *local = priv->local;
836 	struct firmware *wmfw;
837 	struct wmfw_header *header;
838 	struct wmfw_region *region;
839 	__le32 *alg_data, *coeff;
840 
841 	/* Create alg info block with a coefficient */
842 	cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
843 					      cs_dsp_wmfw_err_test_mock_algs[0].id,
844 					      "abc", "de");
845 	cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &mock_coeff_template);
846 	cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
847 
848 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
849 
850 	/* Sanity-check that the good wmfw loads ok */
851 	KUNIT_EXPECT_EQ(test,
852 			cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
853 			0);
854 	cs_dsp_power_down(priv->dsp);
855 
856 	header = (struct wmfw_header *)wmfw->data;
857 	region = (struct wmfw_region *)&wmfw->data[le32_to_cpu(header->len)];
858 	alg_data = (__force __le32 *)region->data;
859 
860 	/* Sanity check we're pointing at the alg header */
861 	KUNIT_ASSERT_EQ(test, le32_to_cpu(alg_data[0]), cs_dsp_wmfw_err_test_mock_algs[0].id);
862 
863 	/* Sanity check we're pointing at the coeff block */
864 	coeff = (__force __le32 *)&alg_data[4];
865 	KUNIT_ASSERT_EQ(test, le32_to_cpu(coeff[0]), mock_coeff_template.mem_type << 16);
866 
867 	/* Add one to the shortname length */
868 	coeff[2] = cpu_to_le32(le32_to_cpu(coeff[2]) + 1);
869 	KUNIT_EXPECT_EQ(test,
870 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
871 			-EOVERFLOW);
872 
873 	/* Maximum shortname length */
874 	coeff[2] = cpu_to_le32(255);
875 	KUNIT_EXPECT_EQ(test,
876 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
877 			-EOVERFLOW);
878 }
879 
880 /* V2+ coefficient fullname exceeds length of coeff block */
wmfw_v2_coeff_fullname_exceeds_block(struct kunit * test)881 static void wmfw_v2_coeff_fullname_exceeds_block(struct kunit *test)
882 {
883 	struct cs_dsp_test *priv = test->priv;
884 	struct cs_dsp_test_local *local = priv->local;
885 	struct firmware *wmfw;
886 	struct wmfw_header *header;
887 	struct wmfw_region *region;
888 	__le32 *alg_data, *coeff, *fullname;
889 	size_t shortlen;
890 
891 	/* Create alg info block with a coefficient */
892 	cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
893 					      cs_dsp_wmfw_err_test_mock_algs[0].id,
894 					      "abc", "de");
895 	cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &mock_coeff_template);
896 	cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
897 
898 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
899 
900 	/* Sanity-check that the good wmfw loads ok */
901 	KUNIT_EXPECT_EQ(test,
902 			cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
903 			0);
904 	cs_dsp_power_down(priv->dsp);
905 
906 	header = (struct wmfw_header *)wmfw->data;
907 	region = (struct wmfw_region *)&wmfw->data[le32_to_cpu(header->len)];
908 	alg_data = (__force __le32 *)region->data;
909 
910 	/* Sanity check we're pointing at the alg header */
911 	KUNIT_ASSERT_EQ(test, le32_to_cpu(alg_data[0]), cs_dsp_wmfw_err_test_mock_algs[0].id);
912 
913 	/* Sanity check we're pointing at the coeff block */
914 	coeff = (__force __le32 *)&alg_data[4];
915 	KUNIT_ASSERT_EQ(test, le32_to_cpu(coeff[0]), mock_coeff_template.mem_type << 16);
916 
917 	/* Fullname follows the shortname rounded up to a __le32 boundary */
918 	shortlen = round_up(le32_to_cpu(coeff[2]) & 0xff, sizeof(__le32));
919 	fullname = &coeff[2] + (shortlen / sizeof(*coeff));
920 
921 	/* Fullname increases in blocks of __le32 so increase past the current __le32 */
922 	fullname[0] = cpu_to_le32(round_up(le32_to_cpu(fullname[0]) + 1, sizeof(__le32)));
923 	KUNIT_EXPECT_EQ(test,
924 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
925 			-EOVERFLOW);
926 
927 	/* Maximum fullname length */
928 	fullname[0] = cpu_to_le32(255);
929 	KUNIT_EXPECT_EQ(test,
930 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
931 			-EOVERFLOW);
932 }
933 
934 /* V2+ coefficient description exceeds length of coeff block */
wmfw_v2_coeff_description_exceeds_block(struct kunit * test)935 static void wmfw_v2_coeff_description_exceeds_block(struct kunit *test)
936 {
937 	struct cs_dsp_test *priv = test->priv;
938 	struct cs_dsp_test_local *local = priv->local;
939 	struct firmware *wmfw;
940 	struct wmfw_header *header;
941 	struct wmfw_region *region;
942 	__le32 *alg_data, *coeff, *fullname, *description;
943 	size_t namelen;
944 
945 	/* Create alg info block with a coefficient */
946 	cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
947 					      cs_dsp_wmfw_err_test_mock_algs[0].id,
948 					      "abc", "de");
949 	cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &mock_coeff_template);
950 	cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
951 
952 	wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
953 
954 	/* Sanity-check that the good wmfw loads ok */
955 	KUNIT_EXPECT_EQ(test,
956 			cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
957 			0);
958 	cs_dsp_power_down(priv->dsp);
959 
960 	header = (struct wmfw_header *)wmfw->data;
961 	region = (struct wmfw_region *)&wmfw->data[le32_to_cpu(header->len)];
962 	alg_data = (__force __le32 *)region->data;
963 
964 	/* Sanity check we're pointing at the alg header */
965 	KUNIT_ASSERT_EQ(test, le32_to_cpu(alg_data[0]), cs_dsp_wmfw_err_test_mock_algs[0].id);
966 
967 	/* Sanity check we're pointing at the coeff block */
968 	coeff = (__force __le32 *)&alg_data[4];
969 	KUNIT_ASSERT_EQ(test, le32_to_cpu(coeff[0]), mock_coeff_template.mem_type << 16);
970 
971 	/* Description follows the shortname and fullname rounded up to __le32 boundaries */
972 	namelen = round_up(le32_to_cpu(coeff[2]) & 0xff, sizeof(__le32));
973 	fullname = &coeff[2] + (namelen / sizeof(*coeff));
974 	namelen = round_up(le32_to_cpu(fullname[0]) & 0xff, sizeof(__le32));
975 	description = fullname + (namelen / sizeof(*fullname));
976 
977 	/* Description increases in blocks of __le32 so increase past the current __le32 */
978 	description[0] = cpu_to_le32(round_up(le32_to_cpu(fullname[0]) + 1, sizeof(__le32)));
979 	KUNIT_EXPECT_EQ(test,
980 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
981 			-EOVERFLOW);
982 
983 	/* Maximum description length */
984 	fullname[0] = cpu_to_le32(0xffff);
985 	KUNIT_EXPECT_EQ(test,
986 			cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
987 			-EOVERFLOW);
988 }
989 
cs_dsp_wmfw_err_test_exit(struct kunit * test)990 static void cs_dsp_wmfw_err_test_exit(struct kunit *test)
991 {
992 	/*
993 	 * Testing error conditions can produce a lot of log output
994 	 * from cs_dsp error messages, so rate limit the test cases.
995 	 */
996 	usleep_range(200, 500);
997 }
998 
cs_dsp_wmfw_err_test_common_init(struct kunit * test,struct cs_dsp * dsp,int wmfw_version)999 static int cs_dsp_wmfw_err_test_common_init(struct kunit *test, struct cs_dsp *dsp,
1000 					    int wmfw_version)
1001 {
1002 	struct cs_dsp_test *priv;
1003 	struct cs_dsp_test_local *local;
1004 	struct device *test_dev;
1005 	int ret;
1006 
1007 	priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
1008 	if (!priv)
1009 		return -ENOMEM;
1010 
1011 	local = kunit_kzalloc(test, sizeof(struct cs_dsp_test_local), GFP_KERNEL);
1012 	if (!local)
1013 		return -ENOMEM;
1014 
1015 	priv->test = test;
1016 	priv->dsp = dsp;
1017 	test->priv = priv;
1018 	priv->local = local;
1019 	local->wmfw_version = wmfw_version;
1020 
1021 	/* Create dummy struct device */
1022 	test_dev = kunit_device_register(test, "cs_dsp_test_drv");
1023 	if (IS_ERR(test_dev))
1024 		return PTR_ERR(test_dev);
1025 
1026 	dsp->dev = get_device(test_dev);
1027 	if (!dsp->dev)
1028 		return -ENODEV;
1029 
1030 	ret = kunit_add_action_or_reset(test, _put_device_wrapper, dsp->dev);
1031 	if (ret)
1032 		return ret;
1033 
1034 	dev_set_drvdata(dsp->dev, priv);
1035 
1036 	/* Allocate regmap */
1037 	ret = cs_dsp_mock_regmap_init(priv);
1038 	if (ret)
1039 		return ret;
1040 
1041 	/*
1042 	 * There must always be a XM header with at least 1 algorithm,
1043 	 * so create a dummy one and pre-populate XM so the wmfw doesn't
1044 	 * have to contain an XM blob.
1045 	 */
1046 	local->xm_header = cs_dsp_create_mock_xm_header(priv,
1047 							cs_dsp_wmfw_err_test_mock_algs,
1048 							ARRAY_SIZE(cs_dsp_wmfw_err_test_mock_algs));
1049 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, local->xm_header);
1050 	cs_dsp_mock_xm_header_write_to_regmap(local->xm_header);
1051 
1052 	local->wmfw_builder = cs_dsp_mock_wmfw_init(priv, local->wmfw_version);
1053 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, local->wmfw_builder);
1054 
1055 	/* Init cs_dsp */
1056 	dsp->client_ops = kunit_kzalloc(test, sizeof(*dsp->client_ops), GFP_KERNEL);
1057 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dsp->client_ops);
1058 
1059 	switch (dsp->type) {
1060 	case WMFW_ADSP2:
1061 		ret = cs_dsp_adsp2_init(dsp);
1062 		break;
1063 	case WMFW_HALO:
1064 		ret = cs_dsp_halo_init(dsp);
1065 		break;
1066 	default:
1067 		KUNIT_FAIL(test, "Untested DSP type %d\n", dsp->type);
1068 		return -EINVAL;
1069 	}
1070 
1071 	if (ret)
1072 		return ret;
1073 
1074 	/* Automatically call cs_dsp_remove() when test case ends */
1075 	return kunit_add_action_or_reset(priv->test, _cs_dsp_remove_wrapper, dsp);
1076 }
1077 
cs_dsp_wmfw_err_test_halo_init(struct kunit * test)1078 static int cs_dsp_wmfw_err_test_halo_init(struct kunit *test)
1079 {
1080 	struct cs_dsp *dsp;
1081 
1082 	/* Fill in cs_dsp and initialize */
1083 	dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
1084 	if (!dsp)
1085 		return -ENOMEM;
1086 
1087 	dsp->num = 1;
1088 	dsp->type = WMFW_HALO;
1089 	dsp->mem = cs_dsp_mock_halo_dsp1_regions;
1090 	dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_halo_dsp1_region_sizes);
1091 	dsp->base = cs_dsp_mock_halo_core_base;
1092 	dsp->base_sysinfo = cs_dsp_mock_halo_sysinfo_base;
1093 
1094 	return cs_dsp_wmfw_err_test_common_init(test, dsp, 3);
1095 }
1096 
cs_dsp_wmfw_err_test_adsp2_32bit_init(struct kunit * test,int wmfw_ver)1097 static int cs_dsp_wmfw_err_test_adsp2_32bit_init(struct kunit *test, int wmfw_ver)
1098 {
1099 	struct cs_dsp *dsp;
1100 
1101 	/* Fill in cs_dsp and initialize */
1102 	dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
1103 	if (!dsp)
1104 		return -ENOMEM;
1105 
1106 	dsp->num = 1;
1107 	dsp->type = WMFW_ADSP2;
1108 	dsp->rev = 1;
1109 	dsp->mem = cs_dsp_mock_adsp2_32bit_dsp1_regions;
1110 	dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_32bit_dsp1_region_sizes);
1111 	dsp->base = cs_dsp_mock_adsp2_32bit_sysbase;
1112 
1113 	return cs_dsp_wmfw_err_test_common_init(test, dsp, wmfw_ver);
1114 }
1115 
cs_dsp_wmfw_err_test_adsp2_32bit_wmfw0_init(struct kunit * test)1116 static int cs_dsp_wmfw_err_test_adsp2_32bit_wmfw0_init(struct kunit *test)
1117 {
1118 	return cs_dsp_wmfw_err_test_adsp2_32bit_init(test, 0);
1119 }
1120 
cs_dsp_wmfw_err_test_adsp2_32bit_wmfw1_init(struct kunit * test)1121 static int cs_dsp_wmfw_err_test_adsp2_32bit_wmfw1_init(struct kunit *test)
1122 {
1123 	return cs_dsp_wmfw_err_test_adsp2_32bit_init(test, 1);
1124 }
1125 
cs_dsp_wmfw_err_test_adsp2_32bit_wmfw2_init(struct kunit * test)1126 static int cs_dsp_wmfw_err_test_adsp2_32bit_wmfw2_init(struct kunit *test)
1127 {
1128 	return cs_dsp_wmfw_err_test_adsp2_32bit_init(test, 2);
1129 }
1130 
cs_dsp_wmfw_err_test_adsp2_16bit_init(struct kunit * test,int wmfw_ver)1131 static int cs_dsp_wmfw_err_test_adsp2_16bit_init(struct kunit *test, int wmfw_ver)
1132 {
1133 	struct cs_dsp *dsp;
1134 
1135 	/* Fill in cs_dsp and initialize */
1136 	dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
1137 	if (!dsp)
1138 		return -ENOMEM;
1139 
1140 	dsp->num = 1;
1141 	dsp->type = WMFW_ADSP2;
1142 	dsp->rev = 0;
1143 	dsp->mem = cs_dsp_mock_adsp2_16bit_dsp1_regions;
1144 	dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_16bit_dsp1_region_sizes);
1145 	dsp->base = cs_dsp_mock_adsp2_16bit_sysbase;
1146 
1147 	return cs_dsp_wmfw_err_test_common_init(test, dsp, wmfw_ver);
1148 }
1149 
cs_dsp_wmfw_err_test_adsp2_16bit_wmfw0_init(struct kunit * test)1150 static int cs_dsp_wmfw_err_test_adsp2_16bit_wmfw0_init(struct kunit *test)
1151 {
1152 	return cs_dsp_wmfw_err_test_adsp2_16bit_init(test, 0);
1153 }
1154 
cs_dsp_wmfw_err_test_adsp2_16bit_wmfw1_init(struct kunit * test)1155 static int cs_dsp_wmfw_err_test_adsp2_16bit_wmfw1_init(struct kunit *test)
1156 {
1157 	return cs_dsp_wmfw_err_test_adsp2_16bit_init(test, 1);
1158 }
1159 
cs_dsp_wmfw_err_test_adsp2_16bit_wmfw2_init(struct kunit * test)1160 static int cs_dsp_wmfw_err_test_adsp2_16bit_wmfw2_init(struct kunit *test)
1161 {
1162 	return cs_dsp_wmfw_err_test_adsp2_16bit_init(test, 2);
1163 }
1164 
cs_dsp_wmfw_err_block_types_desc(const struct cs_dsp_wmfw_test_param * param,char * desc)1165 static void cs_dsp_wmfw_err_block_types_desc(const struct cs_dsp_wmfw_test_param *param,
1166 					     char *desc)
1167 {
1168 	snprintf(desc, KUNIT_PARAM_DESC_SIZE, "block_type:%#x", param->block_type);
1169 }
1170 
1171 static const struct cs_dsp_wmfw_test_param wmfw_valid_block_types_adsp2_cases[] = {
1172 	{ .block_type = WMFW_INFO_TEXT },
1173 	{ .block_type = WMFW_ADSP2_PM },
1174 	{ .block_type = WMFW_ADSP2_YM },
1175 };
1176 
1177 KUNIT_ARRAY_PARAM(wmfw_valid_block_types_adsp2,
1178 		  wmfw_valid_block_types_adsp2_cases,
1179 		  cs_dsp_wmfw_err_block_types_desc);
1180 
1181 static const struct cs_dsp_wmfw_test_param wmfw_valid_block_types_halo_cases[] = {
1182 	{ .block_type = WMFW_INFO_TEXT },
1183 	{ .block_type = WMFW_HALO_PM_PACKED },
1184 	{ .block_type = WMFW_ADSP2_YM },
1185 };
1186 
1187 KUNIT_ARRAY_PARAM(wmfw_valid_block_types_halo,
1188 		  wmfw_valid_block_types_halo_cases,
1189 		  cs_dsp_wmfw_err_block_types_desc);
1190 
1191 static const struct cs_dsp_wmfw_test_param wmfw_invalid_block_types_cases[] = {
1192 	{ .block_type = 0x33 },
1193 	{ .block_type = 0xf5 },
1194 	{ .block_type = 0xc0 },
1195 };
1196 
1197 KUNIT_ARRAY_PARAM(wmfw_invalid_block_types,
1198 		  wmfw_invalid_block_types_cases,
1199 		  cs_dsp_wmfw_err_block_types_desc);
1200 
1201 static struct kunit_case cs_dsp_wmfw_err_test_cases_v0[] = {
1202 	KUNIT_CASE(wmfw_load_with_unknown_blocks),
1203 	KUNIT_CASE(wmfw_err_wrong_magic),
1204 	KUNIT_CASE(wmfw_err_too_short_for_header),
1205 	KUNIT_CASE(wmfw_err_bad_header_length),
1206 	KUNIT_CASE(wmfw_err_bad_core_type),
1207 
1208 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_header, wmfw_valid_block_types_adsp2_gen_params),
1209 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_header, wmfw_invalid_block_types_gen_params),
1210 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_payload, wmfw_valid_block_types_adsp2_gen_params),
1211 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_header, wmfw_invalid_block_types_gen_params),
1212 	KUNIT_CASE_PARAM(wmfw_block_payload_len_garbage, wmfw_valid_block_types_adsp2_gen_params),
1213 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_header, wmfw_invalid_block_types_gen_params),
1214 
1215 	{ } /* terminator */
1216 };
1217 
1218 static struct kunit_case cs_dsp_wmfw_err_test_cases_v1[] = {
1219 	KUNIT_CASE(wmfw_load_with_unknown_blocks),
1220 	KUNIT_CASE(wmfw_err_wrong_magic),
1221 	KUNIT_CASE(wmfw_err_too_short_for_header),
1222 	KUNIT_CASE(wmfw_err_bad_header_length),
1223 	KUNIT_CASE(wmfw_err_bad_core_type),
1224 
1225 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_header, wmfw_valid_block_types_adsp2_gen_params),
1226 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_header, wmfw_invalid_block_types_gen_params),
1227 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_payload, wmfw_valid_block_types_adsp2_gen_params),
1228 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_header, wmfw_invalid_block_types_gen_params),
1229 	KUNIT_CASE_PARAM(wmfw_block_payload_len_garbage, wmfw_valid_block_types_adsp2_gen_params),
1230 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_header, wmfw_invalid_block_types_gen_params),
1231 
1232 	KUNIT_CASE(wmfw_too_short_for_alg_header),
1233 	KUNIT_CASE(wmfw_v1_alg_name_unterminated),
1234 	KUNIT_CASE(wmfw_v1_coeff_count_exceeds_block),
1235 	KUNIT_CASE(wmfw_v1_coeff_name_unterminated),
1236 
1237 	{ } /* terminator */
1238 };
1239 
1240 static struct kunit_case cs_dsp_wmfw_err_test_cases_v2[] = {
1241 	KUNIT_CASE(wmfw_load_with_unknown_blocks),
1242 	KUNIT_CASE(wmfw_err_wrong_magic),
1243 	KUNIT_CASE(wmfw_err_too_short_for_header),
1244 	KUNIT_CASE(wmfw_err_bad_header_length),
1245 	KUNIT_CASE(wmfw_err_bad_core_type),
1246 
1247 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_header, wmfw_valid_block_types_adsp2_gen_params),
1248 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_header, wmfw_invalid_block_types_gen_params),
1249 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_payload, wmfw_valid_block_types_adsp2_gen_params),
1250 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_header, wmfw_invalid_block_types_gen_params),
1251 	KUNIT_CASE_PARAM(wmfw_block_payload_len_garbage, wmfw_valid_block_types_adsp2_gen_params),
1252 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_header, wmfw_invalid_block_types_gen_params),
1253 
1254 	KUNIT_CASE(wmfw_too_short_for_alg_header),
1255 	KUNIT_CASE(wmfw_v2_alg_name_exceeds_block),
1256 	KUNIT_CASE(wmfw_v2_alg_description_exceeds_block),
1257 	KUNIT_CASE(wmfw_v2_coeff_count_exceeds_block),
1258 	KUNIT_CASE(wmfw_v2_coeff_block_size_exceeds_block),
1259 	KUNIT_CASE(wmfw_v2_coeff_shortname_exceeds_block),
1260 	KUNIT_CASE(wmfw_v2_coeff_fullname_exceeds_block),
1261 	KUNIT_CASE(wmfw_v2_coeff_description_exceeds_block),
1262 
1263 	{ } /* terminator */
1264 };
1265 
1266 static struct kunit_case cs_dsp_wmfw_err_test_cases_v3[] = {
1267 	KUNIT_CASE(wmfw_load_with_unknown_blocks),
1268 	KUNIT_CASE(wmfw_err_wrong_magic),
1269 	KUNIT_CASE(wmfw_err_too_short_for_header),
1270 	KUNIT_CASE(wmfw_err_bad_header_length),
1271 	KUNIT_CASE(wmfw_err_bad_core_type),
1272 
1273 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_header, wmfw_valid_block_types_halo_gen_params),
1274 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_header, wmfw_invalid_block_types_gen_params),
1275 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_payload, wmfw_valid_block_types_halo_gen_params),
1276 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_header, wmfw_invalid_block_types_gen_params),
1277 	KUNIT_CASE_PARAM(wmfw_block_payload_len_garbage, wmfw_valid_block_types_halo_gen_params),
1278 	KUNIT_CASE_PARAM(wmfw_too_short_for_block_header, wmfw_invalid_block_types_gen_params),
1279 
1280 	KUNIT_CASE(wmfw_too_short_for_alg_header),
1281 	KUNIT_CASE(wmfw_v2_alg_name_exceeds_block),
1282 	KUNIT_CASE(wmfw_v2_alg_description_exceeds_block),
1283 	KUNIT_CASE(wmfw_v2_coeff_count_exceeds_block),
1284 	KUNIT_CASE(wmfw_v2_coeff_block_size_exceeds_block),
1285 	KUNIT_CASE(wmfw_v2_coeff_shortname_exceeds_block),
1286 	KUNIT_CASE(wmfw_v2_coeff_fullname_exceeds_block),
1287 	KUNIT_CASE(wmfw_v2_coeff_description_exceeds_block),
1288 
1289 	{ } /* terminator */
1290 };
1291 
1292 static struct kunit_suite cs_dsp_wmfw_err_test_halo = {
1293 	.name = "cs_dsp_wmfwV3_err_halo",
1294 	.init = cs_dsp_wmfw_err_test_halo_init,
1295 	.exit = cs_dsp_wmfw_err_test_exit,
1296 	.test_cases = cs_dsp_wmfw_err_test_cases_v3,
1297 };
1298 
1299 static struct kunit_suite cs_dsp_wmfw_err_test_adsp2_32bit_wmfw0 = {
1300 	.name = "cs_dsp_wmfwV0_err_adsp2_32bit",
1301 	.init = cs_dsp_wmfw_err_test_adsp2_32bit_wmfw0_init,
1302 	.exit = cs_dsp_wmfw_err_test_exit,
1303 	.test_cases = cs_dsp_wmfw_err_test_cases_v0,
1304 };
1305 
1306 static struct kunit_suite cs_dsp_wmfw_err_test_adsp2_32bit_wmfw1 = {
1307 	.name = "cs_dsp_wmfwV1_err_adsp2_32bit",
1308 	.init = cs_dsp_wmfw_err_test_adsp2_32bit_wmfw1_init,
1309 	.exit = cs_dsp_wmfw_err_test_exit,
1310 	.test_cases = cs_dsp_wmfw_err_test_cases_v1,
1311 };
1312 
1313 static struct kunit_suite cs_dsp_wmfw_err_test_adsp2_32bit_wmfw2 = {
1314 	.name = "cs_dsp_wmfwV2_err_adsp2_32bit",
1315 	.init = cs_dsp_wmfw_err_test_adsp2_32bit_wmfw2_init,
1316 	.exit = cs_dsp_wmfw_err_test_exit,
1317 	.test_cases = cs_dsp_wmfw_err_test_cases_v2,
1318 };
1319 
1320 static struct kunit_suite cs_dsp_wmfw_err_test_adsp2_16bit_wmfw0 = {
1321 	.name = "cs_dsp_wmfwV0_err_adsp2_16bit",
1322 	.init = cs_dsp_wmfw_err_test_adsp2_16bit_wmfw0_init,
1323 	.exit = cs_dsp_wmfw_err_test_exit,
1324 	.test_cases = cs_dsp_wmfw_err_test_cases_v0,
1325 };
1326 
1327 static struct kunit_suite cs_dsp_wmfw_err_test_adsp2_16bit_wmfw1 = {
1328 	.name = "cs_dsp_wmfwV1_err_adsp2_16bit",
1329 	.init = cs_dsp_wmfw_err_test_adsp2_16bit_wmfw1_init,
1330 	.exit = cs_dsp_wmfw_err_test_exit,
1331 	.test_cases = cs_dsp_wmfw_err_test_cases_v1,
1332 };
1333 
1334 static struct kunit_suite cs_dsp_wmfw_err_test_adsp2_16bit_wmfw2 = {
1335 	.name = "cs_dsp_wmfwV2_err_adsp2_16bit",
1336 	.init = cs_dsp_wmfw_err_test_adsp2_16bit_wmfw2_init,
1337 	.exit = cs_dsp_wmfw_err_test_exit,
1338 	.test_cases = cs_dsp_wmfw_err_test_cases_v2,
1339 };
1340 
1341 kunit_test_suites(&cs_dsp_wmfw_err_test_halo,
1342 		  &cs_dsp_wmfw_err_test_adsp2_32bit_wmfw0,
1343 		  &cs_dsp_wmfw_err_test_adsp2_32bit_wmfw1,
1344 		  &cs_dsp_wmfw_err_test_adsp2_32bit_wmfw2,
1345 		  &cs_dsp_wmfw_err_test_adsp2_16bit_wmfw0,
1346 		  &cs_dsp_wmfw_err_test_adsp2_16bit_wmfw1,
1347 		  &cs_dsp_wmfw_err_test_adsp2_16bit_wmfw2);
1348