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