* [LTP] [PATCH v3 1/3] fw_load: Modernize ltp_fw_load kernel module
2026-06-10 17:07 [LTP] [PATCH v3 0/3] Rewrite fw_load test using new API Andrea Cervesato
@ 2026-06-10 17:07 ` Andrea Cervesato
2026-06-10 19:44 ` [LTP] " linuxtestproject.agent
2026-06-10 17:07 ` [LTP] [PATCH v3 2/3] firmware/fw_load: rewrite firmware loading test using new LTP API Andrea Cervesato
2026-06-10 17:07 ` [LTP] [PATCH v3 3/3] firmware/fw_load: add fw_load02 for custom firmware path Andrea Cervesato
2 siblings, 1 reply; 5+ messages in thread
From: Andrea Cervesato @ 2026-06-10 17:07 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Fix VLA (banned since kernel v4.20), replace sscanf() with
kstrtoint(), reset test_result between invocations, make the
loop variable local and pass expected data explicitly.
Update license headers, fix brace style, drop obsolete README
in favour of the in-source documentation.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
testcases/kernel/firmware/fw_load_kernel/Makefile | 16 +--
testcases/kernel/firmware/fw_load_kernel/README | 16 ---
.../kernel/firmware/fw_load_kernel/ltp_fw_load.c | 114 ++++++++++++---------
3 files changed, 66 insertions(+), 80 deletions(-)
diff --git a/testcases/kernel/firmware/fw_load_kernel/Makefile b/testcases/kernel/firmware/fw_load_kernel/Makefile
index 73996996f3397fb0a7f4616457168213587e654f..e81b300faaa6b4056d4b5f3d1417c381a000256d 100644
--- a/testcases/kernel/firmware/fw_load_kernel/Makefile
+++ b/testcases/kernel/firmware/fw_load_kernel/Makefile
@@ -1,18 +1,6 @@
# Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation; either version 2 of
-# the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it would be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write the Free Software Foundation,
-# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+# Copyright (c) Linux Test Project, 2026
+# SPDX-License-Identifier: GPL-2.0-or-later
ifneq ($(KERNELRELEASE),)
diff --git a/testcases/kernel/firmware/fw_load_kernel/README b/testcases/kernel/firmware/fw_load_kernel/README
deleted file mode 100644
index 97507fd99aa3bead708cd4bf599f4abf9ec6e2da..0000000000000000000000000000000000000000
--- a/testcases/kernel/firmware/fw_load_kernel/README
+++ /dev/null
@@ -1,16 +0,0 @@
-The aim of the test is to check device firmware loading. Since kernel 3.7
-firmware loading changed to direct loading (by-pass udev). The test consists
-of the two parts:
- - userspace part
- - kernelspace part
-
-This is a kernel module, which is a part of the device firmware loading test.
-It allows to call request_firmware kernel function with specified parameters.
-Template firmware file name and expected firmware file's data size are passed
-as the insmod command line parameters. Then, the number of firmware test files
-should be written to sysfs file 'fwnum' (the maximum number is 32). This write
-will initiate request firmware procedure. In the end, results can be read from
-'result' device sysfs file. Also, some information regarding module loading,
-can be obtained by looking at kernel log file.
-
-It is automatically used by userspace part of the test.
diff --git a/testcases/kernel/firmware/fw_load_kernel/ltp_fw_load.c b/testcases/kernel/firmware/fw_load_kernel/ltp_fw_load.c
index b7397e8f13154a16c2626517545c3c14d153a043..6018e9a630d2f2328c8023568f2b61f7b80831fe 100644
--- a/testcases/kernel/firmware/fw_load_kernel/ltp_fw_load.c
+++ b/testcases/kernel/firmware/fw_load_kernel/ltp_fw_load.c
@@ -1,25 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved.
+ * Author: Alexey Kodanev <alexey.kodanev@oracle.com>
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
+ * Copyright (c) 2026 Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*
+ * Kernel module helper for the fw_load test.
*
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Registers a virtual device (ltp_fw_load) that exposes two sysfs
+ * attributes:
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * fwnum (write-only) - accepts the number of firmware files to
+ * request (1-32). Writing triggers request_firmware() for
+ * each file named n<i>_<fw_name> (i = 0 .. fwnum-1).
+ * Each loaded blob is verified against the expected size
+ * (fw_size) and byte pattern (every byte == i).
*
- * Author:
- * Alexey Kodanev <alexey.kodanev@oracle.com>
+ * result (read-only) - bitmask of per-file pass/fail results.
+ * Bit i is set when n<i>_<fw_name> was loaded and
+ * verified successfully.
*
- * This module is trying to load external test firmware files (n#_load_tst.fw).
- * In the end, it writes results to /sys/devices/ltp_fw_load/result file.
+ * Module parameters:
+ * fw_name - template firmware file name (default: load_tst.fw)
+ * fw_size - expected firmware blob size (default: 0x1000)
*/
#include <linux/module.h>
@@ -33,12 +38,11 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alexey Kodanev <alexey.kodanev@oracle.com>");
MODULE_DESCRIPTION("This module is checking device firmware loading");
-#define TCID "ltp_fw_load"
+#define MODULE_NAME "ltp_fw_load"
+#define MAX_NAME 64
static char *fw_name = "load_tst.fw";
static int fw_size = 0x1000;
-static int max_name = 64;
-static int fw;
module_param(fw_name, charp, 0444);
MODULE_PARM_DESC(fw_name, "Template firmware file name: n#_name");
@@ -54,47 +58,53 @@ static int test_result;
static void device_release(struct device *dev)
{
- pr_info(TCID ": device released\n");
+ pr_info(MODULE_NAME ": device released\n");
}
static struct device tdev = {
- .init_name = TCID,
+ .init_name = MODULE_NAME,
.release = device_release,
};
-/* read and print firmware data */
-static int fw_read(const u8 *data, size_t size)
+/* read and verify firmware data */
+static int fw_read(const u8 *data, size_t size, u8 expected)
{
size_t i;
- pr_info(TCID ": Firmware has size '%zu'\n", size);
+
+ pr_info(MODULE_NAME ": Firmware has size '%zu'\n", size);
if (size != fw_size) {
- pr_err(TCID ": Expected firmware size '%d'\n", fw_size);
+ pr_err(MODULE_NAME ": Expected firmware size '%d'\n", fw_size);
return -1;
}
+
for (i = 0; i < size; ++i) {
- if (data[i] != (u8)fw) {
- pr_err(TCID ": Unexpected firmware data\n");
+ if (data[i] != expected) {
+ pr_err(MODULE_NAME ": Unexpected firmware data\n");
return -1;
}
}
+
return 0;
}
-static int try_request_fw(const char *name)
+static int try_request_fw(const char *name, u8 expected)
{
int err;
const struct firmware *fw_entry = NULL;
+
err = request_firmware(&fw_entry, name, &tdev);
if (!err) {
- pr_info(TCID ": firmware '%s' requested\n", name);
- err = fw_read(fw_entry->data, fw_entry->size);
- } else
- pr_err(TCID ": Can't request firmware '%s'\n", name);
+ pr_info(MODULE_NAME ": firmware '%s' requested\n", name);
+ err = fw_read(fw_entry->data, fw_entry->size, expected);
+ } else {
+ pr_err(MODULE_NAME ": Can't request firmware '%s'\n", name);
+ }
+
release_firmware(fw_entry);
return err;
}
-/* print test result to sysfs file */
+/* Print test result to sysfs file. */
static ssize_t sys_result(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -102,26 +112,28 @@ static ssize_t sys_result(struct device *dev,
}
static DEVICE_ATTR(result, S_IRUSR, sys_result, NULL);
-/*
- * get the number of firmware files and
- * perform firmware requests
- */
+/* Get the number of firmware files and perform firmware requests. */
static ssize_t sys_fwnum(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
+ struct device_attribute *attr, const char *buf, size_t count)
{
- int err, fw_num = 0;
+ int err, fw, fw_num;
- sscanf(buf, "%d", &fw_num);
- if (fw_num <= 0 || fw_num > 32) {
- pr_err(TCID ": Unexpected number of firmwares '%d'", fw_num);
- return count;
+ err = kstrtoint(buf, 10, &fw_num);
+ if (err || fw_num <= 0 || fw_num > 32) {
+ pr_err(MODULE_NAME ": Unexpected number of firmwares '%s'", buf);
+ return err ? err : -EINVAL;
}
+
+ test_result = 0;
+
for (fw = 0; fw < fw_num; ++fw) {
- char name[max_name];
- snprintf(name, max_name, "n%d_%s", fw, fw_name);
- err = try_request_fw(name);
+ char name[MAX_NAME];
+
+ snprintf(name, sizeof(name), "n%d_%s", fw, fw_name);
+ err = try_request_fw(name, (u8)fw);
test_result |= (err == 0) << fw;
}
+
return count;
}
static DEVICE_ATTR(fwnum, S_IWUSR, NULL, sys_fwnum);
@@ -132,23 +144,25 @@ static int test_init(void)
err = device_register(&tdev);
if (err) {
- pr_err(TCID ": Unable to register device\n");
+ pr_err(MODULE_NAME ": Unable to register device\n");
return err;
}
- pr_info(TCID ": device registered\n");
+ pr_info(MODULE_NAME ": device registered\n");
err = device_create_file(&tdev, &dev_attr_result);
if (err) {
- pr_err(TCID ": Can't create sysfs file 'result'\n");
+ pr_err(MODULE_NAME ": Can't create sysfs file 'result'\n");
device_unregister(&tdev);
return err;
}
+
err = device_create_file(&tdev, &dev_attr_fwnum);
if (err) {
- pr_err(TCID ": Can't create sysfs file 'fwnum'\n");
+ pr_err(MODULE_NAME ": Can't create sysfs file 'fwnum'\n");
device_remove_file(&tdev, &dev_attr_result);
device_unregister(&tdev);
}
+
return err;
}
module_init(test_init);
@@ -159,6 +173,6 @@ static void test_exit(void)
device_remove_file(&tdev, &dev_attr_fwnum);
device_unregister(&tdev);
- pr_info(TCID ": module exited\n");
+ pr_info(MODULE_NAME ": module exited\n");
}
module_exit(test_exit);
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 5+ messages in thread* [LTP] [PATCH v3 2/3] firmware/fw_load: rewrite firmware loading test using new LTP API
2026-06-10 17:07 [LTP] [PATCH v3 0/3] Rewrite fw_load test using new API Andrea Cervesato
2026-06-10 17:07 ` [LTP] [PATCH v3 1/3] fw_load: Modernize ltp_fw_load kernel module Andrea Cervesato
@ 2026-06-10 17:07 ` Andrea Cervesato
2026-06-10 17:07 ` [LTP] [PATCH v3 3/3] firmware/fw_load: add fw_load02 for custom firmware path Andrea Cervesato
2 siblings, 0 replies; 5+ messages in thread
From: Andrea Cervesato @ 2026-06-10 17:07 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Consolidate the firmware loading tests into a single fw_load directory:
- Convert the kernel-module based test to the new LTP API as fw_load01,
with shared definitions moved to fw_load.h.
- Rename fw_load_kernel to fw_load and keep the ltp_fw_load helper module.
- Remove the obsolete user-helper based fw_load_user test.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/kernel_misc | 2 +-
.../{fw_load_kernel => fw_load}/.gitignore | 1 +
.../firmware/{fw_load_kernel => fw_load}/Makefile | 8 +-
testcases/kernel/firmware/fw_load/fw_load.h | 18 ++
testcases/kernel/firmware/fw_load/fw_load01.c | 162 ++++++++++++++++
.../{fw_load_kernel => fw_load}/ltp_fw_load.c | 0
testcases/kernel/firmware/fw_load_user/.gitignore | 1 -
testcases/kernel/firmware/fw_load_user/Makefile | 20 --
testcases/kernel/firmware/fw_load_user/README | 11 --
testcases/kernel/firmware/fw_load_user/fw_load.c | 213 ---------------------
10 files changed, 187 insertions(+), 249 deletions(-)
diff --git a/runtest/kernel_misc b/runtest/kernel_misc
index 78f00d305fea10367fb4fd2845f25dd151a833ea..19caee1d81da9cf0503088138ecfda13436b96cf 100644
--- a/runtest/kernel_misc
+++ b/runtest/kernel_misc
@@ -1,6 +1,6 @@
cn_pec_sh cn_pec.sh
kmsg01 kmsg01
-fw_load fw_load
+fw_load01 fw_load01
rtc01 rtc01
rtc02 rtc02
block_dev block_dev
diff --git a/testcases/kernel/firmware/fw_load_kernel/.gitignore b/testcases/kernel/firmware/fw_load/.gitignore
similarity index 90%
rename from testcases/kernel/firmware/fw_load_kernel/.gitignore
rename to testcases/kernel/firmware/fw_load/.gitignore
index 6fc82952c7f5406c04195e1064375d0c2bd53909..53c4b4634684ec7ea6deccedec34b214af4cd3a3 100644
--- a/testcases/kernel/firmware/fw_load_kernel/.gitignore
+++ b/testcases/kernel/firmware/fw_load/.gitignore
@@ -5,3 +5,4 @@
/ltp_fw_load.mod.c
/.tmp_versions/
modules.livepatch
+/fw_load01
diff --git a/testcases/kernel/firmware/fw_load_kernel/Makefile b/testcases/kernel/firmware/fw_load/Makefile
similarity index 82%
rename from testcases/kernel/firmware/fw_load_kernel/Makefile
rename to testcases/kernel/firmware/fw_load/Makefile
index e81b300faaa6b4056d4b5f3d1417c381a000256d..91e8d8d153b43c459a899aff2124db612ec9e960 100644
--- a/testcases/kernel/firmware/fw_load_kernel/Makefile
+++ b/testcases/kernel/firmware/fw_load/Makefile
@@ -1,6 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved.
# Copyright (c) Linux Test Project, 2026
-# SPDX-License-Identifier: GPL-2.0-or-later
ifneq ($(KERNELRELEASE),)
@@ -11,11 +11,13 @@ endif
else
top_srcdir ?= ../../../..
-include $(top_srcdir)/include/mk/env_pre.mk
+
+include $(top_srcdir)/include/mk/testcases.mk
REQ_VERSION_MAJOR := 3
REQ_VERSION_PATCH := 7
-MAKE_TARGETS := ltp_fw_load.ko
+
+MAKE_TARGETS := fw_load01 ltp_fw_load.ko
include $(top_srcdir)/include/mk/module.mk
include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/firmware/fw_load/fw_load.h b/testcases/kernel/firmware/fw_load/fw_load.h
new file mode 100644
index 0000000000000000000000000000000000000000..39f207cc30810e2ed9ea76176cedd98aa902d887
--- /dev/null
+++ b/testcases/kernel/firmware/fw_load/fw_load.h
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+#ifndef FW_LOAD_H
+#define FW_LOAD_H
+
+#define MNAME_KO "ltp_fw_load.ko"
+#define FW_NAME "load_tst.fw"
+#define FW_SIZE 0x1000
+#define FW_NUM 5
+#define FW_PATH "/sys/module/firmware_class/parameters/path"
+#define DEV_FWNUM "/sys/devices/ltp_fw_load/fwnum"
+#define DEV_RESULT "/sys/devices/ltp_fw_load/result"
+#define LIB_PATH "/lib/firmware"
+
+#endif
diff --git a/testcases/kernel/firmware/fw_load/fw_load01.c b/testcases/kernel/firmware/fw_load/fw_load01.c
new file mode 100644
index 0000000000000000000000000000000000000000..54ceba421484692be849bac654a7b4f064ddfe0e
--- /dev/null
+++ b/testcases/kernel/firmware/fw_load/fw_load01.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved.
+ * Alexey Kodanev <alexey.kodanev@oracle.com>
+ * Copyright (c) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Verifies that the kernel firmware loader (``CONFIG_FW_LOADER``)
+ * can find and load firmware files from the standard search paths.
+ *
+ * A helper kernel module (``ltp_fw_load.ko``) registers a virtual
+ * device that calls :kernel_doc:`request_firmware` for a set of
+ * numbered firmware blobs. Each blob is verified in-kernel against
+ * its expected size and byte pattern.
+ *
+ * [Algorithm]
+ *
+ * - Load the helper module with ``fw_size`` matching the blob size
+ * - Create firmware files in the standard firmware search
+ * directories:
+ *
+ * - ``/lib/firmware/``
+ * - ``/lib/firmware/<kernel-release>/``
+ * - ``/lib/firmware/updates/``
+ * - ``/lib/firmware/updates/<kernel-release>/``
+ *
+ * - Add one fake firmware entry that has no file on disk
+ * - Write the firmware count to ``/sys/devices/ltp_fw_load/fwnum``
+ * to trigger :kernel_doc:`request_firmware` calls in-kernel
+ * - Read the result bitmask from ``/sys/devices/ltp_fw_load/result``
+ * - Verify that every real firmware file was loaded successfully
+ * and that the fake entry was correctly rejected
+ */
+
+#include <sys/utsname.h>
+#include "tst_test.h"
+#include "tst_module.h"
+#include "fw_load.h"
+
+static int module_loaded;
+static int fw_count;
+
+static struct fw_data {
+ char dir[PATH_MAX];
+ char file[PATH_MAX];
+ int fake;
+} firmware[FW_NUM];
+
+static void create_firmware(const char *dir)
+{
+ struct fw_data *fw = &firmware[fw_count];
+ char buf[FW_SIZE];
+ int fd = -1;
+
+ snprintf(fw->dir, sizeof(fw->dir), "%s", dir);
+ if (access(dir, X_OK) == -1)
+ SAFE_MKDIR(dir, 0755);
+
+ snprintf(fw->file, sizeof(fw->file), "%s/n%d_%s", dir, fw_count, FW_NAME);
+ memset(buf, fw_count, FW_SIZE);
+
+ fd = SAFE_OPEN(fw->file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ SAFE_WRITE(SAFE_WRITE_ALL, fd, buf, FW_SIZE);
+ SAFE_CLOSE(fd);
+
+ fw_count++;
+}
+
+static void run(void)
+{
+ struct fw_data *fw;
+ int result = 0;
+ int fail, offset;
+
+ SAFE_FILE_PRINTF(DEV_FWNUM, "%d", fw_count);
+ SAFE_FILE_SCANF(DEV_RESULT, "%d", &result);
+
+ for (int i = 0; i < fw_count; i++) {
+ fw = &firmware[i];
+
+ fail = (result & (1 << i)) == 0 && !fw->fake;
+ offset = fw->dir[0] ? strlen(fw->dir) : 0;
+
+ if (fw->fake) {
+ tst_res(result & (1 << i) ? TFAIL : TPASS,
+ "Firmware '%s' correctly not loaded",
+ fw->file + offset);
+ } else {
+ tst_res(fail ? TFAIL : TPASS,
+ "Firmware '%s' loaded",
+ fw->file + offset);
+ }
+ }
+}
+
+static void setup(void)
+{
+ char fw_dir[PATH_MAX];
+ char fw_size_param[32];
+ struct utsname name;
+
+ if (access(LIB_PATH, W_OK) == -1)
+ tst_brk(TCONF, "Skipping test due to read-only %s", LIB_PATH);
+
+ tst_requires_module_signature_disabled();
+
+ snprintf(fw_size_param, sizeof(fw_size_param), "fw_size=%d", FW_SIZE);
+ char *const mod_params[] = {fw_size_param, NULL};
+
+ tst_module_load(MNAME_KO, mod_params);
+ module_loaded = 1;
+
+ create_firmware(LIB_PATH);
+
+ uname(&name);
+
+ snprintf(fw_dir, sizeof(fw_dir), "%s/%s", LIB_PATH, name.release);
+ create_firmware(fw_dir);
+
+ snprintf(fw_dir, sizeof(fw_dir), "%s/updates", LIB_PATH);
+ create_firmware(fw_dir);
+
+ snprintf(fw_dir, sizeof(fw_dir), "%s/updates/%s", LIB_PATH, name.release);
+ create_firmware(fw_dir);
+
+ /* add a fake file */
+ snprintf(firmware[fw_count].file, sizeof(firmware[fw_count].file),
+ "/n%d_%s", fw_count, FW_NAME);
+
+ firmware[fw_count].fake = 1;
+ fw_count++;
+}
+
+static void cleanup(void)
+{
+ struct fw_data *fw;
+
+ for (int i = fw_count - 1; i >= 0; i--) {
+ fw = &firmware[i];
+
+ if (access(fw->file, F_OK) != -1)
+ SAFE_UNLINK(fw->file);
+
+ if (access(fw->dir, F_OK) != -1)
+ remove(fw->dir);
+ }
+
+ if (module_loaded)
+ tst_module_unload(MNAME_KO);
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .setup = setup,
+ .cleanup = cleanup,
+ .needs_root = 1,
+ .needs_kconfigs = (const char *[]) {
+ "CONFIG_FW_LOADER=y|CONFIG_FW_LOADER=m",
+ NULL,
+ },
+};
diff --git a/testcases/kernel/firmware/fw_load_kernel/ltp_fw_load.c b/testcases/kernel/firmware/fw_load/ltp_fw_load.c
similarity index 100%
rename from testcases/kernel/firmware/fw_load_kernel/ltp_fw_load.c
rename to testcases/kernel/firmware/fw_load/ltp_fw_load.c
diff --git a/testcases/kernel/firmware/fw_load_user/.gitignore b/testcases/kernel/firmware/fw_load_user/.gitignore
deleted file mode 100644
index 1d0814980660f34b64bd49ba16c25dc1d99e9d07..0000000000000000000000000000000000000000
--- a/testcases/kernel/firmware/fw_load_user/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/fw_load
diff --git a/testcases/kernel/firmware/fw_load_user/Makefile b/testcases/kernel/firmware/fw_load_user/Makefile
deleted file mode 100644
index effd5dae5c05e9115c391093af523ab36ebfc8d6..0000000000000000000000000000000000000000
--- a/testcases/kernel/firmware/fw_load_user/Makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation; either version 2 of
-# the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it would be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write the Free Software Foundation,
-# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-top_srcdir ?= ../../../..
-
-include $(top_srcdir)/include/mk/testcases.mk
-include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/firmware/fw_load_user/README b/testcases/kernel/firmware/fw_load_user/README
deleted file mode 100644
index 702fac90a2ccb41612ea8dc1010b468588cd3446..0000000000000000000000000000000000000000
--- a/testcases/kernel/firmware/fw_load_user/README
+++ /dev/null
@@ -1,11 +0,0 @@
-The aim of the test is to check device firmware loading. Since kernel 3.7
-firmware loading changed to direct loading (by-pass udev). The test consists
-of the two parts:
- - userspace part
- - kernelspace part
-
-This is the userspace part, its tasks are:
- - create firmware files in the standard firmware paths
- - load the module and initiate firmware request procedure
- - read device's result file and print final results
- - unload the module.
diff --git a/testcases/kernel/firmware/fw_load_user/fw_load.c b/testcases/kernel/firmware/fw_load_user/fw_load.c
deleted file mode 100644
index 1f68f2ad67cb34af5bfe19f77482cd032bdbad3f..0000000000000000000000000000000000000000
--- a/testcases/kernel/firmware/fw_load_user/fw_load.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Author:
- * Alexey Kodanev <alexey.kodanev@oracle.com>
- *
- * Test checks device firmware loading.
- */
-
-#define _GNU_SOURCE
-#include <sys/utsname.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-#include "test.h"
-#include "tst_security.h"
-#include "tso_safe_macros.h"
-#include "tso_module.h"
-
-/* number of test firmware files */
-#define FW_FILES 5
-
-char *TCID = "fw_load";
-int TST_TOTAL = FW_FILES;
-
-static int fw_size = 0x1000;
-
-static const char fw_name[] = "load_tst.fw";
-static const char module_name[] = "ltp_fw_load.ko";
-
-/* paths to module's sysfs files */
-static const char dev_fwnum[] = "/sys/devices/ltp_fw_load/fwnum";
-static const char dev_result[] = "/sys/devices/ltp_fw_load/result";
-
-struct fw_file_info {
- char *file;
- char *dir;
- int fake;
- int remove_dir;
- int remove_file;
-};
-
-static struct fw_file_info fw[FW_FILES];
-static int fw_num;
-
-/* test options */
-static char *narg;
-static int nflag;
-static int skip_cleanup;
-static int verbose;
-static const option_t options[] = {
- {"n:", &nflag, &narg},
- {"s", &skip_cleanup, NULL},
- {"v", &verbose, NULL},
- {NULL, NULL, NULL}
-};
-
-static void help(void);
-static void setup(int argc, char *argv[]);
-static void test_run(void);
-static void cleanup(void);
-
-/*
- * create firmware files in the fw_paths
- * @fw_paths: it must be termintated by a NULL pointer
- */
-static void create_firmware(char *const fw_paths[]);
-
-int main(int argc, char *argv[])
-{
- setup(argc, argv);
-
- test_run();
-
- cleanup();
-
- tst_exit();
-}
-
-static void help(void)
-{
- printf(" -n x Write x bytes to firmware file, default is %d\n",
- fw_size);
- printf(" -s Skip cleanup\n");
- printf(" -v Verbose\n");
-}
-
-void setup(int argc, char *argv[])
-{
- if (tst_lockdown_enabled() > 0 || tst_secureboot_enabled() > 0)
- tst_brkm(TCONF, NULL, "Cannot load unsigned modules in Lockdown/Secure Boot");
-
- tst_parse_opts(argc, argv, options, help);
-
- if (nflag) {
- if (sscanf(narg, "%i", &fw_size) != 1)
- tst_brkm(TBROK, NULL, "-n option arg is not a number");
- if (fw_size < 0)
- tst_brkm(TBROK, NULL, "-n option arg is less than 0");
- }
-
- tst_require_root();
- tst_requires_module_signature_disabled();
-
- char fw_size_param[19];
- snprintf(fw_size_param, 19, "fw_size=%d", fw_size);
- char *const mod_params[2] = { fw_size_param, NULL };
- tst_module_load(NULL, module_name, mod_params);
-
- tst_sig(FORK, DEF_HANDLER, cleanup);
-
- /* get current Linux version and make firmware paths */
- struct utsname uts_name;
- uname(&uts_name);
-
- /* 4 firmware paths + NULL */
- char *fw_paths[5] = { "/lib/firmware", "/lib/firmware/updates" };
- SAFE_ASPRINTF(cleanup, &fw_paths[2], "%s/%s", fw_paths[0], uts_name.release);
- SAFE_ASPRINTF(cleanup, &fw_paths[3], "%s/%s", fw_paths[1], uts_name.release);
-
- /* create firmware in the hard coded firmware search paths */
- create_firmware(fw_paths);
-
- free(fw_paths[2]);
- free(fw_paths[3]);
-
- /* make non-existent firmware file */
- SAFE_ASPRINTF(cleanup, &fw[fw_num].file, "/n%d_%s", fw_num, fw_name);
- fw[fw_num].fake = 1;
- ++fw_num;
-}
-
-static void test_run(void)
-{
- /* initiate firmware requests */
- SAFE_FILE_PRINTF(cleanup, dev_fwnum, "%d", fw_num);
-
- /* get module results by reading result bit mask */
- int result = 0;
- SAFE_FILE_SCANF(cleanup, dev_result, "%d", &result);
-
- int i, fail, offset;
- for (i = 0; i < fw_num; ++i) {
- fail = (result & (1 << i)) == 0 && !fw[i].fake;
- offset = (fw[i].dir) ? strlen(fw[i].dir) : 0;
- tst_resm((fail) ? TFAIL : TPASS,
- "Expect: %s load firmware '...%s'",
- (fw[i].fake) ? "can't" : "can",
- fw[i].file + offset);
- }
-}
-
-static void cleanup(void)
-{
- if (skip_cleanup)
- return;
-
- int i;
- /* remove subdirs first and then upper level dirs */
- for (i = fw_num - 1; i >= 0; --i) {
- if (fw[i].remove_file && remove(fw[i].file) == -1)
- tst_resm(TWARN, "Can't remove: %s", fw[i].file);
- free(fw[i].file);
-
- if (fw[i].remove_dir && remove(fw[i].dir) == -1)
- tst_resm(TWARN, "Can't remove %s", fw[i].dir);
- free(fw[i].dir);
- }
-
- tst_module_unload(NULL, module_name);
-}
-
-static void create_firmware(char *const fw_paths[])
-{
- int i = 0;
- while (fw_paths[i] != NULL) {
- struct fw_file_info *fi = &fw[fw_num];
- fi->dir = strdup(fw_paths[i]);
- if (access(fi->dir, X_OK) == -1) {
- /* create dir */
- SAFE_MKDIR(cleanup, fi->dir, 0755);
- fi->remove_dir = 1;
- }
-
- /* create test firmware file */
- SAFE_ASPRINTF(cleanup, &fi->file, "%s/n%d_%s", fi->dir, fw_num, fw_name);
-
- FILE *f = SAFE_FOPEN(cleanup, fi->file, "w");
- fi->remove_file = 1;
- int k, byte = fw_num;
- ++fw_num;
- for (k = 0; k < fw_size; ++k)
- fputc(byte, f);
- SAFE_FCLOSE(cleanup, f);
- ++i;
- }
-}
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 5+ messages in thread* [LTP] [PATCH v3 3/3] firmware/fw_load: add fw_load02 for custom firmware path
2026-06-10 17:07 [LTP] [PATCH v3 0/3] Rewrite fw_load test using new API Andrea Cervesato
2026-06-10 17:07 ` [LTP] [PATCH v3 1/3] fw_load: Modernize ltp_fw_load kernel module Andrea Cervesato
2026-06-10 17:07 ` [LTP] [PATCH v3 2/3] firmware/fw_load: rewrite firmware loading test using new LTP API Andrea Cervesato
@ 2026-06-10 17:07 ` Andrea Cervesato
2 siblings, 0 replies; 5+ messages in thread
From: Andrea Cervesato @ 2026-06-10 17:07 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Add fw_load02 which points the kernel firmware loader at the writable
LTP temporary directory via /sys/module/firmware_class/parameters/path.
Unlike fw_load01, it does not rely on a writable /lib/firmware and so
works on read-only and immutable root filesystems while still exercising
request_firmware() for both successful loads and the not-found case.
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/kernel_misc | 1 +
testcases/kernel/firmware/fw_load/.gitignore | 1 +
testcases/kernel/firmware/fw_load/Makefile | 2 +-
testcases/kernel/firmware/fw_load/fw_load02.c | 144 ++++++++++++++++++++++++++
4 files changed, 147 insertions(+), 1 deletion(-)
diff --git a/runtest/kernel_misc b/runtest/kernel_misc
index 19caee1d81da9cf0503088138ecfda13436b96cf..cc3562cb78cfa9b657692fcbd8fb732f2879092e 100644
--- a/runtest/kernel_misc
+++ b/runtest/kernel_misc
@@ -1,6 +1,7 @@
cn_pec_sh cn_pec.sh
kmsg01 kmsg01
fw_load01 fw_load01
+fw_load02 fw_load02
rtc01 rtc01
rtc02 rtc02
block_dev block_dev
diff --git a/testcases/kernel/firmware/fw_load/.gitignore b/testcases/kernel/firmware/fw_load/.gitignore
index 53c4b4634684ec7ea6deccedec34b214af4cd3a3..206028a8b2111d0db4dda5c2d5bb18e51c95760d 100644
--- a/testcases/kernel/firmware/fw_load/.gitignore
+++ b/testcases/kernel/firmware/fw_load/.gitignore
@@ -6,3 +6,4 @@
/.tmp_versions/
modules.livepatch
/fw_load01
+/fw_load02
diff --git a/testcases/kernel/firmware/fw_load/Makefile b/testcases/kernel/firmware/fw_load/Makefile
index 91e8d8d153b43c459a899aff2124db612ec9e960..bf6f9abd7517654066a5a92ac481e8eb1f959a41 100644
--- a/testcases/kernel/firmware/fw_load/Makefile
+++ b/testcases/kernel/firmware/fw_load/Makefile
@@ -17,7 +17,7 @@ include $(top_srcdir)/include/mk/testcases.mk
REQ_VERSION_MAJOR := 3
REQ_VERSION_PATCH := 7
-MAKE_TARGETS := fw_load01 ltp_fw_load.ko
+MAKE_TARGETS := fw_load01 fw_load02 ltp_fw_load.ko
include $(top_srcdir)/include/mk/module.mk
include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/firmware/fw_load/fw_load02.c b/testcases/kernel/firmware/fw_load/fw_load02.c
new file mode 100644
index 0000000000000000000000000000000000000000..f7c7af6c2ab7e67ae2fbdac9b56c0421057affe4
--- /dev/null
+++ b/testcases/kernel/firmware/fw_load/fw_load02.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved.
+ * Alexey Kodanev <alexey.kodanev@oracle.com>
+ * Copyright (c) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * Verifies that the kernel firmware loader (``CONFIG_FW_LOADER``)
+ * can find and load firmware files from a custom firmware search
+ * path.
+ *
+ * A helper kernel module (``ltp_fw_load.ko``) registers a virtual
+ * device that calls :kernel_doc:`request_firmware` for a set of
+ * numbered firmware blobs. Each blob is verified in-kernel against
+ * its expected size and byte pattern.
+ *
+ * The kernel firmware loader is pointed at the writable LTP temporary
+ * directory through ``/sys/module/firmware_class/parameters/path``.
+ * This avoids writing into ``/lib/firmware`` and therefore works on
+ * read-only or immutable root filesystems. The original value is
+ * saved and restored automatically.
+ *
+ * [Algorithm]
+ *
+ * - Set the firmware search path to the LTP temporary directory
+ * - Create ``FW_NUM - 1`` firmware files there, each named
+ * ``n<i>_load_tst.fw`` and filled with a known byte pattern
+ * - Add one fake firmware entry that has no file on disk
+ * - Load the helper module with ``fw_size`` matching the blob size
+ * - Write the firmware count to ``/sys/devices/ltp_fw_load/fwnum``
+ * to trigger :kernel_doc:`request_firmware` calls in-kernel
+ * - Read the result bitmask from ``/sys/devices/ltp_fw_load/result``
+ * - Verify that every real firmware file was loaded successfully
+ * and that the fake entry was correctly rejected
+ */
+
+#include "tst_test.h"
+#include "tst_module.h"
+#include "fw_load.h"
+
+#define MAX_NAME 64
+
+static int module_loaded;
+static int fw_count;
+
+static struct fw_data {
+ char file[PATH_MAX];
+ char name[MAX_NAME];
+ int fake;
+} firmware[FW_NUM];
+
+static void create_firmware(const char *dir)
+{
+ struct fw_data *fw = &firmware[fw_count];
+ char buf[FW_SIZE];
+ int fd;
+
+ snprintf(fw->name, sizeof(fw->name), "n%d_%s", fw_count, FW_NAME);
+ snprintf(fw->file, sizeof(fw->file), "%s/n%d_%s", dir, fw_count, FW_NAME);
+ memset(buf, fw_count, FW_SIZE);
+
+ fd = SAFE_OPEN(fw->file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ SAFE_WRITE(SAFE_WRITE_ALL, fd, buf, FW_SIZE);
+ SAFE_CLOSE(fd);
+
+ fw_count++;
+}
+
+static void run(void)
+{
+ struct fw_data *fw;
+ int result = 0;
+ int fail;
+
+ SAFE_FILE_PRINTF(DEV_FWNUM, "%d", fw_count);
+ SAFE_FILE_SCANF(DEV_RESULT, "%d", &result);
+
+ for (int i = 0; i < fw_count; i++) {
+ fw = &firmware[i];
+
+ if (fw->fake) {
+ tst_res(result & (1 << i) ? TFAIL : TPASS,
+ "Firmware '%s' correctly not loaded", fw->name);
+ } else {
+ fail = (result & (1 << i)) == 0;
+ tst_res(fail ? TFAIL : TPASS,
+ "Firmware '%s' loaded", fw->name);
+ }
+ }
+}
+
+static void setup(void)
+{
+ char fw_size_param[32];
+ char *tmpdir = tst_tmpdir_path();
+ struct tst_path_val fw_path = {
+ .path = FW_PATH,
+ .val = tmpdir,
+ .flags = TST_SR_TCONF,
+ };
+
+ tst_requires_module_signature_disabled();
+
+ /*
+ * Point the kernel firmware loader at our writable tmpdir so the
+ * test does not depend on a writable /lib/firmware. The previous
+ * value is restored automatically during cleanup.
+ */
+ tst_sys_conf_save(&fw_path);
+
+ snprintf(fw_size_param, sizeof(fw_size_param), "fw_size=%d", FW_SIZE);
+ char *const mod_params[] = {fw_size_param, NULL};
+
+ tst_module_load(MNAME_KO, mod_params);
+ module_loaded = 1;
+
+ for (int i = 0; i < FW_NUM - 1; i++)
+ create_firmware(tmpdir);
+
+ /* add a fake file that is never created on disk */
+ snprintf(firmware[fw_count].name, sizeof(firmware[fw_count].name),
+ "n%d_%s", fw_count, FW_NAME);
+ firmware[fw_count].fake = 1;
+ fw_count++;
+}
+
+static void cleanup(void)
+{
+ if (module_loaded)
+ tst_module_unload(MNAME_KO);
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .setup = setup,
+ .cleanup = cleanup,
+ .needs_root = 1,
+ .needs_tmpdir = 1,
+ .needs_kconfigs = (const char *[]) {
+ "CONFIG_FW_LOADER=y|CONFIG_FW_LOADER=m",
+ NULL,
+ },
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 5+ messages in thread