xref: /openbmc/linux/tools/bootconfig/main.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
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>
13e8684358SMasami Hiramatsu #include <endian.h>
14950313ebSMasami Hiramatsu 
15950313ebSMasami Hiramatsu #include <linux/bootconfig.h>
16950313ebSMasami Hiramatsu 
17160321b2SMasami Hiramatsu #define pr_err(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
18160321b2SMasami Hiramatsu 
xbc_show_value(struct xbc_node * node,bool semicolon)19e4f70b7bSMasami Hiramatsu static int xbc_show_value(struct xbc_node *node, bool semicolon)
20950313ebSMasami Hiramatsu {
21e4f70b7bSMasami Hiramatsu 	const char *val, *eol;
22272da327SMasami Hiramatsu 	char q;
23950313ebSMasami Hiramatsu 	int i = 0;
24950313ebSMasami Hiramatsu 
25e4f70b7bSMasami Hiramatsu 	eol = semicolon ? ";\n" : "\n";
26950313ebSMasami Hiramatsu 	xbc_array_for_each_value(node, val) {
27272da327SMasami Hiramatsu 		if (strchr(val, '"'))
28272da327SMasami Hiramatsu 			q = '\'';
29272da327SMasami Hiramatsu 		else
30272da327SMasami Hiramatsu 			q = '"';
31ca24306dSMasami Hiramatsu 		printf("%c%s%c%s", q, val, q, xbc_node_is_array(node) ? ", " : eol);
32950313ebSMasami Hiramatsu 		i++;
33950313ebSMasami Hiramatsu 	}
34950313ebSMasami Hiramatsu 	return i;
35950313ebSMasami Hiramatsu }
36950313ebSMasami Hiramatsu 
xbc_show_compact_tree(void)37950313ebSMasami Hiramatsu static void xbc_show_compact_tree(void)
38950313ebSMasami Hiramatsu {
39e5efaeb8SMasami Hiramatsu 	struct xbc_node *node, *cnode = NULL, *vnode;
40950313ebSMasami Hiramatsu 	int depth = 0, i;
41950313ebSMasami Hiramatsu 
42950313ebSMasami Hiramatsu 	node = xbc_root_node();
43950313ebSMasami Hiramatsu 	while (node && xbc_node_is_key(node)) {
44950313ebSMasami Hiramatsu 		for (i = 0; i < depth; i++)
45950313ebSMasami Hiramatsu 			printf("\t");
46e5efaeb8SMasami Hiramatsu 		if (!cnode)
47950313ebSMasami Hiramatsu 			cnode = xbc_node_get_child(node);
48950313ebSMasami Hiramatsu 		while (cnode && xbc_node_is_key(cnode) && !cnode->next) {
49e5efaeb8SMasami Hiramatsu 			vnode = xbc_node_get_child(cnode);
50e5efaeb8SMasami Hiramatsu 			/*
51e5efaeb8SMasami Hiramatsu 			 * If @cnode has value and subkeys, this
52e5efaeb8SMasami Hiramatsu 			 * should show it as below.
53e5efaeb8SMasami Hiramatsu 			 *
54e5efaeb8SMasami Hiramatsu 			 * key(@node) {
55e5efaeb8SMasami Hiramatsu 			 *      key(@cnode) = value;
56e5efaeb8SMasami Hiramatsu 			 *      key(@cnode) {
57e5efaeb8SMasami Hiramatsu 			 *          subkeys;
58e5efaeb8SMasami Hiramatsu 			 *      }
59e5efaeb8SMasami Hiramatsu 			 * }
60e5efaeb8SMasami Hiramatsu 			 */
61e5efaeb8SMasami Hiramatsu 			if (vnode && xbc_node_is_value(vnode) && vnode->next)
62e5efaeb8SMasami Hiramatsu 				break;
63950313ebSMasami Hiramatsu 			printf("%s.", xbc_node_get_data(node));
64950313ebSMasami Hiramatsu 			node = cnode;
65e5efaeb8SMasami Hiramatsu 			cnode = vnode;
66950313ebSMasami Hiramatsu 		}
67950313ebSMasami Hiramatsu 		if (cnode && xbc_node_is_key(cnode)) {
68950313ebSMasami Hiramatsu 			printf("%s {\n", xbc_node_get_data(node));
69950313ebSMasami Hiramatsu 			depth++;
70950313ebSMasami Hiramatsu 			node = cnode;
71e5efaeb8SMasami Hiramatsu 			cnode = NULL;
72950313ebSMasami Hiramatsu 			continue;
73950313ebSMasami Hiramatsu 		} else if (cnode && xbc_node_is_value(cnode)) {
74950313ebSMasami Hiramatsu 			printf("%s = ", xbc_node_get_data(node));
75e4f70b7bSMasami Hiramatsu 			xbc_show_value(cnode, true);
76e5efaeb8SMasami Hiramatsu 			/*
77e5efaeb8SMasami Hiramatsu 			 * If @node has value and subkeys, continue
78e5efaeb8SMasami Hiramatsu 			 * looping on subkeys with same node.
79e5efaeb8SMasami Hiramatsu 			 */
80e5efaeb8SMasami Hiramatsu 			if (cnode->next) {
81e5efaeb8SMasami Hiramatsu 				cnode = xbc_node_get_next(cnode);
82e5efaeb8SMasami Hiramatsu 				continue;
83e5efaeb8SMasami Hiramatsu 			}
84950313ebSMasami Hiramatsu 		} else {
85950313ebSMasami Hiramatsu 			printf("%s;\n", xbc_node_get_data(node));
86950313ebSMasami Hiramatsu 		}
87e5efaeb8SMasami Hiramatsu 		cnode = NULL;
88950313ebSMasami Hiramatsu 
89950313ebSMasami Hiramatsu 		if (node->next) {
90950313ebSMasami Hiramatsu 			node = xbc_node_get_next(node);
91950313ebSMasami Hiramatsu 			continue;
92950313ebSMasami Hiramatsu 		}
93950313ebSMasami Hiramatsu 		while (!node->next) {
94950313ebSMasami Hiramatsu 			node = xbc_node_get_parent(node);
95950313ebSMasami Hiramatsu 			if (!node)
96950313ebSMasami Hiramatsu 				return;
97950313ebSMasami Hiramatsu 			if (!xbc_node_get_child(node)->next)
98950313ebSMasami Hiramatsu 				continue;
99e5efaeb8SMasami Hiramatsu 			if (depth) {
100950313ebSMasami Hiramatsu 				depth--;
101950313ebSMasami Hiramatsu 				for (i = 0; i < depth; i++)
102950313ebSMasami Hiramatsu 					printf("\t");
103950313ebSMasami Hiramatsu 				printf("}\n");
104950313ebSMasami Hiramatsu 			}
105e5efaeb8SMasami Hiramatsu 		}
106950313ebSMasami Hiramatsu 		node = xbc_node_get_next(node);
107950313ebSMasami Hiramatsu 	}
108950313ebSMasami Hiramatsu }
109950313ebSMasami Hiramatsu 
xbc_show_list(void)110e4f70b7bSMasami Hiramatsu static void xbc_show_list(void)
111e4f70b7bSMasami Hiramatsu {
112e4f70b7bSMasami Hiramatsu 	char key[XBC_KEYLEN_MAX];
113e4f70b7bSMasami Hiramatsu 	struct xbc_node *leaf;
114e4f70b7bSMasami Hiramatsu 	const char *val;
115903bd067SJulio Faracco 	int ret;
116e4f70b7bSMasami Hiramatsu 
117e4f70b7bSMasami Hiramatsu 	xbc_for_each_key_value(leaf, val) {
118903bd067SJulio Faracco 		ret = xbc_node_compose_key(leaf, key, XBC_KEYLEN_MAX);
119903bd067SJulio Faracco 		if (ret < 0) {
120e5efaeb8SMasami Hiramatsu 			fprintf(stderr, "Failed to compose key %d\n", ret);
121e4f70b7bSMasami Hiramatsu 			break;
122e5efaeb8SMasami Hiramatsu 		}
123e4f70b7bSMasami Hiramatsu 		printf("%s = ", key);
124e4f70b7bSMasami Hiramatsu 		if (!val || val[0] == '\0') {
125e4f70b7bSMasami Hiramatsu 			printf("\"\"\n");
126e4f70b7bSMasami Hiramatsu 			continue;
127e4f70b7bSMasami Hiramatsu 		}
128e4f70b7bSMasami Hiramatsu 		xbc_show_value(xbc_node_get_child(leaf), false);
129e4f70b7bSMasami Hiramatsu 	}
130e4f70b7bSMasami Hiramatsu }
131e4f70b7bSMasami Hiramatsu 
132950313ebSMasami Hiramatsu #define PAGE_SIZE	4096
133950313ebSMasami Hiramatsu 
load_xbc_fd(int fd,char ** buf,int size)134483ce670SMasami Hiramatsu static int load_xbc_fd(int fd, char **buf, int size)
135950313ebSMasami Hiramatsu {
136950313ebSMasami Hiramatsu 	int ret;
137950313ebSMasami Hiramatsu 
138950313ebSMasami Hiramatsu 	*buf = malloc(size + 1);
139950313ebSMasami Hiramatsu 	if (!*buf)
140950313ebSMasami Hiramatsu 		return -ENOMEM;
141950313ebSMasami Hiramatsu 
142950313ebSMasami Hiramatsu 	ret = read(fd, *buf, size);
143950313ebSMasami Hiramatsu 	if (ret < 0)
144950313ebSMasami Hiramatsu 		return -errno;
145950313ebSMasami Hiramatsu 	(*buf)[size] = '\0';
146950313ebSMasami Hiramatsu 
147950313ebSMasami Hiramatsu 	return ret;
148950313ebSMasami Hiramatsu }
149950313ebSMasami Hiramatsu 
150950313ebSMasami Hiramatsu /* Return the read size or -errno */
load_xbc_file(const char * path,char ** buf)151483ce670SMasami Hiramatsu static int load_xbc_file(const char *path, char **buf)
152950313ebSMasami Hiramatsu {
153950313ebSMasami Hiramatsu 	struct stat stat;
154950313ebSMasami Hiramatsu 	int fd, ret;
155950313ebSMasami Hiramatsu 
156950313ebSMasami Hiramatsu 	fd = open(path, O_RDONLY);
157950313ebSMasami Hiramatsu 	if (fd < 0)
158950313ebSMasami Hiramatsu 		return -errno;
159950313ebSMasami Hiramatsu 	ret = fstat(fd, &stat);
160950313ebSMasami Hiramatsu 	if (ret < 0)
161950313ebSMasami Hiramatsu 		return -errno;
162950313ebSMasami Hiramatsu 
163950313ebSMasami Hiramatsu 	ret = load_xbc_fd(fd, buf, stat.st_size);
164950313ebSMasami Hiramatsu 
165950313ebSMasami Hiramatsu 	close(fd);
166950313ebSMasami Hiramatsu 
167950313ebSMasami Hiramatsu 	return ret;
168950313ebSMasami Hiramatsu }
169950313ebSMasami Hiramatsu 
pr_errno(const char * msg,int err)170a61ea637SMasami Hiramatsu static int pr_errno(const char *msg, int err)
171a61ea637SMasami Hiramatsu {
172a61ea637SMasami Hiramatsu 	pr_err("%s: %d\n", msg, err);
173a61ea637SMasami Hiramatsu 	return err;
174a61ea637SMasami Hiramatsu }
175a61ea637SMasami Hiramatsu 
load_xbc_from_initrd(int fd,char ** buf)176483ce670SMasami Hiramatsu static int load_xbc_from_initrd(int fd, char **buf)
177950313ebSMasami Hiramatsu {
178950313ebSMasami Hiramatsu 	struct stat stat;
179950313ebSMasami Hiramatsu 	int ret;
180*4f292c48SMasami Hiramatsu 	uint32_t size = 0, csum = 0, rcsum;
18185c46b78SMasami Hiramatsu 	char magic[BOOTCONFIG_MAGIC_LEN];
18289b74cacSMasami Hiramatsu 	const char *msg;
183950313ebSMasami Hiramatsu 
184950313ebSMasami Hiramatsu 	ret = fstat(fd, &stat);
185950313ebSMasami Hiramatsu 	if (ret < 0)
186950313ebSMasami Hiramatsu 		return -errno;
187950313ebSMasami Hiramatsu 
18885c46b78SMasami Hiramatsu 	if (stat.st_size < 8 + BOOTCONFIG_MAGIC_LEN)
189950313ebSMasami Hiramatsu 		return 0;
190950313ebSMasami Hiramatsu 
191a61ea637SMasami Hiramatsu 	if (lseek(fd, -BOOTCONFIG_MAGIC_LEN, SEEK_END) < 0)
192a61ea637SMasami Hiramatsu 		return pr_errno("Failed to lseek for magic", -errno);
193a61ea637SMasami Hiramatsu 
19485c46b78SMasami Hiramatsu 	if (read(fd, magic, BOOTCONFIG_MAGIC_LEN) < 0)
195a61ea637SMasami Hiramatsu 		return pr_errno("Failed to read", -errno);
196a61ea637SMasami Hiramatsu 
19785c46b78SMasami Hiramatsu 	/* Check the bootconfig magic bytes */
19885c46b78SMasami Hiramatsu 	if (memcmp(magic, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN) != 0)
19985c46b78SMasami Hiramatsu 		return 0;
20085c46b78SMasami Hiramatsu 
201a61ea637SMasami Hiramatsu 	if (lseek(fd, -(8 + BOOTCONFIG_MAGIC_LEN), SEEK_END) < 0)
202a61ea637SMasami Hiramatsu 		return pr_errno("Failed to lseek for size", -errno);
203950313ebSMasami Hiramatsu 
204*4f292c48SMasami Hiramatsu 	if (read(fd, &size, sizeof(uint32_t)) < 0)
205a61ea637SMasami Hiramatsu 		return pr_errno("Failed to read size", -errno);
206e8684358SMasami Hiramatsu 	size = le32toh(size);
207950313ebSMasami Hiramatsu 
208*4f292c48SMasami Hiramatsu 	if (read(fd, &csum, sizeof(uint32_t)) < 0)
209a61ea637SMasami Hiramatsu 		return pr_errno("Failed to read checksum", -errno);
210e8684358SMasami Hiramatsu 	csum = le32toh(csum);
211950313ebSMasami Hiramatsu 
21285c46b78SMasami Hiramatsu 	/* Wrong size error  */
21385c46b78SMasami Hiramatsu 	if (stat.st_size < size + 8 + BOOTCONFIG_MAGIC_LEN) {
21485c46b78SMasami Hiramatsu 		pr_err("bootconfig size is too big\n");
21585c46b78SMasami Hiramatsu 		return -E2BIG;
21685c46b78SMasami Hiramatsu 	}
217950313ebSMasami Hiramatsu 
21885c46b78SMasami Hiramatsu 	if (lseek(fd, stat.st_size - (size + 8 + BOOTCONFIG_MAGIC_LEN),
219a61ea637SMasami Hiramatsu 		  SEEK_SET) < 0)
220a61ea637SMasami Hiramatsu 		return pr_errno("Failed to lseek", -errno);
221950313ebSMasami Hiramatsu 
222950313ebSMasami Hiramatsu 	ret = load_xbc_fd(fd, buf, size);
223950313ebSMasami Hiramatsu 	if (ret < 0)
224950313ebSMasami Hiramatsu 		return ret;
225950313ebSMasami Hiramatsu 
22685c46b78SMasami Hiramatsu 	/* Wrong Checksum */
22799f4f5d6SMasami Hiramatsu 	rcsum = xbc_calc_checksum(*buf, size);
228950313ebSMasami Hiramatsu 	if (csum != rcsum) {
22997378001SMasami Hiramatsu 		pr_err("checksum error: %d != %d\n", csum, rcsum);
23085c46b78SMasami Hiramatsu 		return -EINVAL;
231950313ebSMasami Hiramatsu 	}
232950313ebSMasami Hiramatsu 
233bdac5c2bSMasami Hiramatsu 	ret = xbc_init(*buf, size, &msg, NULL);
23485c46b78SMasami Hiramatsu 	/* Wrong data */
23589b74cacSMasami Hiramatsu 	if (ret < 0) {
23689b74cacSMasami Hiramatsu 		pr_err("parse error: %s.\n", msg);
23785c46b78SMasami Hiramatsu 		return ret;
23889b74cacSMasami Hiramatsu 	}
239950313ebSMasami Hiramatsu 
240950313ebSMasami Hiramatsu 	return size;
241950313ebSMasami Hiramatsu }
242950313ebSMasami Hiramatsu 
show_xbc_error(const char * data,const char * msg,int pos)243d052e1c6SMasami Hiramatsu static void show_xbc_error(const char *data, const char *msg, int pos)
244d052e1c6SMasami Hiramatsu {
245d052e1c6SMasami Hiramatsu 	int lin = 1, col, i;
246d052e1c6SMasami Hiramatsu 
247d052e1c6SMasami Hiramatsu 	if (pos < 0) {
248d052e1c6SMasami Hiramatsu 		pr_err("Error: %s.\n", msg);
249d052e1c6SMasami Hiramatsu 		return;
250d052e1c6SMasami Hiramatsu 	}
251d052e1c6SMasami Hiramatsu 
252d052e1c6SMasami Hiramatsu 	/* Note that pos starts from 0 but lin and col should start from 1. */
253d052e1c6SMasami Hiramatsu 	col = pos + 1;
254d052e1c6SMasami Hiramatsu 	for (i = 0; i < pos; i++) {
255d052e1c6SMasami Hiramatsu 		if (data[i] == '\n') {
256d052e1c6SMasami Hiramatsu 			lin++;
257d052e1c6SMasami Hiramatsu 			col = pos - i;
258d052e1c6SMasami Hiramatsu 		}
259d052e1c6SMasami Hiramatsu 	}
260d052e1c6SMasami Hiramatsu 	pr_err("Parse Error: %s at %d:%d\n", msg, lin, col);
261d052e1c6SMasami Hiramatsu 
262d052e1c6SMasami Hiramatsu }
263d052e1c6SMasami Hiramatsu 
init_xbc_with_error(char * buf,int len)264d052e1c6SMasami Hiramatsu static int init_xbc_with_error(char *buf, int len)
265d052e1c6SMasami Hiramatsu {
266d052e1c6SMasami Hiramatsu 	char *copy = strdup(buf);
267d052e1c6SMasami Hiramatsu 	const char *msg;
268d052e1c6SMasami Hiramatsu 	int ret, pos;
269d052e1c6SMasami Hiramatsu 
270d052e1c6SMasami Hiramatsu 	if (!copy)
271d052e1c6SMasami Hiramatsu 		return -ENOMEM;
272d052e1c6SMasami Hiramatsu 
273bdac5c2bSMasami Hiramatsu 	ret = xbc_init(buf, len, &msg, &pos);
274d052e1c6SMasami Hiramatsu 	if (ret < 0)
275d052e1c6SMasami Hiramatsu 		show_xbc_error(copy, msg, pos);
276d052e1c6SMasami Hiramatsu 	free(copy);
277d052e1c6SMasami Hiramatsu 
278d052e1c6SMasami Hiramatsu 	return ret;
279d052e1c6SMasami Hiramatsu }
280d052e1c6SMasami Hiramatsu 
show_xbc(const char * path,bool list)281483ce670SMasami Hiramatsu static int show_xbc(const char *path, bool list)
282950313ebSMasami Hiramatsu {
283950313ebSMasami Hiramatsu 	int ret, fd;
284950313ebSMasami Hiramatsu 	char *buf = NULL;
285d052e1c6SMasami Hiramatsu 	struct stat st;
286d052e1c6SMasami Hiramatsu 
287d052e1c6SMasami Hiramatsu 	ret = stat(path, &st);
288d052e1c6SMasami Hiramatsu 	if (ret < 0) {
289a61ea637SMasami Hiramatsu 		ret = -errno;
290a61ea637SMasami Hiramatsu 		pr_err("Failed to stat %s: %d\n", path, ret);
291a61ea637SMasami Hiramatsu 		return ret;
292d052e1c6SMasami Hiramatsu 	}
293950313ebSMasami Hiramatsu 
294950313ebSMasami Hiramatsu 	fd = open(path, O_RDONLY);
295950313ebSMasami Hiramatsu 	if (fd < 0) {
296a61ea637SMasami Hiramatsu 		ret = -errno;
297a61ea637SMasami Hiramatsu 		pr_err("Failed to open initrd %s: %d\n", path, ret);
298a61ea637SMasami Hiramatsu 		return ret;
299950313ebSMasami Hiramatsu 	}
300950313ebSMasami Hiramatsu 
301950313ebSMasami Hiramatsu 	ret = load_xbc_from_initrd(fd, &buf);
302d052e1c6SMasami Hiramatsu 	close(fd);
303f91cb5b7SMasami Hiramatsu 	if (ret < 0) {
30497378001SMasami Hiramatsu 		pr_err("Failed to load a boot config from initrd: %d\n", ret);
305f91cb5b7SMasami Hiramatsu 		goto out;
306f91cb5b7SMasami Hiramatsu 	}
307d052e1c6SMasami Hiramatsu 	/* Assume a bootconfig file if it is enough small */
308d052e1c6SMasami Hiramatsu 	if (ret == 0 && st.st_size <= XBC_DATA_MAX) {
309d052e1c6SMasami Hiramatsu 		ret = load_xbc_file(path, &buf);
310d052e1c6SMasami Hiramatsu 		if (ret < 0) {
311d052e1c6SMasami Hiramatsu 			pr_err("Failed to load a boot config: %d\n", ret);
312d052e1c6SMasami Hiramatsu 			goto out;
313d052e1c6SMasami Hiramatsu 		}
314d052e1c6SMasami Hiramatsu 		if (init_xbc_with_error(buf, ret) < 0)
315d052e1c6SMasami Hiramatsu 			goto out;
316d052e1c6SMasami Hiramatsu 	}
317e4f70b7bSMasami Hiramatsu 	if (list)
318e4f70b7bSMasami Hiramatsu 		xbc_show_list();
319e4f70b7bSMasami Hiramatsu 	else
320950313ebSMasami Hiramatsu 		xbc_show_compact_tree();
321f91cb5b7SMasami Hiramatsu 	ret = 0;
322f91cb5b7SMasami Hiramatsu out:
323950313ebSMasami Hiramatsu 	free(buf);
324950313ebSMasami Hiramatsu 
325950313ebSMasami Hiramatsu 	return ret;
326950313ebSMasami Hiramatsu }
327950313ebSMasami Hiramatsu 
delete_xbc(const char * path)328483ce670SMasami Hiramatsu static int delete_xbc(const char *path)
329950313ebSMasami Hiramatsu {
330950313ebSMasami Hiramatsu 	struct stat stat;
331950313ebSMasami Hiramatsu 	int ret = 0, fd, size;
332950313ebSMasami Hiramatsu 	char *buf = NULL;
333950313ebSMasami Hiramatsu 
334950313ebSMasami Hiramatsu 	fd = open(path, O_RDWR);
335950313ebSMasami Hiramatsu 	if (fd < 0) {
336a61ea637SMasami Hiramatsu 		ret = -errno;
337a61ea637SMasami Hiramatsu 		pr_err("Failed to open initrd %s: %d\n", path, ret);
338a61ea637SMasami Hiramatsu 		return ret;
339950313ebSMasami Hiramatsu 	}
340950313ebSMasami Hiramatsu 
341950313ebSMasami Hiramatsu 	size = load_xbc_from_initrd(fd, &buf);
342950313ebSMasami Hiramatsu 	if (size < 0) {
343950313ebSMasami Hiramatsu 		ret = size;
34497378001SMasami Hiramatsu 		pr_err("Failed to load a boot config from initrd: %d\n", ret);
345950313ebSMasami Hiramatsu 	} else if (size > 0) {
346950313ebSMasami Hiramatsu 		ret = fstat(fd, &stat);
347950313ebSMasami Hiramatsu 		if (!ret)
34885c46b78SMasami Hiramatsu 			ret = ftruncate(fd, stat.st_size
34985c46b78SMasami Hiramatsu 					- size - 8 - BOOTCONFIG_MAGIC_LEN);
350950313ebSMasami Hiramatsu 		if (ret)
351950313ebSMasami Hiramatsu 			ret = -errno;
352950313ebSMasami Hiramatsu 	} /* Ignore if there is no boot config in initrd */
353950313ebSMasami Hiramatsu 
354950313ebSMasami Hiramatsu 	close(fd);
355950313ebSMasami Hiramatsu 	free(buf);
356950313ebSMasami Hiramatsu 
357950313ebSMasami Hiramatsu 	return ret;
358950313ebSMasami Hiramatsu }
359950313ebSMasami Hiramatsu 
apply_xbc(const char * path,const char * xbc_path)360483ce670SMasami Hiramatsu static int apply_xbc(const char *path, const char *xbc_path)
361950313ebSMasami Hiramatsu {
362e1cef2d4SMasami Hiramatsu 	char *buf, *data, *p;
363e1cef2d4SMasami Hiramatsu 	size_t total_size;
364a995e6bcSMasami Hiramatsu 	struct stat stat;
36589b74cacSMasami Hiramatsu 	const char *msg;
366*4f292c48SMasami Hiramatsu 	uint32_t size, csum;
367e1cef2d4SMasami Hiramatsu 	int pos, pad;
368e1cef2d4SMasami Hiramatsu 	int ret, fd;
369950313ebSMasami Hiramatsu 
370950313ebSMasami Hiramatsu 	ret = load_xbc_file(xbc_path, &buf);
371950313ebSMasami Hiramatsu 	if (ret < 0) {
37297378001SMasami Hiramatsu 		pr_err("Failed to load %s : %d\n", xbc_path, ret);
373950313ebSMasami Hiramatsu 		return ret;
374950313ebSMasami Hiramatsu 	}
375950313ebSMasami Hiramatsu 	size = strlen(buf) + 1;
37699f4f5d6SMasami Hiramatsu 	csum = xbc_calc_checksum(buf, size);
377950313ebSMasami Hiramatsu 
378e1cef2d4SMasami Hiramatsu 	/* Backup the bootconfig data */
379e1cef2d4SMasami Hiramatsu 	data = calloc(size + BOOTCONFIG_ALIGN +
380*4f292c48SMasami Hiramatsu 		      sizeof(uint32_t) + sizeof(uint32_t) + BOOTCONFIG_MAGIC_LEN, 1);
381950313ebSMasami Hiramatsu 	if (!data)
382950313ebSMasami Hiramatsu 		return -ENOMEM;
383e1cef2d4SMasami Hiramatsu 	memcpy(data, buf, size);
384950313ebSMasami Hiramatsu 
385950313ebSMasami Hiramatsu 	/* Check the data format */
386bdac5c2bSMasami Hiramatsu 	ret = xbc_init(buf, size, &msg, &pos);
387950313ebSMasami Hiramatsu 	if (ret < 0) {
38889b74cacSMasami Hiramatsu 		show_xbc_error(data, msg, pos);
389950313ebSMasami Hiramatsu 		free(data);
390950313ebSMasami Hiramatsu 		free(buf);
39189b74cacSMasami Hiramatsu 
392950313ebSMasami Hiramatsu 		return ret;
393950313ebSMasami Hiramatsu 	}
394950313ebSMasami Hiramatsu 	printf("Apply %s to %s\n", xbc_path, path);
395e306220cSMasami Hiramatsu 	xbc_get_info(&ret, NULL);
3960f0d0a77SMasami Hiramatsu 	printf("\tNumber of nodes: %d\n", ret);
397950313ebSMasami Hiramatsu 	printf("\tSize: %u bytes\n", (unsigned int)size);
398950313ebSMasami Hiramatsu 	printf("\tChecksum: %d\n", (unsigned int)csum);
399950313ebSMasami Hiramatsu 
400950313ebSMasami Hiramatsu 	/* TODO: Check the options by schema */
401115d4d08SMasami Hiramatsu 	xbc_exit();
402950313ebSMasami Hiramatsu 	free(buf);
403950313ebSMasami Hiramatsu 
404950313ebSMasami Hiramatsu 	/* Remove old boot config if exists */
405950313ebSMasami Hiramatsu 	ret = delete_xbc(path);
406950313ebSMasami Hiramatsu 	if (ret < 0) {
40797378001SMasami Hiramatsu 		pr_err("Failed to delete previous boot config: %d\n", ret);
40888426044SYunfeng Ye 		free(data);
409950313ebSMasami Hiramatsu 		return ret;
410950313ebSMasami Hiramatsu 	}
411950313ebSMasami Hiramatsu 
412950313ebSMasami Hiramatsu 	/* Apply new one */
413950313ebSMasami Hiramatsu 	fd = open(path, O_RDWR | O_APPEND);
414950313ebSMasami Hiramatsu 	if (fd < 0) {
415a61ea637SMasami Hiramatsu 		ret = -errno;
416a61ea637SMasami Hiramatsu 		pr_err("Failed to open %s: %d\n", path, ret);
41788426044SYunfeng Ye 		free(data);
418a61ea637SMasami Hiramatsu 		return ret;
419950313ebSMasami Hiramatsu 	}
420950313ebSMasami Hiramatsu 	/* TODO: Ensure the @path is initramfs/initrd image */
421a995e6bcSMasami Hiramatsu 	if (fstat(fd, &stat) < 0) {
422e8ba0b2bSZhen Lei 		ret = -errno;
423a995e6bcSMasami Hiramatsu 		pr_err("Failed to get the size of %s\n", path);
42488426044SYunfeng Ye 		goto out;
425950313ebSMasami Hiramatsu 	}
426e1cef2d4SMasami Hiramatsu 
427e1cef2d4SMasami Hiramatsu 	/* To align up the total size to BOOTCONFIG_ALIGN, get padding size */
428*4f292c48SMasami Hiramatsu 	total_size = stat.st_size + size + sizeof(uint32_t) * 2 + BOOTCONFIG_MAGIC_LEN;
429e1cef2d4SMasami Hiramatsu 	pad = ((total_size + BOOTCONFIG_ALIGN - 1) & (~BOOTCONFIG_ALIGN_MASK)) - total_size;
430e1cef2d4SMasami Hiramatsu 	size += pad;
431e1cef2d4SMasami Hiramatsu 
432e1cef2d4SMasami Hiramatsu 	/* Add a footer */
433e1cef2d4SMasami Hiramatsu 	p = data + size;
434*4f292c48SMasami Hiramatsu 	*(uint32_t *)p = htole32(size);
435*4f292c48SMasami Hiramatsu 	p += sizeof(uint32_t);
436e1cef2d4SMasami Hiramatsu 
437*4f292c48SMasami Hiramatsu 	*(uint32_t *)p = htole32(csum);
438*4f292c48SMasami Hiramatsu 	p += sizeof(uint32_t);
439e1cef2d4SMasami Hiramatsu 
440e1cef2d4SMasami Hiramatsu 	memcpy(p, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN);
441e1cef2d4SMasami Hiramatsu 	p += BOOTCONFIG_MAGIC_LEN;
442e1cef2d4SMasami Hiramatsu 
443e1cef2d4SMasami Hiramatsu 	total_size = p - data;
444e1cef2d4SMasami Hiramatsu 
445e1cef2d4SMasami Hiramatsu 	ret = write(fd, data, total_size);
446e1cef2d4SMasami Hiramatsu 	if (ret < total_size) {
447a995e6bcSMasami Hiramatsu 		if (ret < 0)
448a995e6bcSMasami Hiramatsu 			ret = -errno;
449a995e6bcSMasami Hiramatsu 		pr_err("Failed to apply a boot config: %d\n", ret);
450e1cef2d4SMasami Hiramatsu 		if (ret >= 0)
451a995e6bcSMasami Hiramatsu 			goto out_rollback;
452e1cef2d4SMasami Hiramatsu 	} else
4539d82ccdaSSteven Rostedt (VMware) 		ret = 0;
454e1cef2d4SMasami Hiramatsu 
45588426044SYunfeng Ye out:
456950313ebSMasami Hiramatsu 	close(fd);
457950313ebSMasami Hiramatsu 	free(data);
458950313ebSMasami Hiramatsu 
45988426044SYunfeng Ye 	return ret;
460a995e6bcSMasami Hiramatsu 
461a995e6bcSMasami Hiramatsu out_rollback:
462a995e6bcSMasami Hiramatsu 	/* Map the partial write to -ENOSPC */
463a995e6bcSMasami Hiramatsu 	if (ret >= 0)
464a995e6bcSMasami Hiramatsu 		ret = -ENOSPC;
465a995e6bcSMasami Hiramatsu 	if (ftruncate(fd, stat.st_size) < 0) {
466a995e6bcSMasami Hiramatsu 		ret = -errno;
467a995e6bcSMasami Hiramatsu 		pr_err("Failed to rollback the write error: %d\n", ret);
468a995e6bcSMasami Hiramatsu 		pr_err("The initrd %s may be corrupted. Recommend to rebuild.\n", path);
469a995e6bcSMasami Hiramatsu 	}
470a995e6bcSMasami Hiramatsu 	goto out;
471950313ebSMasami Hiramatsu }
472950313ebSMasami Hiramatsu 
usage(void)473483ce670SMasami Hiramatsu static int usage(void)
474950313ebSMasami Hiramatsu {
475950313ebSMasami Hiramatsu 	printf("Usage: bootconfig [OPTIONS] <INITRD>\n"
476d052e1c6SMasami Hiramatsu 		"Or     bootconfig <CONFIG>\n"
477950313ebSMasami Hiramatsu 		" Apply, delete or show boot config to initrd.\n"
478950313ebSMasami Hiramatsu 		" Options:\n"
479950313ebSMasami Hiramatsu 		"		-a <config>: Apply boot config to initrd\n"
480e4f70b7bSMasami Hiramatsu 		"		-d : Delete boot config file from initrd\n"
481e4f70b7bSMasami Hiramatsu 		"		-l : list boot config in initrd or file\n\n"
482d052e1c6SMasami Hiramatsu 		" If no option is given, show the bootconfig in the given file.\n");
483950313ebSMasami Hiramatsu 	return -1;
484950313ebSMasami Hiramatsu }
485950313ebSMasami Hiramatsu 
main(int argc,char ** argv)486950313ebSMasami Hiramatsu int main(int argc, char **argv)
487950313ebSMasami Hiramatsu {
488950313ebSMasami Hiramatsu 	char *path = NULL;
489950313ebSMasami Hiramatsu 	char *apply = NULL;
490e4f70b7bSMasami Hiramatsu 	bool delete = false, list = false;
491950313ebSMasami Hiramatsu 	int opt;
492950313ebSMasami Hiramatsu 
493e4f70b7bSMasami Hiramatsu 	while ((opt = getopt(argc, argv, "hda:l")) != -1) {
494950313ebSMasami Hiramatsu 		switch (opt) {
495950313ebSMasami Hiramatsu 		case 'd':
496950313ebSMasami Hiramatsu 			delete = true;
497950313ebSMasami Hiramatsu 			break;
498950313ebSMasami Hiramatsu 		case 'a':
499950313ebSMasami Hiramatsu 			apply = optarg;
500950313ebSMasami Hiramatsu 			break;
501e4f70b7bSMasami Hiramatsu 		case 'l':
502e4f70b7bSMasami Hiramatsu 			list = true;
503e4f70b7bSMasami Hiramatsu 			break;
504950313ebSMasami Hiramatsu 		case 'h':
505950313ebSMasami Hiramatsu 		default:
506950313ebSMasami Hiramatsu 			return usage();
507950313ebSMasami Hiramatsu 		}
508950313ebSMasami Hiramatsu 	}
509950313ebSMasami Hiramatsu 
510e4f70b7bSMasami Hiramatsu 	if ((apply && delete) || (delete && list) || (apply && list)) {
511e4f70b7bSMasami Hiramatsu 		pr_err("Error: You can give one of -a, -d or -l at once.\n");
512950313ebSMasami Hiramatsu 		return usage();
513950313ebSMasami Hiramatsu 	}
514950313ebSMasami Hiramatsu 
515950313ebSMasami Hiramatsu 	if (optind >= argc) {
51697378001SMasami Hiramatsu 		pr_err("Error: No initrd is specified.\n");
517950313ebSMasami Hiramatsu 		return usage();
518950313ebSMasami Hiramatsu 	}
519950313ebSMasami Hiramatsu 
520950313ebSMasami Hiramatsu 	path = argv[optind];
521950313ebSMasami Hiramatsu 
522950313ebSMasami Hiramatsu 	if (apply)
523950313ebSMasami Hiramatsu 		return apply_xbc(path, apply);
524950313ebSMasami Hiramatsu 	else if (delete)
525950313ebSMasami Hiramatsu 		return delete_xbc(path);
526950313ebSMasami Hiramatsu 
527e4f70b7bSMasami Hiramatsu 	return show_xbc(path, list);
528950313ebSMasami Hiramatsu }
529