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