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