Linux Audio

Check our new training course

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