From ca24306d83a125df187ad53eddb038fe0cffb8ca Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 2 Jun 2021 17:18:58 +0900 Subject: bootconfig: Change array value to use child node It is not possible to put an array value with subkeys under a key node, because both of subkeys and the array elements are using "next" field of the xbc_node. Thus this changes the array values to use "child" field in the array case. The reason why split this change is to test it easily. Link: https://lkml.kernel.org/r/162262193838.264090.16044473274501498656.stgit@devnote2 Signed-off-by: Masami Hiramatsu Signed-off-by: Steven Rostedt (VMware) --- include/linux/bootconfig.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux/bootconfig.h') diff --git a/include/linux/bootconfig.h b/include/linux/bootconfig.h index 2696eb0fc149..3178a31fdabc 100644 --- a/include/linux/bootconfig.h +++ b/include/linux/bootconfig.h @@ -71,7 +71,7 @@ static inline __init bool xbc_node_is_key(struct xbc_node *node) */ static inline __init bool xbc_node_is_array(struct xbc_node *node) { - return xbc_node_is_value(node) && node->next != 0; + return xbc_node_is_value(node) && node->child != 0; } /** @@ -140,7 +140,7 @@ static inline struct xbc_node * __init xbc_find_node(const char *key) */ #define xbc_array_for_each_value(anode, value) \ for (value = xbc_node_get_data(anode); anode != NULL ; \ - anode = xbc_node_get_next(anode), \ + anode = xbc_node_get_child(anode), \ value = anode ? xbc_node_get_data(anode) : NULL) /** @@ -171,7 +171,7 @@ static inline struct xbc_node * __init xbc_find_node(const char *key) */ #define xbc_node_for_each_array_value(node, key, anode, value) \ for (value = xbc_node_find_value(node, key, &anode); value != NULL; \ - anode = xbc_node_get_next(anode), \ + anode = xbc_node_get_child(anode), \ value = anode ? xbc_node_get_data(anode) : NULL) /** -- cgit v1.2.3 From e5efaeb8a8f527d6e91289ff1f67fbcae452b2ca Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 2 Jun 2021 17:19:07 +0900 Subject: bootconfig: Support mixing a value and subkeys under a key Support mixing a value and subkeys under a key. Since kernel cmdline options will support "aaa.bbb=value1 aaa.bbb.ccc=value2", it is better that the bootconfig supports such configuration too. Note that this does not change syntax itself but just accepts mixed value and subkeys e.g. key = value1 key.subkey = value2 But this is not accepted; key { value1 subkey = value2 } That will make value1 as a subkey. Also, the order of the value node under a key is fixed. If there are a value and subkeys, the value is always the first child node of the key. Thus if user specifies subkeys first, e.g. key.subkey = value1 key = value2 In the program (and /proc/bootconfig), it will be shown as below key = value2 key.subkey = value1 Link: https://lkml.kernel.org/r/162262194685.264090.7738574774030567419.stgit@devnote2 Signed-off-by: Masami Hiramatsu Signed-off-by: Steven Rostedt (VMware) --- include/linux/bootconfig.h | 32 +++++++++++++++++++++++ lib/bootconfig.c | 65 ++++++++++++++++++++++++++++++++-------------- tools/bootconfig/main.c | 45 ++++++++++++++++++++++++++------ 3 files changed, 114 insertions(+), 28 deletions(-) (limited to 'include/linux/bootconfig.h') diff --git a/include/linux/bootconfig.h b/include/linux/bootconfig.h index 3178a31fdabc..e49043ac77c9 100644 --- a/include/linux/bootconfig.h +++ b/include/linux/bootconfig.h @@ -80,6 +80,8 @@ static inline __init bool xbc_node_is_array(struct xbc_node *node) * * Test the @node is a leaf key node which is a key node and has a value node * or no child. Returns true if it is a leaf node, or false if not. + * Note that the leaf node can have subkey nodes in addition to the + * value node. */ static inline __init bool xbc_node_is_leaf(struct xbc_node *node) { @@ -129,6 +131,23 @@ static inline struct xbc_node * __init xbc_find_node(const char *key) return xbc_node_find_child(NULL, key); } +/** + * xbc_node_get_subkey() - Return the first subkey node if exists + * @node: Parent node + * + * Return the first subkey node of the @node. If the @node has no child + * or only value node, this will return NULL. + */ +static inline struct xbc_node * __init xbc_node_get_subkey(struct xbc_node *node) +{ + struct xbc_node *child = xbc_node_get_child(node); + + if (child && xbc_node_is_value(child)) + return xbc_node_get_next(child); + else + return child; +} + /** * xbc_array_for_each_value() - Iterate value nodes on an array * @anode: An XBC arraied value node @@ -149,11 +168,24 @@ static inline struct xbc_node * __init xbc_find_node(const char *key) * @child: Iterated XBC node. * * Iterate child nodes of @parent. Each child nodes are stored to @child. + * The @child can be mixture of a value node and subkey nodes. */ #define xbc_node_for_each_child(parent, child) \ for (child = xbc_node_get_child(parent); child != NULL ; \ child = xbc_node_get_next(child)) +/** + * xbc_node_for_each_subkey() - Iterate child subkey nodes + * @parent: An XBC node. + * @child: Iterated XBC node. + * + * Iterate subkey nodes of @parent. Each child nodes are stored to @child. + * The @child is only the subkey node. + */ +#define xbc_node_for_each_subkey(parent, child) \ + for (child = xbc_node_get_subkey(parent); child != NULL ; \ + child = xbc_node_get_next(child)) + /** * xbc_node_for_each_array_value() - Iterate array entries of geven key * @node: An XBC node. diff --git a/lib/bootconfig.c b/lib/bootconfig.c index 44dcdcbd746a..927017431fb6 100644 --- a/lib/bootconfig.c +++ b/lib/bootconfig.c @@ -156,7 +156,7 @@ xbc_node_find_child(struct xbc_node *parent, const char *key) struct xbc_node *node; if (parent) - node = xbc_node_get_child(parent); + node = xbc_node_get_subkey(parent); else node = xbc_root_node(); @@ -164,7 +164,7 @@ xbc_node_find_child(struct xbc_node *parent, const char *key) if (!xbc_node_match_prefix(node, &key)) node = xbc_node_get_next(node); else if (*key != '\0') - node = xbc_node_get_child(node); + node = xbc_node_get_subkey(node); else break; } @@ -274,6 +274,8 @@ int __init xbc_node_compose_key_after(struct xbc_node *root, struct xbc_node * __init xbc_node_find_next_leaf(struct xbc_node *root, struct xbc_node *node) { + struct xbc_node *next; + if (unlikely(!xbc_data)) return NULL; @@ -282,6 +284,13 @@ struct xbc_node * __init xbc_node_find_next_leaf(struct xbc_node *root, if (!node) node = xbc_nodes; } else { + /* Leaf node may have a subkey */ + next = xbc_node_get_subkey(node); + if (next) { + node = next; + goto found; + } + if (node == root) /* @root was a leaf, no child node. */ return NULL; @@ -296,6 +305,7 @@ struct xbc_node * __init xbc_node_find_next_leaf(struct xbc_node *root, node = xbc_node_get_next(node); } +found: while (node && !xbc_node_is_leaf(node)) node = xbc_node_get_child(node); @@ -375,18 +385,20 @@ static inline __init struct xbc_node *xbc_last_child(struct xbc_node *node) return node; } -static struct xbc_node * __init xbc_add_sibling(char *data, u32 flag) +static struct xbc_node * __init __xbc_add_sibling(char *data, u32 flag, bool head) { struct xbc_node *sib, *node = xbc_add_node(data, flag); if (node) { if (!last_parent) { + /* Ignore @head in this case */ node->parent = XBC_NODE_MAX; sib = xbc_last_sibling(xbc_nodes); sib->next = xbc_node_index(node); } else { node->parent = xbc_node_index(last_parent); - if (!last_parent->child) { + if (!last_parent->child || head) { + node->next = last_parent->child; last_parent->child = xbc_node_index(node); } else { sib = xbc_node_get_child(last_parent); @@ -400,6 +412,16 @@ static struct xbc_node * __init xbc_add_sibling(char *data, u32 flag) return node; } +static inline struct xbc_node * __init xbc_add_sibling(char *data, u32 flag) +{ + return __xbc_add_sibling(data, flag, false); +} + +static inline struct xbc_node * __init xbc_add_head_sibling(char *data, u32 flag) +{ + return __xbc_add_sibling(data, flag, true); +} + static inline __init struct xbc_node *xbc_add_child(char *data, u32 flag) { struct xbc_node *node = xbc_add_sibling(data, flag); @@ -568,8 +590,9 @@ static int __init __xbc_add_key(char *k) node = find_match_node(xbc_nodes, k); else { child = xbc_node_get_child(last_parent); + /* Since the value node is the first child, skip it. */ if (child && xbc_node_is_value(child)) - return xbc_parse_error("Subkey is mixed with value", k); + child = xbc_node_get_next(child); node = find_match_node(child, k); } @@ -612,27 +635,29 @@ static int __init xbc_parse_kv(char **k, char *v, int op) if (ret) return ret; - child = xbc_node_get_child(last_parent); - if (child) { - if (xbc_node_is_key(child)) - return xbc_parse_error("Value is mixed with subkey", v); - else if (op == '=') - return xbc_parse_error("Value is redefined", v); - } - c = __xbc_parse_value(&v, &next); if (c < 0) return c; - if (op == ':' && child) { - xbc_init_node(child, v, XBC_VALUE); - } else { - if (op == '+' && child) - last_parent = xbc_last_child(child); - if (!xbc_add_sibling(v, XBC_VALUE)) - return -ENOMEM; + child = xbc_node_get_child(last_parent); + if (child && xbc_node_is_value(child)) { + if (op == '=') + return xbc_parse_error("Value is redefined", v); + if (op == ':') { + unsigned short nidx = child->next; + + xbc_init_node(child, v, XBC_VALUE); + child->next = nidx; /* keep subkeys */ + goto array; + } + /* op must be '+' */ + last_parent = xbc_last_child(child); } + /* The value node should always be the first child */ + if (!xbc_add_head_sibling(v, XBC_VALUE)) + return -ENOMEM; +array: if (c == ',') { /* Array */ c = xbc_parse_array(&next); if (c < 0) diff --git a/tools/bootconfig/main.c b/tools/bootconfig/main.c index 23569fb634f1..62a3b5064b17 100644 --- a/tools/bootconfig/main.c +++ b/tools/bootconfig/main.c @@ -35,30 +35,55 @@ static int xbc_show_value(struct xbc_node *node, bool semicolon) static void xbc_show_compact_tree(void) { - struct xbc_node *node, *cnode; + struct xbc_node *node, *cnode = NULL, *vnode; int depth = 0, i; node = xbc_root_node(); while (node && xbc_node_is_key(node)) { for (i = 0; i < depth; i++) printf("\t"); - cnode = xbc_node_get_child(node); + if (!cnode) + cnode = xbc_node_get_child(node); while (cnode && xbc_node_is_key(cnode) && !cnode->next) { + vnode = xbc_node_get_child(cnode); + /* + * If @cnode has value and subkeys, this + * should show it as below. + * + * key(@node) { + * key(@cnode) = value; + * key(@cnode) { + * subkeys; + * } + * } + */ + if (vnode && xbc_node_is_value(vnode) && vnode->next) + break; printf("%s.", xbc_node_get_data(node)); node = cnode; - cnode = xbc_node_get_child(node); + cnode = vnode; } if (cnode && xbc_node_is_key(cnode)) { printf("%s {\n", xbc_node_get_data(node)); depth++; node = cnode; + cnode = NULL; continue; } else if (cnode && xbc_node_is_value(cnode)) { printf("%s = ", xbc_node_get_data(node)); xbc_show_value(cnode, true); + /* + * If @node has value and subkeys, continue + * looping on subkeys with same node. + */ + if (cnode->next) { + cnode = xbc_node_get_next(cnode); + continue; + } } else { printf("%s;\n", xbc_node_get_data(node)); } + cnode = NULL; if (node->next) { node = xbc_node_get_next(node); @@ -70,10 +95,12 @@ static void xbc_show_compact_tree(void) return; if (!xbc_node_get_child(node)->next) continue; - depth--; - for (i = 0; i < depth; i++) - printf("\t"); - printf("}\n"); + if (depth) { + depth--; + for (i = 0; i < depth; i++) + printf("\t"); + printf("}\n"); + } } node = xbc_node_get_next(node); } @@ -86,8 +113,10 @@ static void xbc_show_list(void) const char *val; xbc_for_each_key_value(leaf, val) { - if (xbc_node_compose_key(leaf, key, XBC_KEYLEN_MAX) < 0) + if (xbc_node_compose_key(leaf, key, XBC_KEYLEN_MAX) < 0) { + fprintf(stderr, "Failed to compose key %d\n", ret); break; + } printf("%s = ", key); if (!val || val[0] == '\0') { printf("\"\"\n"); -- cgit v1.2.3 From 99f4f5d62338cab9dcf45735344541574daedd20 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 2 Jun 2021 17:19:34 +0900 Subject: bootconfig: Share the checksum function with tools Move the checksum calculation function into the header for sharing it with tools/bootconfig. Link: https://lkml.kernel.org/r/162262197470.264090.16325743685807878807.stgit@devnote2 Signed-off-by: Masami Hiramatsu Signed-off-by: Steven Rostedt (VMware) --- include/linux/bootconfig.h | 20 ++++++++++++++++++++ init/main.c | 12 +----------- tools/bootconfig/main.c | 15 ++------------- 3 files changed, 23 insertions(+), 24 deletions(-) (limited to 'include/linux/bootconfig.h') diff --git a/include/linux/bootconfig.h b/include/linux/bootconfig.h index e49043ac77c9..6bdd94cff4e2 100644 --- a/include/linux/bootconfig.h +++ b/include/linux/bootconfig.h @@ -16,6 +16,26 @@ #define BOOTCONFIG_ALIGN (1 << BOOTCONFIG_ALIGN_SHIFT) #define BOOTCONFIG_ALIGN_MASK (BOOTCONFIG_ALIGN - 1) +/** + * xbc_calc_checksum() - Calculate checksum of bootconfig + * @data: Bootconfig data. + * @size: The size of the bootconfig data. + * + * Calculate the checksum value of the bootconfig data. + * The checksum will be used with the BOOTCONFIG_MAGIC and the size for + * embedding the bootconfig in the initrd image. + */ +static inline __init u32 xbc_calc_checksum(void *data, u32 size) +{ + unsigned char *p = data; + u32 ret = 0; + + while (size--) + ret += *p++; + + return ret; +} + /* XBC tree node */ struct xbc_node { u16 next; diff --git a/init/main.c b/init/main.c index e9c42a183e33..7b150f0501e2 100644 --- a/init/main.c +++ b/init/main.c @@ -386,16 +386,6 @@ static char * __init xbc_make_cmdline(const char *key) return new_cmdline; } -static u32 boot_config_checksum(unsigned char *p, u32 size) -{ - u32 ret = 0; - - while (size--) - ret += *p++; - - return ret; -} - static int __init bootconfig_params(char *param, char *val, const char *unused, void *arg) { @@ -439,7 +429,7 @@ static void __init setup_boot_config(void) return; } - if (boot_config_checksum((unsigned char *)data, size) != csum) { + if (xbc_calc_checksum(data, size) != csum) { pr_err("bootconfig checksum failed\n"); return; } diff --git a/tools/bootconfig/main.c b/tools/bootconfig/main.c index 62a3b5064b17..f45fa992e01d 100644 --- a/tools/bootconfig/main.c +++ b/tools/bootconfig/main.c @@ -126,17 +126,6 @@ static void xbc_show_list(void) } } -/* Simple real checksum */ -static int checksum(unsigned char *buf, int len) -{ - int i, sum = 0; - - for (i = 0; i < len; i++) - sum += buf[i]; - - return sum; -} - #define PAGE_SIZE 4096 static int load_xbc_fd(int fd, char **buf, int size) @@ -232,7 +221,7 @@ static int load_xbc_from_initrd(int fd, char **buf) return ret; /* Wrong Checksum */ - rcsum = checksum((unsigned char *)*buf, size); + rcsum = xbc_calc_checksum(*buf, size); if (csum != rcsum) { pr_err("checksum error: %d != %d\n", csum, rcsum); return -EINVAL; @@ -381,7 +370,7 @@ static int apply_xbc(const char *path, const char *xbc_path) return ret; } size = strlen(buf) + 1; - csum = checksum((unsigned char *)buf, size); + csum = xbc_calc_checksum(buf, size); /* Backup the bootconfig data */ data = calloc(size + BOOTCONFIG_ALIGN + -- cgit v1.2.3