启动配置¶
- 作者:
Masami Hiramatsu <mhiramat@kernel.org>
概述¶
启动配置扩展了当前的内核命令行,以支持在高效启动内核时使用其他键值数据。这允许管理员传递结构化的键配置文件。
配置文件语法¶
启动配置语法是一个简单的结构化键值对。每个键由点连接的单词组成,键和值通过 =
连接。值必须以分号 (;
) 或换行符 (\n
) 结尾。对于数组值,数组条目用逗号 (,
) 分隔。
KEY[.WORD[...]] = VALUE[, VALUE2[...]][;]
与内核命令行语法不同,逗号和 =
周围允许有空格。
每个键字只能包含字母、数字、短划线 (-
) 或下划线 (_
)。并且每个值只能包含可打印字符或空格,但分隔符(如分号 (;
)、换行符 (\n
)、逗号 (,
)、井号 (#
) 和右大括号 (}
))除外。
如果想在值中使用这些分隔符,可以使用双引号 ("VALUE"
) 或单引号 ('VALUE'
) 将其引起来。 请注意,您不能转义这些引号。
可以存在没有值或有空值的键。这些键用于检查键是否存在(类似于布尔值)。
键值语法¶
引导配置文件语法允许用户通过大括号合并部分相同的单词键。 例如
foo.bar.baz = value1
foo.bar.qux.quux = value2
这些也可以写成
foo.bar {
baz = value1
qux.quux = value2
}
或者更短,写成如下
foo.bar { baz = value1; qux.quux = value2 }
在这两种样式中,在启动时解析时,相同的键字会自动合并。 因此,您可以附加相似的树或键值。
相同键值¶
禁止两个或多个值或数组共享相同的键。例如,
foo = bar, baz
foo = qux # !ERROR! we can not re-define same key
如果要更新该值,必须显式使用覆盖运算符 :=
。例如
foo = bar, baz
foo := qux
然后,qux
被分配给 foo
键。这对于通过添加(部分)自定义引导配置来覆盖默认值而无需解析默认引导配置很有用。
如果想将值作为数组成员附加到现有键,可以使用 +=
运算符。 例如
foo = bar, baz
foo += qux
在这种情况下,键 foo
具有 bar
、baz
和 qux
。
此外,子键和值可以共存于父键下。例如,允许以下配置。
foo = value1
foo.bar = value2
foo := value3 # This will update foo's value.
请注意,由于没有语法将原始值直接放在结构化的键下,因此您必须在大括号外定义它。例如
foo {
bar = value1
bar {
baz = value2
qux = value3
}
}
此外,键下的值节点的顺序是固定的。如果有值和子键,则该值始终是键的第一个子节点。因此,如果用户首先指定子键,例如
foo.bar = value1
foo = value2
在程序中(和 /proc/bootconfig 中),它将显示如下
foo = value2
foo.bar = value1
/proc/bootconfig¶
/proc/bootconfig 是引导配置的用户空间接口。 与 /proc/cmdline 不同,此文件显示键值样式列表。每个键值对都以以下样式显示在每一行中
KEY[.WORDS...] = "[VALUE]"[,"VALUE2"...]
使用引导配置启动内核¶
有两种选项可以使用引导配置启动内核:将引导配置附加到 initrd 映像或将其嵌入到内核本身。
将引导配置附加到 Initrd¶
由于引导配置文件默认通过 initrd 加载,因此它将被添加到 initrd (initramfs) 映像文件的末尾,其中包含填充、大小、校验和和 12 字节的魔术字,如下所示。
[initrd][引导配置][填充][大小(le32)][校验和(le32)][#BOOTCONFIGn]
大小和校验和字段是无符号 32 位小端值。
当引导配置添加到 initrd 映像时,总文件大小将对齐到 4 个字节。为了填充间隙,将添加空字符 (\0
)。因此,size
是引导配置文件 + 填充字节的长度。
Linux 内核解码内存中 initrd 映像的最后一部分以获取引导配置数据。 由于这种“搭载”方法,只要引导加载程序传递正确的 initrd 文件大小,就不需要更改或更新引导加载程序和内核映像本身。 如果引导加载程序碰巧传递了更长的尺寸,内核将无法找到引导配置数据。
为了执行此操作,Linux 内核在 tools/bootconfig 下提供了 bootconfig
命令,该命令允许管理员将配置文件应用或删除到/从 initrd 映像。您可以通过以下命令构建它
# make -C tools/bootconfig
要将您的引导配置文件添加到 initrd 映像,请运行 bootconfig 如下 (如果存在旧数据,将自动删除)
# tools/bootconfig/bootconfig -a your-config /boot/initrd.img-X.Y.Z
要从镜像中移除配置,可以使用 -d 选项,如下所示:
# tools/bootconfig/bootconfig -d /boot/initrd.img-X.Y.Z
然后在正常的内核命令行中添加 “bootconfig”,告诉内核在 initrd 文件的末尾查找引导配置。或者,构建内核时选择 CONFIG_BOOT_CONFIG_FORCE
Kconfig 选项。
将引导配置嵌入到内核中¶
如果您无法使用 initrd,您也可以通过 Kconfig 选项将引导配置文件嵌入到内核中。在这种情况下,您需要使用以下配置重新编译内核:
CONFIG_BOOT_CONFIG_EMBED=y
CONFIG_BOOT_CONFIG_EMBED_FILE="/PATH/TO/BOOTCONFIG/FILE"
CONFIG_BOOT_CONFIG_EMBED_FILE
需要一个绝对路径或相对于源代码树或对象树的引导配置文件的相对路径。内核将把它作为默认的引导配置嵌入。
正如将引导配置附加到 initrd 时一样,您需要在内核命令行中使用 bootconfig
选项来启用嵌入的引导配置,或者,也可以通过选择 CONFIG_BOOT_CONFIG_FORCE
Kconfig 选项来构建内核。
请注意,即使您设置了此选项,您也可以通过附加到 initrd 的另一个引导配置来覆盖嵌入的引导配置。
通过引导配置传递内核参数¶
除了内核命令行之外,引导配置还可以用于传递内核参数。 kernel
键下的所有键值对将直接传递给内核命令行。此外,init
键下的键值对将通过命令行传递给 init 进程。这些参数将按照以下顺序与用户给定的内核命令行字符串连接,以便命令行参数可以覆盖引导配置参数(这取决于子系统如何处理参数,但通常情况下,较早的参数会被较晚的参数覆盖)。
[bootconfig params][cmdline params] -- [bootconfig init params][cmdline init params]
以下是用于内核/init 参数的引导配置文件的示例:
kernel {
root = 01234567-89ab-cdef-0123-456789abcd
}
init {
splash
}
这将复制到内核命令行字符串中,如下所示:
root="01234567-89ab-cdef-0123-456789abcd" -- splash
如果用户给出了其他命令行,例如:
ro bootconfig -- quiet
最终的内核命令行将如下所示:
root="01234567-89ab-cdef-0123-456789abcd" ro bootconfig -- splash quiet
配置文件限制¶
目前,最大配置大小为 32KB,且总关键字(非键值条目)必须小于 1024 个节点。注意:这不是条目的数量,而是节点数,一个条目必须消耗 2 个以上的节点(一个关键字和一个值)。因此,理论上,它可以容纳多达 512 个键值对。如果键平均包含 3 个单词,则它可以包含 256 个键值对。在大多数情况下,配置项的数量将少于 100 个条目,并且小于 8KB,因此应该足够了。如果节点数超过 1024 个,则即使文件大小小于 32KB,解析器也会返回错误。(请注意,此最大大小不包括填充的空字符。)无论如何,由于引导配置命令在将引导配置附加到 initrd 镜像时会验证它,因此用户可以在引导之前注意到它。
引导配置 API¶
用户可以查询或循环访问键值对,也可以查找根(前缀)键节点,并查找该节点下的键值。
如果您有一个键字符串,可以使用 xbc_find_value()
直接使用该键查询值。如果您想知道引导配置中存在哪些键,可以使用 xbc_for_each_key_value()
迭代键值对。请注意,您需要使用 xbc_array_for_each_value()
来访问每个数组的值,例如:
vnode = NULL;
xbc_find_value("key.word", &vnode);
if (vnode && xbc_node_is_array(vnode))
xbc_array_for_each_value(vnode, value) {
printk("%s ", value);
}
如果您想关注具有前缀字符串的键,可以使用 xbc_find_node()
根据前缀字符串查找节点,并使用 xbc_node_for_each_key_value()
迭代前缀节点下的键。
但是最典型的用法是获取前缀下的命名值或获取前缀下的命名数组,如下所示:
root = xbc_find_node("key.prefix");
value = xbc_node_find_value(root, "option", &vnode);
...
xbc_node_for_each_array_value(root, "array-option", value, anode) {
...
}
这将访问“key.prefix.option”的值和“key.prefix.array-option”的数组。
不需要锁定,因为在初始化之后,配置将变为只读。如果您需要修改它,则必须复制所有数据和键。
函数和结构体¶
-
uint32_t xbc_calc_checksum(void *data, uint32_t size)¶
计算引导配置的校验和
参数
void *data
引导配置数据。
uint32_t size
引导配置数据的大小。
描述
计算引导配置数据的校验和值。校验和将与 BOOTCONFIG_MAGIC 和大小一起使用,用于将引导配置嵌入到 initrd 镜像中。
-
bool xbc_node_is_value(struct xbc_node *node)¶
测试节点是否是值节点
参数
struct xbc_node *node
一个 XBC 节点。
描述
测试 node 是否是值节点,如果是值节点则返回 true,否则返回 false。
-
bool xbc_node_is_key(struct xbc_node *node)¶
测试节点是否是键节点
参数
struct xbc_node *node
一个 XBC 节点。
描述
测试 node 是否是键节点,如果是键节点则返回 true,否则返回 false。
-
bool xbc_node_is_array(struct xbc_node *node)¶
测试节点是否是数组值节点
参数
struct xbc_node *node
一个 XBC 节点。
描述
测试 node 是否是数组值节点。
-
bool xbc_node_is_leaf(struct xbc_node *node)¶
测试节点是否是叶键节点
参数
struct xbc_node *node
一个 XBC 节点。
描述
测试 node 是否是叶键节点,该节点是键节点并且具有值节点或没有子节点。如果它是叶节点则返回 true,否则返回 false。请注意,除了值节点之外,叶节点还可以具有子键节点。
-
const char *xbc_find_value(const char *key, struct xbc_node **vnode)¶
查找与键匹配的值
参数
const char *key
搜索键
struct xbc_node **vnode
XBC 值节点的容器指针。
描述
从整个 XBC 树中搜索其键与 key 匹配的值,如果找到则返回该值。找到的值节点存储在 *vnode 中。请注意,对于仅包含键(非值)的条目,这可以返回 0 长度的字符串并在 *vnode 中存储 NULL。
-
struct xbc_node *xbc_find_node(const char *key)¶
查找与键匹配的节点
参数
const char *key
搜索键
描述
从整个 XBC 树中搜索其键与 key 匹配的(键)节点,如果找到则返回该节点。如果未找到,则返回 NULL。
-
struct xbc_node *xbc_node_get_subkey(struct xbc_node *node)¶
如果存在,则返回第一个子键节点
参数
struct xbc_node *node
父节点
描述
返回 node 的第一个子键节点。如果 node 没有子节点或只有值节点,则返回 NULL。
-
xbc_array_for_each_value¶
xbc_array_for_each_value (anode, value)
迭代数组上的值节点
参数
anode
一个 XBC 数组值节点
value
一个值
描述
从 anode 开始迭代数组值节点和值。这通常与 xbc_find_value()
和 xbc_node_find_value()
一起使用,以便用户可以处理每个数组条目节点。
-
xbc_node_for_each_child¶
xbc_node_for_each_child (parent, child)
迭代子节点
参数
父节点
一个 XBC 节点。
子节点
迭代的 XBC 节点。
描述
迭代 parent 的子节点。每个子节点都存储到 child。child 可以是值节点和子键节点的混合。
-
xbc_node_for_each_subkey¶
xbc_node_for_each_subkey (parent, child)
迭代子键节点
参数
父节点
一个 XBC 节点。
子节点
迭代的 XBC 节点。
描述
迭代 parent 的子键节点。每个子节点都存储到 child。child 仅是子键节点。
-
xbc_node_for_each_array_value¶
xbc_node_for_each_array_value (node, key, anode, value)
迭代给定键的数组条目
参数
节点
一个 XBC 节点。
键
在 node 下搜索的键字符串
anode
迭代的数组条目的 XBC 节点。
value
迭代的数组条目的值。
描述
迭代 node 下给定 key 的数组条目。每个数组条目节点都存储到 anode 和 value。如果 node 没有 key 节点,则不执行任何操作。请注意,即使找到的键节点只有一个值(不是数组),此块也会执行一次。但是,如果找到的键节点没有值(仅键节点),则不执行任何操作。因此,请不要使用它来测试键值对是否存在。
-
xbc_node_for_each_key_value¶
xbc_node_for_each_key_value (node, knode, value)
迭代节点下的键值对
参数
节点
一个 XBC 节点。
键节点
迭代的键节点
value
迭代的值字符串
描述
迭代 node 下的键值对。每个键节点和值字符串分别存储在 knode 和 value 中。
-
xbc_for_each_key_value¶
xbc_for_each_key_value (knode, value)
迭代键值对
参数
键节点
迭代的键节点
value
迭代的值字符串
描述
迭代整个 XBC 树中的键值对。每个键节点和值字符串分别存储在 knode 和 value 中。
-
int xbc_node_compose_key(struct xbc_node *node, char *buf, size_t size)¶
组合 XBC 节点的完整键字符串
参数
struct xbc_node *node
一个 XBC 节点。
char *buf
用于存储键的缓冲区。
size_t size
buf 的大小。
描述
将 node 的完整长度的键组合到 buf 中。返回存储在 buf 中的键的总长度。如果 node 为 NULL,则返回 -EINVAL;如果键深度大于最大深度,则返回 -ERANGE。
-
int xbc_get_info(int *node_size, size_t *data_size)¶
获取已加载的引导配置的信息
参数
int *node_size
用于存储节点数的指针。
size_t *data_size
用于存储引导配置数据大小的指针。
描述
如果 node_size 不为 NULL,则在其中获取已使用的节点数;如果 data_size 不为 NULL,则在其中获取引导配置数据的大小。如果引导配置已初始化,则返回 0,否则返回 -ENODEV。
-
struct xbc_node *xbc_root_node(void)¶
获取扩展引导配置的根节点
参数
void
无参数
描述
返回扩展引导配置的根节点的地址。如果扩展引导配置未初始化,则返回 NULL。
-
int xbc_node_index(struct xbc_node *node)¶
获取 XBC 节点的索引
参数
struct xbc_node *node
要获取索引的目标节点。
描述
返回 XBC 节点列表中 node 的索引号。
-
struct xbc_node *xbc_node_get_parent(struct xbc_node *node)¶
获取父 XBC 节点
参数
struct xbc_node *node
一个 XBC 节点。
描述
返回 node 的父节点。如果该节点是树的顶部节点,则返回 NULL。
-
struct xbc_node *xbc_node_get_child(struct xbc_node *node)¶
获取子 XBC 节点
参数
struct xbc_node *node
一个 XBC 节点。
描述
返回 node 的第一个子节点。如果该节点没有子节点,则返回 NULL。
-
struct xbc_node *xbc_node_get_next(struct xbc_node *node)¶
获取下一个兄弟 XBC 节点
参数
struct xbc_node *node
一个 XBC 节点。
描述
返回 node 的下一个兄弟节点。如果该节点没有下一个兄弟节点,则返回 NULL。请注意,即使返回 NULL,也并不意味着 node 没有兄弟节点。(您还必须检查父节点的子节点是否为 node。)
-
const char *xbc_node_get_data(struct xbc_node *node)¶
获取 XBC 节点的数据
参数
struct xbc_node *node
一个 XBC 节点。
描述
返回 node 的数据(始终是空终止的字符串)。如果该节点的数据无效,则发出警告并返回 NULL。
-
struct xbc_node *xbc_node_find_subkey(struct xbc_node *parent, const char *key)¶
查找与给定键匹配的子键节点
参数
struct xbc_node *parent
一个 XBC 节点。
const char *key
键字符串。
描述
在 parent 下搜索与 key 匹配的键节点。key 可以包含多个用“.”连接的单词。如果 parent 为 NULL,则从整个树中搜索节点。如果没有匹配的节点,则返回 NULL。
-
const char *xbc_node_find_value(struct xbc_node *parent, const char *key, struct xbc_node **vnode)¶
查找与给定键匹配的值节点
参数
struct xbc_node *parent
一个 XBC 节点。
const char *key
键字符串。
struct xbc_node **vnode
找到的 XBC 节点的容器指针。
描述
在 parent 下搜索其(父)键节点与 key 匹配的值节点,将其存储在 *vnode 中,并返回该值字符串。key 可以包含用 ‘.’ 连接的多个单词。如果 parent 为 NULL,则从整个树中搜索该节点。如果找到匹配的键,则返回该值字符串;如果没有找到匹配的节点,则返回 NULL。请注意,如果该键没有值,则返回长度为 0 的字符串并将 NULL 存储在 *vnode 中。并且,如果该值是一个数组,则它将返回第一个条目的值。
-
int xbc_node_compose_key_after(struct xbc_node *root, struct xbc_node *node, char *buf, size_t size)¶
组合 XBC 节点的局部键字符串
参数
struct xbc_node *root
根 XBC 节点
struct xbc_node *node
目标 XBC 节点。
char *buf
用于存储键的缓冲区。
size_t size
buf 的大小。
描述
将 node 的局部键组合到 buf 中,该键从 root 之后开始(不包括 root)。如果 root 为 NULL,则返回 node 的完整键字。返回存储在 buf 中的键的总长度。如果 node 为 NULL,或者 root 不是 node 的祖先,或者 root 是 node,则返回 -EINVAL;如果键深度大于最大深度,则返回 -ERANGE。这预期与 xbc_find_node()
一起使用,以列出给定键下的所有(子)键。
-
struct xbc_node *xbc_node_find_next_leaf(struct xbc_node *root, struct xbc_node *node)¶
查找给定节点下的下一个叶节点
参数
struct xbc_node *root
一个 XBC 根节点
struct xbc_node *node
一个开始于此的 XBC 节点。
描述
在 root 节点下搜索 node 的下一个叶节点(这意味着终端键节点)(包括 root 节点本身)。如果未找到下一个叶节点,则返回下一个节点,否则返回 NULL。
-
const char *xbc_node_find_next_key_value(struct xbc_node *root, struct xbc_node **leaf)¶
查找下一个键值对节点
参数
struct xbc_node *root
一个 XBC 根节点
struct xbc_node **leaf
一个 XBC 节点的容器指针,从此处开始。
描述
在 root 节点下搜索 *leaf 的下一个叶节点(这意味着终端键节点)。如果找到下一个叶节点,则返回该值并更新 *leaf;如果未找到下一个叶节点,则返回 NULL。请注意,如果该键没有值,则返回长度为 0 的字符串;如果该值是一个数组,则返回第一个条目的值。
-
void _xbc_exit(bool early)¶
清除所有已解析的启动配置
参数
bool early
如果这是在 budy 系统初始化之前调用的,则设置为 true。
描述
这将清除内存中所有已解析的启动配置的数据结构。如果需要使用新的启动配置重复使用 xbc_init()
,则可以使用此函数。
-
int xbc_init(const char *data, size_t size, const char **emsg, int *epos)¶
解析给定的 XBC 文件并构建 XBC 内部树
参数
const char *data
启动配置文本的原始数据
size_t size
data 的大小
const char **emsg
用于存储错误消息的 const char * 的指针
int *epos
用于存储错误位置的 int 的指针
描述
这将解析 data 中的启动配置文本。size 必须小于 XBC_DATA_MAX。如果成功,则返回存储的节点数(>0);如果出现任何错误,则返回 -errno。在错误情况下,emsg 将使用错误消息更新,而 epos 将使用错误位置(即 buf 的字节偏移量)更新。如果错误不是解析器错误,则 epos 将为 -1。
注释¶
配置语法接受 shell 脚本样式的注释。从井号(“#”)开始到换行符(“n”)的注释将被忽略。
这被解析为如下所示
请注意,您不能在值和分隔符(
,
或;
)之间放置注释。这意味着以下配置存在语法错误