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