From: Andrea Cervesato <andrea.cervesato@suse.de>
To: Linux Test Project <ltp@lists.linux.it>
Subject: [LTP] [PATCH v3 2/3] firmware/fw_load: rewrite firmware loading test using new LTP API
Date: Wed, 10 Jun 2026 19:07:05 +0200 [thread overview]
Message-ID: <20260610-fw_load-v3-2-eef32edfe8d5@suse.com> (raw)
In-Reply-To: <20260610-fw_load-v3-0-eef32edfe8d5@suse.com>
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
next prev parent reply other threads:[~2026-06-10 17:08 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
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 19:44 ` [LTP] " linuxtestproject.agent
2026-06-10 17:07 ` Andrea Cervesato [this message]
2026-06-10 17:07 ` [LTP] [PATCH v3 3/3] firmware/fw_load: add fw_load02 for custom firmware path Andrea Cervesato
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260610-fw_load-v3-2-eef32edfe8d5@suse.com \
--to=andrea.cervesato@suse.de \
--cc=ltp@lists.linux.it \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.