[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