]> Git Repo - linux.git/blob - tools/bootconfig/main.c
enetc: Migrate to PHYLINK and PCS_LYNX
[linux.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)
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
34 static 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 */
81 int 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
93 int 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 */
110 int 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
129 int 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
198 int 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;
216 out:
217         close(fd);
218         free(buf);
219
220         return ret;
221 }
222
223 int 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
254 static 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
275 int 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;
345 out:
346         close(fd);
347         free(data);
348
349         return ret;
350 }
351
352 int 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
363 int 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 }
This page took 0.054376 seconds and 4 git commands to generate.