[PATCH v7 2/4] fw_load: rewrite test using new LTP API

Andrea Cervesato <[email protected]>
Newsgroups gmane.linux.ltp
Message-ID <[email protected]>
From: Andrea Cervesato <andrea.cervesato-IBi9RG/[email protected]>

Remove the obsolete user-helper and create a fw_load.h where to
store the firmware support information.

Signed-off-by: Andrea Cervesato <andrea.cervesato-IBi9RG/[email protected]>
---
 testcases/kernel/firmware/fw_load_user/Makefile  |  18 +-
 testcases/kernel/firmware/fw_load_user/README    |  11 -
 testcases/kernel/firmware/fw_load_user/fw_load.c | 286 ++++++++++-------------
 testcases/kernel/firmware/fw_load_user/fw_load.h |  18 ++
 4 files changed, 140 insertions(+), 193 deletions(-)

diff --git a/testcases/kernel/firmware/fw_load_user/Makefile b/testcases/kernel/firmware/fw_load_user/Makefile
index effd5dae5c05e9115c391093af523ab36ebfc8d6..5b031e2eb81597689ff5eab70246fb20ad02e77d 100644
--- a/testcases/kernel/firmware/fw_load_user/Makefile
+++ b/testcases/kernel/firmware/fw_load_user/Makefile
@@ -1,20 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
 # 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
 
-top_srcdir		?= ../../../..
+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
index 1f68f2ad67cb34af5bfe19f77482cd032bdbad3f..402e2c71f508fdbb9c3f73fcd42bf87da280137b 100644
--- a/testcases/kernel/firmware/fw_load_user/fw_load.c
+++ b/testcases/kernel/firmware/fw_load_user/fw_load.c
@@ -1,213 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
 /*
  * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved.
+ *	Alexey Kodanev <[email protected]>
+ * Copyright (c) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato-IBi9RG/[email protected]>
+ */
+
+/*\
+ * Verifies that the kernel firmware loader (``CONFIG_FW_LOADER``)
+ * can find and load firmware files from the standard search paths.
  *
- * 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.
+ * 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.
  *
- * 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.
+ * [Algorithm]
  *
- * 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
+ * - Load the helper module with ``fw_size`` matching the blob size
+ * - Create firmware files in the standard firmware search
+ *   directories:
  *
- * Author:
- * Alexey Kodanev <[email protected]>
+ *   - ``/lib/firmware/``
+ *   - ``/lib/firmware/<kernel-release>/``
+ *   - ``/lib/firmware/updates/``
+ *   - ``/lib/firmware/updates/<kernel-release>/``
  *
- * Test checks device firmware loading.
+ * - 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
  */
 
-#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;
+#include "tst_test.h"
+#include "tst_module.h"
+#include "fw_load.h"
 
-static int fw_size = 0x1000;
+static int module_loaded;
+static int fw_count;
 
-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;
+static struct fw_data {
+	char dir[PATH_MAX];
+	char file[PATH_MAX];
 	int fake;
-	int remove_dir;
-	int remove_file;
-};
+	int created_dir;
+} firmware[FW_NUM];
 
-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 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);
+		fw->created_dir = 1;
+	}
 
-static void help(void);
-static void setup(int argc, char *argv[]);
-static void test_run(void);
-static void cleanup(void);
+	snprintf(fw->file, sizeof(fw->file), "%s/n%d_%s", dir, fw_count, FW_NAME);
+	memset(buf, fw_count, FW_SIZE);
 
-/*
- * 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[]);
+	fd = SAFE_OPEN(fw->file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+	SAFE_WRITE(SAFE_WRITE_ALL, fd, buf, FW_SIZE);
+	SAFE_CLOSE(fd);
 
-int main(int argc, char *argv[])
+	fw_count++;
+}
+
+static void run(void)
 {
-	setup(argc, argv);
+	struct fw_data *fw;
+	int result = 0;
+	int pass, offset;
 
-	test_run();
+	SAFE_FILE_PRINTF(DEV_FWNUM, "%d", fw_count);
+	SAFE_FILE_SCANF(DEV_RESULT, "%d", &result);
 
-	cleanup();
+	for (int i = 0; i < fw_count; i++) {
+		fw = &firmware[i];
 
-	tst_exit();
-}
+		pass = result & (1 << i);
+		offset = fw->dir[0] ? strlen(fw->dir) : 0;
 
-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");
+		if (fw->fake) {
+			tst_res(pass ? TFAIL : TPASS,
+				"Firmware '%s' correctly not loaded",
+				fw->file + offset);
+		} else {
+			tst_res(pass ? TPASS : TFAIL,
+				"Firmware '%s' loaded",
+				fw->file + offset);
+		}
+	}
 }
 
-void setup(int argc, char *argv[])
+static void setup(void)
 {
-	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);
+	char fw_dir[PATH_MAX];
+	char fw_size_param[32];
+	struct utsname name;
 
-	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");
-	}
+	if (access(LIB_PATH, W_OK) == -1)
+		tst_brk(TCONF, "Skipping test due to read-only %s", LIB_PATH);
 
-	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);
+	snprintf(fw_size_param, sizeof(fw_size_param), "fw_size=%d", FW_SIZE);
+	char *const mod_params[] = {fw_size_param, NULL};
 
-	tst_sig(FORK, DEF_HANDLER, cleanup);
+	tst_module_load(MNAME_KO, mod_params);
+	module_loaded = 1;
 
-	/* get current Linux version and make firmware paths */
-	struct utsname uts_name;
-	uname(&uts_name);
+	create_firmware(LIB_PATH);
 
-	/* 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);
+	uname(&name);
 
-	/* create firmware in the hard coded firmware search paths */
-	create_firmware(fw_paths);
+	snprintf(fw_dir, sizeof(fw_dir), "%s/%s", LIB_PATH, name.release);
+	create_firmware(fw_dir);
 
-	free(fw_paths[2]);
-	free(fw_paths[3]);
+	snprintf(fw_dir, sizeof(fw_dir), "%s/updates", LIB_PATH);
+	create_firmware(fw_dir);
 
-	/* 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;
-}
+	snprintf(fw_dir, sizeof(fw_dir), "%s/updates/%s", LIB_PATH, name.release);
+	create_firmware(fw_dir);
 
-static void test_run(void)
-{
-	/* initiate firmware requests */
-	SAFE_FILE_PRINTF(cleanup, dev_fwnum, "%d", fw_num);
+	/* add a fake file */
+	snprintf(firmware[fw_count].file, sizeof(firmware[fw_count].file),
+		 "/n%d_%s", fw_count, FW_NAME);
 
-	/* 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);
-	}
+	firmware[fw_count].fake = 1;
+	fw_count++;
 }
 
 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);
-	}
+	struct fw_data *fw;
 
-	tst_module_unload(NULL, module_name);
-}
+	for (int i = fw_count - 1; i >= 0; i--) {
+		fw = &firmware[i];
 
-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;
-		}
+		if (access(fw->file, F_OK) != -1)
+			SAFE_UNLINK(fw->file);
 
-		/* 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;
+		if (fw->created_dir)
+			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_user/fw_load.h b/testcases/kernel/firmware/fw_load_user/fw_load.h
new file mode 100644
index 0000000000000000000000000000000000000000..39f207cc30810e2ed9ea76176cedd98aa902d887
--- /dev/null
+++ b/testcases/kernel/firmware/fw_load_user/fw_load.h
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 SUSE LLC Andrea Cervesato <andrea.cervesato-IBi9RG/[email protected]>
+ */
+
+#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

-- 
2.51.0


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.