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 18static 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 30static 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 75static char is_gzip; 76 77static 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 104static void close_kconfig(FILE *fp) 105{ 106 if (is_gzip) 107 pclose(fp); 108 else 109 fclose(fp); 110} 111 112static 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 148out: 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 199void 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 216exit: 217 close_kconfig(fp); 218} 219 220static 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 229static 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 241static 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 251static 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 267static 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 343static 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 360static 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 377static 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 */ 395static 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 432static 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 471static 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 498int 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 545char 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