尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

MTK平台LK启动流程详解:从硬件初始化到内核加载的实战指南

发布时间:2026/9/29 2:11:10

资讯中心
01
ARTICLE

MTK平台LK启动流程详解:从硬件初始化到内核加载的实战指南

MTK平台LK启动流程详解:从硬件初始化到内核加载的实战指南
1. 为什么LK值得单独拿出来讲搞嵌入式或者做Android底层开发的朋友对LK这个词应该不陌生。LK是Little Kernel的缩写在MTK平台的启动链路里它扮演的是Bootloader第二阶段的角色。简单说芯片上电之后先跑芯片内部固化的BootROM然后跳到Preloader做最基础的DDR和时钟初始化接着才把LK拉起来。LK干的事情就多了初始化显示、按键、存储、USB解析分区表决定进正常系统还是fastboot、recovery最后把Linux内核镜像解压、校验、传参、跳转。这一套流程走完屏幕上才会出现开机动画内核才开始跑init进程。很多人觉得Bootloader就是个“跳板”没什么好深究的。但实际做项目的时候你会发现开机黑屏、卡在Logo、进不了fastboot、OTA升级失败、按键组合不生效这些问题十有八九都跟LK这一层有关系。尤其是MTK平台它的启动链路和高通差别很大高通用的是XBLABL那一套MTK则是PreloaderLK两者的分区布局、镜像格式、下载模式触发方式都不一样。你如果拿高通的经验直接套MTK大概率会踩坑。这篇文章适合谁看一是刚接触MTK平台BSP开发的工程师想搞清楚从按下电源键到内核跑起来中间到底发生了什么二是做系统移植、OTA、产线烧录的同行需要定位启动相关的问题三是对Bootloader开发感兴趣、想自己动手改LK行为的技术爱好者。我会从硬件初始化讲到内核加载把LK这一层的核心逻辑拆开配上实际项目里踩过的坑和验证过的操作步骤。代码层面以MTK常见平台为例思路是通用的换平台也能参考。2. LK在MTK启动链路中的位置与整体设计2.1 从按下电源键到LK被拉起MTK平台的启动链路可以粗略分成四段BootROM、Preloader、LK、Kernel。BootROM是芯片出厂就固化在内部ROM里的你改不了它只做一件事——根据外部引脚或者efuse的配置从NAND、eMMC、UFS或者SD卡里加载Preloader到内部SRAM运行。Preloader的体积很小通常几十KB到一百多KB因为它跑在SRAM里空间有限。Preloader的核心任务是初始化DDR、PLL、时钟树、串口然后把LK从存储里搬到DDR跳过去执行。LK跑起来的时候DDR已经可用了所以它能做的事情比Preloader多得多。LK本身是一个微内核架构的小型系统有自己的线程调度、内存管理、驱动框架。MTK在原生LK基础上做了大量定制加了自己的显示驱动、按键驱动、充电驱动、fastboot协议、meta模式等等。你可以把LK理解成一个“迷你操作系统”它的使命就是在有限的时间里把硬件环境准备好然后体面地把控制权交给Linux内核。这里有个关键点Preloader和LK之间的交接是通过一个叫boot_tag或者boot argument的结构体传递信息的。Preloader会把DDR大小、存储类型、串口配置、启动模式等信息打包好放在内存固定位置LK启动后第一件事就是去读这个结构体。如果你在移植的时候发现LK读到的DDR容量不对或者串口没输出大概率是Preloader传参那一步出了问题而不是LK本身的bug。2.2 LK的目录结构与核心模块MTK的LK代码通常放在vendor/mediatek/proprietary/bootable/bootloader/lk下面不同平台版本路径可能略有差异。进去之后你会看到几个核心目录app/存放各个功能模块的入口比如app/mt_boot/是正常启动流程app/fastboot/是fastboot模式app/recovery/是恢复模式。platform/平台相关的驱动比如platform/mediatek/下面有显示、按键、存储、USB等驱动。target/具体项目的配置文件比如target/xxx_project/里面有rules.mk、project.mk定义了这个项目用哪些驱动、分区怎么分、按键怎么映射。kernel/LK自己的内核代码包括线程、锁、定时器、内存管理。lib/通用库比如lib/libc/、lib/decompress/。这个结构的设计逻辑是“平台无关的核心 平台相关的驱动 项目相关的配置”三层分离。核心代码尽量不动换平台的时候主要改platform/和target/。实际开发中你改得最多的就是target/下面的配置文件因为按键映射、分区表、启动模式这些跟具体项目强相关。2.3 启动模式是怎么决定的LK启动后会先判断当前应该进哪个模式。判断依据主要有几个来源Preloader传过来的启动模式、按键组合、USB连接状态、RTC闹钟、充电状态。MTK的优先级逻辑通常是这样的如果Preloader标记了强制模式比如产线烧录模式直接进对应模式。检查是否有按键组合被按下比如音量上电源进recovery音量下电源进fastboot。检查USB是否连接且主机发了fastboot命令。检查RTC闹钟是否触发如果是则进正常系统。检查电池电量是否过低过低则进充电模式。以上都不满足进正常系统。这个优先级顺序很重要。我遇到过一个问题项目要求音量下电源进fastboot但实测发现按住组合键后还是进了正常系统。排查后发现是按键检测的时机太晚LK已经走过了模式判断的分支。解决办法是把按键检测提前到显示初始化之前并且加一个短延时等待按键电平稳定。这个细节在MTK的默认代码里不一定处理得很好需要根据实际硬件调整。3. 硬件初始化LK到底初始化了什么3.1 串口初始化与调试输出LK启动后的第一件事通常是初始化串口因为后面所有调试信息都要靠串口输出。MTK平台的串口驱动在platform/mediatek/uart/下面核心是配置波特率、数据位、停止位、校验位。默认波特率一般是921600或者115200具体看项目配置。这里有个实操经验如果你焊接的串口线质量不好或者走线太长921600的波特率可能会丢数据。我建议调试阶段先用115200稳定后再切到921600。另外MTK的串口引脚有时候会和GPIO复用需要在target/的配置文件里确认UART_PORT和引脚mux设置正确。如果串口没输出先量一下TX引脚有没有波形有波形但乱码就是波特率不对没波形就是引脚配置或者时钟没开。串口初始化的代码逻辑大致是这样的void platform_early_init(void) { mt_uart_init(UART_PORT, BAUD_RATE); dprintf(INFO, LK started, UART initialized\n); }dprintf是LK里的调试打印宏INFO是日志级别。你可以通过修改日志级别来控制输出量调试阶段开SPEW级别量产阶段关到CRITICAL减少启动时间。3.2 显示初始化与开机Logo显示初始化是LK里比较重的一块因为要加载Logo图片、配置LCD控制器、初始化背光。MTK的显示驱动在platform/mediatek/lcm/下面每个屏幕型号对应一个驱动文件里面定义了分辨率、时序参数、初始化命令序列。Logo图片通常放在logo.bin分区里LK会从存储里读出来解析成RGB数据然后通过DMA送到LCD控制器。这里的关键参数是LCD的时序hsync、vsync、hbp、hfp、vbp、vfp、clock。这些参数如果配错屏幕会花屏、偏移或者完全不亮。我一般会拿屏幕厂商给的规格书把时序参数抄到驱动文件里然后用示波器量一下时钟频率是否匹配。有个坑要注意MTK的LK支持多Logo显示比如充电Logo、低电量Logo、开机Logo。这些Logo的切换逻辑在app/mt_boot/里面如果你发现充电时显示的Logo不对或者开机Logo一闪而过就要去检查Logo分区的偏移和大小是否和分区表一致。3.3 存储初始化与分区表解析LK要能读内核镜像、读Logo、读分区表所以存储驱动必须初始化。MTK平台常见的存储有eMMC和UFSLK里对应的驱动在platform/mediatek/mmc/和platform/mediatek/ufs/下面。初始化的核心是配置时钟、电压、总线宽度然后枚举设备、读取CID、CSD、EXT_CSD等寄存器。分区表在MTK平台通常有两种形式一种是MBR或GPT另一种是MTK自定义的PGPT。分区表信息一般存放在存储的固定偏移处LK启动时会去解析然后构建一个分区链表。后面读内核、读ramdisk、读logo都是通过分区名来查找的。这里有个实际案例某项目OTA升级后无法开机串口日志显示LK找不到boot分区。排查后发现是OTA升级时分区表被改动了boot分区的偏移变了但LK里缓存的旧分区表没更新。解决办法是在LK启动时强制重新解析分区表而不是用Preloader传过来的缓存。这个改动在platform/mediatek/partition/里面把partition_table_init()的调用时机提前并且加一个标志位强制刷新。3.4 按键与PMIC初始化按键初始化看起来简单但实际项目里问题不少。MTK的按键驱动在platform/mediatek/keypad/下面支持GPIO按键和PMIC按键两种。GPIO按键就是直接读GPIO电平PMIC按键是通过PMIC的寄存器读按键状态。按键检测的核心是去抖。机械按键按下时会有几十毫秒的抖动如果不去抖LK可能会误判。MTK的默认代码里通常有一个简单的延时去抖但延时时间可能不够。我一般会把去抖时间设到50ms以上并且连续采样三次确认电平稳定。PMIC初始化是LK里另一个关键环节。PMIC负责给各个模块供电如果PMIC没初始化好DDR、存储、显示都可能工作不正常。MTK的PMIC驱动在platform/mediatek/pmic/下面初始化流程包括读PMIC ID、配置输出电压、使能各路LDO、设置充电参数。这里要注意的是PMIC的寄存器操作通常通过I2C或者SPI如果I2C总线没初始化好PMIC读写会失败LK会卡在PMIC初始化那一步。4. 内核加载从镜像读取到跳转执行4.1 内核镜像的格式与解压MTK平台的内核镜像通常是boot.img里面包含kernel、ramdisk、dtb设备树和签名信息。boot.img的头部有一个header结构定义了各个段的大小和偏移。LK读boot.img的时候先读头部解析出kernel的加载地址、ramdisk的加载地址、dtb的加载地址然后把对应的数据搬到DDR的指定位置。如果内核是压缩的比如gzip、lz4LK还需要解压。MTK的LK里集成了lib/decompress/库支持多种压缩格式。解压的时候要注意目标地址不能和压缩数据重叠否则会覆盖。我一般会在DDR里预留一块足够大的区域给解压后的内核地址在target/的配置文件里定义。这里有个参数计算的过程假设内核压缩后是8MB解压后是20MBDDR从0x40000000开始ramdisk放在0x4A000000那么内核解压地址可以放在0x40008000留出32KB给内核头部。这个地址不是随便定的要参考内核的TEXT_OFFSET和PHYS_OFFSET确保内核启动后能正确映射。4.2 设备树的处理与传递设备树DTB在ARM Linux里是必须的它描述了硬件的拓扑结构。LK的任务是把DTB从boot.img里读出来做一些必要的修改比如修改bootargs、memory节点、chosen节点然后把DTB的地址传给内核。MTK平台有个特殊的地方它会在DTB里追加一些MTK自己的节点比如mtk,msdc、mtk,i2c、mtk,disp。这些节点在LK阶段可能被修改比如根据实际检测到的存储类型修改msdc节点的属性。如果你发现内核启动后某个驱动probe失败可以去LK里看看对应的DTB节点有没有被正确修改。传递DTB的方式有两种一种是直接放在bootargs里指定地址另一种是通过x0寄存器传递。ARM64通常用x0ARM32用r2。MTK的LK代码里会处理好这个你只需要确认DTB的地址和大小正确就行。4.3 启动参数bootargs的构造bootargs是LK传给内核的字符串里面包含控制台设备、根文件系统、内存大小、启动模式等信息。MTK的LK里bootargs的构造逻辑在app/mt_boot/下面通常会拼接几部分固定的参数、从分区表读的参数、从环境变量读的参数。一个典型的bootargs长这样consolettyMT0,921600n1 root/dev/ram init/init androidboot.hardwaremtxxxx androidboot.selinuxpermissive这里console指定串口控制台root指定根文件系统init指定init程序androidboot.*是Android特有的参数。如果你要改启动行为比如进recovery模式可以在bootargs里加androidboot.moderecovery。有个经验bootargs的长度有限制通常是512字节或者1024字节。如果你加的参数太多可能会被截断导致内核读不到某些参数。我一般会在LK里打印最终的bootargs确认没有截断。4.4 跳转内核前的最后准备跳转内核之前LK要做几件收尾工作关闭中断、关闭MMU、清理缓存、设置CPU寄存器。MTK的LK里跳转函数通常是jump_to_kernel()或者boot_linux()里面会调用arch_disable_cache()、arch_disable_mmu()然后把内核入口地址、DTB地址、机器码ARM32放到对应寄存器最后执行一条跳转指令。这里最容易出问题的是缓存一致性。如果LK在跳转前没有正确清理缓存内核启动后可能会读到旧数据导致随机崩溃。MTK的代码里通常有clean_invalidate_cache()但如果你自己改了内存布局要确保新布局的缓存也被清理了。另一个坑是CPU状态。如果LK把某个CPU核跑在非安全模式或者异常状态下内核启动后可能会挂。我一般会在跳转前把所有CPU核都复位到已知状态只留一个核跑LK其他核用wfi或者spin等待内核唤醒。5. 常见问题与排查技巧实录5.1 串口无输出或输出乱码串口没输出是最常见的问题排查思路如下现象可能原因排查方法完全无输出串口引脚配置错误检查target/里的UART引脚mux完全无输出波特率不匹配尝试115200和921600完全无输出Preloader没跑起来检查Preloader是否烧录正确输出乱码时钟频率不对量UART时钟确认分频系数输出乱码数据位/停止位不对确认是8N1还是7E1输出部分丢失波特率太高降到115200试试我遇到过一次串口完全没输出的情况排查了半天发现是硬件上UART的TX和RX接反了。这种低级错误在实际项目里并不少见尤其是自己焊的调试板。5.2 卡在Logo或黑屏卡Logo或者黑屏通常和显示初始化有关。排查步骤确认串口有输出排除是系统完全没跑起来。看日志里显示初始化是否成功有没有报错。检查Logo分区是否存在Logo数据是否完整。检查LCD时序参数是否和屏幕规格书一致。量背光使能引脚确认背光是否点亮。有个案例某项目换了屏幕供应商新屏幕的初始化命令序列和旧的不一样LK里还是用旧的命令序列导致屏幕不亮。解决办法是更新lcm驱动文件里的初始化命令并且确认时序参数匹配。5.3 进不了fastboot或recovery按键组合不生效的问题排查思路确认按键GPIO配置正确按下时电平变化符合预期。确认按键检测在模式判断之前执行。确认去抖时间足够不会误判。确认Preloader没有强制覆盖启动模式。确认USB连接状态没有干扰模式判断。我踩过一个坑项目要求音量下电源进fastboot但实测发现只要插着USB就进fastboot不插USB才进正常系统。排查后发现是USB检测的优先级高于按键检测LK看到USB连接就默认进fastboot。解决办法是调整优先级按键检测优先于USB检测。5.4 内核启动后崩溃或驱动probe失败内核启动后崩溃很多时候是LK传参不对。排查方向检查bootargs是否完整有没有被截断。检查DTB地址和大小是否正确。检查内存节点是否和实际DDR大小一致。检查initrd地址是否和内核预留区域冲突。检查CPU寄存器状态是否符合内核预期。有个经验如果内核启动后打印Unable to handle kernel paging request大概率是内存布局不对。可以去LK里打印DDR的起始地址和大小和内核dmesg里的Memory:行对比看是否一致。5.5 OTA升级后无法开机OTA升级涉及boot分区、recovery分区、system分区的更新。如果升级后无法开机排查思路检查boot.img是否完整签名是否匹配。检查分区表是否被改动boot分区偏移是否变化。检查LK是否缓存了旧分区表需要强制刷新。检查bootargs里的androidboot.slot是否正确A/B升级。检查内核和ramdisk是否匹配版本是否一致。MTK平台的OTA升级通常由recovery模式下的update_engine或者MTK升级工具完成。升级完成后LK会读新的boot.img启动。如果升级过程中断电boot分区可能写了一半导致LK读到的镜像损坏。这种情况下需要进fastboot重新烧录boot.img。6. 实操建议与个人体会6.1 调试环境的搭建调试LK串口是必须的。我建议准备一根质量好的USB转串口线芯片用FT232或者CP2102不要用那些几块钱的CH340高速波特率下容易丢数据。串口终端用minicom或者SecureCRT开日志保存方便回溯。另外MTK平台通常支持Meta模式和SP Flash Tool烧录。SP Flash Tool可以单独烧录Preloader、LK、boot等分区调试的时候很方便。如果你改了LK代码编译出lk.bin用SP Flash Tool烧到lk分区重启就能看到效果。6.2 代码修改的注意事项改LK代码的时候有几个原则尽量不改核心代码改target/和platform/下面的配置。加日志用dprintf(INFO, ...)不要用printf因为LK的printf可能没初始化。改内存布局的时候画个内存映射图标清楚各段的地址和大小。改完代码先编译确认没有警告和错误再烧录。烧录前备份原始镜像万一改坏了可以回退。6.3 启动时间的优化LK的启动时间直接影响开机速度。优化方向关掉不必要的日志输出把日志级别调到CRITICAL。延迟初始化非关键模块比如充电检测、Meta模式。优化存储读取速度比如提高eMMC时钟频率。减少Logo图片的大小用压缩格式。并行初始化独立模块比如显示和存储可以同时初始化。我实测过把日志级别从INFO调到CRITICAL启动时间能减少100ms左右。如果项目对开机速度要求高这些优化都值得做。6.4 版本管理与协作LK代码通常和Android源码一起管理用git或者repo。我建议给LK单独建一个分支每次修改都提交写清楚改了什么、为什么改。多人协作的时候target/下面的配置文件容易冲突最好约定好谁负责哪个项目避免同时改同一个文件。另外LK的编译产物lk.bin要和Preloader、boot.img的版本对应。如果Preloader和LK的接口变了比如boot_tag结构体改了两边必须同时更新否则会启动失败。我一般会在项目文档里记录每个版本的Preloader、LK、boot.img的对应关系方便回溯。6.5 一个真实的调试案例最后分享一个我实际遇到的案例。某MTK项目量产阶段发现千分之三的设备开机卡在Logo。串口日志显示LK在初始化存储时超时。排查后发现是eMMC的时钟频率设得太高部分批次的eMMC芯片在高温下无法稳定工作在高速模式。解决办法是在LK里加一个降速重试逻辑如果高速模式初始化失败自动降到中速模式重试。这个改动在platform/mediatek/mmc/里面加了一个retry循环和频率切换。这个案例说明LK的稳定性不仅取决于代码逻辑还和硬件批次、工作环境有关。量产阶段一定要留足够的测试时间覆盖高低温、高低压、不同批次的物料。LK这一层的东西说起来就是初始化硬件、加载内核但实际做起来每个模块都有细节每个项目都有坑。希望这篇内容能帮你少走点弯路。如果你在调试过程中遇到奇怪的问题先看串口日志再看代码最后查硬件大部分问题都能定位到。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。