1/* 2 * RouterBOOT configuration utility 3 * 4 * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org> 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 as published 8 * by the Free Software Foundation. 9 * 10 */ 11 12#include <stdlib.h> 13#include <stdio.h> 14#include <stddef.h> 15#include <stdint.h> 16#include <string.h> 17#include <fcntl.h> 18#include <unistd.h> 19#include <sys/stat.h> 20#include <linux/limits.h> 21 22#include "rbcfg.h" 23#include "cyg_crc.h" 24 25#define RBCFG_TMP_FILE "/tmp/.rbcfg" 26#define RBCFG_MTD_NAME "soft_config" 27 28#define RB_ERR_NOTFOUND 1 29#define RB_ERR_INVALID 2 30#define RB_ERR_NOMEM 3 31#define RB_ERR_IO 4 32 33#define ARRAY_SIZE(_a) (sizeof((_a)) / sizeof((_a)[0])) 34 35struct rbcfg_ctx { 36 char *mtd_device; 37 char *tmp_file; 38 char *buf; 39 unsigned buflen; 40}; 41 42struct rbcfg_value { 43 const char *name; 44 const char *desc; 45 union { 46 uint32_t u32; 47 const char *raw; 48 } val; 49}; 50 51#define RBCFG_ENV_TYPE_U32 0 52 53struct rbcfg_env { 54 const char *name; 55 int type; 56 uint16_t id; 57 const struct rbcfg_value *values; 58 int num_values; 59}; 60 61#define CMD_FLAG_USES_CFG 0x01 62 63struct rbcfg_command { 64 const char *command; 65 const char *usage; 66 int flags; 67 int (*exec)(int argc, const char *argv[]); 68}; 69 70static void usage(void); 71 72/* Globals */ 73 74static struct rbcfg_ctx *rbcfg_ctx; 75static char *rbcfg_name; 76 77#define CFG_U32(_name, _desc, _val) { \ 78 .name = (_name), \ 79 .desc = (_desc), \ 80 .val.u32 = (_val), \ 81} 82 83static const struct rbcfg_value rbcfg_boot_delay[] = { 84 CFG_U32("1", "1 second", RB_BOOT_DELAY_1SEC), 85 CFG_U32("2", "2 seconds", RB_BOOT_DELAY_2SEC), 86 CFG_U32("3", "3 seconds", RB_BOOT_DELAY_3SEC), 87 CFG_U32("4", "4 seconds", RB_BOOT_DELAY_4SEC), 88 CFG_U32("5", "5 seconds", RB_BOOT_DELAY_5SEC), 89 CFG_U32("6", "6 seconds", RB_BOOT_DELAY_6SEC), 90 CFG_U32("7", "7 seconds", RB_BOOT_DELAY_7SEC), 91 CFG_U32("8", "8 seconds", RB_BOOT_DELAY_8SEC), 92 CFG_U32("9", "9 seconds", RB_BOOT_DELAY_9SEC), 93}; 94 95static const struct rbcfg_value rbcfg_boot_device[] = { 96 CFG_U32("eth", "boot over Ethernet", 97 RB_BOOT_DEVICE_ETHER), 98 CFG_U32("nandeth", "boot from NAND, if fail then Ethernet", 99 RB_BOOT_DEVICE_NANDETH), 100 CFG_U32("ethnand", "boot Ethernet once, then NAND", 101 RB_BOOT_DEVICE_ETHONCE), 102 CFG_U32("nand", "boot from NAND only", 103 RB_BOOT_DEVICE_NANDONLY), 104}; 105 106static const struct rbcfg_value rbcfg_boot_key[] = { 107 CFG_U32("any", "any key", RB_BOOT_KEY_ANY), 108 CFG_U32("del", "<Delete> key only", RB_BOOT_KEY_DEL), 109}; 110 111static const struct rbcfg_value rbcfg_boot_protocol[] = { 112 CFG_U32("bootp", "BOOTP protocol", RB_BOOT_PROTOCOL_BOOTP), 113 CFG_U32("dhcp", "DHCP protocol", RB_BOOT_PROTOCOL_DHCP), 114}; 115 116static const struct rbcfg_value rbcfg_uart_speed[] = { 117 CFG_U32("115200", "", RB_UART_SPEED_115200), 118 CFG_U32("57600", "", RB_UART_SPEED_57600), 119 CFG_U32("38400", "", RB_UART_SPEED_38400), 120 CFG_U32("19200", "", RB_UART_SPEED_19200), 121 CFG_U32("9600", "", RB_UART_SPEED_9600), 122 CFG_U32("4800", "", RB_UART_SPEED_4800), 123 CFG_U32("2400", "", RB_UART_SPEED_2400), 124 CFG_U32("1200", "", RB_UART_SPEED_1200), 125 CFG_U32("off", "disable console output", RB_UART_SPEED_OFF), 126}; 127 128static const struct rbcfg_value rbcfg_cpu_mode[] = { 129 CFG_U32("powersave", "power save", RB_CPU_MODE_POWERSAVE), 130 CFG_U32("regular", "regular (better for -0c environment)", 131 RB_CPU_MODE_REGULAR), 132}; 133 134static const struct rbcfg_value rbcfg_booter[] = { 135 CFG_U32("regular", "load regular booter", RB_BOOTER_REGULAR), 136 CFG_U32("backup", "force backup-booter loading", RB_BOOTER_BACKUP), 137}; 138 139static const struct rbcfg_env rbcfg_envs[] = { 140 { 141 .name = "boot_delay", 142 .id = RB_ID_BOOT_DELAY, 143 .type = RBCFG_ENV_TYPE_U32, 144 .values = rbcfg_boot_delay, 145 .num_values = ARRAY_SIZE(rbcfg_boot_delay), 146 }, { 147 .name = "boot_device", 148 .id = RB_ID_BOOT_DEVICE, 149 .type = RBCFG_ENV_TYPE_U32, 150 .values = rbcfg_boot_device, 151 .num_values = ARRAY_SIZE(rbcfg_boot_device), 152 }, { 153 .name = "boot_key", 154 .id = RB_ID_BOOT_KEY, 155 .type = RBCFG_ENV_TYPE_U32, 156 .values = rbcfg_boot_key, 157 .num_values = ARRAY_SIZE(rbcfg_boot_key), 158 }, { 159 .name = "boot_protocol", 160 .id = RB_ID_BOOT_PROTOCOL, 161 .type = RBCFG_ENV_TYPE_U32, 162 .values = rbcfg_boot_protocol, 163 .num_values = ARRAY_SIZE(rbcfg_boot_protocol), 164 }, { 165 .name = "booter", 166 .id = RB_ID_BOOTER, 167 .type = RBCFG_ENV_TYPE_U32, 168 .values = rbcfg_booter, 169 .num_values = ARRAY_SIZE(rbcfg_booter), 170 }, { 171 .name = "cpu_mode", 172 .id = RB_ID_CPU_MODE, 173 .type = RBCFG_ENV_TYPE_U32, 174 .values = rbcfg_cpu_mode, 175 .num_values = ARRAY_SIZE(rbcfg_cpu_mode), 176 }, { 177 .name = "uart_speed", 178 .id = RB_ID_UART_SPEED, 179 .type = RBCFG_ENV_TYPE_U32, 180 .values = rbcfg_uart_speed, 181 .num_values = ARRAY_SIZE(rbcfg_uart_speed), 182 } 183}; 184 185static inline uint16_t 186get_u16(const void *buf) 187{ 188 const uint8_t *p = buf; 189 190 return ((uint16_t) p[1] + ((uint16_t) p[0] << 8)); 191} 192 193static inline uint32_t 194get_u32(const void *buf) 195{ 196 const uint8_t *p = buf; 197 198 return ((uint32_t) p[3] + ((uint32_t) p[2] << 8) + 199 ((uint32_t) p[1] << 16) + ((uint32_t) p[0] << 24)); 200} 201 202static inline void 203put_u32(void *buf, uint32_t val) 204{ 205 uint8_t *p = buf; 206 207 p[3] = val & 0xff; 208 p[2] = (val >> 8) & 0xff; 209 p[1] = (val >> 16) & 0xff; 210 p[0] = (val >> 24) & 0xff; 211} 212 213static int 214rbcfg_find_tag(struct rbcfg_ctx *ctx, uint16_t tag_id, uint16_t *tag_len, 215 void **tag_data) 216{ 217 uint16_t id; 218 uint16_t len; 219 char *buf = ctx->buf; 220 unsigned int buflen = ctx->buflen; 221 int ret = RB_ERR_NOTFOUND; 222 223 /* skip magic and CRC value */ 224 buf += 8; 225 buflen -= 8; 226 227 while (buflen > 2) { 228 len = get_u16(buf); 229 buf += 2; 230 buflen -= 2; 231 232 if (buflen < 2) 233 break; 234 235 id = get_u16(buf); 236 buf += 2; 237 buflen -= 2; 238 239 if (id == RB_ID_TERMINATOR) 240 break; 241 242 if (buflen < len) 243 break; 244 245 if (id == tag_id) { 246 *tag_len = len; 247 *tag_data = buf; 248 ret = 0; 249 break; 250 } 251 252 buf += len; 253 buflen -= len; 254 } 255 256 if (ret) 257 fprintf(stderr, "no tag found with id=%u\n", tag_id); 258 259 return ret; 260} 261 262static int 263rbcfg_get_u32(struct rbcfg_ctx *ctx, uint16_t id, uint32_t *val) 264{ 265 void *tag_data; 266 uint16_t tag_len; 267 int err; 268 269 err = rbcfg_find_tag(ctx, id, &tag_len, &tag_data); 270 if (err) 271 return err; 272 273 *val = get_u32(tag_data); 274 return 0; 275} 276 277static int 278rbcfg_set_u32(struct rbcfg_ctx *ctx, uint16_t id, uint32_t val) 279{ 280 void *tag_data; 281 uint16_t tag_len; 282 int err; 283 284 err = rbcfg_find_tag(ctx, id, &tag_len, &tag_data); 285 if (err) 286 return err; 287 288 put_u32(tag_data, val); 289 return 0; 290} 291 292char *rbcfg_find_mtd(const char *name, int *erase_size) 293{ 294 FILE *f; 295 int mtd_num; 296 char dev[PATH_MAX]; 297 char *ret = NULL; 298 struct stat s; 299 int err; 300 301 f = fopen("/proc/mtd", "r"); 302 if (!f) 303 return NULL; 304 305 while (1) { 306 char *p; 307 p = fgets(dev, sizeof(dev), f); 308 if (!p) 309 break; 310 311 if (!strstr(dev, name)) 312 continue; 313 314 err = sscanf(dev, "mtd%d: %08x", &mtd_num, erase_size); 315 if (err != 2) 316 break; 317 318 sprintf(dev, "/dev/mtdblock%d", mtd_num); 319 err = stat(dev, &s); 320 if (err < 0) 321 break; 322 323 if ((s.st_mode & S_IFBLK) == 0) 324 break; 325 326 ret = malloc(strlen(dev) + 1); 327 if (ret == NULL) 328 break; 329 330 strncpy(ret, dev, strlen(dev) + 1); 331 break; 332 } 333 334 fclose(f); 335 return ret; 336} 337 338static int 339rbcfg_check_tmp(struct rbcfg_ctx *ctx) 340{ 341 struct stat s; 342 int err; 343 344 err = stat(ctx->tmp_file, &s); 345 if (err < 0) 346 return 0; 347 348 if ((s.st_mode & S_IFREG) == 0) 349 return 0; 350 351 if (s.st_size != ctx->buflen) 352 return 0; 353 354 return 1; 355} 356 357static int 358rbcfg_load(struct rbcfg_ctx *ctx) 359{ 360 uint32_t magic; 361 uint32_t crc_orig, crc; 362 char *name; 363 int tmp; 364 int fd; 365 int err; 366 367 tmp = rbcfg_check_tmp(ctx); 368 name = (tmp) ? ctx->tmp_file : ctx->mtd_device; 369 370 fd = open(name, O_RDONLY); 371 if (fd < 0) { 372 fprintf(stderr, "unable to open %s\n", name); 373 err = RB_ERR_IO; 374 goto err; 375 } 376 377 err = read(fd, ctx->buf, ctx->buflen); 378 if (err != ctx->buflen) { 379 fprintf(stderr, "unable to read from %s\n", name); 380 err = RB_ERR_IO; 381 goto err_close; 382 } 383 384 magic = get_u32(ctx->buf); 385 if (magic != RB_MAGIC_SOFT) { 386 fprintf(stderr, "invalid configuration\n"); 387 err = RB_ERR_INVALID; 388 goto err_close; 389 } 390 391 crc_orig = get_u32(ctx->buf + 4); 392 put_u32(ctx->buf + 4, 0); 393 crc = cyg_ether_crc32((unsigned char *) ctx->buf, ctx->buflen); 394 if (crc != crc_orig) { 395 fprintf(stderr, "configuration has CRC error\n"); 396 err = RB_ERR_INVALID; 397 goto err_close; 398 } 399 400 err = 0; 401 402 err_close: 403 close(fd); 404 err: 405 return err; 406} 407 408static int 409rbcfg_open() 410{ 411 char *mtd_device; 412 struct rbcfg_ctx *ctx; 413 int buflen; 414 int err; 415 416 mtd_device = rbcfg_find_mtd(RBCFG_MTD_NAME, &buflen); 417 if (!mtd_device) { 418 fprintf(stderr, "unable to find configuration\n"); 419 return RB_ERR_NOTFOUND; 420 } 421 422 ctx = malloc(sizeof(struct rbcfg_ctx) + buflen); 423 if (ctx == NULL) { 424 err = RB_ERR_NOMEM; 425 goto err_free_mtd; 426 } 427 428 ctx->mtd_device = mtd_device; 429 ctx->tmp_file = RBCFG_TMP_FILE; 430 ctx->buflen = buflen; 431 ctx->buf = (char *) &ctx[1]; 432 433 err = rbcfg_load(ctx); 434 if (err) 435 goto err_free_ctx; 436 437 rbcfg_ctx = ctx; 438 return 0; 439 440 err_free_ctx: 441 free(ctx); 442 err_free_mtd: 443 free(mtd_device); 444 return err; 445} 446 447static int 448rbcfg_update(int tmp) 449{ 450 struct rbcfg_ctx *ctx = rbcfg_ctx; 451 char *name; 452 uint32_t crc; 453 int fd; 454 int err; 455 456 put_u32(ctx->buf, RB_MAGIC_SOFT); 457 put_u32(ctx->buf + 4, 0); 458 crc = cyg_ether_crc32((unsigned char *) ctx->buf, ctx->buflen); 459 put_u32(ctx->buf + 4, crc); 460 461 name = (tmp) ? ctx->tmp_file : ctx->mtd_device; 462 fd = open(name, O_WRONLY | O_CREAT); 463 if (fd < 0) { 464 fprintf(stderr, "unable to open %s for writing\n", name); 465 err = RB_ERR_IO; 466 goto out; 467 } 468 469 err = write(fd, ctx->buf, ctx->buflen); 470 if (err != ctx->buflen) { 471 err = RB_ERR_IO; 472 goto out_close; 473 } 474 475 fsync(fd); 476 err = 0; 477 478 out_close: 479 close(fd); 480 out: 481 return err; 482} 483 484static void 485rbcfg_close(void) 486{ 487 struct rbcfg_ctx *ctx; 488 489 ctx = rbcfg_ctx; 490 free(ctx->mtd_device); 491 free(ctx); 492} 493 494static const struct rbcfg_value * 495rbcfg_env_find(const struct rbcfg_env *env, const char *name) 496{ 497 unsigned i; 498 499 for (i = 0; i < env->num_values; i++) { 500 const struct rbcfg_value *v = &env->values[i]; 501 502 if (strcmp(v->name, name) == 0) 503 return v; 504 } 505 506 return NULL; 507} 508 509static const struct rbcfg_value * 510rbcfg_env_find_u32(const struct rbcfg_env *env, uint32_t val) 511{ 512 unsigned i; 513 514 for (i = 0; i < env->num_values; i++) { 515 const struct rbcfg_value *v = &env->values[i]; 516 517 if (v->val.u32 == val) 518 return v; 519 } 520 521 return NULL; 522} 523 524static const char * 525rbcfg_env_get_u32(const struct rbcfg_env *env) 526{ 527 const struct rbcfg_value *v; 528 uint32_t val; 529 int err; 530 531 err = rbcfg_get_u32(rbcfg_ctx, env->id, &val); 532 if (err) 533 return NULL; 534 535 v = rbcfg_env_find_u32(env, val); 536 if (v == NULL) { 537 fprintf(stderr, "unknown value %08x found for %s\n", 538 val, env->name); 539 return NULL; 540 } 541 542 return v->name; 543} 544 545static int 546rbcfg_env_set_u32(const struct rbcfg_env *env, const char *data) 547{ 548 const struct rbcfg_value *v; 549 int err; 550 551 v = rbcfg_env_find(env, data); 552 if (v == NULL) { 553 fprintf(stderr, "invalid value '%s'\n", data); 554 return RB_ERR_INVALID; 555 } 556 557 err = rbcfg_set_u32(rbcfg_ctx, env->id, v->val.u32); 558 return err; 559} 560 561static const char * 562rbcfg_env_get(const struct rbcfg_env *env) 563{ 564 const char *ret = NULL; 565 566 switch (env->type) { 567 case RBCFG_ENV_TYPE_U32: 568 ret = rbcfg_env_get_u32(env); 569 break; 570 } 571 572 return ret; 573} 574 575static int 576rbcfg_env_set(const struct rbcfg_env *env, const char *data) 577{ 578 int ret = 0; 579 580 switch (env->type) { 581 case RBCFG_ENV_TYPE_U32: 582 ret = rbcfg_env_set_u32(env, data); 583 break; 584 } 585 586 return ret; 587} 588 589static int 590rbcfg_cmd_apply(int argc, const char *argv[]) 591{ 592 return rbcfg_update(0); 593} 594 595static int 596rbcfg_cmd_help(int argc, const char *argv[]) 597{ 598 usage(); 599 return 0; 600} 601 602static int 603rbcfg_cmd_get(int argc, const char *argv[]) 604{ 605 int err = RB_ERR_NOTFOUND; 606 int i; 607 608 if (argc != 1) { 609 usage(); 610 return RB_ERR_INVALID; 611 } 612 613 for (i = 0; i < ARRAY_SIZE(rbcfg_envs); i++) { 614 const struct rbcfg_env *env = &rbcfg_envs[i]; 615 const char *value; 616 617 if (strcmp(env->name, argv[0])) 618 continue; 619 620 value = rbcfg_env_get(env); 621 if (value) { 622 fprintf(stdout, "%s\n", value); 623 err = 0; 624 } 625 break; 626 } 627 628 return err; 629} 630 631static int 632rbcfg_cmd_set(int argc, const char *argv[]) 633{ 634 int err = RB_ERR_INVALID; 635 int i; 636 637 if (argc != 2) { 638 /* not enough parameters */ 639 usage(); 640 return RB_ERR_INVALID; 641 } 642 643 for (i = 0; i < ARRAY_SIZE(rbcfg_envs); i++) { 644 const struct rbcfg_env *env = &rbcfg_envs[i]; 645 646 if (strcmp(env->name, argv[0])) 647 continue; 648 649 err = rbcfg_env_set(env, argv[1]); 650 if (err == 0) 651 err = rbcfg_update(1); 652 break; 653 } 654 655 return err; 656} 657 658static int 659rbcfg_cmd_show(int argc, const char *argv[]) 660{ 661 int i; 662 663 if (argc != 0) { 664 usage(); 665 return RB_ERR_INVALID; 666 } 667 668 for (i = 0; i < ARRAY_SIZE(rbcfg_envs); i++) { 669 const struct rbcfg_env *env = &rbcfg_envs[i]; 670 const char *value; 671 672 value = rbcfg_env_get(env); 673 if (value) 674 fprintf(stdout, "%s=%s\n", env->name, value); 675 } 676 677 return 0; 678} 679 680static const struct rbcfg_command rbcfg_commands[] = { 681 { 682 .command = "apply", 683 .usage = "apply\n" 684 "\t- write configuration to the mtd device", 685 .flags = CMD_FLAG_USES_CFG, 686 .exec = rbcfg_cmd_apply, 687 }, { 688 .command = "help", 689 .usage = "help\n" 690 "\t- show this screen", 691 .exec = rbcfg_cmd_help, 692 }, { 693 .command = "get", 694 .usage = "get <name>\n" 695 "\t- get value of the configuration option <name>", 696 .flags = CMD_FLAG_USES_CFG, 697 .exec = rbcfg_cmd_get, 698 }, { 699 .command = "set", 700 .usage = "set <name> <value>\n" 701 "\t- set value of the configuration option <name> to <value>", 702 .flags = CMD_FLAG_USES_CFG, 703 .exec = rbcfg_cmd_set, 704 }, { 705 .command = "show", 706 .usage = "show\n" 707 "\t- show value of all configuration options", 708 .flags = CMD_FLAG_USES_CFG, 709 .exec = rbcfg_cmd_show, 710 } 711}; 712 713static void 714usage(void) 715{ 716 char buf[255]; 717 int len; 718 int i; 719 720 fprintf(stderr, "Usage: %s <command>\n", rbcfg_name); 721 722 fprintf(stderr, "\nCommands:\n"); 723 for (i = 0; i < ARRAY_SIZE(rbcfg_commands); i++) { 724 const struct rbcfg_command *cmd; 725 cmd = &rbcfg_commands[i]; 726 727 len = snprintf(buf, sizeof(buf), "%s", cmd->usage); 728 buf[len] = '\0'; 729 fprintf(stderr, "%s\n", buf); 730 } 731 732 fprintf(stderr, "\nConfiguration options:\n"); 733 for (i = 0; i < ARRAY_SIZE(rbcfg_envs); i++) { 734 const struct rbcfg_env *env; 735 int j; 736 737 env = &rbcfg_envs[i]; 738 fprintf(stderr, "\n%s:\n", env->name); 739 for (j = 0; j < env->num_values; j++) { 740 const struct rbcfg_value *v = &env->values[j]; 741 fprintf(stderr, "\t%-12s %s\n", v->name, v->desc); 742 } 743 } 744 fprintf(stderr, "\n"); 745} 746 747int main(int argc, const char *argv[]) 748{ 749 const struct rbcfg_command *cmd = NULL; 750 int ret; 751 int i; 752 753 rbcfg_name = (char *) argv[0]; 754 755 if (argc < 2) { 756 usage(); 757 return EXIT_FAILURE; 758 } 759 760 for (i = 0; i < ARRAY_SIZE(rbcfg_commands); i++) { 761 if (strcmp(rbcfg_commands[i].command, argv[1]) == 0) { 762 cmd = &rbcfg_commands[i]; 763 break; 764 } 765 } 766 767 if (cmd == NULL) { 768 fprintf(stderr, "unknown command '%s'\n", argv[1]); 769 usage(); 770 return EXIT_FAILURE; 771 } 772 773 argc -= 2; 774 argv += 2; 775 776 if (cmd->flags & CMD_FLAG_USES_CFG) { 777 ret = rbcfg_open(); 778 if (ret) 779 return EXIT_FAILURE; 780 } 781 782 ret = cmd->exec(argc, argv); 783 784 if (cmd->flags & CMD_FLAG_USES_CFG) 785 rbcfg_close(); 786 787 if (ret) 788 return EXIT_FAILURE; 789 790 return EXIT_SUCCESS; 791} 792