xref: /openbmc/linux/tools/bootconfig/main.c (revision 950313ebf79c65702f4c15d29328147766d1f1fd)
1*950313ebSMasami Hiramatsu // SPDX-License-Identifier: GPL-2.0
2*950313ebSMasami Hiramatsu /*
3*950313ebSMasami Hiramatsu  * Boot config tool for initrd image
4*950313ebSMasami Hiramatsu  */
5*950313ebSMasami Hiramatsu #include <stdio.h>
6*950313ebSMasami Hiramatsu #include <stdlib.h>
7*950313ebSMasami Hiramatsu #include <sys/types.h>
8*950313ebSMasami Hiramatsu #include <sys/stat.h>
9*950313ebSMasami Hiramatsu #include <fcntl.h>
10*950313ebSMasami Hiramatsu #include <unistd.h>
11*950313ebSMasami Hiramatsu #include <string.h>
12*950313ebSMasami Hiramatsu #include <errno.h>
13*950313ebSMasami Hiramatsu 
14*950313ebSMasami Hiramatsu #include <linux/kernel.h>
15*950313ebSMasami Hiramatsu #include <linux/bootconfig.h>
16*950313ebSMasami Hiramatsu 
17*950313ebSMasami Hiramatsu int pr_output = 1;
18*950313ebSMasami Hiramatsu 
19*950313ebSMasami Hiramatsu static int xbc_show_array(struct xbc_node *node)
20*950313ebSMasami Hiramatsu {
21*950313ebSMasami Hiramatsu 	const char *val;
22*950313ebSMasami Hiramatsu 	int i = 0;
23*950313ebSMasami Hiramatsu 
24*950313ebSMasami Hiramatsu 	xbc_array_for_each_value(node, val) {
25*950313ebSMasami Hiramatsu 		printf("\"%s\"%s", val, node->next ? ", " : ";\n");
26*950313ebSMasami Hiramatsu 		i++;
27*950313ebSMasami Hiramatsu 	}
28*950313ebSMasami Hiramatsu 	return i;
29*950313ebSMasami Hiramatsu }
30*950313ebSMasami Hiramatsu 
31*950313ebSMasami Hiramatsu static void xbc_show_compact_tree(void)
32*950313ebSMasami Hiramatsu {
33*950313ebSMasami Hiramatsu 	struct xbc_node *node, *cnode;
34*950313ebSMasami Hiramatsu 	int depth = 0, i;
35*950313ebSMasami Hiramatsu 
36*950313ebSMasami Hiramatsu 	node = xbc_root_node();
37*950313ebSMasami Hiramatsu 	while (node && xbc_node_is_key(node)) {
38*950313ebSMasami Hiramatsu 		for (i = 0; i < depth; i++)
39*950313ebSMasami Hiramatsu 			printf("\t");
40*950313ebSMasami Hiramatsu 		cnode = xbc_node_get_child(node);
41*950313ebSMasami Hiramatsu 		while (cnode && xbc_node_is_key(cnode) && !cnode->next) {
42*950313ebSMasami Hiramatsu 			printf("%s.", xbc_node_get_data(node));
43*950313ebSMasami Hiramatsu 			node = cnode;
44*950313ebSMasami Hiramatsu 			cnode = xbc_node_get_child(node);
45*950313ebSMasami Hiramatsu 		}
46*950313ebSMasami Hiramatsu 		if (cnode && xbc_node_is_key(cnode)) {
47*950313ebSMasami Hiramatsu 			printf("%s {\n", xbc_node_get_data(node));
48*950313ebSMasami Hiramatsu 			depth++;
49*950313ebSMasami Hiramatsu 			node = cnode;
50*950313ebSMasami Hiramatsu 			continue;
51*950313ebSMasami Hiramatsu 		} else if (cnode && xbc_node_is_value(cnode)) {
52*950313ebSMasami Hiramatsu 			printf("%s = ", xbc_node_get_data(node));
53*950313ebSMasami Hiramatsu 			if (cnode->next)
54*950313ebSMasami Hiramatsu 				xbc_show_array(cnode);
55*950313ebSMasami Hiramatsu 			else
56*950313ebSMasami Hiramatsu 				printf("\"%s\";\n", xbc_node_get_data(cnode));
57*950313ebSMasami Hiramatsu 		} else {
58*950313ebSMasami Hiramatsu 			printf("%s;\n", xbc_node_get_data(node));
59*950313ebSMasami Hiramatsu 		}
60*950313ebSMasami Hiramatsu 
61*950313ebSMasami Hiramatsu 		if (node->next) {
62*950313ebSMasami Hiramatsu 			node = xbc_node_get_next(node);
63*950313ebSMasami Hiramatsu 			continue;
64*950313ebSMasami Hiramatsu 		}
65*950313ebSMasami Hiramatsu 		while (!node->next) {
66*950313ebSMasami Hiramatsu 			node = xbc_node_get_parent(node);
67*950313ebSMasami Hiramatsu 			if (!node)
68*950313ebSMasami Hiramatsu 				return;
69*950313ebSMasami Hiramatsu 			if (!xbc_node_get_child(node)->next)
70*950313ebSMasami Hiramatsu 				continue;
71*950313ebSMasami Hiramatsu 			depth--;
72*950313ebSMasami Hiramatsu 			for (i = 0; i < depth; i++)
73*950313ebSMasami Hiramatsu 				printf("\t");
74*950313ebSMasami Hiramatsu 			printf("}\n");
75*950313ebSMasami Hiramatsu 		}
76*950313ebSMasami Hiramatsu 		node = xbc_node_get_next(node);
77*950313ebSMasami Hiramatsu 	}
78*950313ebSMasami Hiramatsu }
79*950313ebSMasami Hiramatsu 
80*950313ebSMasami Hiramatsu /* Simple real checksum */
81*950313ebSMasami Hiramatsu int checksum(unsigned char *buf, int len)
82*950313ebSMasami Hiramatsu {
83*950313ebSMasami Hiramatsu 	int i, sum = 0;
84*950313ebSMasami Hiramatsu 
85*950313ebSMasami Hiramatsu 	for (i = 0; i < len; i++)
86*950313ebSMasami Hiramatsu 		sum += buf[i];
87*950313ebSMasami Hiramatsu 
88*950313ebSMasami Hiramatsu 	return sum;
89*950313ebSMasami Hiramatsu }
90*950313ebSMasami Hiramatsu 
91*950313ebSMasami Hiramatsu #define PAGE_SIZE	4096
92*950313ebSMasami Hiramatsu 
93*950313ebSMasami Hiramatsu int load_xbc_fd(int fd, char **buf, int size)
94*950313ebSMasami Hiramatsu {
95*950313ebSMasami Hiramatsu 	int ret;
96*950313ebSMasami Hiramatsu 
97*950313ebSMasami Hiramatsu 	*buf = malloc(size + 1);
98*950313ebSMasami Hiramatsu 	if (!*buf)
99*950313ebSMasami Hiramatsu 		return -ENOMEM;
100*950313ebSMasami Hiramatsu 
101*950313ebSMasami Hiramatsu 	ret = read(fd, *buf, size);
102*950313ebSMasami Hiramatsu 	if (ret < 0)
103*950313ebSMasami Hiramatsu 		return -errno;
104*950313ebSMasami Hiramatsu 	(*buf)[size] = '\0';
105*950313ebSMasami Hiramatsu 
106*950313ebSMasami Hiramatsu 	return ret;
107*950313ebSMasami Hiramatsu }
108*950313ebSMasami Hiramatsu 
109*950313ebSMasami Hiramatsu /* Return the read size or -errno */
110*950313ebSMasami Hiramatsu int load_xbc_file(const char *path, char **buf)
111*950313ebSMasami Hiramatsu {
112*950313ebSMasami Hiramatsu 	struct stat stat;
113*950313ebSMasami Hiramatsu 	int fd, ret;
114*950313ebSMasami Hiramatsu 
115*950313ebSMasami Hiramatsu 	fd = open(path, O_RDONLY);
116*950313ebSMasami Hiramatsu 	if (fd < 0)
117*950313ebSMasami Hiramatsu 		return -errno;
118*950313ebSMasami Hiramatsu 	ret = fstat(fd, &stat);
119*950313ebSMasami Hiramatsu 	if (ret < 0)
120*950313ebSMasami Hiramatsu 		return -errno;
121*950313ebSMasami Hiramatsu 
122*950313ebSMasami Hiramatsu 	ret = load_xbc_fd(fd, buf, stat.st_size);
123*950313ebSMasami Hiramatsu 
124*950313ebSMasami Hiramatsu 	close(fd);
125*950313ebSMasami Hiramatsu 
126*950313ebSMasami Hiramatsu 	return ret;
127*950313ebSMasami Hiramatsu }
128*950313ebSMasami Hiramatsu 
129*950313ebSMasami Hiramatsu int load_xbc_from_initrd(int fd, char **buf)
130*950313ebSMasami Hiramatsu {
131*950313ebSMasami Hiramatsu 	struct stat stat;
132*950313ebSMasami Hiramatsu 	int ret;
133*950313ebSMasami Hiramatsu 	u32 size = 0, csum = 0, rcsum;
134*950313ebSMasami Hiramatsu 
135*950313ebSMasami Hiramatsu 	ret = fstat(fd, &stat);
136*950313ebSMasami Hiramatsu 	if (ret < 0)
137*950313ebSMasami Hiramatsu 		return -errno;
138*950313ebSMasami Hiramatsu 
139*950313ebSMasami Hiramatsu 	if (stat.st_size < 8)
140*950313ebSMasami Hiramatsu 		return 0;
141*950313ebSMasami Hiramatsu 
142*950313ebSMasami Hiramatsu 	if (lseek(fd, -8, SEEK_END) < 0) {
143*950313ebSMasami Hiramatsu 		printf("Faile to lseek: %d\n", -errno);
144*950313ebSMasami Hiramatsu 		return -errno;
145*950313ebSMasami Hiramatsu 	}
146*950313ebSMasami Hiramatsu 
147*950313ebSMasami Hiramatsu 	if (read(fd, &size, sizeof(u32)) < 0)
148*950313ebSMasami Hiramatsu 		return -errno;
149*950313ebSMasami Hiramatsu 
150*950313ebSMasami Hiramatsu 	if (read(fd, &csum, sizeof(u32)) < 0)
151*950313ebSMasami Hiramatsu 		return -errno;
152*950313ebSMasami Hiramatsu 
153*950313ebSMasami Hiramatsu 	/* Wrong size, maybe no boot config here */
154*950313ebSMasami Hiramatsu 	if (stat.st_size < size + 8)
155*950313ebSMasami Hiramatsu 		return 0;
156*950313ebSMasami Hiramatsu 
157*950313ebSMasami Hiramatsu 	if (lseek(fd, stat.st_size - 8 - size, SEEK_SET) < 0) {
158*950313ebSMasami Hiramatsu 		printf("Faile to lseek: %d\n", -errno);
159*950313ebSMasami Hiramatsu 		return -errno;
160*950313ebSMasami Hiramatsu 	}
161*950313ebSMasami Hiramatsu 
162*950313ebSMasami Hiramatsu 	ret = load_xbc_fd(fd, buf, size);
163*950313ebSMasami Hiramatsu 	if (ret < 0)
164*950313ebSMasami Hiramatsu 		return ret;
165*950313ebSMasami Hiramatsu 
166*950313ebSMasami Hiramatsu 	/* Wrong Checksum, maybe no boot config here */
167*950313ebSMasami Hiramatsu 	rcsum = checksum((unsigned char *)*buf, size);
168*950313ebSMasami Hiramatsu 	if (csum != rcsum) {
169*950313ebSMasami Hiramatsu 		printf("checksum error: %d != %d\n", csum, rcsum);
170*950313ebSMasami Hiramatsu 		return 0;
171*950313ebSMasami Hiramatsu 	}
172*950313ebSMasami Hiramatsu 
173*950313ebSMasami Hiramatsu 	ret = xbc_init(*buf);
174*950313ebSMasami Hiramatsu 	/* Wrong data, maybe no boot config here */
175*950313ebSMasami Hiramatsu 	if (ret < 0)
176*950313ebSMasami Hiramatsu 		return 0;
177*950313ebSMasami Hiramatsu 
178*950313ebSMasami Hiramatsu 	return size;
179*950313ebSMasami Hiramatsu }
180*950313ebSMasami Hiramatsu 
181*950313ebSMasami Hiramatsu int show_xbc(const char *path)
182*950313ebSMasami Hiramatsu {
183*950313ebSMasami Hiramatsu 	int ret, fd;
184*950313ebSMasami Hiramatsu 	char *buf = NULL;
185*950313ebSMasami Hiramatsu 
186*950313ebSMasami Hiramatsu 	fd = open(path, O_RDONLY);
187*950313ebSMasami Hiramatsu 	if (fd < 0) {
188*950313ebSMasami Hiramatsu 		printf("Failed to open initrd %s: %d\n", path, fd);
189*950313ebSMasami Hiramatsu 		return -errno;
190*950313ebSMasami Hiramatsu 	}
191*950313ebSMasami Hiramatsu 
192*950313ebSMasami Hiramatsu 	ret = load_xbc_from_initrd(fd, &buf);
193*950313ebSMasami Hiramatsu 	if (ret < 0)
194*950313ebSMasami Hiramatsu 		printf("Failed to load a boot config from initrd: %d\n", ret);
195*950313ebSMasami Hiramatsu 	else
196*950313ebSMasami Hiramatsu 		xbc_show_compact_tree();
197*950313ebSMasami Hiramatsu 
198*950313ebSMasami Hiramatsu 	close(fd);
199*950313ebSMasami Hiramatsu 	free(buf);
200*950313ebSMasami Hiramatsu 
201*950313ebSMasami Hiramatsu 	return ret;
202*950313ebSMasami Hiramatsu }
203*950313ebSMasami Hiramatsu 
204*950313ebSMasami Hiramatsu int delete_xbc(const char *path)
205*950313ebSMasami Hiramatsu {
206*950313ebSMasami Hiramatsu 	struct stat stat;
207*950313ebSMasami Hiramatsu 	int ret = 0, fd, size;
208*950313ebSMasami Hiramatsu 	char *buf = NULL;
209*950313ebSMasami Hiramatsu 
210*950313ebSMasami Hiramatsu 	fd = open(path, O_RDWR);
211*950313ebSMasami Hiramatsu 	if (fd < 0) {
212*950313ebSMasami Hiramatsu 		printf("Failed to open initrd %s: %d\n", path, fd);
213*950313ebSMasami Hiramatsu 		return -errno;
214*950313ebSMasami Hiramatsu 	}
215*950313ebSMasami Hiramatsu 
216*950313ebSMasami Hiramatsu 	/*
217*950313ebSMasami Hiramatsu 	 * Suppress error messages in xbc_init() because it can be just a
218*950313ebSMasami Hiramatsu 	 * data which concidentally matches the size and checksum footer.
219*950313ebSMasami Hiramatsu 	 */
220*950313ebSMasami Hiramatsu 	pr_output = 0;
221*950313ebSMasami Hiramatsu 	size = load_xbc_from_initrd(fd, &buf);
222*950313ebSMasami Hiramatsu 	pr_output = 1;
223*950313ebSMasami Hiramatsu 	if (size < 0) {
224*950313ebSMasami Hiramatsu 		ret = size;
225*950313ebSMasami Hiramatsu 		printf("Failed to load a boot config from initrd: %d\n", ret);
226*950313ebSMasami Hiramatsu 	} else if (size > 0) {
227*950313ebSMasami Hiramatsu 		ret = fstat(fd, &stat);
228*950313ebSMasami Hiramatsu 		if (!ret)
229*950313ebSMasami Hiramatsu 			ret = ftruncate(fd, stat.st_size - size - 8);
230*950313ebSMasami Hiramatsu 		if (ret)
231*950313ebSMasami Hiramatsu 			ret = -errno;
232*950313ebSMasami Hiramatsu 	} /* Ignore if there is no boot config in initrd */
233*950313ebSMasami Hiramatsu 
234*950313ebSMasami Hiramatsu 	close(fd);
235*950313ebSMasami Hiramatsu 	free(buf);
236*950313ebSMasami Hiramatsu 
237*950313ebSMasami Hiramatsu 	return ret;
238*950313ebSMasami Hiramatsu }
239*950313ebSMasami Hiramatsu 
240*950313ebSMasami Hiramatsu int apply_xbc(const char *path, const char *xbc_path)
241*950313ebSMasami Hiramatsu {
242*950313ebSMasami Hiramatsu 	u32 size, csum;
243*950313ebSMasami Hiramatsu 	char *buf, *data;
244*950313ebSMasami Hiramatsu 	int ret, fd;
245*950313ebSMasami Hiramatsu 
246*950313ebSMasami Hiramatsu 	ret = load_xbc_file(xbc_path, &buf);
247*950313ebSMasami Hiramatsu 	if (ret < 0) {
248*950313ebSMasami Hiramatsu 		printf("Failed to load %s : %d\n", xbc_path, ret);
249*950313ebSMasami Hiramatsu 		return ret;
250*950313ebSMasami Hiramatsu 	}
251*950313ebSMasami Hiramatsu 	size = strlen(buf) + 1;
252*950313ebSMasami Hiramatsu 	csum = checksum((unsigned char *)buf, size);
253*950313ebSMasami Hiramatsu 
254*950313ebSMasami Hiramatsu 	/* Prepare xbc_path data */
255*950313ebSMasami Hiramatsu 	data = malloc(size + 8);
256*950313ebSMasami Hiramatsu 	if (!data)
257*950313ebSMasami Hiramatsu 		return -ENOMEM;
258*950313ebSMasami Hiramatsu 	strcpy(data, buf);
259*950313ebSMasami Hiramatsu 	*(u32 *)(data + size) = size;
260*950313ebSMasami Hiramatsu 	*(u32 *)(data + size + 4) = csum;
261*950313ebSMasami Hiramatsu 
262*950313ebSMasami Hiramatsu 	/* Check the data format */
263*950313ebSMasami Hiramatsu 	ret = xbc_init(buf);
264*950313ebSMasami Hiramatsu 	if (ret < 0) {
265*950313ebSMasami Hiramatsu 		printf("Failed to parse %s: %d\n", xbc_path, ret);
266*950313ebSMasami Hiramatsu 		free(data);
267*950313ebSMasami Hiramatsu 		free(buf);
268*950313ebSMasami Hiramatsu 		return ret;
269*950313ebSMasami Hiramatsu 	}
270*950313ebSMasami Hiramatsu 	printf("Apply %s to %s\n", xbc_path, path);
271*950313ebSMasami Hiramatsu 	printf("\tSize: %u bytes\n", (unsigned int)size);
272*950313ebSMasami Hiramatsu 	printf("\tChecksum: %d\n", (unsigned int)csum);
273*950313ebSMasami Hiramatsu 
274*950313ebSMasami Hiramatsu 	/* TODO: Check the options by schema */
275*950313ebSMasami Hiramatsu 	xbc_destroy_all();
276*950313ebSMasami Hiramatsu 	free(buf);
277*950313ebSMasami Hiramatsu 
278*950313ebSMasami Hiramatsu 	/* Remove old boot config if exists */
279*950313ebSMasami Hiramatsu 	ret = delete_xbc(path);
280*950313ebSMasami Hiramatsu 	if (ret < 0) {
281*950313ebSMasami Hiramatsu 		printf("Failed to delete previous boot config: %d\n", ret);
282*950313ebSMasami Hiramatsu 		return ret;
283*950313ebSMasami Hiramatsu 	}
284*950313ebSMasami Hiramatsu 
285*950313ebSMasami Hiramatsu 	/* Apply new one */
286*950313ebSMasami Hiramatsu 	fd = open(path, O_RDWR | O_APPEND);
287*950313ebSMasami Hiramatsu 	if (fd < 0) {
288*950313ebSMasami Hiramatsu 		printf("Failed to open %s: %d\n", path, fd);
289*950313ebSMasami Hiramatsu 		return fd;
290*950313ebSMasami Hiramatsu 	}
291*950313ebSMasami Hiramatsu 	/* TODO: Ensure the @path is initramfs/initrd image */
292*950313ebSMasami Hiramatsu 	ret = write(fd, data, size + 8);
293*950313ebSMasami Hiramatsu 	if (ret < 0) {
294*950313ebSMasami Hiramatsu 		printf("Failed to apply a boot config: %d\n", ret);
295*950313ebSMasami Hiramatsu 		return ret;
296*950313ebSMasami Hiramatsu 	}
297*950313ebSMasami Hiramatsu 	close(fd);
298*950313ebSMasami Hiramatsu 	free(data);
299*950313ebSMasami Hiramatsu 
300*950313ebSMasami Hiramatsu 	return 0;
301*950313ebSMasami Hiramatsu }
302*950313ebSMasami Hiramatsu 
303*950313ebSMasami Hiramatsu int usage(void)
304*950313ebSMasami Hiramatsu {
305*950313ebSMasami Hiramatsu 	printf("Usage: bootconfig [OPTIONS] <INITRD>\n"
306*950313ebSMasami Hiramatsu 		" Apply, delete or show boot config to initrd.\n"
307*950313ebSMasami Hiramatsu 		" Options:\n"
308*950313ebSMasami Hiramatsu 		"		-a <config>: Apply boot config to initrd\n"
309*950313ebSMasami Hiramatsu 		"		-d : Delete boot config file from initrd\n\n"
310*950313ebSMasami Hiramatsu 		" If no option is given, show current applied boot config.\n");
311*950313ebSMasami Hiramatsu 	return -1;
312*950313ebSMasami Hiramatsu }
313*950313ebSMasami Hiramatsu 
314*950313ebSMasami Hiramatsu int main(int argc, char **argv)
315*950313ebSMasami Hiramatsu {
316*950313ebSMasami Hiramatsu 	char *path = NULL;
317*950313ebSMasami Hiramatsu 	char *apply = NULL;
318*950313ebSMasami Hiramatsu 	bool delete = false;
319*950313ebSMasami Hiramatsu 	int opt;
320*950313ebSMasami Hiramatsu 
321*950313ebSMasami Hiramatsu 	while ((opt = getopt(argc, argv, "hda:")) != -1) {
322*950313ebSMasami Hiramatsu 		switch (opt) {
323*950313ebSMasami Hiramatsu 		case 'd':
324*950313ebSMasami Hiramatsu 			delete = true;
325*950313ebSMasami Hiramatsu 			break;
326*950313ebSMasami Hiramatsu 		case 'a':
327*950313ebSMasami Hiramatsu 			apply = optarg;
328*950313ebSMasami Hiramatsu 			break;
329*950313ebSMasami Hiramatsu 		case 'h':
330*950313ebSMasami Hiramatsu 		default:
331*950313ebSMasami Hiramatsu 			return usage();
332*950313ebSMasami Hiramatsu 		}
333*950313ebSMasami Hiramatsu 	}
334*950313ebSMasami Hiramatsu 
335*950313ebSMasami Hiramatsu 	if (apply && delete) {
336*950313ebSMasami Hiramatsu 		printf("Error: You can not specify both -a and -d at once.\n");
337*950313ebSMasami Hiramatsu 		return usage();
338*950313ebSMasami Hiramatsu 	}
339*950313ebSMasami Hiramatsu 
340*950313ebSMasami Hiramatsu 	if (optind >= argc) {
341*950313ebSMasami Hiramatsu 		printf("Error: No initrd is specified.\n");
342*950313ebSMasami Hiramatsu 		return usage();
343*950313ebSMasami Hiramatsu 	}
344*950313ebSMasami Hiramatsu 
345*950313ebSMasami Hiramatsu 	path = argv[optind];
346*950313ebSMasami Hiramatsu 
347*950313ebSMasami Hiramatsu 	if (apply)
348*950313ebSMasami Hiramatsu 		return apply_xbc(path, apply);
349*950313ebSMasami Hiramatsu 	else if (delete)
350*950313ebSMasami Hiramatsu 		return delete_xbc(path);
351*950313ebSMasami Hiramatsu 
352*950313ebSMasami Hiramatsu 	return show_xbc(path);
353*950313ebSMasami Hiramatsu }
354