1*950313ebSMasami Hiramatsu // SPDX-License-Identifier: GPL-2.0 2*950313ebSMasami Hiramatsu /* 3*950313ebSMasami Hiramatsu * Boot config tool for initrd image 4*950313ebSMasami Hiramatsu */ 5*950313ebSMasami Hiramatsu #include <stdio.h> 6*950313ebSMasami Hiramatsu #include <stdlib.h> 7*950313ebSMasami Hiramatsu #include <sys/types.h> 8*950313ebSMasami Hiramatsu #include <sys/stat.h> 9*950313ebSMasami Hiramatsu #include <fcntl.h> 10*950313ebSMasami Hiramatsu #include <unistd.h> 11*950313ebSMasami Hiramatsu #include <string.h> 12*950313ebSMasami Hiramatsu #include <errno.h> 13*950313ebSMasami Hiramatsu 14*950313ebSMasami Hiramatsu #include <linux/kernel.h> 15*950313ebSMasami Hiramatsu #include <linux/bootconfig.h> 16*950313ebSMasami Hiramatsu 17*950313ebSMasami Hiramatsu int pr_output = 1; 18*950313ebSMasami Hiramatsu 19*950313ebSMasami Hiramatsu static int xbc_show_array(struct xbc_node *node) 20*950313ebSMasami Hiramatsu { 21*950313ebSMasami Hiramatsu const char *val; 22*950313ebSMasami Hiramatsu int i = 0; 23*950313ebSMasami Hiramatsu 24*950313ebSMasami Hiramatsu xbc_array_for_each_value(node, val) { 25*950313ebSMasami Hiramatsu printf("\"%s\"%s", val, node->next ? ", " : ";\n"); 26*950313ebSMasami Hiramatsu i++; 27*950313ebSMasami Hiramatsu } 28*950313ebSMasami Hiramatsu return i; 29*950313ebSMasami Hiramatsu } 30*950313ebSMasami Hiramatsu 31*950313ebSMasami Hiramatsu static void xbc_show_compact_tree(void) 32*950313ebSMasami Hiramatsu { 33*950313ebSMasami Hiramatsu struct xbc_node *node, *cnode; 34*950313ebSMasami Hiramatsu int depth = 0, i; 35*950313ebSMasami Hiramatsu 36*950313ebSMasami Hiramatsu node = xbc_root_node(); 37*950313ebSMasami Hiramatsu while (node && xbc_node_is_key(node)) { 38*950313ebSMasami Hiramatsu for (i = 0; i < depth; i++) 39*950313ebSMasami Hiramatsu printf("\t"); 40*950313ebSMasami Hiramatsu cnode = xbc_node_get_child(node); 41*950313ebSMasami Hiramatsu while (cnode && xbc_node_is_key(cnode) && !cnode->next) { 42*950313ebSMasami Hiramatsu printf("%s.", xbc_node_get_data(node)); 43*950313ebSMasami Hiramatsu node = cnode; 44*950313ebSMasami Hiramatsu cnode = xbc_node_get_child(node); 45*950313ebSMasami Hiramatsu } 46*950313ebSMasami Hiramatsu if (cnode && xbc_node_is_key(cnode)) { 47*950313ebSMasami Hiramatsu printf("%s {\n", xbc_node_get_data(node)); 48*950313ebSMasami Hiramatsu depth++; 49*950313ebSMasami Hiramatsu node = cnode; 50*950313ebSMasami Hiramatsu continue; 51*950313ebSMasami Hiramatsu } else if (cnode && xbc_node_is_value(cnode)) { 52*950313ebSMasami Hiramatsu printf("%s = ", xbc_node_get_data(node)); 53*950313ebSMasami Hiramatsu if (cnode->next) 54*950313ebSMasami Hiramatsu xbc_show_array(cnode); 55*950313ebSMasami Hiramatsu else 56*950313ebSMasami Hiramatsu printf("\"%s\";\n", xbc_node_get_data(cnode)); 57*950313ebSMasami Hiramatsu } else { 58*950313ebSMasami Hiramatsu printf("%s;\n", xbc_node_get_data(node)); 59*950313ebSMasami Hiramatsu } 60*950313ebSMasami Hiramatsu 61*950313ebSMasami Hiramatsu if (node->next) { 62*950313ebSMasami Hiramatsu node = xbc_node_get_next(node); 63*950313ebSMasami Hiramatsu continue; 64*950313ebSMasami Hiramatsu } 65*950313ebSMasami Hiramatsu while (!node->next) { 66*950313ebSMasami Hiramatsu node = xbc_node_get_parent(node); 67*950313ebSMasami Hiramatsu if (!node) 68*950313ebSMasami Hiramatsu return; 69*950313ebSMasami Hiramatsu if (!xbc_node_get_child(node)->next) 70*950313ebSMasami Hiramatsu continue; 71*950313ebSMasami Hiramatsu depth--; 72*950313ebSMasami Hiramatsu for (i = 0; i < depth; i++) 73*950313ebSMasami Hiramatsu printf("\t"); 74*950313ebSMasami Hiramatsu printf("}\n"); 75*950313ebSMasami Hiramatsu } 76*950313ebSMasami Hiramatsu node = xbc_node_get_next(node); 77*950313ebSMasami Hiramatsu } 78*950313ebSMasami Hiramatsu } 79*950313ebSMasami Hiramatsu 80*950313ebSMasami Hiramatsu /* Simple real checksum */ 81*950313ebSMasami Hiramatsu int checksum(unsigned char *buf, int len) 82*950313ebSMasami Hiramatsu { 83*950313ebSMasami Hiramatsu int i, sum = 0; 84*950313ebSMasami Hiramatsu 85*950313ebSMasami Hiramatsu for (i = 0; i < len; i++) 86*950313ebSMasami Hiramatsu sum += buf[i]; 87*950313ebSMasami Hiramatsu 88*950313ebSMasami Hiramatsu return sum; 89*950313ebSMasami Hiramatsu } 90*950313ebSMasami Hiramatsu 91*950313ebSMasami Hiramatsu #define PAGE_SIZE 4096 92*950313ebSMasami Hiramatsu 93*950313ebSMasami Hiramatsu int load_xbc_fd(int fd, char **buf, int size) 94*950313ebSMasami Hiramatsu { 95*950313ebSMasami Hiramatsu int ret; 96*950313ebSMasami Hiramatsu 97*950313ebSMasami Hiramatsu *buf = malloc(size + 1); 98*950313ebSMasami Hiramatsu if (!*buf) 99*950313ebSMasami Hiramatsu return -ENOMEM; 100*950313ebSMasami Hiramatsu 101*950313ebSMasami Hiramatsu ret = read(fd, *buf, size); 102*950313ebSMasami Hiramatsu if (ret < 0) 103*950313ebSMasami Hiramatsu return -errno; 104*950313ebSMasami Hiramatsu (*buf)[size] = '\0'; 105*950313ebSMasami Hiramatsu 106*950313ebSMasami Hiramatsu return ret; 107*950313ebSMasami Hiramatsu } 108*950313ebSMasami Hiramatsu 109*950313ebSMasami Hiramatsu /* Return the read size or -errno */ 110*950313ebSMasami Hiramatsu int load_xbc_file(const char *path, char **buf) 111*950313ebSMasami Hiramatsu { 112*950313ebSMasami Hiramatsu struct stat stat; 113*950313ebSMasami Hiramatsu int fd, ret; 114*950313ebSMasami Hiramatsu 115*950313ebSMasami Hiramatsu fd = open(path, O_RDONLY); 116*950313ebSMasami Hiramatsu if (fd < 0) 117*950313ebSMasami Hiramatsu return -errno; 118*950313ebSMasami Hiramatsu ret = fstat(fd, &stat); 119*950313ebSMasami Hiramatsu if (ret < 0) 120*950313ebSMasami Hiramatsu return -errno; 121*950313ebSMasami Hiramatsu 122*950313ebSMasami Hiramatsu ret = load_xbc_fd(fd, buf, stat.st_size); 123*950313ebSMasami Hiramatsu 124*950313ebSMasami Hiramatsu close(fd); 125*950313ebSMasami Hiramatsu 126*950313ebSMasami Hiramatsu return ret; 127*950313ebSMasami Hiramatsu } 128*950313ebSMasami Hiramatsu 129*950313ebSMasami Hiramatsu int load_xbc_from_initrd(int fd, char **buf) 130*950313ebSMasami Hiramatsu { 131*950313ebSMasami Hiramatsu struct stat stat; 132*950313ebSMasami Hiramatsu int ret; 133*950313ebSMasami Hiramatsu u32 size = 0, csum = 0, rcsum; 134*950313ebSMasami Hiramatsu 135*950313ebSMasami Hiramatsu ret = fstat(fd, &stat); 136*950313ebSMasami Hiramatsu if (ret < 0) 137*950313ebSMasami Hiramatsu return -errno; 138*950313ebSMasami Hiramatsu 139*950313ebSMasami Hiramatsu if (stat.st_size < 8) 140*950313ebSMasami Hiramatsu return 0; 141*950313ebSMasami Hiramatsu 142*950313ebSMasami Hiramatsu if (lseek(fd, -8, SEEK_END) < 0) { 143*950313ebSMasami Hiramatsu printf("Faile to lseek: %d\n", -errno); 144*950313ebSMasami Hiramatsu return -errno; 145*950313ebSMasami Hiramatsu } 146*950313ebSMasami Hiramatsu 147*950313ebSMasami Hiramatsu if (read(fd, &size, sizeof(u32)) < 0) 148*950313ebSMasami Hiramatsu return -errno; 149*950313ebSMasami Hiramatsu 150*950313ebSMasami Hiramatsu if (read(fd, &csum, sizeof(u32)) < 0) 151*950313ebSMasami Hiramatsu return -errno; 152*950313ebSMasami Hiramatsu 153*950313ebSMasami Hiramatsu /* Wrong size, maybe no boot config here */ 154*950313ebSMasami Hiramatsu if (stat.st_size < size + 8) 155*950313ebSMasami Hiramatsu return 0; 156*950313ebSMasami Hiramatsu 157*950313ebSMasami Hiramatsu if (lseek(fd, stat.st_size - 8 - size, SEEK_SET) < 0) { 158*950313ebSMasami Hiramatsu printf("Faile to lseek: %d\n", -errno); 159*950313ebSMasami Hiramatsu return -errno; 160*950313ebSMasami Hiramatsu } 161*950313ebSMasami Hiramatsu 162*950313ebSMasami Hiramatsu ret = load_xbc_fd(fd, buf, size); 163*950313ebSMasami Hiramatsu if (ret < 0) 164*950313ebSMasami Hiramatsu return ret; 165*950313ebSMasami Hiramatsu 166*950313ebSMasami Hiramatsu /* Wrong Checksum, maybe no boot config here */ 167*950313ebSMasami Hiramatsu rcsum = checksum((unsigned char *)*buf, size); 168*950313ebSMasami Hiramatsu if (csum != rcsum) { 169*950313ebSMasami Hiramatsu printf("checksum error: %d != %d\n", csum, rcsum); 170*950313ebSMasami Hiramatsu return 0; 171*950313ebSMasami Hiramatsu } 172*950313ebSMasami Hiramatsu 173*950313ebSMasami Hiramatsu ret = xbc_init(*buf); 174*950313ebSMasami Hiramatsu /* Wrong data, maybe no boot config here */ 175*950313ebSMasami Hiramatsu if (ret < 0) 176*950313ebSMasami Hiramatsu return 0; 177*950313ebSMasami Hiramatsu 178*950313ebSMasami Hiramatsu return size; 179*950313ebSMasami Hiramatsu } 180*950313ebSMasami Hiramatsu 181*950313ebSMasami Hiramatsu int show_xbc(const char *path) 182*950313ebSMasami Hiramatsu { 183*950313ebSMasami Hiramatsu int ret, fd; 184*950313ebSMasami Hiramatsu char *buf = NULL; 185*950313ebSMasami Hiramatsu 186*950313ebSMasami Hiramatsu fd = open(path, O_RDONLY); 187*950313ebSMasami Hiramatsu if (fd < 0) { 188*950313ebSMasami Hiramatsu printf("Failed to open initrd %s: %d\n", path, fd); 189*950313ebSMasami Hiramatsu return -errno; 190*950313ebSMasami Hiramatsu } 191*950313ebSMasami Hiramatsu 192*950313ebSMasami Hiramatsu ret = load_xbc_from_initrd(fd, &buf); 193*950313ebSMasami Hiramatsu if (ret < 0) 194*950313ebSMasami Hiramatsu printf("Failed to load a boot config from initrd: %d\n", ret); 195*950313ebSMasami Hiramatsu else 196*950313ebSMasami Hiramatsu xbc_show_compact_tree(); 197*950313ebSMasami Hiramatsu 198*950313ebSMasami Hiramatsu close(fd); 199*950313ebSMasami Hiramatsu free(buf); 200*950313ebSMasami Hiramatsu 201*950313ebSMasami Hiramatsu return ret; 202*950313ebSMasami Hiramatsu } 203*950313ebSMasami Hiramatsu 204*950313ebSMasami Hiramatsu int delete_xbc(const char *path) 205*950313ebSMasami Hiramatsu { 206*950313ebSMasami Hiramatsu struct stat stat; 207*950313ebSMasami Hiramatsu int ret = 0, fd, size; 208*950313ebSMasami Hiramatsu char *buf = NULL; 209*950313ebSMasami Hiramatsu 210*950313ebSMasami Hiramatsu fd = open(path, O_RDWR); 211*950313ebSMasami Hiramatsu if (fd < 0) { 212*950313ebSMasami Hiramatsu printf("Failed to open initrd %s: %d\n", path, fd); 213*950313ebSMasami Hiramatsu return -errno; 214*950313ebSMasami Hiramatsu } 215*950313ebSMasami Hiramatsu 216*950313ebSMasami Hiramatsu /* 217*950313ebSMasami Hiramatsu * Suppress error messages in xbc_init() because it can be just a 218*950313ebSMasami Hiramatsu * data which concidentally matches the size and checksum footer. 219*950313ebSMasami Hiramatsu */ 220*950313ebSMasami Hiramatsu pr_output = 0; 221*950313ebSMasami Hiramatsu size = load_xbc_from_initrd(fd, &buf); 222*950313ebSMasami Hiramatsu pr_output = 1; 223*950313ebSMasami Hiramatsu if (size < 0) { 224*950313ebSMasami Hiramatsu ret = size; 225*950313ebSMasami Hiramatsu printf("Failed to load a boot config from initrd: %d\n", ret); 226*950313ebSMasami Hiramatsu } else if (size > 0) { 227*950313ebSMasami Hiramatsu ret = fstat(fd, &stat); 228*950313ebSMasami Hiramatsu if (!ret) 229*950313ebSMasami Hiramatsu ret = ftruncate(fd, stat.st_size - size - 8); 230*950313ebSMasami Hiramatsu if (ret) 231*950313ebSMasami Hiramatsu ret = -errno; 232*950313ebSMasami Hiramatsu } /* Ignore if there is no boot config in initrd */ 233*950313ebSMasami Hiramatsu 234*950313ebSMasami Hiramatsu close(fd); 235*950313ebSMasami Hiramatsu free(buf); 236*950313ebSMasami Hiramatsu 237*950313ebSMasami Hiramatsu return ret; 238*950313ebSMasami Hiramatsu } 239*950313ebSMasami Hiramatsu 240*950313ebSMasami Hiramatsu int apply_xbc(const char *path, const char *xbc_path) 241*950313ebSMasami Hiramatsu { 242*950313ebSMasami Hiramatsu u32 size, csum; 243*950313ebSMasami Hiramatsu char *buf, *data; 244*950313ebSMasami Hiramatsu int ret, fd; 245*950313ebSMasami Hiramatsu 246*950313ebSMasami Hiramatsu ret = load_xbc_file(xbc_path, &buf); 247*950313ebSMasami Hiramatsu if (ret < 0) { 248*950313ebSMasami Hiramatsu printf("Failed to load %s : %d\n", xbc_path, ret); 249*950313ebSMasami Hiramatsu return ret; 250*950313ebSMasami Hiramatsu } 251*950313ebSMasami Hiramatsu size = strlen(buf) + 1; 252*950313ebSMasami Hiramatsu csum = checksum((unsigned char *)buf, size); 253*950313ebSMasami Hiramatsu 254*950313ebSMasami Hiramatsu /* Prepare xbc_path data */ 255*950313ebSMasami Hiramatsu data = malloc(size + 8); 256*950313ebSMasami Hiramatsu if (!data) 257*950313ebSMasami Hiramatsu return -ENOMEM; 258*950313ebSMasami Hiramatsu strcpy(data, buf); 259*950313ebSMasami Hiramatsu *(u32 *)(data + size) = size; 260*950313ebSMasami Hiramatsu *(u32 *)(data + size + 4) = csum; 261*950313ebSMasami Hiramatsu 262*950313ebSMasami Hiramatsu /* Check the data format */ 263*950313ebSMasami Hiramatsu ret = xbc_init(buf); 264*950313ebSMasami Hiramatsu if (ret < 0) { 265*950313ebSMasami Hiramatsu printf("Failed to parse %s: %d\n", xbc_path, ret); 266*950313ebSMasami Hiramatsu free(data); 267*950313ebSMasami Hiramatsu free(buf); 268*950313ebSMasami Hiramatsu return ret; 269*950313ebSMasami Hiramatsu } 270*950313ebSMasami Hiramatsu printf("Apply %s to %s\n", xbc_path, path); 271*950313ebSMasami Hiramatsu printf("\tSize: %u bytes\n", (unsigned int)size); 272*950313ebSMasami Hiramatsu printf("\tChecksum: %d\n", (unsigned int)csum); 273*950313ebSMasami Hiramatsu 274*950313ebSMasami Hiramatsu /* TODO: Check the options by schema */ 275*950313ebSMasami Hiramatsu xbc_destroy_all(); 276*950313ebSMasami Hiramatsu free(buf); 277*950313ebSMasami Hiramatsu 278*950313ebSMasami Hiramatsu /* Remove old boot config if exists */ 279*950313ebSMasami Hiramatsu ret = delete_xbc(path); 280*950313ebSMasami Hiramatsu if (ret < 0) { 281*950313ebSMasami Hiramatsu printf("Failed to delete previous boot config: %d\n", ret); 282*950313ebSMasami Hiramatsu return ret; 283*950313ebSMasami Hiramatsu } 284*950313ebSMasami Hiramatsu 285*950313ebSMasami Hiramatsu /* Apply new one */ 286*950313ebSMasami Hiramatsu fd = open(path, O_RDWR | O_APPEND); 287*950313ebSMasami Hiramatsu if (fd < 0) { 288*950313ebSMasami Hiramatsu printf("Failed to open %s: %d\n", path, fd); 289*950313ebSMasami Hiramatsu return fd; 290*950313ebSMasami Hiramatsu } 291*950313ebSMasami Hiramatsu /* TODO: Ensure the @path is initramfs/initrd image */ 292*950313ebSMasami Hiramatsu ret = write(fd, data, size + 8); 293*950313ebSMasami Hiramatsu if (ret < 0) { 294*950313ebSMasami Hiramatsu printf("Failed to apply a boot config: %d\n", ret); 295*950313ebSMasami Hiramatsu return ret; 296*950313ebSMasami Hiramatsu } 297*950313ebSMasami Hiramatsu close(fd); 298*950313ebSMasami Hiramatsu free(data); 299*950313ebSMasami Hiramatsu 300*950313ebSMasami Hiramatsu return 0; 301*950313ebSMasami Hiramatsu } 302*950313ebSMasami Hiramatsu 303*950313ebSMasami Hiramatsu int usage(void) 304*950313ebSMasami Hiramatsu { 305*950313ebSMasami Hiramatsu printf("Usage: bootconfig [OPTIONS] <INITRD>\n" 306*950313ebSMasami Hiramatsu " Apply, delete or show boot config to initrd.\n" 307*950313ebSMasami Hiramatsu " Options:\n" 308*950313ebSMasami Hiramatsu " -a <config>: Apply boot config to initrd\n" 309*950313ebSMasami Hiramatsu " -d : Delete boot config file from initrd\n\n" 310*950313ebSMasami Hiramatsu " If no option is given, show current applied boot config.\n"); 311*950313ebSMasami Hiramatsu return -1; 312*950313ebSMasami Hiramatsu } 313*950313ebSMasami Hiramatsu 314*950313ebSMasami Hiramatsu int main(int argc, char **argv) 315*950313ebSMasami Hiramatsu { 316*950313ebSMasami Hiramatsu char *path = NULL; 317*950313ebSMasami Hiramatsu char *apply = NULL; 318*950313ebSMasami Hiramatsu bool delete = false; 319*950313ebSMasami Hiramatsu int opt; 320*950313ebSMasami Hiramatsu 321*950313ebSMasami Hiramatsu while ((opt = getopt(argc, argv, "hda:")) != -1) { 322*950313ebSMasami Hiramatsu switch (opt) { 323*950313ebSMasami Hiramatsu case 'd': 324*950313ebSMasami Hiramatsu delete = true; 325*950313ebSMasami Hiramatsu break; 326*950313ebSMasami Hiramatsu case 'a': 327*950313ebSMasami Hiramatsu apply = optarg; 328*950313ebSMasami Hiramatsu break; 329*950313ebSMasami Hiramatsu case 'h': 330*950313ebSMasami Hiramatsu default: 331*950313ebSMasami Hiramatsu return usage(); 332*950313ebSMasami Hiramatsu } 333*950313ebSMasami Hiramatsu } 334*950313ebSMasami Hiramatsu 335*950313ebSMasami Hiramatsu if (apply && delete) { 336*950313ebSMasami Hiramatsu printf("Error: You can not specify both -a and -d at once.\n"); 337*950313ebSMasami Hiramatsu return usage(); 338*950313ebSMasami Hiramatsu } 339*950313ebSMasami Hiramatsu 340*950313ebSMasami Hiramatsu if (optind >= argc) { 341*950313ebSMasami Hiramatsu printf("Error: No initrd is specified.\n"); 342*950313ebSMasami Hiramatsu return usage(); 343*950313ebSMasami Hiramatsu } 344*950313ebSMasami Hiramatsu 345*950313ebSMasami Hiramatsu path = argv[optind]; 346*950313ebSMasami Hiramatsu 347*950313ebSMasami Hiramatsu if (apply) 348*950313ebSMasami Hiramatsu return apply_xbc(path, apply); 349*950313ebSMasami Hiramatsu else if (delete) 350*950313ebSMasami Hiramatsu return delete_xbc(path); 351*950313ebSMasami Hiramatsu 352*950313ebSMasami Hiramatsu return show_xbc(path); 353*950313ebSMasami Hiramatsu } 354