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