使用设备树时将内核参数从 U-Boot 传递到 ARM Linux
Passing kernel params from U-Boot to ARM Linux when device tree is used
我一直在研究对一些嵌入式软件的更改,因为我们希望 U-Boot 能够将特定的命令行参数传递给内核,这些参数事先不一定知道。
这样内核就可以告诉它是由哪个 U-Boot分区启动的(我们有两个副本,一个在/dev/mmc3boot0
和/dev/mmc3boot1
,它们都共享一个(冗余)环境 space,所以我们不能单独使用它来唯一标识实例)。
一个想法是简单地让每个 U-Boot 在启动时将其 ID 写入共享环境,但缺点是目前有一些变体不这样做。因此,如果我们从一个有 ID 的设备启动,它会写入其 ID,如果我们从一个没有的设备启动,它不会 将 ID 更改 回空白,从而导致如果我们依赖该信息,则信息不正确。
这就是我们考虑使用内核参数的原因 - 因为旧式 U-Boot 实例从不提供 ID,所以我们知道它在 boot0
中是 运行。较新的样式会提供它们的实际 ID,这样我们就可以搜索两个 boot
分区以查看它在哪个分区中。
为此,我修改了 U-Boot,以便它设置 ATAG 以传递所需的额外参数。具体来说:
- 我在
arch\arm\include\asm\config.h
中定义了 CONFIG_SYS_BOOT_GET_CMDLINE
以便调用 boot_get_cmdline()
。
- 我修改了
boot_get_cmdline()
函数,以便它在 附加正常参数之前设置一个特定参数 。换句话说,我们现在得到 uboot_instance=42 plugh=xyzzy
. 而不是 plugh=xyzzy
这一切都编译得很好,U-Boot 成功启动了内核,但是额外的信息没有反映在 Linux 内核中,它的内核参数设置为常规的 plugh=xyzzy
。
经过进一步研究,我们似乎与调用内核的两种可能方式发生了冲突。其中一个是 ATAG,一个是扁平设备树 (FDT),它们似乎是互斥的(内核启动代码根据传入的签名选择一个或另一个,指针引用 ATAG 或 FDT 结构).
所以我的问题是这样的。鉴于设备树是您描述的设备的 fixed 结构,您如何传递 arbitrary 内核命令行参数(在运行时计算)当引导加载程序调用内核时?
您可以在 include/configs/<board>.h
中为您的平台使用虚拟环境变量。
例如,假设您有以下(简化的)UBoot 环境变量用于引导:
bootcmd=run mmcargs
run loadimage loadfdt
bootz ${loadaddr} - ${fdt_addr}
mmcargs=setenv bootargs blah=blah
这使用mmcargs
设置要使用的内核命令行。我们需要做的是以当前 UBoot 实例不提供任何内容而新实例提供实际 ID 的方式插入该虚拟环境变量。这只需通过以下更改即可完成:
mmcargs=setenv bootargs ${uboot_id_stanza} blah=blah
然后,在电路板初始化期间,您可以使用 env_set
API 设置此变量,具体是通过在 board/<vendor>/<init_code>.c
.
以下行应放在 board_late_init
函数的末尾:
setenv("uboot_id_stanza", "uboot_id=<uniqueId>");
那样,uboot_id
变量设置被添加到内核命令行,但是,由于您没有执行 saveenv
,它不会变得持久。每个 UBoot 实例都会设置正确的 ID(包括那些不设置 ID 的旧实例)。
我一直在研究对一些嵌入式软件的更改,因为我们希望 U-Boot 能够将特定的命令行参数传递给内核,这些参数事先不一定知道。
这样内核就可以告诉它是由哪个 U-Boot分区启动的(我们有两个副本,一个在/dev/mmc3boot0
和/dev/mmc3boot1
,它们都共享一个(冗余)环境 space,所以我们不能单独使用它来唯一标识实例)。
一个想法是简单地让每个 U-Boot 在启动时将其 ID 写入共享环境,但缺点是目前有一些变体不这样做。因此,如果我们从一个有 ID 的设备启动,它会写入其 ID,如果我们从一个没有的设备启动,它不会 将 ID 更改 回空白,从而导致如果我们依赖该信息,则信息不正确。
这就是我们考虑使用内核参数的原因 - 因为旧式 U-Boot 实例从不提供 ID,所以我们知道它在 boot0
中是 运行。较新的样式会提供它们的实际 ID,这样我们就可以搜索两个 boot
分区以查看它在哪个分区中。
为此,我修改了 U-Boot,以便它设置 ATAG 以传递所需的额外参数。具体来说:
- 我在
arch\arm\include\asm\config.h
中定义了CONFIG_SYS_BOOT_GET_CMDLINE
以便调用boot_get_cmdline()
。 - 我修改了
boot_get_cmdline()
函数,以便它在 附加正常参数之前设置一个特定参数 。换句话说,我们现在得到uboot_instance=42 plugh=xyzzy
. 而不是
plugh=xyzzy
这一切都编译得很好,U-Boot 成功启动了内核,但是额外的信息没有反映在 Linux 内核中,它的内核参数设置为常规的 plugh=xyzzy
。
经过进一步研究,我们似乎与调用内核的两种可能方式发生了冲突。其中一个是 ATAG,一个是扁平设备树 (FDT),它们似乎是互斥的(内核启动代码根据传入的签名选择一个或另一个,指针引用 ATAG 或 FDT 结构).
所以我的问题是这样的。鉴于设备树是您描述的设备的 fixed 结构,您如何传递 arbitrary 内核命令行参数(在运行时计算)当引导加载程序调用内核时?
您可以在 include/configs/<board>.h
中为您的平台使用虚拟环境变量。
例如,假设您有以下(简化的)UBoot 环境变量用于引导:
bootcmd=run mmcargs
run loadimage loadfdt
bootz ${loadaddr} - ${fdt_addr}
mmcargs=setenv bootargs blah=blah
这使用mmcargs
设置要使用的内核命令行。我们需要做的是以当前 UBoot 实例不提供任何内容而新实例提供实际 ID 的方式插入该虚拟环境变量。这只需通过以下更改即可完成:
mmcargs=setenv bootargs ${uboot_id_stanza} blah=blah
然后,在电路板初始化期间,您可以使用 env_set
API 设置此变量,具体是通过在 board/<vendor>/<init_code>.c
.
以下行应放在 board_late_init
函数的末尾:
setenv("uboot_id_stanza", "uboot_id=<uniqueId>");
那样,uboot_id
变量设置被添加到内核命令行,但是,由于您没有执行 saveenv
,它不会变得持久。每个 UBoot 实例都会设置正确的 ID(包括那些不设置 ID 的旧实例)。