MTK平台启动流程全解析:从Pre-loader到Kernel的引导与调试
我当年第一次用MTK平台做bringup最懵的不是写驱动而是上电之后串口里刷过几行引导打印屏幕就停住了。Pre-loader没有任何输出、DDR training的日志也抓不到我当时差点把板子当成坏的。后来才慢慢搞清楚MTK这套Bootable启动流程从Pre-loader到Kernel每一级都有明确分工级与级之间的握手又藏着一大堆坑。这篇文章我就把自己这些年梳理的启动链路完整写一遍把Pre-loader、LK、Kernel各自干什么、怎么交接、出问题怎么排都说清楚适合正在做平台开发、系统移植、驱动适配或者整天跟刷机工具打交道的朋友。MTK和别的平台最大的不同在于它的启动链不是BootROM直接拉Kernel而是分了三段甚至四段每一段都有独立镜像、独立校验、独立日志。你只有把这条链路从头到尾吃透遇到问题才知道该在哪一级下手。我见过不少同事调了几天Kernel最后发现是Pre-loader阶段DDR参数没配对的诡异问题这种弯路真没必要走。1. 先搞清楚整条链路在做什么1.1 为什么MTK要多级引导而不是直接启动Kernel要说清楚这个问题得先明白芯片上电瞬间的“窘境”。手机SoC上电以后CPU要跑代码但这个时候DDR内存还没初始化eMMC/UFS存储控制器的驱动也还没跑起来芯片内部那点SRAM又小得可怜可能只有几百KB。BootROM就是固化在芯片硅片里的一段只读代码它很小没法也没必要做成一个完整的操作系统引导器。那为什么不把DDR初始化和存储驱动的逻辑都塞进BootROM这就涉及到一个商业现实内存颗粒型号、闪存型号、电源管理芯片型号不同方案上用的都不一样。DDR training参数、eMMC时序参数这些如果固化在BootROM里平台厂商就没法灵活适配硬件了。所以MTK把第一段可替代代码放在Pre-loader里让厂商能针对自己的硬件配置去调整。Pre-loader跑起来、DDR初始化完毕之后才有足够的内存空间去加载下一级引导程序。打个比方BootROM像急诊室的导诊台它只负责把人分到对应科室Pre-loader是手术室的准备工作负责把器械消毒、把环境准备好LK是决定做哪台手术的方案制定者Kernel才是真正开始治疗的主刀医生。任何一级掉链子后面都走不下去。1.2 一条完整的启动链路从BootROM到init进程MTK标准启动链路可以概括成这个顺序BootROM → Pre-loader → LK (Little Kernel) → Kernel → init进程每一级镜像和它所在的介质位置大致是这样的阶段镜像/代码位置核心任务主要的下一级交接物BootROM芯片内部ROM无法更改初始化基础时钟、检测启动介质、校验并加载Pre-loaderPre-loader镜像Pre-loadereMMC boot1/boot2分区或SPI存储可替换PMIC初始化、DDR training、存储控制器初始化、下载模式检测、加载LKLK镜像LKbootloader分区可替换显示LCM初始化、按键检测、fastboot协议、安全校验AVB/vbmeta、加载boot镜像Kernel镜像、DTB、bootargsKernelboot分区中的kernel image初始化全部硬件驱动、内存管理、调度器、挂载rootfsinit进程不同项目、不同平台在细节上会有差异。比如新一些的MTK平台可能把“第二级引导”改成u-boot或者ABLAndroid Boot Loader但整体职责边界是差不多的BootROM只干最基础的活Pre-loader负责“把硬件环境搞出来”LK负责“决定启动什么OS并做好校验”Kernel才开始真正接管整个系统。1.3 级与级之间的握手到底传了什么我要强调一个很容易被忽略的细节每一级引导程序并不是“无脑跳转”它们之间会通过固定内存地址、寄存器、共享内存结构体来传递信息。BootROM跳转Pre-loader时通常是在内部SRAM里解压/拷贝好Pre-loader然后在一个固定物理地址跳过去。Pre-loader启动早期可能还不知道DDR在哪所以它自己也在SRAM或者内部RAM里执行做完DDR training之后再把完整逻辑搬到内存里去。Pre-loader跳转LK时会通过一块共享内存区域把一些关键标志传给LK比如boot_reason关机充电、按键开机、闹钟开机、异常重启等、安全启动状态、下载模式标志。这个boot_reason我在调试“为什么机器自己进recovery”“为什么一插电就自动开机”这类问题时经常要回头核对。LK跳转Kernel则是走标准的ARM Linux启动协议CPU进入SVC模式、关闭中断、MMU关闭然后通过寄存器x0/x1/x2传参其中最关键的是把DTB设备树二进制的物理地址传给Kernel。Kernel拿到DTB之后才能知道自己跑在什么硬件上、需要初始化哪些外设。2. Pre-loader第一道真正可定制的关卡2.1 Pre-loader到底干了哪些活Pre-loader的源码在MTK SDK里通常在vendor/mediatek/proprietary/bootable/bootloader/preloader目录下按平台分目录比如platform/mtxxxx/。它的编译产物一般是preloader_xxx.bin烧写在eMMC的boot1或boot2分区里。Pre-loader的主要工作我整理成这么几块PMIC初始化设置CPU和各路外设的供电时序包括vcore、vsys、vddr等。这一步做错了后面所有事情都会崩但错误又不明显。DDR training对内存控制器和DDR颗粒做校准包括读写时序、眼图优化、频率配置。内存颗粒换了参数不对就会卡死在这里。存储控制器初始化eMMC、UFS或者SPI NAND的初始化让BootROM之后能够从存储介质读取镜像。启动介质选择和镜像加载决定从哪块存储、哪个分区加载LK镜像。按键与模式检测检测音量键、组合键等决定是正常启动、进下载模式、进META模式还是做关机充电。安全启动校验如果使能了secure bootPre-loader要用Root Key校验自己镜像的签名、校验LK的签名形成芯片到系统的一条信任链。我接手过一个项目换了一颗DDR颗粒之后板子就再也起不来了。串口输出停在Pre-loader阶段连一行DDR training成功的日志都没有。查了很久最后就是DDR参数表里没有这颗颗粒的配置补上之后秒过。所以我一直强调改硬件配置之前先把Pre-loader里对应的参数表翻一遍。2.2 按键检测与下载模式包括“按键进拍照”这类工厂功能的原理很多人问“MTK按键进入拍照模式”是什么原理其实这背后就是启动阶段的按键检测和工厂模式机制。MTK平台的按键检测不只是给开机用的工程机在开发阶段会预留很多隐藏模式入口而检测这些按键的代码就在Pre-loader和LK阶段。Pre-loader阶段有一个keypad.c之类的驱动会扫描按键矩阵或者EINT外部中断引脚。常见的组合有音量下电源进入BROM下载模式、音量上电源进入recovery、同时按特定组合键进入META模式或者工厂测试模式。不同项目的组合定义可以改通常写在cust_keypad.cfg这样的配置文件里。“按键进入拍照”在小米、OPPO这类项目上通常是指工程机的工厂相机测试模式或者所谓的“工程模式应用”。工程模式入口本身是应用层的东西但“按键能不能被正确识别”这个源头在LK/Pre-loader阶段就已经定下来了。如果你改了按键矩阵配置导致GPIO在LK阶段没被初始化那到应用层按什么组合键都没反应。2.3 Pre-loader调试经验日志、点灯和BROM模式Pre-loader的日志通常走UART输出常见波特率是921600日志会有[PRELOADER]开头的tag。典型输出类似[PRELOADER] Battery is not enough [PRELOADER] Load LK from EMMC_BOOT_1 [PRELOADER] LK loaded, jumping to 0x41E00000这些打印看着简单但实际调试中价值极高。比如如果你看到Battery is not enough说明PMIC读取电池电量有问题或者充电路径不对而不是什么Kernel配置错误。如果连一行Pre-loader日志都没有大概率连UART都没初始化好或者PMIC输出时序异常、时钟没起振。在没有串口输出、或者UART早期初始化不生效的时候可以靠点灯来定位。提前在Pre-loader源码的某个阶段点一个GPIO驱动的LED看灯亮不亮就能知道代码跑到哪里。这个方法土但在硬件环境极其恶劣的时候非常管用。另外BROM下载模式和Pre-loader下载模式是两个概念。BROM下载模式是BootROM自己被特殊方式触发比如按住特定按键上电BootROM直接枚举出一个USB设备电脑端工具可以和它通信可以无视Pre-loader镜像是否存在直接烧写整个Flash。Pre-loader下载模式则是BootROM正常加载了Pre-loader之后Pre-loader再枚举出USB设备这个模式功能更全能做分区读写、DA下载等。SP Flash Tool和mtkclient这类工具就是跟这两个模式打交道的。3. LK/ABL引导选择的执行者3.1 LK的职责与源码结构LK全称Little Kernel是MTK用作bootloader的开放源码内核。它的源码通常在bootable/bootloader/lk目录结构里有app/aboot、fastboot、mtkfb等、platform/mtxxxx/平台相关启动代码、dev/各种外设驱动。新一些的MTK平台也有改用u-boot作为bootloader的但核心职责一致只是代码风格和命令接口不同。LK是从Pre-loader接手控制权的第二级引导程序。它的核心工作包括初始化显示相关硬件打开LCM电源、初始化MIPI DSI接口、点亮屏幕把开机Logo显示出来。读取misc分区解析misc分区里的bootloader_message结构体判断是正常开机、进recovery、进fastboot还是别的模式。按键检测继续处理按键逻辑比如音量下是fastboot、音量上是recovery长按电源关机等。加载boot镜像读取boot分区解析boot header把kernel、ramdisk、DTB读到内存。安全校验如果是user build且使能AVBLK要对boot、system、vendor等分区做完整性校验。提供fastboot接口开发阶段最常用的fastboot flash、fastboot boot、fastboot oem unlock都由LK/ABL实现。3.2 开机模式判断与按键逻辑的实际实现MTK方案里misc分区的数据格式和AOSP标准基本兼容。系统要进recovery时会往misc分区的command字段写入boot-recovery然后重启LK启动时读到这个字段就知道要加载recovery镜像而不是boot镜像。这个机制在普通使用中不会出问题但是有一种很经典的现象一个错误的重启比如系统crash后主动写入了recovery命令但crash信息没写完会导致设备莫名其妙地进入recovery有人误以为坏了。我排查过不少这样的case最后发现是应用层或者OTA流程在某次异常中断后遗留了command字段。所以LK启动阶段的日志输出里如果能看到它打印出读到的bootloader_message内容排查这种问题就会快很多。按键逻辑在LK里通常封装在app/aboot或者平台相关的key.c中。开发过程中最常用的一招就是“音量下电源键”强制进fastboot。这个逻辑依赖于LK阶段GPIO和keypad的配置如果按键没响应先不要怀疑硬件坏了回去查一下LK里按键扫描的配置看看对应的GPIO模式下拉/上拉是否正确。3.3 从LK到Kernel的跳转参数是怎么传的LK跳Kernel的流程可以概括为这么几步LK从boot分区读取boot image头部校验magic是否为ANDROID!。根据boot header里的信息把kernel镜像加载到指定物理内存地址比如0x40008000附近。把ramdisk加载到另一个地址。从DTB区域可能是boot image里的dtb段也可能是单独的dtbo分区找到匹配的DTB放入内存。把cmdline参数序列化拼上androidboot.hardwaremtxxxx、androidboot.selinuxpermissive/enforcing等平台参数。设置寄存器、关中断、关MMU跳转Kernel入口。这段里面最容易出问题的就是cmdline和DTB地址。我遇到过一种情况Kernel起来得特别慢查了半天发现是LK传过去的cmdline里带了重复的console参数导致uart日志和实际驱动初始化顺序完全对不上。看起来是小事但调试时非常折磨人。所以你自己改LK的时候一定要把最终传给Kernel的cmdline完整打印出来确认每个key都只出现一次。4. Kernel阶段的接管与初始化4.1 DTB/DTBO机制与硬件匹配Kernel启动后会先做设备树解析。现代ARM64 Linux内核依赖设备树来描述硬件拓扑MTK平台也不例外。设备树的二进制DTB可能直接内嵌在boot image里也可能由单独的dtbo分区通过overlay方式叠加。overlay机制的好处是一个内核版本可以适配多个硬件衍生型号只需要在dtbo里覆盖差异节点即可。MTK平台的Kernel代码里arch/arm64/boot/dts/mediatek目录下会有大量mtxxxx.dtsi和具体板卡的dts文件。编译时会生成dtb再通过mkbootimg打包进boot image。如果你改了dts之后没编对dtbo或者LK加载的DTB与Kernel里配置的平台ID不匹配会出现驱动probe不到、GPIO全乱、甚至Kernel panic这类问题。在启动阶段你可以通过串口看到类似这样的日志[0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd030] [0.000000] OF: fdt: Machine model: MTK MTxxxx Board如果这里的Machine model和你板子对不上十有八九是DTB匹配出了问题。4.2 MTK Kernel启动时的日志观察点Kernel日志和Pre-loader/LK日志的差异在于Kernel启动早期串口控制台可能还没完全初始化所以要靠earlycon或者earlyprintk把早期日志打印出来。你在cmdline里一般会看到consolettyMT0,921600n8这是MTK平台串口终端节点的标准写法。调试早期部分时如果发现启动卡得很早比如在“Booting Linux”之后就完全没有输出了可以先在cmdline里加earlycon看看能不能把更早期的打印拉出来。Kernel起来之后很多MTK私有驱动的初始化日志会用[BSP]、[ISP]、[GPU]这样的tag打印。往dmesg里grep这些关键tag能看到各外设驱动是否probe成功。常见的问题比如某个驱动probe失败会直接打出init source code xxx之类这类信息在排查启动崩溃时比纯看调用栈更直观。4.3 MTK与高通启动流程的一些差异顺手说一下和昆腾对比。高通的启动链通常是PBLPrimary Boot Loader→ SBL/XBLSecondary Boot Loader→ ABLAndroid Boot Loader→ Kernel。MTK是BootROM → Pre-loader → LK/ABL → Kernel。两者最大的差异不只是命名不同而是下载模式和底层协议完全是两条路线。高通开发经常和EDLEmergency Download模式、Firehose协议打交道MTK则是BROM/Pre-loader模式和DADownload Agent协议。mtkclient和SP Flash Tool面对的是MTK的DA协议和高通的QFIL工具不是一回事。另外MTK平台的分区表非常“散”preloader、nvram、proinfo这些分区在移植和调试时经常要特别小心。高通平台里没有完全对应的概念从高通平台转过来的人往往需要一点时间适应“嘿嘿这个分区怎么又满了”这种现状。5. 实战调试与常见问题排查5.1 工具链准备与Log抓取MTK平台开发调试我建议你常备这几样一块USB转串口小板接板子的UART调试口波特率设为9216008N1。Linux下用minicom或tioWindows下用SecureCRT或MobaXterm。一台能装驱动的电脑安装MTK VCOM驱动保证设备进入BROM/Pre-loader模式时能被工具识别。SP Flash Tool或mtkclient用来刷机和做分区备份。adb/fastboot工具链用于系统起来之后的常规调试。抓日志时有一点要记住Pre-loader和LK阶段的日志只有通过串口能看到系统起来之后也可以从/sys/fs/pstore/下看上一次启动的ramoops但那只能覆盖Kernel段Pre-loader/LK段基本没有留存。所以如果你要排查“系统起不来但其实BootROM已经正常工作了”这种问题串口日志几乎是唯一入口。5.2 卡死在Pre-loader阶段的排查思路设备上电后串口完全没有输出首先别急着怀疑Pre-loader镜像问题。先按住音量上/下插USB看电脑有没有枚举出COM口。如果枚举出来了说明BootROM本身是活的BootROM到Pre-loader的交接大概率也没问题问题可能出在Pre-loader没运行起来或者它的UART没输出。常见的几类情况PMIC供电时序异常vcore电压不对CPU根本没有稳定时钟日志自然什么都没有。这种要上示波器量各路供电的时序。DDR training失败换了内存颗粒、改了频率或者DDR参数表配置不对。日志可能停在DDR calibration相关阶段或者干脆无输出。BootROM找不到合法介质比如preloader没烧进去、烧到了错误的分区BootROM会进入下载模式等待工具连接串口上也可能没输出。波特率不匹配有些板子的UART波特率可能是115200而不是921600先试几个常见波特率再判断。我个人的排查习惯是先按按键进BROM下载模式看工具能否识别设备。能识别就能把电脑和板子之间的USB链路排除掉再回头查UART/PMIC/DDR这些硬件环境。如果BROM都进不去那就从电源、时钟、复位开始查这个时候别谈什么代码问题硬件还没亮软件无从谈起。5.3 卡死在LK或Kernel阶段的排查思路如果设备能进Pre-loader且日志显示了Load LK但屏幕一直黑的或者停在Logo不动重点检查LK阶段。先看串口日志停在哪一行常见的有Load boot image failed说明LK找不到boot分区或者boot image头部magic不对。检查分区表和烧写内容。vbmeta verify failedAVB校验失败。这种情况在user build里很常见一般是固件签名和vbmeta不匹配。工程机可以用fastboot oem unlock解锁但不能说这是最佳实践只是调试时需要明确原因。LCM初始化卡住屏幕不亮但有日志可能是MIPI DSI配置不对或者LCM的reset脚时序有问题。Kernel阶段卡住第一件事是确认串口控制台有没有完整打印。如果Kernel启动日志在某一处停了可以先在cmdline里加earlycon用fastboot boot临时加载一个带调试参数的内核镜像而不用反复烧机。日志里出现Kernel panic - not syncing的话把panic前的function trace和dtsi里的相关节点结合起来看MTK平台的很多panic都是由于某个外设驱动在probe阶段访问了未就绪的硬件资源导致。5.4 一个典型的“启动一半”问题排查实例我之前处理过一个case机器冷启动偶尔能进系统但概率只有三分之一。串口日志显示每次卡死的位置不一样有时候在DDR training有时候在Kernel的initcall阶段。查了很久最终发现是电源轨的纹波偏大导致DDR在高负载下偶发错误表现出来的现象就是启动链路各阶段随机失败。这类问题最坑的就是“每次报错位置都在变”因为根因在硬件不稳定而软件只是在不同阶段被引爆。排查时不能被表象带偏要用示波器量供电、看时钟波形同时用长时间压测来复现。如果只盯着软件日志去改配置很可能白忙一周。6. 最后再聊几个容易被忽略的启动细节写到这里还想分享几个实际项目中容易忽略的点这些不是理论是我踩过的坑。第一个量产机可能把Pre-loader和LK阶段的UART日志整个关掉了。系统启动失败时你只能看到一个黑屏串口一点输出都没有。这时候不要怀疑自己工具坏了先确认固件是不是release版本。Release版本一般会关掉调试日志以节省资源和时间只有在userdebug/eng版本里才会保留完整打印所以调试阶段尽量用带日志的固件。第二个改内存颗粒或者闪存型号之后除了改Pre-loader里的DDR参数还要同步检查分区表。MTK的分区表通常由partition_table*相关文件或工具生成Flash容量变了分区大小、起始地址都要调整。否则Pre-loader从错误的物理地址读分区表轻则读不到LK重则把不该覆盖的区域写坏。第三个一定要重视开关机、关机充电、闹钟开机这些特殊启动场景。很多项目在正常开机路径上没问题一插充电器就自动开机、一设闹钟就死机这类问题往往和boot_reason的判断有关。Pre-loader和LK里会根据PMIC和按键状态判断是“冷启动”还是“充电开机”分支写得不严谨就会在特殊场景下走错路径。最后说一个我自己养成的习惯拿到新平台之后我会把Pre-loader、LK、Kernel每一级交接时的关键打印单独加一行比如[BOOT] jump from PRELOADER to LK、[BOOT] jump from LK to KERNEL。这些打印平时看起来没什么用但一旦定位启动问题它们能让我在5秒钟内知道堡垒失守在哪道防线。启动流程越复杂越要靠清晰的分界点来定位问题与其等出了问题再去猜不如提前把审计日志埋好。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →