Skip to content

Commit 5d90e05

Browse files
sbrandengregkh
authored andcommitted
test_firmware: Test partial read support
Add additional hooks to test_firmware to pass in support for partial file read using request_firmware_into_buf(): buf_size: size of buffer to request firmware into partial: indicates that a partial file request is being made file_offset: to indicate offset into file to request Also update firmware selftests to use the new partial read test API. Signed-off-by: Scott Branden <[email protected]> Co-developed-by: Kees Cook <[email protected]> Signed-off-by: Kees Cook <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 59cdb23 commit 5d90e05

File tree

2 files changed

+233
-12
lines changed

2 files changed

+233
-12
lines changed

lib/test_firmware.c

Lines changed: 142 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ struct test_batched_req {
5252
* @name: the name of the firmware file to look for
5353
* @into_buf: when the into_buf is used if this is true
5454
* request_firmware_into_buf() will be used instead.
55+
* @buf_size: size of buf to allocate when into_buf is true
56+
* @file_offset: file offset to request when calling request_firmware_into_buf
57+
* @partial: partial read opt when calling request_firmware_into_buf
5558
* @sync_direct: when the sync trigger is used if this is true
5659
* request_firmware_direct() will be used instead.
5760
* @send_uevent: whether or not to send a uevent for async requests
@@ -91,6 +94,9 @@ struct test_batched_req {
9194
struct test_config {
9295
char *name;
9396
bool into_buf;
97+
size_t buf_size;
98+
size_t file_offset;
99+
bool partial;
94100
bool sync_direct;
95101
bool send_uevent;
96102
u8 num_requests;
@@ -185,6 +191,9 @@ static int __test_firmware_config_init(void)
185191
test_fw_config->num_requests = TEST_FIRMWARE_NUM_REQS;
186192
test_fw_config->send_uevent = true;
187193
test_fw_config->into_buf = false;
194+
test_fw_config->buf_size = TEST_FIRMWARE_BUF_SIZE;
195+
test_fw_config->file_offset = 0;
196+
test_fw_config->partial = false;
188197
test_fw_config->sync_direct = false;
189198
test_fw_config->req_firmware = request_firmware;
190199
test_fw_config->test_result = 0;
@@ -238,28 +247,35 @@ static ssize_t config_show(struct device *dev,
238247
dev_name(dev));
239248

240249
if (test_fw_config->name)
241-
len += scnprintf(buf+len, PAGE_SIZE - len,
250+
len += scnprintf(buf + len, PAGE_SIZE - len,
242251
"name:\t%s\n",
243252
test_fw_config->name);
244253
else
245-
len += scnprintf(buf+len, PAGE_SIZE - len,
254+
len += scnprintf(buf + len, PAGE_SIZE - len,
246255
"name:\tEMTPY\n");
247256

248-
len += scnprintf(buf+len, PAGE_SIZE - len,
257+
len += scnprintf(buf + len, PAGE_SIZE - len,
249258
"num_requests:\t%u\n", test_fw_config->num_requests);
250259

251-
len += scnprintf(buf+len, PAGE_SIZE - len,
260+
len += scnprintf(buf + len, PAGE_SIZE - len,
252261
"send_uevent:\t\t%s\n",
253262
test_fw_config->send_uevent ?
254263
"FW_ACTION_HOTPLUG" :
255264
"FW_ACTION_NOHOTPLUG");
256-
len += scnprintf(buf+len, PAGE_SIZE - len,
265+
len += scnprintf(buf + len, PAGE_SIZE - len,
257266
"into_buf:\t\t%s\n",
258267
test_fw_config->into_buf ? "true" : "false");
259-
len += scnprintf(buf+len, PAGE_SIZE - len,
268+
len += scnprintf(buf + len, PAGE_SIZE - len,
269+
"buf_size:\t%zu\n", test_fw_config->buf_size);
270+
len += scnprintf(buf + len, PAGE_SIZE - len,
271+
"file_offset:\t%zu\n", test_fw_config->file_offset);
272+
len += scnprintf(buf + len, PAGE_SIZE - len,
273+
"partial:\t\t%s\n",
274+
test_fw_config->partial ? "true" : "false");
275+
len += scnprintf(buf + len, PAGE_SIZE - len,
260276
"sync_direct:\t\t%s\n",
261277
test_fw_config->sync_direct ? "true" : "false");
262-
len += scnprintf(buf+len, PAGE_SIZE - len,
278+
len += scnprintf(buf + len, PAGE_SIZE - len,
263279
"read_fw_idx:\t%u\n", test_fw_config->read_fw_idx);
264280

265281
mutex_unlock(&test_fw_mutex);
@@ -317,6 +333,30 @@ static ssize_t test_dev_config_show_bool(char *buf, bool val)
317333
return snprintf(buf, PAGE_SIZE, "%d\n", val);
318334
}
319335

336+
static int test_dev_config_update_size_t(const char *buf,
337+
size_t size,
338+
size_t *cfg)
339+
{
340+
int ret;
341+
long new;
342+
343+
ret = kstrtol(buf, 10, &new);
344+
if (ret)
345+
return ret;
346+
347+
mutex_lock(&test_fw_mutex);
348+
*(size_t *)cfg = new;
349+
mutex_unlock(&test_fw_mutex);
350+
351+
/* Always return full write size even if we didn't consume all */
352+
return size;
353+
}
354+
355+
static ssize_t test_dev_config_show_size_t(char *buf, size_t val)
356+
{
357+
return snprintf(buf, PAGE_SIZE, "%zu\n", val);
358+
}
359+
320360
static ssize_t test_dev_config_show_int(char *buf, int val)
321361
{
322362
return snprintf(buf, PAGE_SIZE, "%d\n", val);
@@ -402,6 +442,83 @@ static ssize_t config_into_buf_show(struct device *dev,
402442
}
403443
static DEVICE_ATTR_RW(config_into_buf);
404444

445+
static ssize_t config_buf_size_store(struct device *dev,
446+
struct device_attribute *attr,
447+
const char *buf, size_t count)
448+
{
449+
int rc;
450+
451+
mutex_lock(&test_fw_mutex);
452+
if (test_fw_config->reqs) {
453+
pr_err("Must call release_all_firmware prior to changing config\n");
454+
rc = -EINVAL;
455+
mutex_unlock(&test_fw_mutex);
456+
goto out;
457+
}
458+
mutex_unlock(&test_fw_mutex);
459+
460+
rc = test_dev_config_update_size_t(buf, count,
461+
&test_fw_config->buf_size);
462+
463+
out:
464+
return rc;
465+
}
466+
467+
static ssize_t config_buf_size_show(struct device *dev,
468+
struct device_attribute *attr,
469+
char *buf)
470+
{
471+
return test_dev_config_show_size_t(buf, test_fw_config->buf_size);
472+
}
473+
static DEVICE_ATTR_RW(config_buf_size);
474+
475+
static ssize_t config_file_offset_store(struct device *dev,
476+
struct device_attribute *attr,
477+
const char *buf, size_t count)
478+
{
479+
int rc;
480+
481+
mutex_lock(&test_fw_mutex);
482+
if (test_fw_config->reqs) {
483+
pr_err("Must call release_all_firmware prior to changing config\n");
484+
rc = -EINVAL;
485+
mutex_unlock(&test_fw_mutex);
486+
goto out;
487+
}
488+
mutex_unlock(&test_fw_mutex);
489+
490+
rc = test_dev_config_update_size_t(buf, count,
491+
&test_fw_config->file_offset);
492+
493+
out:
494+
return rc;
495+
}
496+
497+
static ssize_t config_file_offset_show(struct device *dev,
498+
struct device_attribute *attr,
499+
char *buf)
500+
{
501+
return test_dev_config_show_size_t(buf, test_fw_config->file_offset);
502+
}
503+
static DEVICE_ATTR_RW(config_file_offset);
504+
505+
static ssize_t config_partial_store(struct device *dev,
506+
struct device_attribute *attr,
507+
const char *buf, size_t count)
508+
{
509+
return test_dev_config_update_bool(buf,
510+
count,
511+
&test_fw_config->partial);
512+
}
513+
514+
static ssize_t config_partial_show(struct device *dev,
515+
struct device_attribute *attr,
516+
char *buf)
517+
{
518+
return test_dev_config_show_bool(buf, test_fw_config->partial);
519+
}
520+
static DEVICE_ATTR_RW(config_partial);
521+
405522
static ssize_t config_sync_direct_store(struct device *dev,
406523
struct device_attribute *attr,
407524
const char *buf, size_t count)
@@ -659,11 +776,21 @@ static int test_fw_run_batch_request(void *data)
659776
if (!test_buf)
660777
return -ENOSPC;
661778

662-
req->rc = request_firmware_into_buf(&req->fw,
663-
req->name,
664-
req->dev,
665-
test_buf,
666-
TEST_FIRMWARE_BUF_SIZE);
779+
if (test_fw_config->partial)
780+
req->rc = request_partial_firmware_into_buf
781+
(&req->fw,
782+
req->name,
783+
req->dev,
784+
test_buf,
785+
test_fw_config->buf_size,
786+
test_fw_config->file_offset);
787+
else
788+
req->rc = request_firmware_into_buf
789+
(&req->fw,
790+
req->name,
791+
req->dev,
792+
test_buf,
793+
test_fw_config->buf_size);
667794
if (!req->fw)
668795
kfree(test_buf);
669796
} else {
@@ -936,6 +1063,9 @@ static struct attribute *test_dev_attrs[] = {
9361063
TEST_FW_DEV_ATTR(config_name),
9371064
TEST_FW_DEV_ATTR(config_num_requests),
9381065
TEST_FW_DEV_ATTR(config_into_buf),
1066+
TEST_FW_DEV_ATTR(config_buf_size),
1067+
TEST_FW_DEV_ATTR(config_file_offset),
1068+
TEST_FW_DEV_ATTR(config_partial),
9391069
TEST_FW_DEV_ATTR(config_sync_direct),
9401070
TEST_FW_DEV_ATTR(config_send_uevent),
9411071
TEST_FW_DEV_ATTR(config_read_fw_idx),

tools/testing/selftests/firmware/fw_filesystem.sh

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,26 @@ config_unset_into_buf()
149149
echo 0 > $DIR/config_into_buf
150150
}
151151

152+
config_set_buf_size()
153+
{
154+
echo $1 > $DIR/config_buf_size
155+
}
156+
157+
config_set_file_offset()
158+
{
159+
echo $1 > $DIR/config_file_offset
160+
}
161+
162+
config_set_partial()
163+
{
164+
echo 1 > $DIR/config_partial
165+
}
166+
167+
config_unset_partial()
168+
{
169+
echo 0 > $DIR/config_partial
170+
}
171+
152172
config_set_sync_direct()
153173
{
154174
echo 1 > $DIR/config_sync_direct
@@ -207,6 +227,35 @@ read_firmwares()
207227
done
208228
}
209229

230+
read_partial_firmwares()
231+
{
232+
if [ "$(cat $DIR/config_into_buf)" == "1" ]; then
233+
fwfile="${FW_INTO_BUF}"
234+
else
235+
fwfile="${FW}"
236+
fi
237+
238+
if [ "$1" = "xzonly" ]; then
239+
fwfile="${fwfile}-orig"
240+
fi
241+
242+
# Strip fwfile down to match partial offset and length
243+
partial_data="$(cat $fwfile)"
244+
partial_data="${partial_data:$2:$3}"
245+
246+
for i in $(seq 0 3); do
247+
config_set_read_fw_idx $i
248+
249+
read_firmware="$(cat $DIR/read_firmware)"
250+
251+
# Verify the contents are what we expect.
252+
if [ $read_firmware != $partial_data ]; then
253+
echo "request #$i: partial firmware was not loaded" >&2
254+
exit 1
255+
fi
256+
done
257+
}
258+
210259
read_firmwares_expect_nofile()
211260
{
212261
for i in $(seq 0 3); do
@@ -242,6 +291,21 @@ test_batched_request_firmware_into_buf_nofile()
242291
echo "OK"
243292
}
244293

294+
test_request_partial_firmware_into_buf_nofile()
295+
{
296+
echo -n "Test request_partial_firmware_into_buf() off=$1 size=$2 nofile: "
297+
config_reset
298+
config_set_name nope-test-firmware.bin
299+
config_set_into_buf
300+
config_set_partial
301+
config_set_buf_size $2
302+
config_set_file_offset $1
303+
config_trigger_sync
304+
read_firmwares_expect_nofile
305+
release_all_firmware
306+
echo "OK"
307+
}
308+
245309
test_batched_request_firmware_direct_nofile()
246310
{
247311
echo -n "Batched request_firmware_direct() nofile try #$1: "
@@ -356,6 +420,21 @@ test_request_firmware_nowait_custom()
356420
echo "OK"
357421
}
358422

423+
test_request_partial_firmware_into_buf()
424+
{
425+
echo -n "Test request_partial_firmware_into_buf() off=$1 size=$2: "
426+
config_reset
427+
config_set_name $TEST_FIRMWARE_INTO_BUF_FILENAME
428+
config_set_into_buf
429+
config_set_partial
430+
config_set_buf_size $2
431+
config_set_file_offset $1
432+
config_trigger_sync
433+
read_partial_firmwares normal $1 $2
434+
release_all_firmware
435+
echo "OK"
436+
}
437+
359438
# Only continue if batched request triggers are present on the
360439
# test-firmware driver
361440
test_config_present
@@ -383,6 +462,12 @@ for i in $(seq 1 5); do
383462
test_request_firmware_nowait_custom $i normal
384463
done
385464

465+
# Partial loads cannot use fallback, so do not repeat tests.
466+
test_request_partial_firmware_into_buf 0 10
467+
test_request_partial_firmware_into_buf 0 5
468+
test_request_partial_firmware_into_buf 1 6
469+
test_request_partial_firmware_into_buf 2 10
470+
386471
# Test for file not found, errors are expected, the failure would be
387472
# a hung task, which would require a hard reset.
388473
echo
@@ -407,6 +492,12 @@ for i in $(seq 1 5); do
407492
test_request_firmware_nowait_custom_nofile $i
408493
done
409494

495+
# Partial loads cannot use fallback, so do not repeat tests.
496+
test_request_partial_firmware_into_buf_nofile 0 10
497+
test_request_partial_firmware_into_buf_nofile 0 5
498+
test_request_partial_firmware_into_buf_nofile 1 6
499+
test_request_partial_firmware_into_buf_nofile 2 10
500+
410501
test "$HAS_FW_LOADER_COMPRESS" != "yes" && exit 0
411502

412503
# test with both files present

0 commit comments

Comments
 (0)