xref: /openbmc/linux/tools/bootconfig/main.c (revision 973780011106c534d69c4d25fe0749bd3a5f0b53)
1950313ebSMasami Hiramatsu // SPDX-License-Identifier: GPL-2.0
2950313ebSMasami Hiramatsu /*
3950313ebSMasami Hiramatsu  * Boot config tool for initrd image
4950313ebSMasami Hiramatsu  */
5950313ebSMasami Hiramatsu #include <stdio.h>
6950313ebSMasami Hiramatsu #include <stdlib.h>
7950313ebSMasami Hiramatsu #include <sys/types.h>
8950313ebSMasami Hiramatsu #include <sys/stat.h>
9950313ebSMasami Hiramatsu #include <fcntl.h>
10950313ebSMasami Hiramatsu #include <unistd.h>
11950313ebSMasami Hiramatsu #include <string.h>
12950313ebSMasami Hiramatsu #include <errno.h>
13950313ebSMasami Hiramatsu 
14950313ebSMasami Hiramatsu #include <linux/kernel.h>
15950313ebSMasami Hiramatsu #include <linux/bootconfig.h>
16950313ebSMasami Hiramatsu 
17950313ebSMasami Hiramatsu int pr_output = 1;
18950313ebSMasami Hiramatsu 
19950313ebSMasami Hiramatsu static int xbc_show_array(struct xbc_node *node)
20950313ebSMasami Hiramatsu {
21950313ebSMasami Hiramatsu 	const char *val;
22950313ebSMasami Hiramatsu 	int i = 0;
23950313ebSMasami Hiramatsu 
24950313ebSMasami Hiramatsu 	xbc_array_for_each_value(node, val) {
25950313ebSMasami Hiramatsu 		printf("\"%s\"%s", val, node->next ? ", " : ";\n");
26950313ebSMasami Hiramatsu 		i++;
27950313ebSMasami Hiramatsu 	}
28950313ebSMasami Hiramatsu 	return i;
29950313ebSMasami Hiramatsu }
30950313ebSMasami Hiramatsu 
31950313ebSMasami Hiramatsu static void xbc_show_compact_tree(void)
32950313ebSMasami Hiramatsu {
33950313ebSMasami Hiramatsu 	struct xbc_node *node, *cnode;
34950313ebSMasami Hiramatsu 	int depth = 0, i;
35950313ebSMasami Hiramatsu 
36950313ebSMasami Hiramatsu 	node = xbc_root_node();
37950313ebSMasami Hiramatsu 	while (node && xbc_node_is_key(node)) {
38950313ebSMasami Hiramatsu 		for (i = 0; i < depth; i++)
39950313ebSMasami Hiramatsu 			printf("\t");
40950313ebSMasami Hiramatsu 		cnode = xbc_node_get_child(node);
41950313ebSMasami Hiramatsu 		while (cnode && xbc_node_is_key(cnode) && !cnode->next) {
42950313ebSMasami Hiramatsu 			printf("%s.", xbc_node_get_data(node));
43950313ebSMasami Hiramatsu 			node = cnode;
44950313ebSMasami Hiramatsu 			cnode = xbc_node_get_child(node);
45950313ebSMasami Hiramatsu 		}
46950313ebSMasami Hiramatsu 		if (cnode && xbc_node_is_key(cnode)) {
47950313ebSMasami Hiramatsu 			printf("%s {\n", xbc_node_get_data(node));
48950313ebSMasami Hiramatsu 			depth++;
49950313ebSMasami Hiramatsu 			node = cnode;
50950313ebSMasami Hiramatsu 			continue;
51950313ebSMasami Hiramatsu 		} else if (cnode && xbc_node_is_value(cnode)) {
52950313ebSMasami Hiramatsu 			printf("%s = ", xbc_node_get_data(node));
53950313ebSMasami Hiramatsu 			if (cnode->next)
54950313ebSMasami Hiramatsu 				xbc_show_array(cnode);
55950313ebSMasami Hiramatsu 			else
56950313ebSMasami Hiramatsu 				printf("\"%s\";\n", xbc_node_get_data(cnode));
57950313ebSMasami Hiramatsu 		} else {
58950313ebSMasami Hiramatsu 			printf("%s;\n", xbc_node_get_data(node));
59950313ebSMasami Hiramatsu 		}
60950313ebSMasami Hiramatsu 
61950313ebSMasami Hiramatsu 		if (node->next) {
62950313ebSMasami Hiramatsu 			node = xbc_node_get_next(node);
63950313ebSMasami Hiramatsu 			continue;
64950313ebSMasami Hiramatsu 		}
65950313ebSMasami Hiramatsu 		while (!node->next) {
66950313ebSMasami Hiramatsu 			node = xbc_node_get_parent(node);
67950313ebSMasami Hiramatsu 			if (!node)
68950313ebSMasami Hiramatsu 				return;
69950313ebSMasami Hiramatsu 			if (!xbc_node_get_child(node)->next)
70950313ebSMasami Hiramatsu 				continue;
71950313ebSMasami Hiramatsu 			depth--;
72950313ebSMasami Hiramatsu 			for (i = 0; i < depth; i++)
73950313ebSMasami Hiramatsu 				printf("\t");
74950313ebSMasami Hiramatsu 			printf("}\n");
75950313ebSMasami Hiramatsu 		}
76950313ebSMasami Hiramatsu 		node = xbc_node_get_next(node);
77950313ebSMasami Hiramatsu 	}
78950313ebSMasami Hiramatsu }
79950313ebSMasami Hiramatsu 
80950313ebSMasami Hiramatsu /* Simple real checksum */
81950313ebSMasami Hiramatsu int checksum(unsigned char *buf, int len)
82950313ebSMasami Hiramatsu {
83950313ebSMasami Hiramatsu 	int i, sum = 0;
84950313ebSMasami Hiramatsu 
85950313ebSMasami Hiramatsu 	for (i = 0; i < len; i++)
86950313ebSMasami Hiramatsu 		sum += buf[i];
87950313ebSMasami Hiramatsu 
88950313ebSMasami Hiramatsu 	return sum;
89950313ebSMasami Hiramatsu }
90950313ebSMasami Hiramatsu 
91950313ebSMasami Hiramatsu #define PAGE_SIZE	4096
92950313ebSMasami Hiramatsu 
93950313ebSMasami Hiramatsu int load_xbc_fd(int fd, char **buf, int size)
94950313ebSMasami Hiramatsu {
95950313ebSMasami Hiramatsu 	int ret;
96950313ebSMasami Hiramatsu 
97950313ebSMasami Hiramatsu 	*buf = malloc(size + 1);
98950313ebSMasami Hiramatsu 	if (!*buf)
99950313ebSMasami Hiramatsu 		return -ENOMEM;
100950313ebSMasami Hiramatsu 
101950313ebSMasami Hiramatsu 	ret = read(fd, *buf, size);
102950313ebSMasami Hiramatsu 	if (ret < 0)
103950313ebSMasami Hiramatsu 		return -errno;
104950313ebSMasami Hiramatsu 	(*buf)[size] = '\0';
105950313ebSMasami Hiramatsu 
106950313ebSMasami Hiramatsu 	return ret;
107950313ebSMasami Hiramatsu }
108950313ebSMasami Hiramatsu 
109950313ebSMasami Hiramatsu /* Return the read size or -errno */
110950313ebSMasami Hiramatsu int load_xbc_file(const char *path, char **buf)
111950313ebSMasami Hiramatsu {
112950313ebSMasami Hiramatsu 	struct stat stat;
113950313ebSMasami Hiramatsu 	int fd, ret;
114950313ebSMasami Hiramatsu 
115950313ebSMasami Hiramatsu 	fd = open(path, O_RDONLY);
116950313ebSMasami Hiramatsu 	if (fd < 0)
117950313ebSMasami Hiramatsu 		return -errno;
118950313ebSMasami Hiramatsu 	ret = fstat(fd, &stat);
119950313ebSMasami Hiramatsu 	if (ret < 0)
120950313ebSMasami Hiramatsu 		return -errno;
121950313ebSMasami Hiramatsu 
122950313ebSMasami Hiramatsu 	ret = load_xbc_fd(fd, buf, stat.st_size);
123950313ebSMasami Hiramatsu 
124950313ebSMasami Hiramatsu 	close(fd);
125950313ebSMasami Hiramatsu 
126950313ebSMasami Hiramatsu 	return ret;
127950313ebSMasami Hiramatsu }
128950313ebSMasami Hiramatsu 
129950313ebSMasami Hiramatsu int load_xbc_from_initrd(int fd, char **buf)
130950313ebSMasami Hiramatsu {
131950313ebSMasami Hiramatsu 	struct stat stat;
132950313ebSMasami Hiramatsu 	int ret;
133950313ebSMasami Hiramatsu 	u32 size = 0, csum = 0, rcsum;
134950313ebSMasami Hiramatsu 
135950313ebSMasami Hiramatsu 	ret = fstat(fd, &stat);
136950313ebSMasami Hiramatsu 	if (ret < 0)
137950313ebSMasami Hiramatsu 		return -errno;
138950313ebSMasami Hiramatsu 
139950313ebSMasami Hiramatsu 	if (stat.st_size < 8)
140950313ebSMasami Hiramatsu 		return 0;
141950313ebSMasami Hiramatsu 
142950313ebSMasami Hiramatsu 	if (lseek(fd, -8, SEEK_END) < 0) {
143*97378001SMasami Hiramatsu 		pr_err("Failed to lseek: %d\n", -errno);
144950313ebSMasami Hiramatsu 		return -errno;
145950313ebSMasami Hiramatsu 	}
146950313ebSMasami Hiramatsu 
147950313ebSMasami Hiramatsu 	if (read(fd, &size, sizeof(u32)) < 0)
148950313ebSMasami Hiramatsu 		return -errno;
149950313ebSMasami Hiramatsu 
150950313ebSMasami Hiramatsu 	if (read(fd, &csum, sizeof(u32)) < 0)
151950313ebSMasami Hiramatsu 		return -errno;
152950313ebSMasami Hiramatsu 
153950313ebSMasami Hiramatsu 	/* Wrong size, maybe no boot config here */
154950313ebSMasami Hiramatsu 	if (stat.st_size < size + 8)
155950313ebSMasami Hiramatsu 		return 0;
156950313ebSMasami Hiramatsu 
157950313ebSMasami Hiramatsu 	if (lseek(fd, stat.st_size - 8 - size, SEEK_SET) < 0) {
158*97378001SMasami Hiramatsu 		pr_err("Failed to lseek: %d\n", -errno);
159950313ebSMasami Hiramatsu 		return -errno;
160950313ebSMasami Hiramatsu 	}
161950313ebSMasami Hiramatsu 
162950313ebSMasami Hiramatsu 	ret = load_xbc_fd(fd, buf, size);
163950313ebSMasami Hiramatsu 	if (ret < 0)
164950313ebSMasami Hiramatsu 		return ret;
165950313ebSMasami Hiramatsu 
166950313ebSMasami Hiramatsu 	/* Wrong Checksum, maybe no boot config here */
167950313ebSMasami Hiramatsu 	rcsum = checksum((unsigned char *)*buf, size);
168950313ebSMasami Hiramatsu 	if (csum != rcsum) {
169*97378001SMasami Hiramatsu 		pr_err("checksum error: %d != %d\n", csum, rcsum);
170950313ebSMasami Hiramatsu 		return 0;
171950313ebSMasami Hiramatsu 	}
172950313ebSMasami Hiramatsu 
173950313ebSMasami Hiramatsu 	ret = xbc_init(*buf);
174950313ebSMasami Hiramatsu 	/* Wrong data, maybe no boot config here */
175950313ebSMasami Hiramatsu 	if (ret < 0)
176950313ebSMasami Hiramatsu 		return 0;
177950313ebSMasami Hiramatsu 
178950313ebSMasami Hiramatsu 	return size;
179950313ebSMasami Hiramatsu }
180950313ebSMasami Hiramatsu 
181950313ebSMasami Hiramatsu int show_xbc(const char *path)
182950313ebSMasami Hiramatsu {
183950313ebSMasami Hiramatsu 	int ret, fd;
184950313ebSMasami Hiramatsu 	char *buf = NULL;
185950313ebSMasami Hiramatsu 
186950313ebSMasami Hiramatsu 	fd = open(path, O_RDONLY);
187950313ebSMasami Hiramatsu 	if (fd < 0) {
188*97378001SMasami Hiramatsu 		pr_err("Failed to open initrd %s: %d\n", path, fd);
189950313ebSMasami Hiramatsu 		return -errno;
190950313ebSMasami Hiramatsu 	}
191950313ebSMasami Hiramatsu 
192950313ebSMasami Hiramatsu 	ret = load_xbc_from_initrd(fd, &buf);
193950313ebSMasami Hiramatsu 	if (ret < 0)
194*97378001SMasami Hiramatsu 		pr_err("Failed to load a boot config from initrd: %d\n", ret);
195950313ebSMasami Hiramatsu 	else
196950313ebSMasami Hiramatsu 		xbc_show_compact_tree();
197950313ebSMasami Hiramatsu 
198950313ebSMasami Hiramatsu 	close(fd);
199950313ebSMasami Hiramatsu 	free(buf);
200950313ebSMasami Hiramatsu 
201950313ebSMasami Hiramatsu 	return ret;
202950313ebSMasami Hiramatsu }
203950313ebSMasami Hiramatsu 
204950313ebSMasami Hiramatsu int delete_xbc(const char *path)
205950313ebSMasami Hiramatsu {
206950313ebSMasami Hiramatsu 	struct stat stat;
207950313ebSMasami Hiramatsu 	int ret = 0, fd, size;
208950313ebSMasami Hiramatsu 	char *buf = NULL;
209950313ebSMasami Hiramatsu 
210950313ebSMasami Hiramatsu 	fd = open(path, O_RDWR);
211950313ebSMasami Hiramatsu 	if (fd < 0) {
212*97378001SMasami Hiramatsu 		pr_err("Failed to open initrd %s: %d\n", path, fd);
213950313ebSMasami Hiramatsu 		return -errno;
214950313ebSMasami Hiramatsu 	}
215950313ebSMasami Hiramatsu 
216950313ebSMasami Hiramatsu 	/*
217950313ebSMasami Hiramatsu 	 * Suppress error messages in xbc_init() because it can be just a
218950313ebSMasami Hiramatsu 	 * data which concidentally matches the size and checksum footer.
219950313ebSMasami Hiramatsu 	 */
220950313ebSMasami Hiramatsu 	pr_output = 0;
221950313ebSMasami Hiramatsu 	size = load_xbc_from_initrd(fd, &buf);
222950313ebSMasami Hiramatsu 	pr_output = 1;
223950313ebSMasami Hiramatsu 	if (size < 0) {
224950313ebSMasami Hiramatsu 		ret = size;
225*97378001SMasami Hiramatsu 		pr_err("Failed to load a boot config from initrd: %d\n", ret);
226950313ebSMasami Hiramatsu 	} else if (size > 0) {
227950313ebSMasami Hiramatsu 		ret = fstat(fd, &stat);
228950313ebSMasami Hiramatsu 		if (!ret)
229950313ebSMasami Hiramatsu 			ret = ftruncate(fd, stat.st_size - size - 8);
230950313ebSMasami Hiramatsu 		if (ret)
231950313ebSMasami Hiramatsu 			ret = -errno;
232950313ebSMasami Hiramatsu 	} /* Ignore if there is no boot config in initrd */
233950313ebSMasami Hiramatsu 
234950313ebSMasami Hiramatsu 	close(fd);
235950313ebSMasami Hiramatsu 	free(buf);
236950313ebSMasami Hiramatsu 
237950313ebSMasami Hiramatsu 	return ret;
238950313ebSMasami Hiramatsu }
239950313ebSMasami Hiramatsu 
240950313ebSMasami Hiramatsu int apply_xbc(const char *path, const char *xbc_path)
241950313ebSMasami Hiramatsu {
242950313ebSMasami Hiramatsu 	u32 size, csum;
243950313ebSMasami Hiramatsu 	char *buf, *data;
244950313ebSMasami Hiramatsu 	int ret, fd;
245950313ebSMasami Hiramatsu 
246950313ebSMasami Hiramatsu 	ret = load_xbc_file(xbc_path, &buf);
247950313ebSMasami Hiramatsu 	if (ret < 0) {
248*97378001SMasami Hiramatsu 		pr_err("Failed to load %s : %d\n", xbc_path, ret);
249950313ebSMasami Hiramatsu 		return ret;
250950313ebSMasami Hiramatsu 	}
251950313ebSMasami Hiramatsu 	size = strlen(buf) + 1;
252950313ebSMasami Hiramatsu 	csum = checksum((unsigned char *)buf, size);
253950313ebSMasami Hiramatsu 
254950313ebSMasami Hiramatsu 	/* Prepare xbc_path data */
255950313ebSMasami Hiramatsu 	data = malloc(size + 8);
256950313ebSMasami Hiramatsu 	if (!data)
257950313ebSMasami Hiramatsu 		return -ENOMEM;
258950313ebSMasami Hiramatsu 	strcpy(data, buf);
259950313ebSMasami Hiramatsu 	*(u32 *)(data + size) = size;
260950313ebSMasami Hiramatsu 	*(u32 *)(data + size + 4) = csum;
261950313ebSMasami Hiramatsu 
262950313ebSMasami Hiramatsu 	/* Check the data format */
263950313ebSMasami Hiramatsu 	ret = xbc_init(buf);
264950313ebSMasami Hiramatsu 	if (ret < 0) {
265*97378001SMasami Hiramatsu 		pr_err("Failed to parse %s: %d\n", xbc_path, ret);
266950313ebSMasami Hiramatsu 		free(data);
267950313ebSMasami Hiramatsu 		free(buf);
268950313ebSMasami Hiramatsu 		return ret;
269950313ebSMasami Hiramatsu 	}
270950313ebSMasami Hiramatsu 	printf("Apply %s to %s\n", xbc_path, path);
2710f0d0a77SMasami Hiramatsu 	printf("\tNumber of nodes: %d\n", ret);
272950313ebSMasami Hiramatsu 	printf("\tSize: %u bytes\n", (unsigned int)size);
273950313ebSMasami Hiramatsu 	printf("\tChecksum: %d\n", (unsigned int)csum);
274950313ebSMasami Hiramatsu 
275950313ebSMasami Hiramatsu 	/* TODO: Check the options by schema */
276950313ebSMasami Hiramatsu 	xbc_destroy_all();
277950313ebSMasami Hiramatsu 	free(buf);
278950313ebSMasami Hiramatsu 
279950313ebSMasami Hiramatsu 	/* Remove old boot config if exists */
280950313ebSMasami Hiramatsu 	ret = delete_xbc(path);
281950313ebSMasami Hiramatsu 	if (ret < 0) {
282*97378001SMasami Hiramatsu 		pr_err("Failed to delete previous boot config: %d\n", ret);
283950313ebSMasami Hiramatsu 		return ret;
284950313ebSMasami Hiramatsu 	}
285950313ebSMasami Hiramatsu 
286950313ebSMasami Hiramatsu 	/* Apply new one */
287950313ebSMasami Hiramatsu 	fd = open(path, O_RDWR | O_APPEND);
288950313ebSMasami Hiramatsu 	if (fd < 0) {
289*97378001SMasami Hiramatsu 		pr_err("Failed to open %s: %d\n", path, fd);
290950313ebSMasami Hiramatsu 		return fd;
291950313ebSMasami Hiramatsu 	}
292950313ebSMasami Hiramatsu 	/* TODO: Ensure the @path is initramfs/initrd image */
293950313ebSMasami Hiramatsu 	ret = write(fd, data, size + 8);
294950313ebSMasami Hiramatsu 	if (ret < 0) {
295*97378001SMasami Hiramatsu 		pr_err("Failed to apply a boot config: %d\n", ret);
296950313ebSMasami Hiramatsu 		return ret;
297950313ebSMasami Hiramatsu 	}
298950313ebSMasami Hiramatsu 	close(fd);
299950313ebSMasami Hiramatsu 	free(data);
300950313ebSMasami Hiramatsu 
301950313ebSMasami Hiramatsu 	return 0;
302950313ebSMasami Hiramatsu }
303950313ebSMasami Hiramatsu 
304950313ebSMasami Hiramatsu int usage(void)
305950313ebSMasami Hiramatsu {
306950313ebSMasami Hiramatsu 	printf("Usage: bootconfig [OPTIONS] <INITRD>\n"
307950313ebSMasami Hiramatsu 		" Apply, delete or show boot config to initrd.\n"
308950313ebSMasami Hiramatsu 		" Options:\n"
309950313ebSMasami Hiramatsu 		"		-a <config>: Apply boot config to initrd\n"
310950313ebSMasami Hiramatsu 		"		-d : Delete boot config file from initrd\n\n"
311950313ebSMasami Hiramatsu 		" If no option is given, show current applied boot config.\n");
312950313ebSMasami Hiramatsu 	return -1;
313950313ebSMasami Hiramatsu }
314950313ebSMasami Hiramatsu 
315950313ebSMasami Hiramatsu int main(int argc, char **argv)
316950313ebSMasami Hiramatsu {
317950313ebSMasami Hiramatsu 	char *path = NULL;
318950313ebSMasami Hiramatsu 	char *apply = NULL;
319950313ebSMasami Hiramatsu 	bool delete = false;
320950313ebSMasami Hiramatsu 	int opt;
321950313ebSMasami Hiramatsu 
322950313ebSMasami Hiramatsu 	while ((opt = getopt(argc, argv, "hda:")) != -1) {
323950313ebSMasami Hiramatsu 		switch (opt) {
324950313ebSMasami Hiramatsu 		case 'd':
325950313ebSMasami Hiramatsu 			delete = true;
326950313ebSMasami Hiramatsu 			break;
327950313ebSMasami Hiramatsu 		case 'a':
328950313ebSMasami Hiramatsu 			apply = optarg;
329950313ebSMasami Hiramatsu 			break;
330950313ebSMasami Hiramatsu 		case 'h':
331950313ebSMasami Hiramatsu 		default:
332950313ebSMasami Hiramatsu 			return usage();
333950313ebSMasami Hiramatsu 		}
334950313ebSMasami Hiramatsu 	}
335950313ebSMasami Hiramatsu 
336950313ebSMasami Hiramatsu 	if (apply && delete) {
337*97378001SMasami Hiramatsu 		pr_err("Error: You can not specify both -a and -d at once.\n");
338950313ebSMasami Hiramatsu 		return usage();
339950313ebSMasami Hiramatsu 	}
340950313ebSMasami Hiramatsu 
341950313ebSMasami Hiramatsu 	if (optind >= argc) {
342*97378001SMasami Hiramatsu 		pr_err("Error: No initrd is specified.\n");
343950313ebSMasami Hiramatsu 		return usage();
344950313ebSMasami Hiramatsu 	}
345950313ebSMasami Hiramatsu 
346950313ebSMasami Hiramatsu 	path = argv[optind];
347950313ebSMasami Hiramatsu 
348950313ebSMasami Hiramatsu 	if (apply)
349950313ebSMasami Hiramatsu 		return apply_xbc(path, apply);
350950313ebSMasami Hiramatsu 	else if (delete)
351950313ebSMasami Hiramatsu 		return delete_xbc(path);
352950313ebSMasami Hiramatsu 
353950313ebSMasami Hiramatsu 	return show_xbc(path);
354950313ebSMasami Hiramatsu }
355