1950313ebSMasami Hiramatsu // SPDX-License-Identifier: GPL-2.0 2950313ebSMasami Hiramatsu /* 3950313ebSMasami Hiramatsu * Boot config tool for initrd image 4950313ebSMasami Hiramatsu */ 5950313ebSMasami Hiramatsu #include <stdio.h> 6950313ebSMasami Hiramatsu #include <stdlib.h> 7950313ebSMasami Hiramatsu #include <sys/types.h> 8950313ebSMasami Hiramatsu #include <sys/stat.h> 9950313ebSMasami Hiramatsu #include <fcntl.h> 10950313ebSMasami Hiramatsu #include <unistd.h> 11950313ebSMasami Hiramatsu #include <string.h> 12950313ebSMasami Hiramatsu #include <errno.h> 13950313ebSMasami Hiramatsu 14950313ebSMasami Hiramatsu #include <linux/kernel.h> 15950313ebSMasami Hiramatsu #include <linux/bootconfig.h> 16950313ebSMasami Hiramatsu 17950313ebSMasami Hiramatsu int pr_output = 1; 18950313ebSMasami Hiramatsu 19950313ebSMasami Hiramatsu static int xbc_show_array(struct xbc_node *node) 20950313ebSMasami Hiramatsu { 21950313ebSMasami Hiramatsu const char *val; 22950313ebSMasami Hiramatsu int i = 0; 23950313ebSMasami Hiramatsu 24950313ebSMasami Hiramatsu xbc_array_for_each_value(node, val) { 25950313ebSMasami Hiramatsu printf("\"%s\"%s", val, node->next ? ", " : ";\n"); 26950313ebSMasami Hiramatsu i++; 27950313ebSMasami Hiramatsu } 28950313ebSMasami Hiramatsu return i; 29950313ebSMasami Hiramatsu } 30950313ebSMasami Hiramatsu 31950313ebSMasami Hiramatsu static void xbc_show_compact_tree(void) 32950313ebSMasami Hiramatsu { 33950313ebSMasami Hiramatsu struct xbc_node *node, *cnode; 34950313ebSMasami Hiramatsu int depth = 0, i; 35950313ebSMasami Hiramatsu 36950313ebSMasami Hiramatsu node = xbc_root_node(); 37950313ebSMasami Hiramatsu while (node && xbc_node_is_key(node)) { 38950313ebSMasami Hiramatsu for (i = 0; i < depth; i++) 39950313ebSMasami Hiramatsu printf("\t"); 40950313ebSMasami Hiramatsu cnode = xbc_node_get_child(node); 41950313ebSMasami Hiramatsu while (cnode && xbc_node_is_key(cnode) && !cnode->next) { 42950313ebSMasami Hiramatsu printf("%s.", xbc_node_get_data(node)); 43950313ebSMasami Hiramatsu node = cnode; 44950313ebSMasami Hiramatsu cnode = xbc_node_get_child(node); 45950313ebSMasami Hiramatsu } 46950313ebSMasami Hiramatsu if (cnode && xbc_node_is_key(cnode)) { 47950313ebSMasami Hiramatsu printf("%s {\n", xbc_node_get_data(node)); 48950313ebSMasami Hiramatsu depth++; 49950313ebSMasami Hiramatsu node = cnode; 50950313ebSMasami Hiramatsu continue; 51950313ebSMasami Hiramatsu } else if (cnode && xbc_node_is_value(cnode)) { 52950313ebSMasami Hiramatsu printf("%s = ", xbc_node_get_data(node)); 53950313ebSMasami Hiramatsu if (cnode->next) 54950313ebSMasami Hiramatsu xbc_show_array(cnode); 55950313ebSMasami Hiramatsu else 56950313ebSMasami Hiramatsu printf("\"%s\";\n", xbc_node_get_data(cnode)); 57950313ebSMasami Hiramatsu } else { 58950313ebSMasami Hiramatsu printf("%s;\n", xbc_node_get_data(node)); 59950313ebSMasami Hiramatsu } 60950313ebSMasami Hiramatsu 61950313ebSMasami Hiramatsu if (node->next) { 62950313ebSMasami Hiramatsu node = xbc_node_get_next(node); 63950313ebSMasami Hiramatsu continue; 64950313ebSMasami Hiramatsu } 65950313ebSMasami Hiramatsu while (!node->next) { 66950313ebSMasami Hiramatsu node = xbc_node_get_parent(node); 67950313ebSMasami Hiramatsu if (!node) 68950313ebSMasami Hiramatsu return; 69950313ebSMasami Hiramatsu if (!xbc_node_get_child(node)->next) 70950313ebSMasami Hiramatsu continue; 71950313ebSMasami Hiramatsu depth--; 72950313ebSMasami Hiramatsu for (i = 0; i < depth; i++) 73950313ebSMasami Hiramatsu printf("\t"); 74950313ebSMasami Hiramatsu printf("}\n"); 75950313ebSMasami Hiramatsu } 76950313ebSMasami Hiramatsu node = xbc_node_get_next(node); 77950313ebSMasami Hiramatsu } 78950313ebSMasami Hiramatsu } 79950313ebSMasami Hiramatsu 80950313ebSMasami Hiramatsu /* Simple real checksum */ 81950313ebSMasami Hiramatsu int checksum(unsigned char *buf, int len) 82950313ebSMasami Hiramatsu { 83950313ebSMasami Hiramatsu int i, sum = 0; 84950313ebSMasami Hiramatsu 85950313ebSMasami Hiramatsu for (i = 0; i < len; i++) 86950313ebSMasami Hiramatsu sum += buf[i]; 87950313ebSMasami Hiramatsu 88950313ebSMasami Hiramatsu return sum; 89950313ebSMasami Hiramatsu } 90950313ebSMasami Hiramatsu 91950313ebSMasami Hiramatsu #define PAGE_SIZE 4096 92950313ebSMasami Hiramatsu 93950313ebSMasami Hiramatsu int load_xbc_fd(int fd, char **buf, int size) 94950313ebSMasami Hiramatsu { 95950313ebSMasami Hiramatsu int ret; 96950313ebSMasami Hiramatsu 97950313ebSMasami Hiramatsu *buf = malloc(size + 1); 98950313ebSMasami Hiramatsu if (!*buf) 99950313ebSMasami Hiramatsu return -ENOMEM; 100950313ebSMasami Hiramatsu 101950313ebSMasami Hiramatsu ret = read(fd, *buf, size); 102950313ebSMasami Hiramatsu if (ret < 0) 103950313ebSMasami Hiramatsu return -errno; 104950313ebSMasami Hiramatsu (*buf)[size] = '\0'; 105950313ebSMasami Hiramatsu 106950313ebSMasami Hiramatsu return ret; 107950313ebSMasami Hiramatsu } 108950313ebSMasami Hiramatsu 109950313ebSMasami Hiramatsu /* Return the read size or -errno */ 110950313ebSMasami Hiramatsu int load_xbc_file(const char *path, char **buf) 111950313ebSMasami Hiramatsu { 112950313ebSMasami Hiramatsu struct stat stat; 113950313ebSMasami Hiramatsu int fd, ret; 114950313ebSMasami Hiramatsu 115950313ebSMasami Hiramatsu fd = open(path, O_RDONLY); 116950313ebSMasami Hiramatsu if (fd < 0) 117950313ebSMasami Hiramatsu return -errno; 118950313ebSMasami Hiramatsu ret = fstat(fd, &stat); 119950313ebSMasami Hiramatsu if (ret < 0) 120950313ebSMasami Hiramatsu return -errno; 121950313ebSMasami Hiramatsu 122950313ebSMasami Hiramatsu ret = load_xbc_fd(fd, buf, stat.st_size); 123950313ebSMasami Hiramatsu 124950313ebSMasami Hiramatsu close(fd); 125950313ebSMasami Hiramatsu 126950313ebSMasami Hiramatsu return ret; 127950313ebSMasami Hiramatsu } 128950313ebSMasami Hiramatsu 129950313ebSMasami Hiramatsu int load_xbc_from_initrd(int fd, char **buf) 130950313ebSMasami Hiramatsu { 131950313ebSMasami Hiramatsu struct stat stat; 132950313ebSMasami Hiramatsu int ret; 133950313ebSMasami Hiramatsu u32 size = 0, csum = 0, rcsum; 134950313ebSMasami Hiramatsu 135950313ebSMasami Hiramatsu ret = fstat(fd, &stat); 136950313ebSMasami Hiramatsu if (ret < 0) 137950313ebSMasami Hiramatsu return -errno; 138950313ebSMasami Hiramatsu 139950313ebSMasami Hiramatsu if (stat.st_size < 8) 140950313ebSMasami Hiramatsu return 0; 141950313ebSMasami Hiramatsu 142950313ebSMasami Hiramatsu if (lseek(fd, -8, SEEK_END) < 0) { 143bcc717edSColin Ian King printf("Failed to lseek: %d\n", -errno); 144950313ebSMasami Hiramatsu return -errno; 145950313ebSMasami Hiramatsu } 146950313ebSMasami Hiramatsu 147950313ebSMasami Hiramatsu if (read(fd, &size, sizeof(u32)) < 0) 148950313ebSMasami Hiramatsu return -errno; 149950313ebSMasami Hiramatsu 150950313ebSMasami Hiramatsu if (read(fd, &csum, sizeof(u32)) < 0) 151950313ebSMasami Hiramatsu return -errno; 152950313ebSMasami Hiramatsu 153950313ebSMasami Hiramatsu /* Wrong size, maybe no boot config here */ 154950313ebSMasami Hiramatsu if (stat.st_size < size + 8) 155950313ebSMasami Hiramatsu return 0; 156950313ebSMasami Hiramatsu 157950313ebSMasami Hiramatsu if (lseek(fd, stat.st_size - 8 - size, SEEK_SET) < 0) { 158bcc717edSColin Ian King printf("Failed to lseek: %d\n", -errno); 159950313ebSMasami Hiramatsu return -errno; 160950313ebSMasami Hiramatsu } 161950313ebSMasami Hiramatsu 162950313ebSMasami Hiramatsu ret = load_xbc_fd(fd, buf, size); 163950313ebSMasami Hiramatsu if (ret < 0) 164950313ebSMasami Hiramatsu return ret; 165950313ebSMasami Hiramatsu 166950313ebSMasami Hiramatsu /* Wrong Checksum, maybe no boot config here */ 167950313ebSMasami Hiramatsu rcsum = checksum((unsigned char *)*buf, size); 168950313ebSMasami Hiramatsu if (csum != rcsum) { 169950313ebSMasami Hiramatsu printf("checksum error: %d != %d\n", csum, rcsum); 170950313ebSMasami Hiramatsu return 0; 171950313ebSMasami Hiramatsu } 172950313ebSMasami Hiramatsu 173950313ebSMasami Hiramatsu ret = xbc_init(*buf); 174950313ebSMasami Hiramatsu /* Wrong data, maybe no boot config here */ 175950313ebSMasami Hiramatsu if (ret < 0) 176950313ebSMasami Hiramatsu return 0; 177950313ebSMasami Hiramatsu 178950313ebSMasami Hiramatsu return size; 179950313ebSMasami Hiramatsu } 180950313ebSMasami Hiramatsu 181950313ebSMasami Hiramatsu int show_xbc(const char *path) 182950313ebSMasami Hiramatsu { 183950313ebSMasami Hiramatsu int ret, fd; 184950313ebSMasami Hiramatsu char *buf = NULL; 185950313ebSMasami Hiramatsu 186950313ebSMasami Hiramatsu fd = open(path, O_RDONLY); 187950313ebSMasami Hiramatsu if (fd < 0) { 188950313ebSMasami Hiramatsu printf("Failed to open initrd %s: %d\n", path, fd); 189950313ebSMasami Hiramatsu return -errno; 190950313ebSMasami Hiramatsu } 191950313ebSMasami Hiramatsu 192950313ebSMasami Hiramatsu ret = load_xbc_from_initrd(fd, &buf); 193950313ebSMasami Hiramatsu if (ret < 0) 194950313ebSMasami Hiramatsu printf("Failed to load a boot config from initrd: %d\n", ret); 195950313ebSMasami Hiramatsu else 196950313ebSMasami Hiramatsu xbc_show_compact_tree(); 197950313ebSMasami Hiramatsu 198950313ebSMasami Hiramatsu close(fd); 199950313ebSMasami Hiramatsu free(buf); 200950313ebSMasami Hiramatsu 201950313ebSMasami Hiramatsu return ret; 202950313ebSMasami Hiramatsu } 203950313ebSMasami Hiramatsu 204950313ebSMasami Hiramatsu int delete_xbc(const char *path) 205950313ebSMasami Hiramatsu { 206950313ebSMasami Hiramatsu struct stat stat; 207950313ebSMasami Hiramatsu int ret = 0, fd, size; 208950313ebSMasami Hiramatsu char *buf = NULL; 209950313ebSMasami Hiramatsu 210950313ebSMasami Hiramatsu fd = open(path, O_RDWR); 211950313ebSMasami Hiramatsu if (fd < 0) { 212950313ebSMasami Hiramatsu printf("Failed to open initrd %s: %d\n", path, fd); 213950313ebSMasami Hiramatsu return -errno; 214950313ebSMasami Hiramatsu } 215950313ebSMasami Hiramatsu 216950313ebSMasami Hiramatsu /* 217950313ebSMasami Hiramatsu * Suppress error messages in xbc_init() because it can be just a 218950313ebSMasami Hiramatsu * data which concidentally matches the size and checksum footer. 219950313ebSMasami Hiramatsu */ 220950313ebSMasami Hiramatsu pr_output = 0; 221950313ebSMasami Hiramatsu size = load_xbc_from_initrd(fd, &buf); 222950313ebSMasami Hiramatsu pr_output = 1; 223950313ebSMasami Hiramatsu if (size < 0) { 224950313ebSMasami Hiramatsu ret = size; 225950313ebSMasami Hiramatsu printf("Failed to load a boot config from initrd: %d\n", ret); 226950313ebSMasami Hiramatsu } else if (size > 0) { 227950313ebSMasami Hiramatsu ret = fstat(fd, &stat); 228950313ebSMasami Hiramatsu if (!ret) 229950313ebSMasami Hiramatsu ret = ftruncate(fd, stat.st_size - size - 8); 230950313ebSMasami Hiramatsu if (ret) 231950313ebSMasami Hiramatsu ret = -errno; 232950313ebSMasami Hiramatsu } /* Ignore if there is no boot config in initrd */ 233950313ebSMasami Hiramatsu 234950313ebSMasami Hiramatsu close(fd); 235950313ebSMasami Hiramatsu free(buf); 236950313ebSMasami Hiramatsu 237950313ebSMasami Hiramatsu return ret; 238950313ebSMasami Hiramatsu } 239950313ebSMasami Hiramatsu 240950313ebSMasami Hiramatsu int apply_xbc(const char *path, const char *xbc_path) 241950313ebSMasami Hiramatsu { 242950313ebSMasami Hiramatsu u32 size, csum; 243950313ebSMasami Hiramatsu char *buf, *data; 244950313ebSMasami Hiramatsu int ret, fd; 245950313ebSMasami Hiramatsu 246950313ebSMasami Hiramatsu ret = load_xbc_file(xbc_path, &buf); 247950313ebSMasami Hiramatsu if (ret < 0) { 248950313ebSMasami Hiramatsu printf("Failed to load %s : %d\n", xbc_path, ret); 249950313ebSMasami Hiramatsu return ret; 250950313ebSMasami Hiramatsu } 251950313ebSMasami Hiramatsu size = strlen(buf) + 1; 252950313ebSMasami Hiramatsu csum = checksum((unsigned char *)buf, size); 253950313ebSMasami Hiramatsu 254950313ebSMasami Hiramatsu /* Prepare xbc_path data */ 255950313ebSMasami Hiramatsu data = malloc(size + 8); 256950313ebSMasami Hiramatsu if (!data) 257950313ebSMasami Hiramatsu return -ENOMEM; 258950313ebSMasami Hiramatsu strcpy(data, buf); 259950313ebSMasami Hiramatsu *(u32 *)(data + size) = size; 260950313ebSMasami Hiramatsu *(u32 *)(data + size + 4) = csum; 261950313ebSMasami Hiramatsu 262950313ebSMasami Hiramatsu /* Check the data format */ 263950313ebSMasami Hiramatsu ret = xbc_init(buf); 264950313ebSMasami Hiramatsu if (ret < 0) { 265950313ebSMasami Hiramatsu printf("Failed to parse %s: %d\n", xbc_path, ret); 266950313ebSMasami Hiramatsu free(data); 267950313ebSMasami Hiramatsu free(buf); 268950313ebSMasami Hiramatsu return ret; 269950313ebSMasami Hiramatsu } 270950313ebSMasami Hiramatsu printf("Apply %s to %s\n", xbc_path, path); 271*0f0d0a77SMasami Hiramatsu printf("\tNumber of nodes: %d\n", ret); 272950313ebSMasami Hiramatsu printf("\tSize: %u bytes\n", (unsigned int)size); 273950313ebSMasami Hiramatsu printf("\tChecksum: %d\n", (unsigned int)csum); 274950313ebSMasami Hiramatsu 275950313ebSMasami Hiramatsu /* TODO: Check the options by schema */ 276950313ebSMasami Hiramatsu xbc_destroy_all(); 277950313ebSMasami Hiramatsu free(buf); 278950313ebSMasami Hiramatsu 279950313ebSMasami Hiramatsu /* Remove old boot config if exists */ 280950313ebSMasami Hiramatsu ret = delete_xbc(path); 281950313ebSMasami Hiramatsu if (ret < 0) { 282950313ebSMasami Hiramatsu printf("Failed to delete previous boot config: %d\n", ret); 283950313ebSMasami Hiramatsu return ret; 284950313ebSMasami Hiramatsu } 285950313ebSMasami Hiramatsu 286950313ebSMasami Hiramatsu /* Apply new one */ 287950313ebSMasami Hiramatsu fd = open(path, O_RDWR | O_APPEND); 288950313ebSMasami Hiramatsu if (fd < 0) { 289950313ebSMasami Hiramatsu printf("Failed to open %s: %d\n", path, fd); 290950313ebSMasami Hiramatsu return fd; 291950313ebSMasami Hiramatsu } 292950313ebSMasami Hiramatsu /* TODO: Ensure the @path is initramfs/initrd image */ 293950313ebSMasami Hiramatsu ret = write(fd, data, size + 8); 294950313ebSMasami Hiramatsu if (ret < 0) { 295950313ebSMasami Hiramatsu printf("Failed to apply a boot config: %d\n", ret); 296950313ebSMasami Hiramatsu return ret; 297950313ebSMasami Hiramatsu } 298950313ebSMasami Hiramatsu close(fd); 299950313ebSMasami Hiramatsu free(data); 300950313ebSMasami Hiramatsu 301950313ebSMasami Hiramatsu return 0; 302950313ebSMasami Hiramatsu } 303950313ebSMasami Hiramatsu 304950313ebSMasami Hiramatsu int usage(void) 305950313ebSMasami Hiramatsu { 306950313ebSMasami Hiramatsu printf("Usage: bootconfig [OPTIONS] <INITRD>\n" 307950313ebSMasami Hiramatsu " Apply, delete or show boot config to initrd.\n" 308950313ebSMasami Hiramatsu " Options:\n" 309950313ebSMasami Hiramatsu " -a <config>: Apply boot config to initrd\n" 310950313ebSMasami Hiramatsu " -d : Delete boot config file from initrd\n\n" 311950313ebSMasami Hiramatsu " If no option is given, show current applied boot config.\n"); 312950313ebSMasami Hiramatsu return -1; 313950313ebSMasami Hiramatsu } 314950313ebSMasami Hiramatsu 315950313ebSMasami Hiramatsu int main(int argc, char **argv) 316950313ebSMasami Hiramatsu { 317950313ebSMasami Hiramatsu char *path = NULL; 318950313ebSMasami Hiramatsu char *apply = NULL; 319950313ebSMasami Hiramatsu bool delete = false; 320950313ebSMasami Hiramatsu int opt; 321950313ebSMasami Hiramatsu 322950313ebSMasami Hiramatsu while ((opt = getopt(argc, argv, "hda:")) != -1) { 323950313ebSMasami Hiramatsu switch (opt) { 324950313ebSMasami Hiramatsu case 'd': 325950313ebSMasami Hiramatsu delete = true; 326950313ebSMasami Hiramatsu break; 327950313ebSMasami Hiramatsu case 'a': 328950313ebSMasami Hiramatsu apply = optarg; 329950313ebSMasami Hiramatsu break; 330950313ebSMasami Hiramatsu case 'h': 331950313ebSMasami Hiramatsu default: 332950313ebSMasami Hiramatsu return usage(); 333950313ebSMasami Hiramatsu } 334950313ebSMasami Hiramatsu } 335950313ebSMasami Hiramatsu 336950313ebSMasami Hiramatsu if (apply && delete) { 337950313ebSMasami Hiramatsu printf("Error: You can not specify both -a and -d at once.\n"); 338950313ebSMasami Hiramatsu return usage(); 339950313ebSMasami Hiramatsu } 340950313ebSMasami Hiramatsu 341950313ebSMasami Hiramatsu if (optind >= argc) { 342950313ebSMasami Hiramatsu printf("Error: No initrd is specified.\n"); 343950313ebSMasami Hiramatsu return usage(); 344950313ebSMasami Hiramatsu } 345950313ebSMasami Hiramatsu 346950313ebSMasami Hiramatsu path = argv[optind]; 347950313ebSMasami Hiramatsu 348950313ebSMasami Hiramatsu if (apply) 349950313ebSMasami Hiramatsu return apply_xbc(path, apply); 350950313ebSMasami Hiramatsu else if (delete) 351950313ebSMasami Hiramatsu return delete_xbc(path); 352950313ebSMasami Hiramatsu 353950313ebSMasami Hiramatsu return show_xbc(path); 354950313ebSMasami Hiramatsu } 355