162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Boot config tool for initrd image 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <stdio.h> 662306a36Sopenharmony_ci#include <stdlib.h> 762306a36Sopenharmony_ci#include <sys/types.h> 862306a36Sopenharmony_ci#include <sys/stat.h> 962306a36Sopenharmony_ci#include <fcntl.h> 1062306a36Sopenharmony_ci#include <unistd.h> 1162306a36Sopenharmony_ci#include <string.h> 1262306a36Sopenharmony_ci#include <errno.h> 1362306a36Sopenharmony_ci#include <endian.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/bootconfig.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define pr_err(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__) 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic int xbc_show_value(struct xbc_node *node, bool semicolon) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci const char *val, *eol; 2262306a36Sopenharmony_ci char q; 2362306a36Sopenharmony_ci int i = 0; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci eol = semicolon ? ";\n" : "\n"; 2662306a36Sopenharmony_ci xbc_array_for_each_value(node, val) { 2762306a36Sopenharmony_ci if (strchr(val, '"')) 2862306a36Sopenharmony_ci q = '\''; 2962306a36Sopenharmony_ci else 3062306a36Sopenharmony_ci q = '"'; 3162306a36Sopenharmony_ci printf("%c%s%c%s", q, val, q, xbc_node_is_array(node) ? ", " : eol); 3262306a36Sopenharmony_ci i++; 3362306a36Sopenharmony_ci } 3462306a36Sopenharmony_ci return i; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic void xbc_show_compact_tree(void) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct xbc_node *node, *cnode = NULL, *vnode; 4062306a36Sopenharmony_ci int depth = 0, i; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci node = xbc_root_node(); 4362306a36Sopenharmony_ci while (node && xbc_node_is_key(node)) { 4462306a36Sopenharmony_ci for (i = 0; i < depth; i++) 4562306a36Sopenharmony_ci printf("\t"); 4662306a36Sopenharmony_ci if (!cnode) 4762306a36Sopenharmony_ci cnode = xbc_node_get_child(node); 4862306a36Sopenharmony_ci while (cnode && xbc_node_is_key(cnode) && !cnode->next) { 4962306a36Sopenharmony_ci vnode = xbc_node_get_child(cnode); 5062306a36Sopenharmony_ci /* 5162306a36Sopenharmony_ci * If @cnode has value and subkeys, this 5262306a36Sopenharmony_ci * should show it as below. 5362306a36Sopenharmony_ci * 5462306a36Sopenharmony_ci * key(@node) { 5562306a36Sopenharmony_ci * key(@cnode) = value; 5662306a36Sopenharmony_ci * key(@cnode) { 5762306a36Sopenharmony_ci * subkeys; 5862306a36Sopenharmony_ci * } 5962306a36Sopenharmony_ci * } 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci if (vnode && xbc_node_is_value(vnode) && vnode->next) 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci printf("%s.", xbc_node_get_data(node)); 6462306a36Sopenharmony_ci node = cnode; 6562306a36Sopenharmony_ci cnode = vnode; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci if (cnode && xbc_node_is_key(cnode)) { 6862306a36Sopenharmony_ci printf("%s {\n", xbc_node_get_data(node)); 6962306a36Sopenharmony_ci depth++; 7062306a36Sopenharmony_ci node = cnode; 7162306a36Sopenharmony_ci cnode = NULL; 7262306a36Sopenharmony_ci continue; 7362306a36Sopenharmony_ci } else if (cnode && xbc_node_is_value(cnode)) { 7462306a36Sopenharmony_ci printf("%s = ", xbc_node_get_data(node)); 7562306a36Sopenharmony_ci xbc_show_value(cnode, true); 7662306a36Sopenharmony_ci /* 7762306a36Sopenharmony_ci * If @node has value and subkeys, continue 7862306a36Sopenharmony_ci * looping on subkeys with same node. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci if (cnode->next) { 8162306a36Sopenharmony_ci cnode = xbc_node_get_next(cnode); 8262306a36Sopenharmony_ci continue; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci } else { 8562306a36Sopenharmony_ci printf("%s;\n", xbc_node_get_data(node)); 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci cnode = NULL; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (node->next) { 9062306a36Sopenharmony_ci node = xbc_node_get_next(node); 9162306a36Sopenharmony_ci continue; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci while (!node->next) { 9462306a36Sopenharmony_ci node = xbc_node_get_parent(node); 9562306a36Sopenharmony_ci if (!node) 9662306a36Sopenharmony_ci return; 9762306a36Sopenharmony_ci if (!xbc_node_get_child(node)->next) 9862306a36Sopenharmony_ci continue; 9962306a36Sopenharmony_ci if (depth) { 10062306a36Sopenharmony_ci depth--; 10162306a36Sopenharmony_ci for (i = 0; i < depth; i++) 10262306a36Sopenharmony_ci printf("\t"); 10362306a36Sopenharmony_ci printf("}\n"); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci node = xbc_node_get_next(node); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void xbc_show_list(void) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci char key[XBC_KEYLEN_MAX]; 11362306a36Sopenharmony_ci struct xbc_node *leaf; 11462306a36Sopenharmony_ci const char *val; 11562306a36Sopenharmony_ci int ret; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci xbc_for_each_key_value(leaf, val) { 11862306a36Sopenharmony_ci ret = xbc_node_compose_key(leaf, key, XBC_KEYLEN_MAX); 11962306a36Sopenharmony_ci if (ret < 0) { 12062306a36Sopenharmony_ci fprintf(stderr, "Failed to compose key %d\n", ret); 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci printf("%s = ", key); 12462306a36Sopenharmony_ci if (!val || val[0] == '\0') { 12562306a36Sopenharmony_ci printf("\"\"\n"); 12662306a36Sopenharmony_ci continue; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci xbc_show_value(xbc_node_get_child(leaf), false); 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci#define PAGE_SIZE 4096 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic int load_xbc_fd(int fd, char **buf, int size) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci int ret; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci *buf = malloc(size + 1); 13962306a36Sopenharmony_ci if (!*buf) 14062306a36Sopenharmony_ci return -ENOMEM; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci ret = read(fd, *buf, size); 14362306a36Sopenharmony_ci if (ret < 0) 14462306a36Sopenharmony_ci return -errno; 14562306a36Sopenharmony_ci (*buf)[size] = '\0'; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return ret; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* Return the read size or -errno */ 15162306a36Sopenharmony_cistatic int load_xbc_file(const char *path, char **buf) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct stat stat; 15462306a36Sopenharmony_ci int fd, ret; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci fd = open(path, O_RDONLY); 15762306a36Sopenharmony_ci if (fd < 0) 15862306a36Sopenharmony_ci return -errno; 15962306a36Sopenharmony_ci ret = fstat(fd, &stat); 16062306a36Sopenharmony_ci if (ret < 0) 16162306a36Sopenharmony_ci return -errno; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci ret = load_xbc_fd(fd, buf, stat.st_size); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci close(fd); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return ret; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int pr_errno(const char *msg, int err) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci pr_err("%s: %d\n", msg, err); 17362306a36Sopenharmony_ci return err; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int load_xbc_from_initrd(int fd, char **buf) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct stat stat; 17962306a36Sopenharmony_ci int ret; 18062306a36Sopenharmony_ci uint32_t size = 0, csum = 0, rcsum; 18162306a36Sopenharmony_ci char magic[BOOTCONFIG_MAGIC_LEN]; 18262306a36Sopenharmony_ci const char *msg; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci ret = fstat(fd, &stat); 18562306a36Sopenharmony_ci if (ret < 0) 18662306a36Sopenharmony_ci return -errno; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (stat.st_size < 8 + BOOTCONFIG_MAGIC_LEN) 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (lseek(fd, -BOOTCONFIG_MAGIC_LEN, SEEK_END) < 0) 19262306a36Sopenharmony_ci return pr_errno("Failed to lseek for magic", -errno); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (read(fd, magic, BOOTCONFIG_MAGIC_LEN) < 0) 19562306a36Sopenharmony_ci return pr_errno("Failed to read", -errno); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* Check the bootconfig magic bytes */ 19862306a36Sopenharmony_ci if (memcmp(magic, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN) != 0) 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (lseek(fd, -(8 + BOOTCONFIG_MAGIC_LEN), SEEK_END) < 0) 20262306a36Sopenharmony_ci return pr_errno("Failed to lseek for size", -errno); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (read(fd, &size, sizeof(uint32_t)) < 0) 20562306a36Sopenharmony_ci return pr_errno("Failed to read size", -errno); 20662306a36Sopenharmony_ci size = le32toh(size); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (read(fd, &csum, sizeof(uint32_t)) < 0) 20962306a36Sopenharmony_ci return pr_errno("Failed to read checksum", -errno); 21062306a36Sopenharmony_ci csum = le32toh(csum); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Wrong size error */ 21362306a36Sopenharmony_ci if (stat.st_size < size + 8 + BOOTCONFIG_MAGIC_LEN) { 21462306a36Sopenharmony_ci pr_err("bootconfig size is too big\n"); 21562306a36Sopenharmony_ci return -E2BIG; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (lseek(fd, stat.st_size - (size + 8 + BOOTCONFIG_MAGIC_LEN), 21962306a36Sopenharmony_ci SEEK_SET) < 0) 22062306a36Sopenharmony_ci return pr_errno("Failed to lseek", -errno); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ret = load_xbc_fd(fd, buf, size); 22362306a36Sopenharmony_ci if (ret < 0) 22462306a36Sopenharmony_ci return ret; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* Wrong Checksum */ 22762306a36Sopenharmony_ci rcsum = xbc_calc_checksum(*buf, size); 22862306a36Sopenharmony_ci if (csum != rcsum) { 22962306a36Sopenharmony_ci pr_err("checksum error: %d != %d\n", csum, rcsum); 23062306a36Sopenharmony_ci return -EINVAL; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci ret = xbc_init(*buf, size, &msg, NULL); 23462306a36Sopenharmony_ci /* Wrong data */ 23562306a36Sopenharmony_ci if (ret < 0) { 23662306a36Sopenharmony_ci pr_err("parse error: %s.\n", msg); 23762306a36Sopenharmony_ci return ret; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return size; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic void show_xbc_error(const char *data, const char *msg, int pos) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci int lin = 1, col, i; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (pos < 0) { 24862306a36Sopenharmony_ci pr_err("Error: %s.\n", msg); 24962306a36Sopenharmony_ci return; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Note that pos starts from 0 but lin and col should start from 1. */ 25362306a36Sopenharmony_ci col = pos + 1; 25462306a36Sopenharmony_ci for (i = 0; i < pos; i++) { 25562306a36Sopenharmony_ci if (data[i] == '\n') { 25662306a36Sopenharmony_ci lin++; 25762306a36Sopenharmony_ci col = pos - i; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci pr_err("Parse Error: %s at %d:%d\n", msg, lin, col); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int init_xbc_with_error(char *buf, int len) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci char *copy = strdup(buf); 26762306a36Sopenharmony_ci const char *msg; 26862306a36Sopenharmony_ci int ret, pos; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (!copy) 27162306a36Sopenharmony_ci return -ENOMEM; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci ret = xbc_init(buf, len, &msg, &pos); 27462306a36Sopenharmony_ci if (ret < 0) 27562306a36Sopenharmony_ci show_xbc_error(copy, msg, pos); 27662306a36Sopenharmony_ci free(copy); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return ret; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int show_xbc(const char *path, bool list) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci int ret, fd; 28462306a36Sopenharmony_ci char *buf = NULL; 28562306a36Sopenharmony_ci struct stat st; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ret = stat(path, &st); 28862306a36Sopenharmony_ci if (ret < 0) { 28962306a36Sopenharmony_ci ret = -errno; 29062306a36Sopenharmony_ci pr_err("Failed to stat %s: %d\n", path, ret); 29162306a36Sopenharmony_ci return ret; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci fd = open(path, O_RDONLY); 29562306a36Sopenharmony_ci if (fd < 0) { 29662306a36Sopenharmony_ci ret = -errno; 29762306a36Sopenharmony_ci pr_err("Failed to open initrd %s: %d\n", path, ret); 29862306a36Sopenharmony_ci return ret; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci ret = load_xbc_from_initrd(fd, &buf); 30262306a36Sopenharmony_ci close(fd); 30362306a36Sopenharmony_ci if (ret < 0) { 30462306a36Sopenharmony_ci pr_err("Failed to load a boot config from initrd: %d\n", ret); 30562306a36Sopenharmony_ci goto out; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci /* Assume a bootconfig file if it is enough small */ 30862306a36Sopenharmony_ci if (ret == 0 && st.st_size <= XBC_DATA_MAX) { 30962306a36Sopenharmony_ci ret = load_xbc_file(path, &buf); 31062306a36Sopenharmony_ci if (ret < 0) { 31162306a36Sopenharmony_ci pr_err("Failed to load a boot config: %d\n", ret); 31262306a36Sopenharmony_ci goto out; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci if (init_xbc_with_error(buf, ret) < 0) 31562306a36Sopenharmony_ci goto out; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci if (list) 31862306a36Sopenharmony_ci xbc_show_list(); 31962306a36Sopenharmony_ci else 32062306a36Sopenharmony_ci xbc_show_compact_tree(); 32162306a36Sopenharmony_ci ret = 0; 32262306a36Sopenharmony_ciout: 32362306a36Sopenharmony_ci free(buf); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci return ret; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int delete_xbc(const char *path) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct stat stat; 33162306a36Sopenharmony_ci int ret = 0, fd, size; 33262306a36Sopenharmony_ci char *buf = NULL; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci fd = open(path, O_RDWR); 33562306a36Sopenharmony_ci if (fd < 0) { 33662306a36Sopenharmony_ci ret = -errno; 33762306a36Sopenharmony_ci pr_err("Failed to open initrd %s: %d\n", path, ret); 33862306a36Sopenharmony_ci return ret; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci size = load_xbc_from_initrd(fd, &buf); 34262306a36Sopenharmony_ci if (size < 0) { 34362306a36Sopenharmony_ci ret = size; 34462306a36Sopenharmony_ci pr_err("Failed to load a boot config from initrd: %d\n", ret); 34562306a36Sopenharmony_ci } else if (size > 0) { 34662306a36Sopenharmony_ci ret = fstat(fd, &stat); 34762306a36Sopenharmony_ci if (!ret) 34862306a36Sopenharmony_ci ret = ftruncate(fd, stat.st_size 34962306a36Sopenharmony_ci - size - 8 - BOOTCONFIG_MAGIC_LEN); 35062306a36Sopenharmony_ci if (ret) 35162306a36Sopenharmony_ci ret = -errno; 35262306a36Sopenharmony_ci } /* Ignore if there is no boot config in initrd */ 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci close(fd); 35562306a36Sopenharmony_ci free(buf); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return ret; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic int apply_xbc(const char *path, const char *xbc_path) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci char *buf, *data, *p; 36362306a36Sopenharmony_ci size_t total_size; 36462306a36Sopenharmony_ci struct stat stat; 36562306a36Sopenharmony_ci const char *msg; 36662306a36Sopenharmony_ci uint32_t size, csum; 36762306a36Sopenharmony_ci int pos, pad; 36862306a36Sopenharmony_ci int ret, fd; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci ret = load_xbc_file(xbc_path, &buf); 37162306a36Sopenharmony_ci if (ret < 0) { 37262306a36Sopenharmony_ci pr_err("Failed to load %s : %d\n", xbc_path, ret); 37362306a36Sopenharmony_ci return ret; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci size = strlen(buf) + 1; 37662306a36Sopenharmony_ci csum = xbc_calc_checksum(buf, size); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* Backup the bootconfig data */ 37962306a36Sopenharmony_ci data = calloc(size + BOOTCONFIG_ALIGN + 38062306a36Sopenharmony_ci sizeof(uint32_t) + sizeof(uint32_t) + BOOTCONFIG_MAGIC_LEN, 1); 38162306a36Sopenharmony_ci if (!data) 38262306a36Sopenharmony_ci return -ENOMEM; 38362306a36Sopenharmony_ci memcpy(data, buf, size); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* Check the data format */ 38662306a36Sopenharmony_ci ret = xbc_init(buf, size, &msg, &pos); 38762306a36Sopenharmony_ci if (ret < 0) { 38862306a36Sopenharmony_ci show_xbc_error(data, msg, pos); 38962306a36Sopenharmony_ci free(data); 39062306a36Sopenharmony_ci free(buf); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci return ret; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci printf("Apply %s to %s\n", xbc_path, path); 39562306a36Sopenharmony_ci xbc_get_info(&ret, NULL); 39662306a36Sopenharmony_ci printf("\tNumber of nodes: %d\n", ret); 39762306a36Sopenharmony_ci printf("\tSize: %u bytes\n", (unsigned int)size); 39862306a36Sopenharmony_ci printf("\tChecksum: %d\n", (unsigned int)csum); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* TODO: Check the options by schema */ 40162306a36Sopenharmony_ci xbc_exit(); 40262306a36Sopenharmony_ci free(buf); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* Remove old boot config if exists */ 40562306a36Sopenharmony_ci ret = delete_xbc(path); 40662306a36Sopenharmony_ci if (ret < 0) { 40762306a36Sopenharmony_ci pr_err("Failed to delete previous boot config: %d\n", ret); 40862306a36Sopenharmony_ci free(data); 40962306a36Sopenharmony_ci return ret; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* Apply new one */ 41362306a36Sopenharmony_ci fd = open(path, O_RDWR | O_APPEND); 41462306a36Sopenharmony_ci if (fd < 0) { 41562306a36Sopenharmony_ci ret = -errno; 41662306a36Sopenharmony_ci pr_err("Failed to open %s: %d\n", path, ret); 41762306a36Sopenharmony_ci free(data); 41862306a36Sopenharmony_ci return ret; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci /* TODO: Ensure the @path is initramfs/initrd image */ 42162306a36Sopenharmony_ci if (fstat(fd, &stat) < 0) { 42262306a36Sopenharmony_ci ret = -errno; 42362306a36Sopenharmony_ci pr_err("Failed to get the size of %s\n", path); 42462306a36Sopenharmony_ci goto out; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* To align up the total size to BOOTCONFIG_ALIGN, get padding size */ 42862306a36Sopenharmony_ci total_size = stat.st_size + size + sizeof(uint32_t) * 2 + BOOTCONFIG_MAGIC_LEN; 42962306a36Sopenharmony_ci pad = ((total_size + BOOTCONFIG_ALIGN - 1) & (~BOOTCONFIG_ALIGN_MASK)) - total_size; 43062306a36Sopenharmony_ci size += pad; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* Add a footer */ 43362306a36Sopenharmony_ci p = data + size; 43462306a36Sopenharmony_ci *(uint32_t *)p = htole32(size); 43562306a36Sopenharmony_ci p += sizeof(uint32_t); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci *(uint32_t *)p = htole32(csum); 43862306a36Sopenharmony_ci p += sizeof(uint32_t); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci memcpy(p, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN); 44162306a36Sopenharmony_ci p += BOOTCONFIG_MAGIC_LEN; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci total_size = p - data; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci ret = write(fd, data, total_size); 44662306a36Sopenharmony_ci if (ret < total_size) { 44762306a36Sopenharmony_ci if (ret < 0) 44862306a36Sopenharmony_ci ret = -errno; 44962306a36Sopenharmony_ci pr_err("Failed to apply a boot config: %d\n", ret); 45062306a36Sopenharmony_ci if (ret >= 0) 45162306a36Sopenharmony_ci goto out_rollback; 45262306a36Sopenharmony_ci } else 45362306a36Sopenharmony_ci ret = 0; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ciout: 45662306a36Sopenharmony_ci close(fd); 45762306a36Sopenharmony_ci free(data); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci return ret; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ciout_rollback: 46262306a36Sopenharmony_ci /* Map the partial write to -ENOSPC */ 46362306a36Sopenharmony_ci if (ret >= 0) 46462306a36Sopenharmony_ci ret = -ENOSPC; 46562306a36Sopenharmony_ci if (ftruncate(fd, stat.st_size) < 0) { 46662306a36Sopenharmony_ci ret = -errno; 46762306a36Sopenharmony_ci pr_err("Failed to rollback the write error: %d\n", ret); 46862306a36Sopenharmony_ci pr_err("The initrd %s may be corrupted. Recommend to rebuild.\n", path); 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci goto out; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic int usage(void) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci printf("Usage: bootconfig [OPTIONS] <INITRD>\n" 47662306a36Sopenharmony_ci "Or bootconfig <CONFIG>\n" 47762306a36Sopenharmony_ci " Apply, delete or show boot config to initrd.\n" 47862306a36Sopenharmony_ci " Options:\n" 47962306a36Sopenharmony_ci " -a <config>: Apply boot config to initrd\n" 48062306a36Sopenharmony_ci " -d : Delete boot config file from initrd\n" 48162306a36Sopenharmony_ci " -l : list boot config in initrd or file\n\n" 48262306a36Sopenharmony_ci " If no option is given, show the bootconfig in the given file.\n"); 48362306a36Sopenharmony_ci return -1; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ciint main(int argc, char **argv) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci char *path = NULL; 48962306a36Sopenharmony_ci char *apply = NULL; 49062306a36Sopenharmony_ci bool delete = false, list = false; 49162306a36Sopenharmony_ci int opt; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci while ((opt = getopt(argc, argv, "hda:l")) != -1) { 49462306a36Sopenharmony_ci switch (opt) { 49562306a36Sopenharmony_ci case 'd': 49662306a36Sopenharmony_ci delete = true; 49762306a36Sopenharmony_ci break; 49862306a36Sopenharmony_ci case 'a': 49962306a36Sopenharmony_ci apply = optarg; 50062306a36Sopenharmony_ci break; 50162306a36Sopenharmony_ci case 'l': 50262306a36Sopenharmony_ci list = true; 50362306a36Sopenharmony_ci break; 50462306a36Sopenharmony_ci case 'h': 50562306a36Sopenharmony_ci default: 50662306a36Sopenharmony_ci return usage(); 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if ((apply && delete) || (delete && list) || (apply && list)) { 51162306a36Sopenharmony_ci pr_err("Error: You can give one of -a, -d or -l at once.\n"); 51262306a36Sopenharmony_ci return usage(); 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (optind >= argc) { 51662306a36Sopenharmony_ci pr_err("Error: No initrd is specified.\n"); 51762306a36Sopenharmony_ci return usage(); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci path = argv[optind]; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (apply) 52362306a36Sopenharmony_ci return apply_xbc(path, apply); 52462306a36Sopenharmony_ci else if (delete) 52562306a36Sopenharmony_ci return delete_xbc(path); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return show_xbc(path, list); 52862306a36Sopenharmony_ci} 529