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