• 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 	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