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