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