init.s文件介绍在preloader/platform/mt6761/src/init/init.s是 MTK 平台 preloader 的AArch32 启动入口汇编运行在芯片内部 SRAM 中此时 DDR 内存尚未初始化是 BROM 验签通过后跳转执行的第一段代码。核心执行主线保存启动参数 → CPU 硬件勘误修复 → 处理器模式/中断配置 → 缓存/总线时序优化 → 清零 BSS 段 → 初始化栈指针 → 跳转 C 语言 main 函数。代码后半段为导出给 C 语言调用的工具函数缓存操作、架构切换等不属于启动顺序执行链路。.section .text.start .equ MODE_USR ,0x10 .equ MODE_FIQ ,0x11 .equ MODE_IRQ ,0x12 .equ MODE_SVC ,0x13 .equ MODE_MON ,0x16 .equ MODE_ABT ,0x17 .equ MODE_UNDEF ,0x1B .equ MODE_SYS ,0x1F .equ I_BIT ,0x80 .equ F_BIT ,0x40 .equ INT_BIT ,0xC0 .extern sys_stack .extern sys_stack_sz .extern bl31_base_addr .extern rst_vector_base_addr /* bldr argument address */ .globl bldr_args_addr bldr_args_addr: .word 0x0 .globl _start .type _start,STT_FUNC _start: b resethandler bss1_start: .word _bss1_start bss1_end: .word _bss1_end bss2_start: .word _bss2_start bss2_end: .word _bss2_end stack: .long sys_stack stacksz: .long sys_stack_sz resethandler: LDR r6, bldr_args_addr STR r4, [r6] MOV r0, #0 MOV r1, #0 MOV r2, #0 MOV r3, #0 MOV r4, #0 MOV r5, #0 MOV r6, #0 MOV r7, #0 MOV r8, #0 MOV r9, #0 MOV r10, #0 MOV r11, #0 MOV r12, #0 MOV sp, #0 MOV lr, #0 /* CONFIG_ARM_ERRATA_826319 */ mrc p15, 0, r8, c1, c0, 0 Read System Control Register into Rt bic r8, r8, #0x4 disable D-Cache bic r8, r8, #0x1000 clear I-Cache mcr p15, 0, r8, c1, c0, 0 Write Rt to System Control Register mrc p15, 1, r8, c15, c0, 0 Read L2ACTLR into Rt orr r8, r8, #0x8 Set Bit[3]1, disable eviction transaction bic r8, r8, #0x1 14 Set Bit[14]]0 mcr p15, 1, r8, c15, c0, 0 Write Rt to L2ACTLR isb /* CONFIG_ARM_ERRATA_836870 */ /** CONFIG_ARM_ERRATA_836870y (for 6595/6752/6735, prior to r0p4) * Prog CatC, * Non-allocating reads might prevent a store exclusive from passing * worksround: set the CPUACTLR.DTAH bit. * The CPU Auxiliary Control Register can be written only when the system * is idle. ARM recommends that you write to this register after a powerup * reset, before the MMU is enabled, and before any ACE or ACP traffic * begins. **/ mrrc p15, 0, r8, r9, c15 Read CPUACTLR into Rt, Rt2 orr r8, r8, #0x1 24 Set Bit[24]1, set the CPUACTLR.DTAH bit. mcrr p15, 0, r8, r9, c15 Write Rt, Rt2 to CPUACTLR isb /* CONFIG_ARM_ERRATA_855873 */ mrrc p15, 0, r8, r9, c15 Read CPUACTLR into Rt(Low), Rt2(High) orr r9, r9, #0x1 12 Set Bit[44]1, set the CPUACTLR.ENDCCASCI bit. mcrr p15, 0, r8, r9, c15 Write Rt, Rt2 to CPUACTLR isb /* set the cpu to SVC32 mode */ MRS r0,cpsr BIC r0,r0,#0x1f ORR r0,r0,#0xd3 MSR cpsr,r0 /* disable interrupt */ MRS r0, cpsr MOV r1, #INT_BIT ORR r0, r0, r1 MSR cpsr_cxsf, r0 /* * L2 write to write access 2T/3T programmable * Program before I/D bits enable * 0x0c53_0604 bit[21] set to 1’b1 * 1’b0 - L2 write to write 2T (default) * 1’b1 - L2 write to write 3T */ ldr r0, 0x0C530604 ldr r1, [r0] orr r1, r1, #0x1 21 str r1, [r0] dsb isb /* enable IZSMP bits and disable D bit */ MRC p15, 0, ip, c1, c0, 0 ORR ip, ip, #0x1840 /* IZSMP bits */ BIC ip, ip, #0x4 /* C bit */ MCR p15, 0, ip, c1, c0, 0 /* Make BROM connect to META mode more stable, request by Chandler.Li */ DSB ISB clear_bss1 : LDR r0, bss1_start /* find start of bss segment */ LDR r1, bss1_end /* stop here */ MOV r2, #0x00000000 /* clear */ CMP r0, r1 BEQ clear_bss2 /* clear loop... */ clbss1_l : STR r2, [r0] ADD r0, r0, #4 CMP r0, r1 BNE clbss1_l clear_bss2 : LDR r0, bss2_start /* find start of bss segment */ LDR r1, bss2_end /* stop here */ MOV r2, #0x00000000 /* clear */ CMP r0, r1 BEQ setup_stk /* clear loop... */ clbss2_l : STR r2, [r0] ADD r0, r0, #4 CMP r0, r1 BNE clbss2_l setup_stk : /* setup stack */ LDR r0, stack LDR r1, stacksz /* buffer overflow detect pattern */ LDR r2, 0xDEADBEFF STR r2, [r0] LDR r1, [r1] SUB r1, r1, #0x04 ADD r1, r0, r1 MOV sp, r1 entry : LDR r0, bldr_args_addr B main .globl jump .type jump,STT_FUNC jump: MOV r4, r1 /* r4 argument */ MOV r5, r2 /* r5 argument */ MOV pc, r0 /* jump to addr */ .globl apmcu_icache_invalidate .type apmcu_icache_invalidate,STT_FUNC apmcu_icache_invalidate: MOV r0, #0 MCR p15, 0, r0, c7, c5, 0 /* CHECKME: c5 or c1 */ BX lr .globl apmcu_isb .type apmcu_isb,STT_FUNC apmcu_isb: ISB BX lr .globl apmcu_disable_icache .type apmcu_disable_icache,STT_FUNC apmcu_disable_icache: MOV r0,#0 MCR p15,0,r0,c7,c5,6 /* Flush entire branch target cache */ MRC p15,0,r0,c1,c0,0 BIC r0,r0,#0x1800 /* IZ bits */ MCR p15,0,r0,c1,c0,0 BX lr .globl apmcu_disable_smp .type apmcu_disable_smp,STT_FUNC apmcu_disable_smp: MRC p15,0,r0,c1,c0,1 BIC r0,r0,#0x040 /* SMP bit */ MCR p15,0,r0,c1,c0,1 BX lr .section .text.arch64 .globl jumparch64 .type jumparch64,STT_FUNC jumparch64: /* r0 as bl31 bootarg and r1 as reserved variable for ATF */ /* setup the reset vector base address after warm reset to Aarch64 */ LDR r5, bl31_base_addr LDR r5,[r5] LDR r6, rst_vector_base_addr LDR r6,[r6] str r5,[r6] /* setup the excution state after warm reset: 1:Aarch64, 0:Aarch32 */ MRC p15,0,r6,c12,c0,2 orr r6, r6, #1 MCR p15,0,r6,c12,c0,2 DSB ISB /* do warm reset:reset request */ MRC p15,0,r6,c12,c0,2 orr r6, r6, #2 MCR p15,0,r6,c12,c0,2 DSB ISB .globl WFI_LOOP .type WFI_LOOP,STT_FUNC WFI_LOOP: /* enter WFI to request a warm reset */ WFI B WFI_LOOP .globl jumparch64_slt .type jumparch64_slt,STT_FUNC jumparch64_slt: /* setup the reset vector base address after warm reset to Aarch64 */ /* ldr r1,RVBADDRESS_CPU0 */ /* ldr r1,[r1] */ /* LDR r0, 0x40000000 */ LDR r0, 0x40000000 LDR r1, 0x10200038 str r0,[r1] /* setup the excution state after warm reset: 1:Aarch64, 0:Aarch32 */ MRC p15,0,r0,c12,c0,2 orr r0, r0, #1 MCR p15,0,r0,c12,c0,2 DSB ISB /* do warm reset:reset request */ MRC p15,0,r0,c12,c0,2 orr r0, r0, #2 MCR p15,0,r0,c12,c0,2 DSB ISB /* set r0 as 0x40000300 for dtb */ ldr r0, 0x40000300 1: /* enter WFI to request a warm reset */ WFI B 1blink_descriptor.ld链接脚本定义整个 preloader 的内存布局规则链接脚本的核心作用是规定「代码、数据、栈、BSS 分别放在内存的什么地址、按什么顺序排列」同时指定程序的入口点是整个镜像的「地址地图」。OUTPUT_ARCH(arm) ENTRY(_start) romBase 0x00201000; bss1Base 0x00102180; bss2Base 0x00108C00; stackBase 0x00200000; MEMORY { bss1 : ORIGIN 0x00102180, LENGTH 0x6C00 rom : ORIGIN 0x00201000, LENGTH 0x3F000 bss2 : ORIGIN 0x00108C00, LENGTH 0x5000 stack : ORIGIN 0x00200000, LENGTH 0xC00 } SECTIONS { /* rom region */ . romBase; .start ALIGN(4) : { *(.text.start) } rom . romBase 0x01FC; .rom_info ALIGN(4) : { *(.data.rom_info) } rom .text ALIGN(4) : { *(.text) *(.text.*) } rom .rodata ALIGN(4) : { *(.rodata) *(.rodata.*) } rom .data ALIGN(4) : { *(.data) *(.data.*) } rom .got ALIGN(4) : { *(.got) *(.got.*) } rom __boot_end .; /* bss1 region */ . bss1Base; .bss1 ALIGN(16) : { _bss1_start .; *print.o (.bss.log_sram_buf) *efi.o (.bss.gpt_sram_buf) *efi.o (.bss.gpt_sram_crc32_table) *efi.o (.bss.gpt_sram_part_info) *efi.o (.bss.gpt_sram_part_meta_info) *dramc_*.o (.bss .bss.* COMMON) /* make _bss1_end as 4 bytes alignment */ . ALIGN(4); _bss1_end .; } bss1 ASSERT((SIZEOF(.bss1) LENGTH(bss1)), Error: .bss1 over size!) /* stack */ . stackBase; .stack ALIGN(4) : { _stack_start .; *(.bss.sys_stack) _stack_end .; } stack /* bss2 region */ . bss2Base; .bss2 ALIGN(16) : { _bss2_start .; *(.bss) *(.bss.*) *(COMMON) /* make _bss2_end as 4 bytes alignment */ . ALIGN(4); _bss2_end .; } bss2 ASSERT((SIZEOF(.bss2) LENGTH(bss2)), Error: .bss2 over size!) /* Clang needs .ARM.exidx, but we do not use it for debug. */ /DISCARD/ : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } }MTK preloader 运行在芯片内部 SRAM此时 DDR 尚未初始化因此所有地址都对应片上 SRAM 空间和后续DDR内存无关。1. MEMORY 命令划分可用内存块脚本先定义了 4 块独立的 SRAM 内存区域对应不同用途内存块起始地址长度核心用途bss10x001021800x6C00第一阶段BSS存放DDR初始化、GPT、日志缓冲区等早期初始化变量bss20x00108C000x5000第二阶段BSS存放普通全局未初始化变量stack0x002000000xC00栈空间栈向下增长rom0x002010000x3F000代码只读区存放所有指令、只读数据、已初始化全局变量2. SECTIONS 命令段的排布规则把各个源文件的段.text/.data/.bss 等按顺序分配到上述内存块中有三个核心规则入口锚定ENTRY(_start) 声明整个程序的入口符号是 _startBROM 加载 preloader 并验签通过后会直接跳转到该符号对应的地址执行。启动段置顶.start 段放在 rom 区域的最开头地址 0x00201000内容是所有文件的 .text.start 段——也就是你给出的汇编代码所在的段保证启动指令是镜像的第一条有效代码。符号导出在 BSS、栈等段的首尾定义 _bss1_start、_bss1_end、_bss2_start、_bss2_end 等全局符号供汇编代码和C代码直接引用链接时自动填充真实地址无需硬编码。init.s 与链接脚本的三重绑定关系启动汇编init.s不是游离的代码它通过三个维度和链接脚本精准对应确保被链接器放到正确的内存位置、能正确访问所有内存区域。1. 段名绑定启动代码固定在镜像最开头汇编开头用 .section .text.start 声明自身属于 .text.start 段链接脚本中 .start ALIGN(4) : { *(.text.start) } rom 把所有 .text.start 段放到 rom 区域的最起始位置。这就保证了上电后 BROM 跳转的第一个地址就是汇编里的 _start 标签不会被其他C代码挤占位置。2. 入口符号绑定_start 作为程序执行起点汇编中用 .globl _start 导出全局符号 _start并作为第一段代码的标签链接脚本中 ENTRY(_start) 把该符号设为整个镜像的入口点生成的 ELF 文件入口地址就是 _start 的物理地址。二者配合确保BROM跳转到镜像起始地址后能直接执行预设的启动逻辑。3. 地址符号联动汇编直接使用链接脚本定义的常量汇编代码中引用的 _bss1_start、_bss1_end、_bss2_start、_bss2_end、sys_stack 等符号都不是硬编码的数值而是在链接脚本中定义、链接阶段自动赋值的地址常量。举个完整的联动例子链接脚本在 .bss1 段开头定义 _bss1_start .;结尾定义 _bss1_end .;. 代表当前地址计数器汇编里用 bss1_start: .word _bss1_start 把该地址存为一个数据变量清 BSS 时LDR r0, bss1_start 就能直接拿到 BSS 段的真实起始地址。这种设计的好处是修改内存布局只需改链接脚本汇编代码无需改动地址自动同步适配不同SRAM配置的芯片时非常灵活。顺序执行主流程从上电到进入C语言阶段1入口跳转与启动参数保存代码对应.globl _start _start: b resethandler ; 中间是数据常量区bss起止地址、栈地址等 resethandler: LDR r6, bldr_args_addr STR r4, [r6] ; 清零所有通用寄存器、sp、lr执行逻辑与作用_start 是整个镜像的入口点链接脚本 ENTRY(_start) 指定BROM 验签通过后直接跳转到这里。第一条指令直接跳转到 resethandler中间的 bss1_start/bss1_end/stack 等是数据常量存放链接脚本导出的内存地址供后续代码读取不会被执行。保存 BROM 传入的启动参数BROM 跳转前会把启动信息如启动原因、设备模式等的指针放在 r4 寄存器这里将其存入全局变量 bldr_args_addr后续 C 语言代码可以读取该参数。清零 r0~r12、sp、lr 所有通用寄存器构建干净的启动环境避免上电脏数据导致异常。阶段2CPU 硬件勘误Errata修复代码对应三段 CONFIG_ARM_ERRATA_xxx 配置分别操作系统控制寄存器、L2 辅助控制寄存器、CPU 辅助控制寄存器。执行逻辑与作用这是 ARM Cortex-A 系列 CPU 的标准硬件 bug 修复步骤必须在 MMU、数据缓存开启前完成是 MTK 平台的强制适配项Errata 826319关闭数据缓存D-Cache、清除指令缓存I-Cache避免启动阶段缓存一致性问题。L2ACTLR 配置配置二级缓存辅助控制寄存器关闭驱逐事务、调整总线时序提升 L2 缓存运行稳定性。Errata 836870设置 CPUACTLR 的 DTAH 位bit24修复“非分配读可能阻碍存储排他指令执行”的硬件缺陷。Errata 855873设置 CPUACTLR 的 ENDCCASCI 位bit44对应 r9 寄存器的 bit12修复缓存一致性相关的硬件 bug。每次寄存器配置后执行 isb指令同步屏障确保配置立即生效避免流水线预取旧指令。阶段3处理器模式与中断配置代码对应; 切换到 SVC32 模式 MRS r0,cpsr BIC r0,r0,#0x1f ORR r0,r0,#0xd3 MSR cpsr,r0 ; 关闭总中断 MRS r0, cpsr MOV r1, #INT_BIT ORR r0, r0, r1 MSR cpsr_cxsf, r0执行逻辑与作用强制切换到 SVC 管理模式ARM 处理器的最高特权模式保证后续所有系统寄存器配置都有权限执行是引导程序的标准启动模式。关闭 IRQ、FIQ 所有中断启动阶段异常向量表尚未初始化中断触发会导致程序跑飞同时底层初始化不允许被打断必须保持原子性。阶段4总线时序与缓存性能配置代码对应L2 写时序配置寄存器 0x0C530604bit21 置 1开启 I-Cache、Z 分支预测、SMP 位关闭 D-Cache执行逻辑与作用L2 写时序调整将 L2 缓存写-写访问时序从默认 2T 改为 3T牺牲少量性能换取更高的总线稳定性是 MTK 平台针对 SRAM 运行场景的标准优化。缓存配置开启指令缓存I-Cache和分支预测Z 位提升启动阶段代码执行速度指令缓存不涉及内存一致性问题可提前开启。开启 SMP 位为后续多核启动做准备提前就绪缓存一致性协议。保持数据缓存D-Cache关闭此时 MMU 未配置、DDR 未初始化开启数据缓存会导致地址映射混乱、数据一致性错误必须等内存初始化完成后再开启。阶段5清零 BSS 段分两段执行代码对应clear_bss1 → clbss1_l 循环 → clear_bss2 → clbss2_l 循环执行逻辑与作用1.BSS 段的意义存放 C 语言中未初始化的全局变量C 语言标准要求这类变量默认值为 0。上电后 SRAM 中是随机脏数据必须手动清零否则变量初始值随机会导致程序异常。2.分两段清零的原因对应链接脚本中的 bss1 和 bss2 两块独立 SRAM 区域bss1存放 DDR 初始化、GPT、日志缓冲区等早期核心变量在 DDR 初始化前就会使用bss2存放普通全局变量在基础硬件初始化完成后启用。3.清零逻辑逐 4 字节向内存写入 0从段首循环到段尾完成全段清零。阶段6栈指针初始化代码对应setup_stk: LDR r0, stack ; 栈起始地址最低地址 LDR r1, stacksz ; 栈大小 ; 写入栈溢出检测魔数 LDR r2, 0xDEADBEFF STR r2, [r0] ; 计算栈顶高地址栈向下增长 LDR r1, [r1] SUB r1, r1, #0x04 ADD r1, r0, r1 MOV sp, r1执行逻辑与作用栈是 C 语言运行的基础函数调用、局部变量、返回地址都依赖栈空间ARM 栈默认向下增长从高地址向低地址延伸因此 sp 栈指针初始化为栈区域的最高地址。栈溢出检测在栈的最低地址写入魔数 0xDEADBEFF后续程序可检测该值是否被覆盖判断是否发生栈溢出是嵌入式启动的标准调试手段。阶段7跳转 C 语言主函数代码对应entry: LDR r0, bldr_args_addr B main执行逻辑与作用将启动参数地址放入 r0作为 main 函数的第一个入参ARM32 C 调用约定前四个参数放 r0~r3。执行 B main 跳转到 C 语言编写的主函数汇编启动阶段结束正式进入 C 语言逻辑后续 DDR 初始化、存储驱动、LK 加载等复杂逻辑都由 C 代码实现。非顺序执行导出工具函数代码后半段的 .globl 标号函数是导出给 C 语言调用的底层操作接口不会在启动流程中自动执行按需调用1. 基础跳转与缓存操作jump直接跳转到指定地址支持传递两个参数r4、r5用于跳转到下一级镜像如 LK。apmcu_icache_invalidate失效全部指令缓存apmcu_disable_icache关闭指令缓存与分支预测apmcu_disable_smp关闭 SMP 一致性位用于运行时动态调整缓存配置或切换模式前的缓存清理。2. AArch64 架构切换函数jumparch64、jumparch64_slt 是 64 位 MTK 平台的核心逻辑preloader 本身运行在 32 位模式完成硬件初始化后需要切换到 64 位模式启动 BL31ARM Trusted Firmware再由 BL31 启动 LK。切换执行流程以 jumparch64 为例读取 bl31_base_addrBL31 镜像入口地址写入复位向量基地址寄存器配置暖复位后的启动入口。配置系统控制寄存器设置暖复位后进入AArch64 执行状态默认是 AArch32。置位复位请求位触发 CPU 暖复位warm reset。执行 WFI等待中断进入低功耗等待复位生效复位后 CPU 以 AArch64 模式从 BL31 入口启动完成架构切换。整体设计逻辑总结分层递进从最底层的硬件修复到处理器模式配置再到 C 语言环境搭建逐步向上传递控制权符合嵌入式启动的通用范式。适配 SRAM 限制所有操作都在片内 SRAM 中完成不依赖 DDR拆分 BSS、精简栈大小都是为了适配有限的片上内存。前后衔接向上承接 BROM 的启动参数向下对接 C 语言主逻辑同时预留 64 位架构切换能力完整串联起「BROM → Preloader → BL31 → LK」的 MTK 整条启动链。