Merge remote-tracking branch 'asoc/for-5.10' into asoc-linus
[linux-2.6-microblaze.git] / tools / bootconfig / main.c
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 }