Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tools: bootconfig: Add bootconfig command

Add "bootconfig" command which operates the bootconfig
config-data on initrd image.

User can add/delete/verify the boot config on initrd
image using this command.

e.g.
Add a boot config to initrd image
# bootconfig -a myboot.conf /boot/initrd.img

Remove it.
# bootconfig -d /boot/initrd.img

Or verify (and show) it.
# bootconfig /boot/initrd.img

Link: http://lkml.kernel.org/r/157867223582.17873.14342161849213219982.stgit@devnote2

Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
[ Removed extra blank line at end of bootconfig.c ]
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>

authored by

Masami Hiramatsu and committed by
Steven Rostedt (VMware)
950313eb 7684b858

+481 -5
+1
MAINTAINERS
··· 15775 15775 S: Maintained 15776 15776 F: lib/bootconfig.c 15777 15777 F: include/linux/bootconfig.h 15778 + F: tools/bootconfig/* 15778 15779 15779 15780 SUN3/3X 15780 15781 M: Sam Creasey <sammy@sammy.net>
+6 -5
tools/Makefile
··· 28 28 @echo ' pci - PCI tools' 29 29 @echo ' perf - Linux performance measurement and analysis tool' 30 30 @echo ' selftests - various kernel selftests' 31 + @echo ' bootconfig - boot config tool' 31 32 @echo ' spi - spi tools' 32 33 @echo ' tmon - thermal monitoring and tuning tool' 33 34 @echo ' turbostat - Intel CPU idle stats and freq reporting tool' ··· 64 63 cpupower: FORCE 65 64 $(call descend,power/$@) 66 65 67 - cgroup firewire hv guest spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging: FORCE 66 + cgroup firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging: FORCE 68 67 $(call descend,$@) 69 68 70 69 liblockdep: FORCE ··· 97 96 $(call descend,kvm/$@) 98 97 99 98 all: acpi cgroup cpupower gpio hv firewire liblockdep \ 100 - perf selftests spi turbostat usb \ 99 + perf selftests bootconfig spi turbostat usb \ 101 100 virtio vm bpf x86_energy_perf_policy \ 102 101 tmon freefall iio objtool kvm_stat wmi \ 103 102 pci debugging ··· 108 107 cpupower_install: 109 108 $(call descend,power/$(@:_install=),install) 110 109 111 - cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install: 110 + cgroup_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install: 112 111 $(call descend,$(@:_install=),install) 113 112 114 113 liblockdep_install: ··· 142 141 cpupower_clean: 143 142 $(call descend,power/cpupower,clean) 144 143 145 - cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean: 144 + cgroup_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean: 146 145 $(call descend,$(@:_clean=),clean) 147 146 148 147 liblockdep_clean: ··· 177 176 $(call descend,build,clean) 178 177 179 178 clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \ 180 - perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \ 179 + perf_clean selftests_clean turbostat_clean bootconfig_clean spi_clean usb_clean virtio_clean \ 181 180 vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ 182 181 freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \ 183 182 gpio_clean objtool_clean leds_clean wmi_clean pci_clean firmware_clean debugging_clean \
+1
tools/bootconfig/.gitignore
··· 1 + bootconfig
+20
tools/bootconfig/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + # Makefile for bootconfig command 3 + 4 + bindir ?= /usr/bin 5 + 6 + HEADER = include/linux/bootconfig.h 7 + CFLAGS = -Wall -g -I./include 8 + 9 + PROGS = bootconfig 10 + 11 + all: $(PROGS) 12 + 13 + bootconfig: ../../lib/bootconfig.c main.c $(HEADER) 14 + $(CC) $(filter %.c,$^) $(CFLAGS) -o $@ 15 + 16 + install: $(PROGS) 17 + install bootconfig $(DESTDIR)$(bindir) 18 + 19 + clean: 20 + $(RM) -f *.o bootconfig
+7
tools/bootconfig/include/linux/bootconfig.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _BOOTCONFIG_LINUX_BOOTCONFIG_H 3 + #define _BOOTCONFIG_LINUX_BOOTCONFIG_H 4 + 5 + #include "../../../../include/linux/bootconfig.h" 6 + 7 + #endif
+12
tools/bootconfig/include/linux/bug.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _SKC_LINUX_BUG_H 3 + #define _SKC_LINUX_BUG_H 4 + 5 + #include <stdio.h> 6 + #include <stdlib.h> 7 + 8 + #define WARN_ON(cond) \ 9 + ((cond) ? printf("Internal warning(%s:%d, %s): %s\n", \ 10 + __FILE__, __LINE__, __func__, #cond) : 0) 11 + 12 + #endif
+7
tools/bootconfig/include/linux/ctype.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _SKC_LINUX_CTYPE_H 3 + #define _SKC_LINUX_CTYPE_H 4 + 5 + #include <ctype.h> 6 + 7 + #endif
+7
tools/bootconfig/include/linux/errno.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _SKC_LINUX_ERRNO_H 3 + #define _SKC_LINUX_ERRNO_H 4 + 5 + #include <asm/errno.h> 6 + 7 + #endif
+18
tools/bootconfig/include/linux/kernel.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _SKC_LINUX_KERNEL_H 3 + #define _SKC_LINUX_KERNEL_H 4 + 5 + #include <stdlib.h> 6 + #include <stdbool.h> 7 + 8 + #include <linux/printk.h> 9 + 10 + typedef unsigned short u16; 11 + typedef unsigned int u32; 12 + 13 + #define unlikely(cond) (cond) 14 + 15 + #define __init 16 + #define __initdata 17 + 18 + #endif
+17
tools/bootconfig/include/linux/printk.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _SKC_LINUX_PRINTK_H 3 + #define _SKC_LINUX_PRINTK_H 4 + 5 + #include <stdio.h> 6 + 7 + /* controllable printf */ 8 + extern int pr_output; 9 + #define printk(fmt, ...) \ 10 + (pr_output ? printf(fmt, __VA_ARGS__) : 0) 11 + 12 + #define pr_err printk 13 + #define pr_warn printk 14 + #define pr_info printk 15 + #define pr_debug printk 16 + 17 + #endif
+32
tools/bootconfig/include/linux/string.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _SKC_LINUX_STRING_H 3 + #define _SKC_LINUX_STRING_H 4 + 5 + #include <string.h> 6 + 7 + /* Copied from lib/string.c */ 8 + static inline char *skip_spaces(const char *str) 9 + { 10 + while (isspace(*str)) 11 + ++str; 12 + return (char *)str; 13 + } 14 + 15 + static inline char *strim(char *s) 16 + { 17 + size_t size; 18 + char *end; 19 + 20 + size = strlen(s); 21 + if (!size) 22 + return s; 23 + 24 + end = s + size - 1; 25 + while (end >= s && isspace(*end)) 26 + end--; 27 + *(end + 1) = '\0'; 28 + 29 + return skip_spaces(s); 30 + } 31 + 32 + #endif
+353
tools/bootconfig/main.c
··· 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("Faile 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("Faile 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("\tSize: %u bytes\n", (unsigned int)size); 272 + printf("\tChecksum: %d\n", (unsigned int)csum); 273 + 274 + /* TODO: Check the options by schema */ 275 + xbc_destroy_all(); 276 + free(buf); 277 + 278 + /* Remove old boot config if exists */ 279 + ret = delete_xbc(path); 280 + if (ret < 0) { 281 + printf("Failed to delete previous boot config: %d\n", ret); 282 + return ret; 283 + } 284 + 285 + /* Apply new one */ 286 + fd = open(path, O_RDWR | O_APPEND); 287 + if (fd < 0) { 288 + printf("Failed to open %s: %d\n", path, fd); 289 + return fd; 290 + } 291 + /* TODO: Ensure the @path is initramfs/initrd image */ 292 + ret = write(fd, data, size + 8); 293 + if (ret < 0) { 294 + printf("Failed to apply a boot config: %d\n", ret); 295 + return ret; 296 + } 297 + close(fd); 298 + free(data); 299 + 300 + return 0; 301 + } 302 + 303 + int usage(void) 304 + { 305 + printf("Usage: bootconfig [OPTIONS] <INITRD>\n" 306 + " Apply, delete or show boot config to initrd.\n" 307 + " Options:\n" 308 + " -a <config>: Apply boot config to initrd\n" 309 + " -d : Delete boot config file from initrd\n\n" 310 + " If no option is given, show current applied boot config.\n"); 311 + return -1; 312 + } 313 + 314 + int main(int argc, char **argv) 315 + { 316 + char *path = NULL; 317 + char *apply = NULL; 318 + bool delete = false; 319 + int opt; 320 + 321 + while ((opt = getopt(argc, argv, "hda:")) != -1) { 322 + switch (opt) { 323 + case 'd': 324 + delete = true; 325 + break; 326 + case 'a': 327 + apply = optarg; 328 + break; 329 + case 'h': 330 + default: 331 + return usage(); 332 + } 333 + } 334 + 335 + if (apply && delete) { 336 + printf("Error: You can not specify both -a and -d at once.\n"); 337 + return usage(); 338 + } 339 + 340 + if (optind >= argc) { 341 + printf("Error: No initrd is specified.\n"); 342 + return usage(); 343 + } 344 + 345 + path = argv[optind]; 346 + 347 + if (apply) 348 + return apply_xbc(path, apply); 349 + else if (delete) 350 + return delete_xbc(path); 351 + 352 + return show_xbc(path); 353 + }