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