本文基于: linux-4.19 + ARMv7
介绍Linux启动之__vet_atags函数分析。
head.S启动代码:
ENTRY(stext)ARM_BE8(setend be ) @ ensure we are in BE8 modeTHUMB( badr r9, 1f ) @ Kernel is always entered in ARM.THUMB( bx r9 ) @ If this is a Thumb-2 kernel,THUMB( .thumb ) @ switch to Thumb now.THUMB(1: )#ifdef CONFIG_ARM_VIRT_EXTbl __hyp_stub_install
#endif@ ensure svc mode and all interrupts maskedsafe_svcmode_maskall r9mrc p15, 0, r9, c0, c0 @ get processor idbl __lookup_processor_type @ r5=procinfo r9=cpuid....../** r1 = machine no, r2 = atags or dtb,* r8 = phys_offset, r9 = cpuid, r10 = procinfo*/bl __vet_atags
__vet_atags代码实现位于head-common.S文件:
/* Determine validity of the r2 atags pointer. The heuristic requires* that the pointer be aligned, in the first 16k of physical RAM and* that the ATAG_CORE marker is first and present. If CONFIG_OF_FLATTREE* is selected, then it will also accept a dtb pointer. Future revisions* of this function may be more lenient with the physical address and* may also be able to move the ATAGS block if necessary.** Returns:* r2 either valid atags pointer, valid dtb pointer, or zero* r5, r6 corrupted*/
__vet_atags:tst r2, #0x3 @ aligned?bne 1fldr r5, [r2, #0]
#ifdef CONFIG_OF_FLATTREEldr r6, =OF_DT_MAGIC @ is it a DTB?cmp r5, r6beq 2f
#endifcmp r5, #ATAG_CORE_SIZE @ is first tag ATAG_CORE?cmpne r5, #ATAG_CORE_SIZE_EMPTYbne 1fldr r5, [r2, #4]ldr r6, =ATAG_COREcmp r5, r6bne 1f2: ret lr @ atag/dtb pointer is ok1: mov r2, #0ret lr
ENDPROC(__vet_atags)
- 核心功能概述
__vet_atags函数用于验证启动时传入的r2
寄存器值是否符合 ATAGS(ARM Tagged List)或 DTB(Device Tree Blob)的合法性要求,确保内核能正确解析硬件参数。若验证失败,r2
会被清零。
- 验证条件分解
(1) 对齐检查(Alignment Check)
tst r2, #0x3 @ 检查r2是否4字节对齐
bne 1f @ 若未对齐,跳转到标签1(返回r2=0)
- 意义:ATAGS/DTB的起始地址必须按4字节对齐,否则视为非法。
(2) DTB合法性检查
#ifdef CONFIG_OF_FLATTREE @ 若启用设备树(DTB)支持
ldr r6, =OF_DT_MAGIC @ 加载DTB魔数(0xd00dfeed)
cmp r5, r6 @ 比较r5(r2[0])与DTB魔数
beq 2f @ 相等则跳转到标签2(返回有效r2)
#endif
- 逻辑:若内核配置了设备树支持(
CONFIG_OF_FLATTREE
),则检查r2[0]
是否为DTB魔数0xd00dfeed
。
(3) ATAGS合法性检查
cmp r5, #ATAG_CORE_SIZE @ 检查r2[0]是否为ATAG_CORE_SIZE(5) | #define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)
cmpne r5, #ATAG_CORE_SIZE_EMPTY @ 或ATAG_CORE_SIZE_EMPTY(2)| #define ATAG_CORE_SIZE_EMPTY ((2*4) >> 2)
bne 1f @ 若不匹配,跳转到标签1(返回r2=0)
ldr r5, [r2, #4] @ 加载r2[1](ATAG_CORE标记)
ldr r6, =ATAG_CORE @ 加载标准ATAG_CORE值(0x54410001)| #define ATAG_CORE 0x54410001
cmp r5, r6 @ 比较标记是否合法
bne 1f @ 不匹配则返回r2=0
- ATAG_CORE规范:
r2[0]
必须为ATAG_CORE_SIZE
(5)或ATAG_CORE_SIZE_EMPTY
(2)。r2[1]
必须为ATAG_CORE
标记(0x54410001)。
(4) 返回结果
- 标签2(有效):直接返回,保留
r2
原始值。 - 标签1(无效):将
r2
清零后返回。
- 技术背景与设计意图
- 物理地址限制:
- 注释提到ATAGS需位于 物理RAM前16KB,但代码未显式检查(可能由调用方保证)。
- 兼容性设计:
- 同时支持传统ATAGS和现代DTB,适应不同启动协议(如U-Boot传递DTB时无需ATAGS)。
- 安全性扩展:
- 未来可能放宽物理地址限制或动态调整ATAGS位置(当前版本仅静态验证)。
- 代码流程图解
- 应用场景与影响
- 启动阶段:在Linux内核启动初期(
start_kernel
之前),用于校验Bootloader传递的参数合法性。 - 错误处理:若校验失败,内核可能拒绝启动或回退到默认硬件配置。
- 硬件兼容:确保不同厂商的Bootloader(如U-Boot、RedBoot)遵循ARM启动协议。
总结
__vet_atags
是ARM Linux启动流程中的关键验证环节,通过严格的指针检查保障内核稳定启动。其设计平衡了传统ATAGS与现代DTB的兼容性,同时为未来安全性和灵活性扩展预留空间。