• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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