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