Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/cirrus/test/cs_dsp_mock_wmfw.c
26444 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
//
3
// wmfw file builder for cs_dsp KUnit tests.
4
//
5
// Copyright (C) 2024 Cirrus Logic, Inc. and
6
// Cirrus Logic International Semiconductor Ltd.
7
8
#include <kunit/resource.h>
9
#include <kunit/test.h>
10
#include <linux/firmware/cirrus/cs_dsp.h>
11
#include <linux/firmware/cirrus/cs_dsp_test_utils.h>
12
#include <linux/firmware/cirrus/wmfw.h>
13
#include <linux/firmware.h>
14
#include <linux/math.h>
15
#include <linux/overflow.h>
16
#include <linux/string.h>
17
#include <linux/vmalloc.h>
18
19
/* Buffer large enough for bin file content */
20
#define CS_DSP_MOCK_WMFW_BUF_SIZE 131072
21
22
struct cs_dsp_mock_wmfw_builder {
23
struct cs_dsp_test *test_priv;
24
int format_version;
25
void *buf;
26
size_t buf_size_bytes;
27
void *write_p;
28
size_t bytes_used;
29
30
void *alg_data_header;
31
unsigned int num_coeffs;
32
};
33
34
struct wmfw_adsp2_halo_header {
35
struct wmfw_header header;
36
struct wmfw_adsp2_sizes sizes;
37
struct wmfw_footer footer;
38
} __packed;
39
40
struct wmfw_long_string {
41
__le16 len;
42
u8 data[] __nonstring __counted_by(len);
43
} __packed;
44
45
struct wmfw_short_string {
46
u8 len;
47
u8 data[] __nonstring __counted_by(len);
48
} __packed;
49
50
KUNIT_DEFINE_ACTION_WRAPPER(vfree_action_wrapper, vfree, void *)
51
52
/**
53
* cs_dsp_mock_wmfw_format_version() - Return format version.
54
*
55
* @builder: Pointer to struct cs_dsp_mock_wmfw_builder.
56
*
57
* Return: Format version.
58
*/
59
int cs_dsp_mock_wmfw_format_version(struct cs_dsp_mock_wmfw_builder *builder)
60
{
61
return builder->format_version;
62
}
63
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_format_version, "FW_CS_DSP_KUNIT_TEST_UTILS");
64
65
/**
66
* cs_dsp_mock_wmfw_get_firmware() - Get struct firmware wrapper for data.
67
*
68
* @builder: Pointer to struct cs_dsp_mock_wmfw_builder.
69
*
70
* Return: Pointer to a struct firmware wrapper for the data.
71
*/
72
struct firmware *cs_dsp_mock_wmfw_get_firmware(struct cs_dsp_mock_wmfw_builder *builder)
73
{
74
struct firmware *fw;
75
76
if (!builder)
77
return NULL;
78
79
fw = kunit_kzalloc(builder->test_priv->test, sizeof(*fw), GFP_KERNEL);
80
KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, fw);
81
82
fw->data = builder->buf;
83
fw->size = builder->bytes_used;
84
85
return fw;
86
}
87
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_get_firmware, "FW_CS_DSP_KUNIT_TEST_UTILS");
88
89
/**
90
* cs_dsp_mock_wmfw_add_raw_block() - Add a block to the wmfw file.
91
*
92
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
93
* @block_type: Block type.
94
* @offset: Offset.
95
* @payload_data: Pointer to buffer containing the payload data,
96
* or NULL if no data.
97
* @payload_len_bytes: Length of payload data in bytes, or zero.
98
*/
99
void cs_dsp_mock_wmfw_add_raw_block(struct cs_dsp_mock_wmfw_builder *builder,
100
int block_type, unsigned int offset,
101
const void *payload_data, size_t payload_len_bytes)
102
{
103
struct wmfw_region *header = builder->write_p;
104
unsigned int bytes_needed = struct_size_t(struct wmfw_region, data, payload_len_bytes);
105
106
KUNIT_ASSERT_TRUE(builder->test_priv->test,
107
(builder->write_p + bytes_needed) <
108
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
109
110
header->offset = cpu_to_le32(offset | (block_type << 24));
111
header->len = cpu_to_le32(payload_len_bytes);
112
if (payload_len_bytes > 0)
113
memcpy(header->data, payload_data, payload_len_bytes);
114
115
builder->write_p += bytes_needed;
116
builder->bytes_used += bytes_needed;
117
}
118
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_add_raw_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
119
120
/**
121
* cs_dsp_mock_wmfw_add_info() - Add an info block to the wmfw file.
122
*
123
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
124
* @info: Pointer to info string to be copied into the file.
125
*
126
* The string will be padded to a length that is a multiple of 4 bytes.
127
*/
128
void cs_dsp_mock_wmfw_add_info(struct cs_dsp_mock_wmfw_builder *builder,
129
const char *info)
130
{
131
size_t info_len = strlen(info);
132
char *tmp = NULL;
133
134
if (info_len % 4) {
135
/* Create a padded string with length a multiple of 4 */
136
size_t copy_len = info_len;
137
info_len = round_up(info_len, 4);
138
tmp = kunit_kzalloc(builder->test_priv->test, info_len, GFP_KERNEL);
139
KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, tmp);
140
memcpy(tmp, info, copy_len);
141
info = tmp;
142
}
143
144
cs_dsp_mock_wmfw_add_raw_block(builder, WMFW_INFO_TEXT, 0, info, info_len);
145
kunit_kfree(builder->test_priv->test, tmp);
146
}
147
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_add_info, "FW_CS_DSP_KUNIT_TEST_UTILS");
148
149
/**
150
* cs_dsp_mock_wmfw_add_data_block() - Add a data block to the wmfw file.
151
*
152
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
153
* @mem_region: Memory region for the block.
154
* @mem_offset_dsp_words: Offset to start of destination in DSP words.
155
* @payload_data: Pointer to buffer containing the payload data.
156
* @payload_len_bytes: Length of payload data in bytes.
157
*/
158
void cs_dsp_mock_wmfw_add_data_block(struct cs_dsp_mock_wmfw_builder *builder,
159
int mem_region, unsigned int mem_offset_dsp_words,
160
const void *payload_data, size_t payload_len_bytes)
161
{
162
/* Blob payload length must be a multiple of 4 */
163
KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0);
164
165
cs_dsp_mock_wmfw_add_raw_block(builder, mem_region, mem_offset_dsp_words,
166
payload_data, payload_len_bytes);
167
}
168
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_add_data_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
169
170
void cs_dsp_mock_wmfw_start_alg_info_block(struct cs_dsp_mock_wmfw_builder *builder,
171
unsigned int alg_id,
172
const char *name,
173
const char *description)
174
{
175
struct wmfw_region *rgn = builder->write_p;
176
struct wmfw_adsp_alg_data *v1;
177
struct wmfw_short_string *shortstring;
178
struct wmfw_long_string *longstring;
179
size_t bytes_needed, name_len, description_len;
180
int offset;
181
182
KUNIT_ASSERT_LE(builder->test_priv->test, alg_id, 0xffffff);
183
184
/* Bytes needed for region header */
185
bytes_needed = offsetof(struct wmfw_region, data);
186
187
builder->alg_data_header = builder->write_p;
188
builder->num_coeffs = 0;
189
190
switch (builder->format_version) {
191
case 0:
192
KUNIT_FAIL(builder->test_priv->test, "wmfwV0 does not have alg blocks\n");
193
return;
194
case 1:
195
bytes_needed += offsetof(struct wmfw_adsp_alg_data, data);
196
KUNIT_ASSERT_TRUE(builder->test_priv->test,
197
(builder->write_p + bytes_needed) <
198
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
199
200
memset(builder->write_p, 0, bytes_needed);
201
202
/* Create region header */
203
rgn->offset = cpu_to_le32(WMFW_ALGORITHM_DATA << 24);
204
205
/* Create algorithm entry */
206
v1 = (struct wmfw_adsp_alg_data *)&rgn->data[0];
207
v1->id = cpu_to_le32(alg_id);
208
if (name)
209
strscpy(v1->name, name, sizeof(v1->name));
210
211
if (description)
212
strscpy(v1->descr, description, sizeof(v1->descr));
213
break;
214
default:
215
name_len = 0;
216
description_len = 0;
217
218
if (name)
219
name_len = strlen(name);
220
221
if (description)
222
description_len = strlen(description);
223
224
bytes_needed += sizeof(__le32); /* alg id */
225
bytes_needed += round_up(name_len + sizeof(u8), sizeof(__le32));
226
bytes_needed += round_up(description_len + sizeof(__le16), sizeof(__le32));
227
bytes_needed += sizeof(__le32); /* coeff count */
228
229
KUNIT_ASSERT_TRUE(builder->test_priv->test,
230
(builder->write_p + bytes_needed) <
231
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
232
233
memset(builder->write_p, 0, bytes_needed);
234
235
/* Create region header */
236
rgn->offset = cpu_to_le32(WMFW_ALGORITHM_DATA << 24);
237
238
/* Create algorithm entry */
239
*(__force __le32 *)&rgn->data[0] = cpu_to_le32(alg_id);
240
241
shortstring = (struct wmfw_short_string *)&rgn->data[4];
242
shortstring->len = name_len;
243
244
if (name_len)
245
memcpy(shortstring->data, name, name_len);
246
247
/* Round up to next __le32 */
248
offset = round_up(4 + struct_size_t(struct wmfw_short_string, data, name_len),
249
sizeof(__le32));
250
251
longstring = (struct wmfw_long_string *)&rgn->data[offset];
252
longstring->len = cpu_to_le16(description_len);
253
254
if (description_len)
255
memcpy(longstring->data, description, description_len);
256
break;
257
}
258
259
builder->write_p += bytes_needed;
260
builder->bytes_used += bytes_needed;
261
}
262
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_start_alg_info_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
263
264
void cs_dsp_mock_wmfw_add_coeff_desc(struct cs_dsp_mock_wmfw_builder *builder,
265
const struct cs_dsp_mock_coeff_def *def)
266
{
267
struct wmfw_adsp_coeff_data *v1;
268
struct wmfw_short_string *shortstring;
269
struct wmfw_long_string *longstring;
270
size_t bytes_needed, shortname_len, fullname_len, description_len;
271
__le32 *ple32;
272
273
KUNIT_ASSERT_NOT_NULL(builder->test_priv->test, builder->alg_data_header);
274
275
switch (builder->format_version) {
276
case 0:
277
return;
278
case 1:
279
bytes_needed = offsetof(struct wmfw_adsp_coeff_data, data);
280
KUNIT_ASSERT_TRUE(builder->test_priv->test,
281
(builder->write_p + bytes_needed) <
282
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
283
284
v1 = (struct wmfw_adsp_coeff_data *)builder->write_p;
285
memset(v1, 0, sizeof(*v1));
286
v1->hdr.offset = cpu_to_le16(def->offset_dsp_words);
287
v1->hdr.type = cpu_to_le16(def->mem_type);
288
v1->hdr.size = cpu_to_le32(bytes_needed - sizeof(v1->hdr));
289
v1->ctl_type = cpu_to_le16(def->type);
290
v1->flags = cpu_to_le16(def->flags);
291
v1->len = cpu_to_le32(def->length_bytes);
292
293
if (def->fullname)
294
strscpy(v1->name, def->fullname, sizeof(v1->name));
295
296
if (def->description)
297
strscpy(v1->descr, def->description, sizeof(v1->descr));
298
break;
299
default:
300
fullname_len = 0;
301
description_len = 0;
302
shortname_len = strlen(def->shortname);
303
304
if (def->fullname)
305
fullname_len = strlen(def->fullname);
306
307
if (def->description)
308
description_len = strlen(def->description);
309
310
bytes_needed = sizeof(__le32) * 2; /* type, offset and size */
311
bytes_needed += round_up(shortname_len + sizeof(u8), sizeof(__le32));
312
bytes_needed += round_up(fullname_len + sizeof(u8), sizeof(__le32));
313
bytes_needed += round_up(description_len + sizeof(__le16), sizeof(__le32));
314
bytes_needed += sizeof(__le32) * 2; /* flags, type and length */
315
KUNIT_ASSERT_TRUE(builder->test_priv->test,
316
(builder->write_p + bytes_needed) <
317
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
318
319
ple32 = (__force __le32 *)builder->write_p;
320
*ple32++ = cpu_to_le32(def->offset_dsp_words | (def->mem_type << 16));
321
*ple32++ = cpu_to_le32(bytes_needed - sizeof(__le32) - sizeof(__le32));
322
323
shortstring = (__force struct wmfw_short_string *)ple32;
324
shortstring->len = shortname_len;
325
memcpy(shortstring->data, def->shortname, shortname_len);
326
327
/* Round up to next __le32 multiple */
328
ple32 += round_up(struct_size_t(struct wmfw_short_string, data, shortname_len),
329
sizeof(*ple32)) / sizeof(*ple32);
330
331
shortstring = (__force struct wmfw_short_string *)ple32;
332
shortstring->len = fullname_len;
333
memcpy(shortstring->data, def->fullname, fullname_len);
334
335
/* Round up to next __le32 multiple */
336
ple32 += round_up(struct_size_t(struct wmfw_short_string, data, fullname_len),
337
sizeof(*ple32)) / sizeof(*ple32);
338
339
longstring = (__force struct wmfw_long_string *)ple32;
340
longstring->len = cpu_to_le16(description_len);
341
memcpy(longstring->data, def->description, description_len);
342
343
/* Round up to next __le32 multiple */
344
ple32 += round_up(struct_size_t(struct wmfw_long_string, data, description_len),
345
sizeof(*ple32)) / sizeof(*ple32);
346
347
*ple32++ = cpu_to_le32(def->type | (def->flags << 16));
348
*ple32 = cpu_to_le32(def->length_bytes);
349
break;
350
}
351
352
builder->write_p += bytes_needed;
353
builder->bytes_used += bytes_needed;
354
builder->num_coeffs++;
355
}
356
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_add_coeff_desc, "FW_CS_DSP_KUNIT_TEST_UTILS");
357
358
void cs_dsp_mock_wmfw_end_alg_info_block(struct cs_dsp_mock_wmfw_builder *builder)
359
{
360
struct wmfw_region *rgn = builder->alg_data_header;
361
struct wmfw_adsp_alg_data *v1;
362
const struct wmfw_short_string *shortstring;
363
const struct wmfw_long_string *longstring;
364
size_t offset;
365
366
KUNIT_ASSERT_NOT_NULL(builder->test_priv->test, rgn);
367
368
/* Fill in data size */
369
rgn->len = cpu_to_le32((u8 *)builder->write_p - (u8 *)rgn->data);
370
371
/* Fill in coefficient count */
372
switch (builder->format_version) {
373
case 0:
374
return;
375
case 1:
376
v1 = (struct wmfw_adsp_alg_data *)&rgn->data[0];
377
v1->ncoeff = cpu_to_le32(builder->num_coeffs);
378
break;
379
default:
380
offset = 4; /* skip alg id */
381
382
/* Get name length and round up to __le32 multiple */
383
shortstring = (const struct wmfw_short_string *)&rgn->data[offset];
384
offset += round_up(struct_size_t(struct wmfw_short_string, data, shortstring->len),
385
sizeof(__le32));
386
387
/* Get description length and round up to __le32 multiple */
388
longstring = (const struct wmfw_long_string *)&rgn->data[offset];
389
offset += round_up(struct_size_t(struct wmfw_long_string, data,
390
le16_to_cpu(longstring->len)),
391
sizeof(__le32));
392
393
*(__force __le32 *)&rgn->data[offset] = cpu_to_le32(builder->num_coeffs);
394
break;
395
}
396
397
builder->alg_data_header = NULL;
398
}
399
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_end_alg_info_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
400
401
static void cs_dsp_init_adsp2_halo_wmfw(struct cs_dsp_mock_wmfw_builder *builder)
402
{
403
struct wmfw_adsp2_halo_header *hdr = builder->buf;
404
const struct cs_dsp *dsp = builder->test_priv->dsp;
405
406
memcpy(hdr->header.magic, "WMFW", sizeof(hdr->header.magic));
407
hdr->header.len = cpu_to_le32(sizeof(*hdr));
408
hdr->header.ver = builder->format_version;
409
hdr->header.core = dsp->type;
410
hdr->header.rev = cpu_to_le16(dsp->rev);
411
412
hdr->sizes.pm = cpu_to_le32(cs_dsp_mock_size_of_region(dsp, WMFW_ADSP2_PM));
413
hdr->sizes.xm = cpu_to_le32(cs_dsp_mock_size_of_region(dsp, WMFW_ADSP2_XM));
414
hdr->sizes.ym = cpu_to_le32(cs_dsp_mock_size_of_region(dsp, WMFW_ADSP2_YM));
415
416
switch (dsp->type) {
417
case WMFW_ADSP2:
418
hdr->sizes.zm = cpu_to_le32(cs_dsp_mock_size_of_region(dsp, WMFW_ADSP2_ZM));
419
break;
420
default:
421
break;
422
}
423
424
builder->write_p = &hdr[1];
425
builder->bytes_used += sizeof(*hdr);
426
}
427
428
/**
429
* cs_dsp_mock_wmfw_init() - Initialize a struct cs_dsp_mock_wmfw_builder.
430
*
431
* @priv: Pointer to struct cs_dsp_test.
432
* @format_version: Required wmfw format version.
433
*
434
* Return: Pointer to created struct cs_dsp_mock_wmfw_builder.
435
*/
436
struct cs_dsp_mock_wmfw_builder *cs_dsp_mock_wmfw_init(struct cs_dsp_test *priv,
437
int format_version)
438
{
439
struct cs_dsp_mock_wmfw_builder *builder;
440
441
KUNIT_ASSERT_LE(priv->test, format_version, 0xff);
442
443
/* If format version isn't given use the default for the target core */
444
if (format_version < 0) {
445
switch (priv->dsp->type) {
446
case WMFW_ADSP2:
447
format_version = 2;
448
break;
449
default:
450
format_version = 3;
451
break;
452
}
453
}
454
455
builder = kunit_kzalloc(priv->test, sizeof(*builder), GFP_KERNEL);
456
KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder);
457
458
builder->test_priv = priv;
459
builder->format_version = format_version;
460
461
builder->buf = vmalloc(CS_DSP_MOCK_WMFW_BUF_SIZE);
462
KUNIT_ASSERT_NOT_NULL(priv->test, builder->buf);
463
kunit_add_action_or_reset(priv->test, vfree_action_wrapper, builder->buf);
464
465
builder->buf_size_bytes = CS_DSP_MOCK_WMFW_BUF_SIZE;
466
467
switch (priv->dsp->type) {
468
case WMFW_ADSP2:
469
case WMFW_HALO:
470
cs_dsp_init_adsp2_halo_wmfw(builder);
471
break;
472
default:
473
break;
474
}
475
476
return builder;
477
}
478
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_init, "FW_CS_DSP_KUNIT_TEST_UTILS");
479
480