xref: /openbmc/linux/tools/bootconfig/main.c (revision abe9af53)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Boot config tool for initrd image
4  */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <string.h>
12 #include <errno.h>
13 
14 #include <linux/kernel.h>
15 #include <linux/bootconfig.h>
16 
17 static int xbc_show_value(struct xbc_node *node, bool semicolon)
18 {
19 	const char *val, *eol;
20 	char q;
21 	int i = 0;
22 
23 	eol = semicolon ? ";\n" : "\n";
24 	xbc_array_for_each_value(node, val) {
25 		if (strchr(val, '"'))
26 			q = '\'';
27 		else
28 			q = '"';
29 		printf("%c%s%c%s", q, val, q, node->next ? ", " : eol);
30 		i++;
31 	}
32 	return i;
33 }
34 
35 static void xbc_show_compact_tree(void)
36 {
37 	struct xbc_node *node, *cnode;
38 	int depth = 0, i;
39 
40 	node = xbc_root_node();
41 	while (node && xbc_node_is_key(node)) {
42 		for (i = 0; i < depth; i++)
43 			printf("\t");
44 		cnode = xbc_node_get_child(node);
45 		while (cnode && xbc_node_is_key(cnode) && !cnode->next) {
46 			printf("%s.", xbc_node_get_data(node));
47 			node = cnode;
48 			cnode = xbc_node_get_child(node);
49 		}
50 		if (cnode && xbc_node_is_key(cnode)) {
51 			printf("%s {\n", xbc_node_get_data(node));
52 			depth++;
53 			node = cnode;
54 			continue;
55 		} else if (cnode && xbc_node_is_value(cnode)) {
56 			printf("%s = ", xbc_node_get_data(node));
57 			xbc_show_value(cnode, true);
58 		} else {
59 			printf("%s;\n", xbc_node_get_data(node));
60 		}
61 
62 		if (node->next) {
63 			node = xbc_node_get_next(node);
64 			continue;
65 		}
66 		while (!node->next) {
67 			node = xbc_node_get_parent(node);
68 			if (!node)
69 				return;
70 			if (!xbc_node_get_child(node)->next)
71 				continue;
72 			depth--;
73 			for (i = 0; i < depth; i++)
74 				printf("\t");
75 			printf("}\n");
76 		}
77 		node = xbc_node_get_next(node);
78 	}
79 }
80 
81 static void xbc_show_list(void)
82 {
83 	char key[XBC_KEYLEN_MAX];
84 	struct xbc_node *leaf;
85 	const char *val;
86 	int ret = 0;
87 
88 	xbc_for_each_key_value(leaf, val) {
89 		ret = xbc_node_compose_key(leaf, key, XBC_KEYLEN_MAX);
90 		if (ret < 0)
91 			break;
92 		printf("%s = ", key);
93 		if (!val || val[0] == '\0') {
94 			printf("\"\"\n");
95 			continue;
96 		}
97 		xbc_show_value(xbc_node_get_child(leaf), false);
98 	}
99 }
100 
101 /* Simple real checksum */
102 static int checksum(unsigned char *buf, int len)
103 {
104 	int i, sum = 0;
105 
106 	for (i = 0; i < len; i++)
107 		sum += buf[i];
108 
109 	return sum;
110 }
111 
112 #define PAGE_SIZE	4096
113 
114 static int load_xbc_fd(int fd, char **buf, int size)
115 {
116 	int ret;
117 
118 	*buf = malloc(size + 1);
119 	if (!*buf)
120 		return -ENOMEM;
121 
122 	ret = read(fd, *buf, size);
123 	if (ret < 0)
124 		return -errno;
125 	(*buf)[size] = '\0';
126 
127 	return ret;
128 }
129 
130 /* Return the read size or -errno */
131 static int load_xbc_file(const char *path, char **buf)
132 {
133 	struct stat stat;
134 	int fd, ret;
135 
136 	fd = open(path, O_RDONLY);
137 	if (fd < 0)
138 		return -errno;
139 	ret = fstat(fd, &stat);
140 	if (ret < 0)
141 		return -errno;
142 
143 	ret = load_xbc_fd(fd, buf, stat.st_size);
144 
145 	close(fd);
146 
147 	return ret;
148 }
149 
150 static int load_xbc_from_initrd(int fd, char **buf)
151 {
152 	struct stat stat;
153 	int ret;
154 	u32 size = 0, csum = 0, rcsum;
155 	char magic[BOOTCONFIG_MAGIC_LEN];
156 	const char *msg;
157 
158 	ret = fstat(fd, &stat);
159 	if (ret < 0)
160 		return -errno;
161 
162 	if (stat.st_size < 8 + BOOTCONFIG_MAGIC_LEN)
163 		return 0;
164 
165 	if (lseek(fd, -BOOTCONFIG_MAGIC_LEN, SEEK_END) < 0) {
166 		pr_err("Failed to lseek: %d\n", -errno);
167 		return -errno;
168 	}
169 	if (read(fd, magic, BOOTCONFIG_MAGIC_LEN) < 0)
170 		return -errno;
171 	/* Check the bootconfig magic bytes */
172 	if (memcmp(magic, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN) != 0)
173 		return 0;
174 
175 	if (lseek(fd, -(8 + BOOTCONFIG_MAGIC_LEN), SEEK_END) < 0) {
176 		pr_err("Failed to lseek: %d\n", -errno);
177 		return -errno;
178 	}
179 
180 	if (read(fd, &size, sizeof(u32)) < 0)
181 		return -errno;
182 
183 	if (read(fd, &csum, sizeof(u32)) < 0)
184 		return -errno;
185 
186 	/* Wrong size error  */
187 	if (stat.st_size < size + 8 + BOOTCONFIG_MAGIC_LEN) {
188 		pr_err("bootconfig size is too big\n");
189 		return -E2BIG;
190 	}
191 
192 	if (lseek(fd, stat.st_size - (size + 8 + BOOTCONFIG_MAGIC_LEN),
193 		  SEEK_SET) < 0) {
194 		pr_err("Failed to lseek: %d\n", -errno);
195 		return -errno;
196 	}
197 
198 	ret = load_xbc_fd(fd, buf, size);
199 	if (ret < 0)
200 		return ret;
201 
202 	/* Wrong Checksum */
203 	rcsum = checksum((unsigned char *)*buf, size);
204 	if (csum != rcsum) {
205 		pr_err("checksum error: %d != %d\n", csum, rcsum);
206 		return -EINVAL;
207 	}
208 
209 	ret = xbc_init(*buf, &msg, NULL);
210 	/* Wrong data */
211 	if (ret < 0) {
212 		pr_err("parse error: %s.\n", msg);
213 		return ret;
214 	}
215 
216 	return size;
217 }
218 
219 static void show_xbc_error(const char *data, const char *msg, int pos)
220 {
221 	int lin = 1, col, i;
222 
223 	if (pos < 0) {
224 		pr_err("Error: %s.\n", msg);
225 		return;
226 	}
227 
228 	/* Note that pos starts from 0 but lin and col should start from 1. */
229 	col = pos + 1;
230 	for (i = 0; i < pos; i++) {
231 		if (data[i] == '\n') {
232 			lin++;
233 			col = pos - i;
234 		}
235 	}
236 	pr_err("Parse Error: %s at %d:%d\n", msg, lin, col);
237 
238 }
239 
240 static int init_xbc_with_error(char *buf, int len)
241 {
242 	char *copy = strdup(buf);
243 	const char *msg;
244 	int ret, pos;
245 
246 	if (!copy)
247 		return -ENOMEM;
248 
249 	ret = xbc_init(buf, &msg, &pos);
250 	if (ret < 0)
251 		show_xbc_error(copy, msg, pos);
252 	free(copy);
253 
254 	return ret;
255 }
256 
257 static int show_xbc(const char *path, bool list)
258 {
259 	int ret, fd;
260 	char *buf = NULL;
261 	struct stat st;
262 
263 	ret = stat(path, &st);
264 	if (ret < 0) {
265 		pr_err("Failed to stat %s: %d\n", path, -errno);
266 		return -errno;
267 	}
268 
269 	fd = open(path, O_RDONLY);
270 	if (fd < 0) {
271 		pr_err("Failed to open initrd %s: %d\n", path, fd);
272 		return -errno;
273 	}
274 
275 	ret = load_xbc_from_initrd(fd, &buf);
276 	close(fd);
277 	if (ret < 0) {
278 		pr_err("Failed to load a boot config from initrd: %d\n", ret);
279 		goto out;
280 	}
281 	/* Assume a bootconfig file if it is enough small */
282 	if (ret == 0 && st.st_size <= XBC_DATA_MAX) {
283 		ret = load_xbc_file(path, &buf);
284 		if (ret < 0) {
285 			pr_err("Failed to load a boot config: %d\n", ret);
286 			goto out;
287 		}
288 		if (init_xbc_with_error(buf, ret) < 0)
289 			goto out;
290 	}
291 	if (list)
292 		xbc_show_list();
293 	else
294 		xbc_show_compact_tree();
295 	ret = 0;
296 out:
297 	free(buf);
298 
299 	return ret;
300 }
301 
302 static int delete_xbc(const char *path)
303 {
304 	struct stat stat;
305 	int ret = 0, fd, size;
306 	char *buf = NULL;
307 
308 	fd = open(path, O_RDWR);
309 	if (fd < 0) {
310 		pr_err("Failed to open initrd %s: %d\n", path, fd);
311 		return -errno;
312 	}
313 
314 	size = load_xbc_from_initrd(fd, &buf);
315 	if (size < 0) {
316 		ret = size;
317 		pr_err("Failed to load a boot config from initrd: %d\n", ret);
318 	} else if (size > 0) {
319 		ret = fstat(fd, &stat);
320 		if (!ret)
321 			ret = ftruncate(fd, stat.st_size
322 					- size - 8 - BOOTCONFIG_MAGIC_LEN);
323 		if (ret)
324 			ret = -errno;
325 	} /* Ignore if there is no boot config in initrd */
326 
327 	close(fd);
328 	free(buf);
329 
330 	return ret;
331 }
332 
333 static int apply_xbc(const char *path, const char *xbc_path)
334 {
335 	u32 size, csum;
336 	char *buf, *data;
337 	int ret, fd;
338 	const char *msg;
339 	int pos;
340 
341 	ret = load_xbc_file(xbc_path, &buf);
342 	if (ret < 0) {
343 		pr_err("Failed to load %s : %d\n", xbc_path, ret);
344 		return ret;
345 	}
346 	size = strlen(buf) + 1;
347 	csum = checksum((unsigned char *)buf, size);
348 
349 	/* Prepare xbc_path data */
350 	data = malloc(size + 8);
351 	if (!data)
352 		return -ENOMEM;
353 	strcpy(data, buf);
354 	*(u32 *)(data + size) = size;
355 	*(u32 *)(data + size + 4) = csum;
356 
357 	/* Check the data format */
358 	ret = xbc_init(buf, &msg, &pos);
359 	if (ret < 0) {
360 		show_xbc_error(data, msg, pos);
361 		free(data);
362 		free(buf);
363 
364 		return ret;
365 	}
366 	printf("Apply %s to %s\n", xbc_path, path);
367 	printf("\tNumber of nodes: %d\n", ret);
368 	printf("\tSize: %u bytes\n", (unsigned int)size);
369 	printf("\tChecksum: %d\n", (unsigned int)csum);
370 
371 	/* TODO: Check the options by schema */
372 	xbc_destroy_all();
373 	free(buf);
374 
375 	/* Remove old boot config if exists */
376 	ret = delete_xbc(path);
377 	if (ret < 0) {
378 		pr_err("Failed to delete previous boot config: %d\n", ret);
379 		free(data);
380 		return ret;
381 	}
382 
383 	/* Apply new one */
384 	fd = open(path, O_RDWR | O_APPEND);
385 	if (fd < 0) {
386 		pr_err("Failed to open %s: %d\n", path, fd);
387 		free(data);
388 		return fd;
389 	}
390 	/* TODO: Ensure the @path is initramfs/initrd image */
391 	ret = write(fd, data, size + 8);
392 	if (ret < 0) {
393 		pr_err("Failed to apply a boot config: %d\n", ret);
394 		goto out;
395 	}
396 	/* Write a magic word of the bootconfig */
397 	ret = write(fd, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN);
398 	if (ret < 0) {
399 		pr_err("Failed to apply a boot config magic: %d\n", ret);
400 		goto out;
401 	}
402 	ret = 0;
403 out:
404 	close(fd);
405 	free(data);
406 
407 	return ret;
408 }
409 
410 static int usage(void)
411 {
412 	printf("Usage: bootconfig [OPTIONS] <INITRD>\n"
413 		"Or     bootconfig <CONFIG>\n"
414 		" Apply, delete or show boot config to initrd.\n"
415 		" Options:\n"
416 		"		-a <config>: Apply boot config to initrd\n"
417 		"		-d : Delete boot config file from initrd\n"
418 		"		-l : list boot config in initrd or file\n\n"
419 		" If no option is given, show the bootconfig in the given file.\n");
420 	return -1;
421 }
422 
423 int main(int argc, char **argv)
424 {
425 	char *path = NULL;
426 	char *apply = NULL;
427 	bool delete = false, list = false;
428 	int opt;
429 
430 	while ((opt = getopt(argc, argv, "hda:l")) != -1) {
431 		switch (opt) {
432 		case 'd':
433 			delete = true;
434 			break;
435 		case 'a':
436 			apply = optarg;
437 			break;
438 		case 'l':
439 			list = true;
440 			break;
441 		case 'h':
442 		default:
443 			return usage();
444 		}
445 	}
446 
447 	if ((apply && delete) || (delete && list) || (apply && list)) {
448 		pr_err("Error: You can give one of -a, -d or -l at once.\n");
449 		return usage();
450 	}
451 
452 	if (optind >= argc) {
453 		pr_err("Error: No initrd is specified.\n");
454 		return usage();
455 	}
456 
457 	path = argv[optind];
458 
459 	if (apply)
460 		return apply_xbc(path, apply);
461 	else if (delete)
462 		return delete_xbc(path);
463 
464 	return show_xbc(path, list);
465 }
466