Linux 安全启动流程
Linux 安全启动基于 ROM/KM0/KM4 安全启动。Linux 部分的安全启动流程如下所示。
备注
通常情况下,内核命令行将是 Kernel command line: console=ttyS0,1500000 earlycon psci=enable ubi.mtd=8 ubi.block=0,0 ubi.mtd=9 dm-mod.create="system,,0,ro, 0 55928 verity 1 /dev/ubiblock0_0 /dev/ubiblock0_0 4096 4096 6991 6991 sha512 <salt> <hash> 2 ignore_corruption ignore_zero_blocks" root=/dev/dm-0 rootfstype=squashfs
。
内核初始化时会显示命令行,其内容由 <sdk>/sources/boot/uboot/cmd/realtek_avb.c
提供。
函数 realtek_organize_cmdline()
将提取关键信息并组织一个新的命令行用于安全启动。
ubi.mtd=8
表示 mtd 中 rootfs 块的索引,它用于将 mtd8 设为字符设备 ubi0。ubi.block=0,0
表示将字符设备 ubi0 设为块设备 ubiblock0,因为 dm verity 只能对块设备进行操作。dm-mod.create
参数是由 dm-verity 驱动程序定义的。这些参数包括要验证的块设备、哈希块的大小、哈希块的数量、rootfs 哈希树的起始地址、验证所需的盐值和摘要等。摘要是指在 uboot 中已经被 VBmeta 验证过的 rootfs 的根哈希值。ubi.block0_0
被附加到 dm-0 上。root=/dev/dm-0
将 dm-0 挂载为根文件系统,这也意味着将 mtd8 区域的信息设为 rootfs。当用户访问 rootfs 中的某些块时,dm-verity 会对任何块及其相关块进行动态哈希计算。
有关 dm-verity 的更多细节,请参考 dm verify 。
本节介绍构造参数、操作原理、哈希树、磁盘格式以及一些示例。内核命令行是 rootfs 哈希树验证的唯一起点,哈希树的根哈希摘要将被插入 VBmeta,其有效性已通过之前的流程确认。
上述框架的代码可以分为三个部分: Flash 区域、 U-Boot 区域和 kernel 区域。下面仅列出每个部分的核心代码目录。
区域 |
路径 |
介绍 |
---|---|---|
Flash |
<sdk>/sources/yocto/meta-realtek/tools/verified_boot |
制作安全相关固件的脚本 |
U-Boot |
<uboot>/cmd/mtd.c |
读取 NAND 闪存的操作步骤 |
<uboot>/cmd/realtek-avb.c |
vbmeta/公钥/内核/设备树的验证 |
|
Kernel |
<linux>/drivers/md/* |
此目录由名为 dm-verity 的 GPI Linux 内核提供,用于验证 rootfs 的哈希树 |
更多关于安卓验证启动的信息,请参考 android avb 。
配置
Yocto SDK 使用 mksecure.sh
来编译安全固件,该脚本位于 <sdk>/sources/yocto/meta-realtek/tools/verified_boot
。
该脚本的用法说明如下:
mksecure.sh
--key_dir=<secure key path>
--output_dir=<secure image output path>
--input_dir=<directory of the normal images>
--boot_image=<boot image path>
--dtb_image=<device tree blob image path>
--dtb_part_size=<tree blob image partition size>
--kernel_image=<kernel image path>
--kernel_part_size=<kernel partition size>
--recovery_dtb_image=<recovery device tree blob image path>
--recovery_dtb_part_size=<recovery device tree blob partition size>
--recovery_kernel_image=<recovery kernel image path>
--recovery_kernel_part_size=<recovery kernel partition size>
--root_image=<rootfs image path>
--root_part_size=<rootfs partition size>
--km4_boot=<firmware boot image path>
--km4_app=<firmware app image path>
--imgtool_flashloader=<imgtool flashloader path>
--use_dtb_size =<enable the auto parse of image size>
其参数如下所列:
参数 |
必选/可选? |
描述 |
---|---|---|
key_dir |
必选 |
表示安全密钥的位置,默认在 |
output_dir |
必选 |
表示安全固件的输出路径。 |
input_dir |
可选 |
表示普通固件的目录。该目录中的固件将用作安全固件的来源。
如果指定了 input_dir,则可以省略 boot_image、kernel_image、recovery_kernel_image、root_image、km4_boot、km4_app 和
imgtool_flashloader。mksecure.sh 将默认使用指定 input_dir 中的 boot.img、uImage、<va7, va8>.rootfs.squashfs、km4_boot_all.bin、
km0_km4_app.bin、imgtool_flashloader.bin 和 uImage-initramfs-rtl8730elh-recovery.bin。
如果某些固件是通过其他输入参数指定的,mksecure.sh 将优先使用指定的路径。
如果未指定 input_dir,则必须分别提供所有固件路径。
|
dtb_image |
必选 |
表示非安全设备树 blob 固件的路径。 |
recovery_dtb_image |
当开启 recovery 时必选 |
表示恢复设备树 blob 固件的路径,如果需要刷入非安全恢复固件,则此参数不是必需的。 |
boot_image |
当存在 input_dir 时可选 |
表示非安全启动固件的路径。 |
kernel_image |
当存在 input_dir 时可选 |
表示非安全内核固件的路径。 |
recovery_kernel_image |
当存在 input_dir 时可选 |
表示非安全恢复内核固件的路径,如果需要刷入非安全恢复固件,则此参数不是必需的。 |
root_image |
当存在 input_dir 时可选 |
表示非安全 rootfs squashfs 固件的路径,并且只需要 squashfs 固件。 |
km4_boot |
当存在 input_dir 时可选 |
表示非安全 firmware 启动固件的路径。 |
km4_app |
当存在 input_dir 时可选 |
表示非安全 firmware 应用固件的路径。 |
imgtool_flashloader |
当存在 input_dir 时可选 |
表示文件 |
use_dtb_size |
可选, 另名为 -s |
设置为 1 以启用自动设备树解析。
mksecure.mk 将根据选定的 dtb_image 解析大小信息。如果启用了 -s,则可以省略 dtb_part_size、kernel_part_size、recovery_dtb_part_size、recovery_kernel_part_size 和 root_part_size
的大小信息。然而,请确保在 dts 中的布局描述中包含
Device Tree Blob 、Kernel Image 和 Rootfs Image 的关键名称(字母的大小写并不重要)。
|
dtb_part_size |
只在 -s 未设置时有效 |
表示在 |
kernel_part_size |
只在 -s 未设置时有效 |
表示在 |
recovery_dtb_part_size |
只在 -s 未设置时有效 |
表示在
rtl8730e-spi-nand-256m.dtsi (rtl8730e-spi-nand-128m.dtsi) 中描述的恢复设备树blob分区的大小,如果需要刷入非安全恢复映像,则此参数不是必需的。
|
recovery_kernel_part_size |
只在 -s 未设置时有效 |
表示在
rtl8730e-spi-nand-256m.dtsi (rtl8730e-spi-nand-128m.dtsi) 中描述的恢复内核分区的大小,如果需要刷入非安全恢复映像,则此参数不是必需的。
|
root_part_size |
表示在 |
其中 <dts_dir> 表示设备树文件的目录,对于内核 5.4.x,该目录是 <sdk>/kernel/linux-5.4/arch/arm/boot/dts
,而对于内核 6.6.x,该目录是 <sdk>/kernel/linux-6.6/arch/arm/boot/dts/realtek/ameba
。
如何使用安全启动
下列步骤列出了如何使用安全启动。
启用 uboot 安全启动配置。
准备安全启动的密钥。
制作并生成原始固件。
编译安全固件。
系统启动后,启用 firmware 安全启动相关的 OTP 位。
启用 uboot 安全启动配置
要启用 Linux 安全启动,需要打开与 U-Boot 相关的安全启动配置。以下列出了位于 <sdk>/sources/boot/uboot/configs
中的 U-Boot 配置以及与安全启动相关的配置:
CONFIG_VERIFIED_BOOT=y
CONFIG_OTP_RTK_AMEBA=y
确保上面的配置已打开。
当前,我们已默认启用了这些配置。
准备安全启动的密钥
要使用 Linux 验证启动,必须准备安全密钥。正如上面提到的,执行 mksecure.sh
时,需要通过配置 --key_dir
参数来设置密钥路径。
默认的清单/密钥文件(manifest.json/key.json
)和密钥位于 <sdk>/sources/yocto/meta-realtek/tools/verified_boot/security_keys/test
。
Linux SDK 提供了工具 make_key``(位于 ``<sdk>/sources/yocto/meta-realtek/tools/verified_boot
),供用户在特定密钥路径下生成自己的安全密钥。使用以下命令生成安全密钥,生成后的密钥将位于以下路径:
make_key <key path>
安全密钥如下所列:
文件 |
描述 |
---|---|
fw.key |
用于签署固件引导加载程序和密钥证书。 |
root.key |
用于签署固件NSPE/SPE和Linux启动固件。 |
vbmeta.priv.key/vbmeta.pub.key |
用于签署Linux vbmeta固件。 |
编译安全固件
在制作安全固件之前,所有原始固件(firmware 固件除外)都需要先生成。请参考 配置,在需要时使用 mksecure.sh
制作安全固件。
elf2bin 用于在 mksecure.sh
中制作安全固件,elf2bin 的用法是:
elf2bin manifest <manifest_json> <key_json> <img_file> <out_file> [app|boot]
elf2bin manifest
用于为特定固件生成 manifest.bin
。
- <manifest_json>:
manifest 文件用于描述固件的相关信息。默认的 manifest 文件位于
<sdk>/sources/yocto/meta-realtek/tools/verified_boot/security_keys/test
。- <key_json>:
密钥文件用于描述固件的密钥相关信息。默认的密钥文件也位于上述路径。
- <img_file>:
生成
manifest.bin
所需的固件。- <out_file>:
为
<img_file>
输出安全清单文件。- [app|boot]:
指示固件的清单类型。
boot
用于 firmware 启动,app
用于包含 Linux 引导加载程序的 firmware 应用固件。
elf2bin cert <manifest_json> <key_json> <out_file> <key_id1> [app|vbmeta] <key1_json>
elf2bin cert
用于为所有应用固件生成 cert.bin
。cert.bin
包含应用固件的安全信息,用于验证每个应用固件。
- <manifest_json>:
描述固件相关信息的清单文件。默认的清单文件位于
<sdk>/sources/yocto/meta-realtek/tools/verified_boot/security_keys/test
。- <key_json>:
描述固件密钥相关信息的密钥文件。默认的密钥文件也位于上述路径。
- <out_file>:
输出安全证书文件。
- <key_id1> [app|vbmeta] <key1_json>:
<key_id1>
表示cert.bin
中的密钥 ID,其值从 0 开始。[app|vbmeta]
表示在<key1_json>
中描述的固件类型。vbmeta
用于验证 Linux vbmeta 固件,而 app 用于验证其他应用固件(firmware 应用固件、Linux 引导加载程序)。
启用安全启动
在刷写安全固件并启动系统后,通过编程 OTP 位来启用安全启动。
启动 km4_console 以编程 OTP。
$km4_console
在 OTP 中编程安全启动根公钥哈希。
根公钥哈希值来自
fw.key
的public key hash
字段。{ "algorithm":"ed25519", "private key":"6AA34203018334474B25A0600996CA0968AA6228B886FF234B4EB9628B703C0A", "public key":"E2A0D6500BBF1DD8DC212098C230EB731ECE3A81AA11D0E6E538FA36BBA4FF6E", "public key hash":"72B2E1CB0E8F715262AF38DFA0E522C95660D0EBFD920F4B1A229845E599C697" }
使用以下命令编程 OTP PK1:
efuse wraw 320 20 72B2E1CB0E8F715262AF38DFA0E522C95660D0EBFD920F4B1A229845E599C697
在 OTP 中启用安全启动。
首先使用
efuse rmap
检查 0x2 和 0x3 中的值:$efuse rmap 0 16
0x2 0x3 的值对应 1080。
启用安全启动使能位(将 0x3[2] 设置为 1)
$efuse wmap 2 2 1084
使用 efuse rmap 检查安全启动位是否成功设置。
复位开发板。
当安全启动成功时,您会看到以下日志:
IMG1 SBOOT EN: secure boot is enabled
IMG1(OTA1) VALID, ret: 0: bootloader authentication pass
IMG2 VERIFY PASS: IMAGE2 authentication pass
AP BL1&FIP VERIFY PASS: AP uboot authentication pass
Public Key Hash Verified Success
Rollback Index: Version PASS!: rollback authentication pass
VbMeta Signature Verified Success!: vbmeta authentication pass
Kernel Image verified success!: linux kernel authentication pass
DTB/FDT Image verified success!: Linux DTB authentication pass
linux verified boot: success!