From: Andrea Cervesato <andrea.cervesato@suse.de>
To: Linux Test Project <ltp@lists.linux.it>
Subject: [LTP] [PATCH v3 3/3] firmware/fw_load: add fw_load02 for custom firmware path
Date: Wed, 10 Jun 2026 19:07:06 +0200 [thread overview]
Message-ID: <20260610-fw_load-v3-3-eef32edfe8d5@suse.com> (raw)
In-Reply-To: <20260610-fw_load-v3-0-eef32edfe8d5@suse.com>
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
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 ` [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 [this message]
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-3-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox