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