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