1 /*
2 * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Author:
19 * Alexey Kodanev <alexey.kodanev@oracle.com>
20 *
21 * Test checks device firmware loading.
22 */
23
24 #define _GNU_SOURCE
25 #include <sys/utsname.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <string.h>
30
31 #include "test.h"
32 #include "safe_macros.h"
33 #include "old_module.h"
34
35 /* number of test firmware files */
36 #define FW_FILES 5
37
38 char *TCID = "fw_load";
39 int TST_TOTAL = FW_FILES;
40
41 static int fw_size = 0x1000;
42
43 static const char fw_name[] = "load_tst.fw";
44 static const char module_name[] = "ltp_fw_load.ko";
45
46 /* paths to module's sysfs files */
47 static const char dev_fwnum[] = "/sys/devices/ltp_fw_load/fwnum";
48 static const char dev_result[] = "/sys/devices/ltp_fw_load/result";
49
50 struct fw_file_info {
51 char *file;
52 char *dir;
53 int fake;
54 int remove_dir;
55 int remove_file;
56 };
57
58 static struct fw_file_info fw[FW_FILES];
59 static int fw_num;
60
61 /* test options */
62 static char *narg;
63 static int nflag;
64 static int skip_cleanup;
65 static int verbose;
66 static const option_t options[] = {
67 {"n:", &nflag, &narg},
68 {"s", &skip_cleanup, NULL},
69 {"v", &verbose, NULL},
70 {NULL, NULL, NULL}
71 };
72
73 static void help(void);
74 static void setup(int argc, char *argv[]);
75 static void test_run(void);
76 static void cleanup(void);
77
78 /*
79 * create firmware files in the fw_paths
80 * @fw_paths: it must be termintated by a NULL pointer
81 */
82 static void create_firmware(char *const fw_paths[]);
83
main(int argc,char * argv[])84 int main(int argc, char *argv[])
85 {
86 setup(argc, argv);
87
88 test_run();
89
90 cleanup();
91
92 tst_exit();
93 }
94
help(void)95 static void help(void)
96 {
97 printf(" -n x Write x bytes to firmware file, default is %d\n",
98 fw_size);
99 printf(" -s Skip cleanup\n");
100 printf(" -v Verbose\n");
101 }
102
setup(int argc,char * argv[])103 void setup(int argc, char *argv[])
104 {
105
106 tst_parse_opts(argc, argv, options, help);
107
108 if (nflag) {
109 if (sscanf(narg, "%i", &fw_size) != 1)
110 tst_brkm(TBROK, NULL, "-n option arg is not a number");
111 if (fw_size < 0)
112 tst_brkm(TBROK, NULL, "-n option arg is less than 0");
113 }
114
115 tst_require_root();
116
117 if (tst_kvercmp(3, 7, 0) < 0) {
118 tst_brkm(TCONF, NULL,
119 "Test must be run with kernel 3.7 or newer");
120 }
121
122 char fw_size_param[19];
123 snprintf(fw_size_param, 19, "fw_size=%d", fw_size);
124 char *const mod_params[2] = { fw_size_param, NULL };
125 tst_module_load(NULL, module_name, mod_params);
126
127 tst_sig(FORK, DEF_HANDLER, cleanup);
128
129 /* get current Linux version and make firmware paths */
130 struct utsname uts_name;
131 uname(&uts_name);
132
133 /* 4 firmware paths + NULL */
134 char *fw_paths[5] = { "/lib/firmware", "/lib/firmware/updates" };
135 SAFE_ASPRINTF(cleanup, &fw_paths[2], "%s/%s", fw_paths[0], uts_name.release);
136 SAFE_ASPRINTF(cleanup, &fw_paths[3], "%s/%s", fw_paths[1], uts_name.release);
137
138 /* create firmware in the hard coded firmware search paths */
139 create_firmware(fw_paths);
140
141 free(fw_paths[2]);
142 free(fw_paths[3]);
143
144 /* make non-existent firmware file */
145 SAFE_ASPRINTF(cleanup, &fw[fw_num].file, "/n%d_%s", fw_num, fw_name);
146 fw[fw_num].fake = 1;
147 ++fw_num;
148 }
149
test_run(void)150 static void test_run(void)
151 {
152 /* initiate firmware requests */
153 SAFE_FILE_PRINTF(cleanup, dev_fwnum, "%d", fw_num);
154
155 /* get module results by reading result bit mask */
156 int result = 0;
157 SAFE_FILE_SCANF(cleanup, dev_result, "%d", &result);
158
159 int i, fail, offset;
160 for (i = 0; i < fw_num; ++i) {
161 fail = (result & (1 << i)) == 0 && !fw[i].fake;
162 offset = (fw[i].dir) ? strlen(fw[i].dir) : 0;
163 tst_resm((fail) ? TFAIL : TPASS,
164 "Expect: %s load firmware '...%s'",
165 (fw[i].fake) ? "can't" : "can",
166 fw[i].file + offset);
167 }
168 }
169
cleanup(void)170 static void cleanup(void)
171 {
172 if (skip_cleanup)
173 return;
174
175 int i;
176 /* remove subdirs first and then upper level dirs */
177 for (i = fw_num - 1; i >= 0; --i) {
178 if (fw[i].remove_file && remove(fw[i].file) == -1)
179 tst_resm(TWARN, "Can't remove: %s", fw[i].file);
180 free(fw[i].file);
181
182 if (fw[i].remove_dir && remove(fw[i].dir) == -1)
183 tst_resm(TWARN, "Can't remove %s", fw[i].dir);
184 free(fw[i].dir);
185 }
186
187 tst_module_unload(NULL, module_name);
188 }
189
create_firmware(char * const fw_paths[])190 static void create_firmware(char *const fw_paths[])
191 {
192 int i = 0;
193 while (fw_paths[i] != NULL) {
194 struct fw_file_info *fi = &fw[fw_num];
195 fi->dir = strdup(fw_paths[i]);
196 if (access(fi->dir, X_OK) == -1) {
197 /* create dir */
198 SAFE_MKDIR(cleanup, fi->dir, 0755);
199 fi->remove_dir = 1;
200 }
201
202 /* create test firmware file */
203 SAFE_ASPRINTF(cleanup, &fi->file, "%s/n%d_%s", fi->dir, fw_num, fw_name);
204
205 FILE *f = SAFE_FOPEN(cleanup, fi->file, "w");
206 fi->remove_file = 1;
207 int k, byte = fw_num;
208 ++fw_num;
209 for (k = 0; k < fw_size; ++k)
210 fputc(byte, f);
211 SAFE_FCLOSE(cleanup, f);
212 ++i;
213 }
214 }
215