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