1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2018 Cyril Hrubis <chrubis@suse.cz>
4 */
5
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <ctype.h>
10 #include <sys/utsname.h>
11
12 #define TST_NO_DEFAULT_MAIN
13 #include "tst_test.h"
14 #include "tst_private.h"
15 #include "tst_kconfig.h"
16 #include "tst_bool_expr.h"
17
kconfig_path(char * path_buf,size_t path_buf_len)18 static const char *kconfig_path(char *path_buf, size_t path_buf_len)
19 {
20 const char *path = getenv("KCONFIG_PATH");
21 struct utsname un;
22
23 if (path) {
24 if (!access(path, F_OK))
25 return path;
26
27 tst_res(TWARN, "KCONFIG_PATH='%s' does not exist", path);
28 }
29
30 if (!access("/proc/config.gz", F_OK))
31 return "/proc/config.gz";
32
33 uname(&un);
34
35 /* Debian and derivatives */
36 snprintf(path_buf, path_buf_len, "/boot/config-%s", un.release);
37
38 if (!access(path_buf, F_OK))
39 return path_buf;
40
41 /* Clear Linux */
42 snprintf(path_buf, path_buf_len, "/lib/kernel/config-%s", un.release);
43
44 if (!access(path_buf, F_OK))
45 return path_buf;
46
47 tst_res(TINFO, "Couldn't locate kernel config!");
48
49 return NULL;
50 }
51
52 static char is_gzip;
53
open_kconfig(void)54 static FILE *open_kconfig(void)
55 {
56 FILE *fp;
57 char buf[1064];
58 char path_buf[1024];
59 const char *path = kconfig_path(path_buf, sizeof(path_buf));
60
61 if (!path)
62 return NULL;
63
64 tst_res(TINFO, "Parsing kernel config '%s'", path);
65
66 is_gzip = !!strstr(path, ".gz");
67
68 if (is_gzip) {
69 snprintf(buf, sizeof(buf), "zcat '%s'", path);
70 fp = popen(buf, "r");
71 } else {
72 fp = fopen(path, "r");
73 }
74
75 if (!fp)
76 tst_brk(TBROK | TERRNO, "Failed to open '%s'", path);
77
78 return fp;
79 }
80
close_kconfig(FILE * fp)81 static void close_kconfig(FILE *fp)
82 {
83 if (is_gzip)
84 pclose(fp);
85 else
86 fclose(fp);
87 }
88
kconfig_parse_line(const char * line,struct tst_kconfig_var * vars,unsigned int vars_len)89 static inline int kconfig_parse_line(const char *line,
90 struct tst_kconfig_var *vars,
91 unsigned int vars_len)
92 {
93 unsigned int i, var_len = 0;
94 const char *var;
95 int is_not_set = 0;
96
97 while (isspace(*line))
98 line++;
99
100 if (*line == '#') {
101 if (!strstr(line, "is not set"))
102 return 0;
103
104 is_not_set = 1;
105 }
106
107 var = strstr(line, "CONFIG_");
108
109 if (!var)
110 return 0;
111
112 for (;;) {
113 switch (var[var_len]) {
114 case 'A' ... 'Z':
115 case '0' ... '9':
116 case '_':
117 var_len++;
118 break;
119 default:
120 goto out;
121 break;
122 }
123 }
124
125 out:
126
127 for (i = 0; i < vars_len; i++) {
128 const char *val;
129 unsigned int val_len = 0;
130
131 if (vars[i].id_len != var_len)
132 continue;
133
134 if (strncmp(vars[i].id, var, var_len))
135 continue;
136
137 if (is_not_set) {
138 vars[i].choice = 'n';
139 return 1;
140 }
141
142 val = var + var_len;
143
144 while (isspace(*val))
145 val++;
146
147 if (*val != '=')
148 return 0;
149
150 val++;
151
152 while (isspace(*val))
153 val++;
154
155 while (!isspace(val[val_len]))
156 val_len++;
157
158 if (val_len == 1) {
159 switch (val[0]) {
160 case 'y':
161 vars[i].choice = 'y';
162 return 1;
163 case 'm':
164 vars[i].choice = 'm';
165 return 1;
166 }
167 }
168
169 vars[i].choice = 'v';
170 vars[i].val = strndup(val, val_len);
171 }
172
173 return 0;
174 }
175
tst_kconfig_read(struct tst_kconfig_var vars[],size_t vars_len)176 void tst_kconfig_read(struct tst_kconfig_var vars[], size_t vars_len)
177 {
178 char line[128];
179 unsigned int vars_found = 0;
180
181 FILE *fp = open_kconfig();
182 if (!fp)
183 tst_brk(TBROK, "Cannot parse kernel .config");
184
185 while (fgets(line, sizeof(line), fp)) {
186 if (kconfig_parse_line(line, vars, vars_len))
187 vars_found++;
188
189 if (vars_found == vars_len)
190 goto exit;
191 }
192
193 exit:
194 close_kconfig(fp);
195 }
196
array_len(const char * const kconfigs[])197 static size_t array_len(const char *const kconfigs[])
198 {
199 size_t i = 0;
200
201 while (kconfigs[++i]);
202
203 return i;
204 }
205
strnchr(const char * s,int c,unsigned int len)206 static const char *strnchr(const char *s, int c, unsigned int len)
207 {
208 unsigned int i;
209
210 for (i = 0; i < len; i++) {
211 if (s[i] == c)
212 return s + i;
213 }
214
215 return NULL;
216 }
217
get_len(const char * kconfig,unsigned int len)218 static inline unsigned int get_len(const char* kconfig, unsigned int len)
219 {
220 const char *sep = strnchr(kconfig, '=', len);
221
222 if (!sep)
223 return len;
224
225 return sep - kconfig;
226 }
227
print_err(FILE * f,const struct tst_expr_tok * var,size_t spaces,const char * err)228 static void print_err(FILE *f, const struct tst_expr_tok *var,
229 size_t spaces, const char *err)
230 {
231 size_t i;
232
233 for (i = 0; i < var->tok_len; i++)
234 fputc(var->tok[i], f);
235
236 fputc('\n', f);
237
238 while (spaces--)
239 fputc(' ', f);
240
241 fprintf(f, "^\n%s\n\n", err);
242 }
243
validate_var(const struct tst_expr_tok * var)244 static int validate_var(const struct tst_expr_tok *var)
245 {
246 size_t i = 7;
247
248 if (var->tok_len < 7 || strncmp(var->tok, "CONFIG_", 7)) {
249 print_err(stderr, var, 0, "Expected CONFIG_ prefix");
250 return 1;
251 }
252
253 while (var->tok[i]) {
254 char c;
255
256 if (i >= var->tok_len)
257 return 0;
258
259 c = var->tok[i];
260
261 if ((c >= 'A' && c <= 'Z') || c == '_') {
262 i++;
263 continue;
264 }
265
266 if (c >= '0' && c <= '9') {
267 i++;
268 continue;
269 }
270
271 if (c == '=') {
272 i++;
273 break;
274 }
275
276 print_err(stderr, var, i, "Unexpected character in variable name");
277 return 1;
278 }
279
280 if (i >= var->tok_len) {
281
282 if (var->tok[i-1] == '=') {
283 print_err(stderr, var, i, "Missing value");
284 return -1;
285 }
286
287 return 0;
288 }
289
290 if (var->tok[i] == '"') {
291 do {
292 i++;
293 } while (i < var->tok_len && var->tok[i] != '"');
294
295 if (i < var->tok_len - 1) {
296 print_err(stderr, var, i, "Garbage after a string");
297 return 1;
298 }
299
300 if (var->tok[i] != '"') {
301 print_err(stderr, var, i, "Untermianted string");
302 return 1;
303 }
304
305 return 0;
306 }
307
308 do {
309 i++;
310 } while (i < var->tok_len && isalnum(var->tok[i]));
311
312 if (i < var->tok_len) {
313 print_err(stderr, var, i, "Invalid character in variable value");
314 return 1;
315 }
316
317 return 0;
318 }
319
validate_vars(struct tst_expr * const exprs[],unsigned int expr_cnt)320 static int validate_vars(struct tst_expr *const exprs[], unsigned int expr_cnt)
321 {
322 unsigned int i;
323 const struct tst_expr_tok *j;
324 unsigned int ret = 0;
325
326 for (i = 0; i < expr_cnt; i++) {
327 for (j = exprs[i]->rpn; j; j = j->next) {
328 if (j->op == TST_OP_VAR)
329 ret |= validate_var(j);
330 }
331 }
332
333 return ret;
334 }
335
336
get_var_cnt(struct tst_expr * const exprs[],unsigned int expr_cnt)337 static inline unsigned int get_var_cnt(struct tst_expr *const exprs[],
338 unsigned int expr_cnt)
339 {
340 unsigned int i;
341 const struct tst_expr_tok *j;
342 unsigned int cnt = 0;
343
344 for (i = 0; i < expr_cnt; i++) {
345 for (j = exprs[i]->rpn; j; j = j->next) {
346 if (j->op == TST_OP_VAR)
347 cnt++;
348 }
349 }
350
351 return cnt;
352 }
353
find_var(const struct tst_kconfig_var vars[],unsigned int var_cnt,const char * var)354 static const struct tst_kconfig_var *find_var(const struct tst_kconfig_var vars[],
355 unsigned int var_cnt,
356 const char *var)
357 {
358 unsigned int i;
359
360 for (i = 0; i < var_cnt; i++) {
361 if (!strcmp(vars[i].id, var))
362 return &vars[i];
363 }
364
365 return NULL;
366 }
367
368 /*
369 * Fill in the kconfig variables array from the expressions. Also makes sure
370 * that each variable is copied to the array exaclty once.
371 */
populate_vars(struct tst_expr * exprs[],unsigned int expr_cnt,struct tst_kconfig_var vars[])372 static inline unsigned int populate_vars(struct tst_expr *exprs[],
373 unsigned int expr_cnt,
374 struct tst_kconfig_var vars[])
375 {
376 unsigned int i;
377 struct tst_expr_tok *j;
378 unsigned int cnt = 0;
379
380 for (i = 0; i < expr_cnt; i++) {
381 for (j = exprs[i]->rpn; j; j = j->next) {
382 const struct tst_kconfig_var *var;
383
384 if (j->op != TST_OP_VAR)
385 continue;
386
387 vars[cnt].id_len = get_len(j->tok, j->tok_len);
388
389 if (vars[cnt].id_len + 1 >= sizeof(vars[cnt].id))
390 tst_brk(TBROK, "kconfig var id too long!");
391
392 strncpy(vars[cnt].id, j->tok, vars[cnt].id_len);
393 vars[cnt].id[vars[cnt].id_len] = 0;
394 vars[cnt].choice = 0;
395
396 var = find_var(vars, cnt, vars[cnt].id);
397
398 if (var)
399 j->priv = var;
400 else
401 j->priv = &vars[cnt++];
402 }
403 }
404
405 return cnt;
406 }
407
map(struct tst_expr_tok * expr)408 static int map(struct tst_expr_tok *expr)
409 {
410 const struct tst_kconfig_var *var = expr->priv;
411
412 if (var->choice == 0)
413 return 0;
414
415 const char *val = strnchr(expr->tok, '=', expr->tok_len);
416
417 /* CONFIG_FOO evaluates to true if y or m */
418 if (!val)
419 return var->choice == 'y' || var->choice == 'm';
420
421 val++;
422
423 unsigned int len = expr->tok_len - (val - expr->tok);
424 char choice = 'v';
425
426 if (!strncmp(val, "n", len))
427 choice = 'n';
428
429 if (!strncmp(val, "y", len))
430 choice = 'y';
431
432 if (!strncmp(val, "m", len))
433 choice = 'm';
434
435 if (choice != 'v')
436 return var->choice == choice;
437
438 if (strlen(var->val) != len)
439 return 0;
440
441 return !strncmp(val, var->val, len);
442 }
443
dump_vars(const struct tst_expr * expr)444 static void dump_vars(const struct tst_expr *expr)
445 {
446 const struct tst_expr_tok *i;
447 const struct tst_kconfig_var *var;
448
449 tst_res(TINFO, "Variables:");
450
451 for (i = expr->rpn; i; i = i->next) {
452 if (i->op != TST_OP_VAR)
453 continue;
454
455 var = i->priv;
456
457 if (!var->choice) {
458 tst_res(TINFO, " %s Undefined", var->id);
459 continue;
460 }
461
462 if (var->choice == 'v') {
463 tst_res(TINFO, " %s=%s", var->id, var->val);
464 continue;
465 }
466
467 tst_res(TINFO, " %s=%c", var->id, var->choice);
468 }
469 }
470
tst_kconfig_check(const char * const kconfigs[])471 void tst_kconfig_check(const char *const kconfigs[])
472 {
473 size_t expr_cnt = array_len(kconfigs);
474 struct tst_expr *exprs[expr_cnt];
475 unsigned int i, var_cnt;
476 int abort_test = 0;
477
478 for (i = 0; i < expr_cnt; i++) {
479 exprs[i] = tst_bool_expr_parse(kconfigs[i]);
480
481 if (!exprs[i])
482 tst_brk(TBROK, "Invalid kconfig expression!");
483 }
484
485 if (validate_vars(exprs, expr_cnt))
486 tst_brk(TBROK, "Invalid kconfig variables!");
487
488 var_cnt = get_var_cnt(exprs, expr_cnt);
489 struct tst_kconfig_var vars[var_cnt];
490
491 var_cnt = populate_vars(exprs, expr_cnt, vars);
492
493 tst_kconfig_read(vars, var_cnt);
494
495 for (i = 0; i < expr_cnt; i++) {
496 int val = tst_bool_expr_eval(exprs[i], map);
497
498 if (val != 1) {
499 abort_test = 1;
500 tst_res(TINFO, "Constrain '%s' not satisfied!", kconfigs[i]);
501 dump_vars(exprs[i]);
502 }
503
504 tst_bool_expr_free(exprs[i]);
505 }
506
507 for (i = 0; i < var_cnt; i++) {
508 if (vars[i].choice == 'v')
509 free(vars[i].val);
510 }
511
512 if (abort_test)
513 tst_brk(TCONF, "Aborting due to unsuitable kernel config, see above!");
514 }
515
tst_kconfig_get(const char * confname)516 char tst_kconfig_get(const char *confname)
517 {
518 struct tst_kconfig_var var;
519
520 var.id_len = strlen(confname);
521
522 if (var.id_len >= sizeof(var.id))
523 tst_brk(TBROK, "Kconfig var name \"%s\" too long", confname);
524
525 strcpy(var.id, confname);
526 var.choice = 0;
527 var.val = NULL;
528
529 tst_kconfig_read(&var, 1);
530
531 if (var.choice == 'v')
532 free(var.val);
533
534 return var.choice;
535 }
536