ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OpenHarmony标准系统内核启动全流程解析:从U-Boot到init的排障指南

OpenHarmony标准系统内核启动全流程解析:从U-Boot到init的排障指南 搞OpenHarmony设备开发最难啃的骨头之一就是标准系统内核启动。我最早接触的时候对着串口日志一头雾水先是U-Boot刷屏然后“Starting kernel ...”之后就没动静要么无限重启要么卡死在设备树解析。折腾过几天才明白标准系统启动不是一个黑盒而是一条可以拆成若干节点的链路每个节点都有对应的配置和日志。这篇文章以RK3568这类常见开发板为背景把OpenHarmony标准系统从上电到内核态、再到init进程拉起的整个过程拆开讲透同时会带上我在编译、烧录、调参、排障过程中踩过的实际坑。适合正在做系统移植、驱动适配或者准备跑XTS认证的工程师参考。先说清楚一个容易绕晕的概念OpenHarmony有三种系统形态轻量系统M核、小型系统A核和标准系统富设备。我们说的标准系统跑的是Linux内核启动路径和传统Linux、AOSP有很多相似之处但它又加了自己的一套init框架、HDF驱动框架和HDI接口规范。所以网上那些通用Linux启动文章只能解决一半问题另一半必须结合OpenHarmony的镜像分区、启动参数和init机制来理解。1. OpenHarmony标准系统启动流程全景1.1 标准系统的启动链路和轻量系统完全不同轻量系统比如Hi3861用的是LiteOS-M没有MMU启动就是简单的ROM到RAM的拷贝然后一个main函数整个过程不到几百行代码就能讲完。标准系统则复杂得多以我使用的RK3568开发板为例硬件上电后要经历BootROM固化代码初始化DDR加载U-Boot到内存U-Boot读取分区和启动参数加载boot.img中的内核Image和DTB内核自解压并初始化内存、中断、设备树、驱动框架最后挂载rootfs并拉起init进程。这个链路里U-Boot是第一个可以干预的节点。OpenHarmony标准系统一般会使用U-Boot配合fastboot协议用来刷机、启动内核、调整bootargs。烧录时我们会看到一堆分区boot、vendor、system、userdata等。boot分区里通常打包了内核Image.gz、DTB文件和一个ramdiskvendor分区放硬件厂商的驱动和HDI实现system分区放OpenHarmony的公共框架。理解这个分区关系才能看懂后面的启动日志和异常。还有一个常见的误区是认为“标准系统启动”就是“内核启动”。其实内核启动只是前半段后半段还要由system/init进程去解析init.cfg、启动ueventd、hilogd、hdf服务、以及各种系统服务。很多新人在内核日志已经正常打印“Run /init as init process”之后看到设备反复重启就以为内核还有问题实际上问题在用户态init阶段。1.2 从CPU复位到U-Boot的一段“看不到”的流程CPU上电后首先执行的是固化在芯片内部的BootROM代码。这段代码没有源码给你调它的任务是做最基础的时钟、DDR初始化然后根据启动介质选择eMMC、SD卡、USB等把U-Boot加载到内存。RK3568的BootROM还会在串口打印少量信息但很多板子的调试串口在这个阶段还没有完全初始化所以你不一定看得到。接下来U-Boot开始运行它会读取自己环境变量中的bootcmd决定去哪里取内核镜像。常见的OpenHarmony开发的U-Boot会支持fastboot命令所以我们在PC端可以通过fastboot flash boot boot.img写镜像也可以通过fastboot boot boot.img临时启动一个内核不做持久烧录。这个“临时启动”在调试时非常有用配合fastboot boot可以快速尝试不同内核省去反复擦写eMMC的时间。U-Boot阶段你能看到的有效信息包括芯片型号、DDR容量、启动介质、加载地址、Starting kernel ...。如果串口日志停在这一句之前基本可以判断问题在U-Boot本身或boot.img的加载地址不对如果停在这一句之后则是内核早期初始化卡住需要进入下一层去排查。1.3 内核启动和用户态启动的边界在哪里很多人喜欢用日志里的一句话来判断内核是否启动完成。我个人的经验是看“Run /init as init process”这一行它是Linux内核在完成初始化、准备切换到用户态时打印的。在这之前内核已经完成了自解压、设备树解析、CPU和内存初始化、中断控制器、时钟、串口控制台、以及大部分内建驱动的初始化。OpenHarmony标准系统在init进程之后首先会解析第一个配置文件通常是根文件系统中的init.cfg或/init。这个init会先处理first_stage_init完成基础挂载和权限准备工作然后再进入第二阶段启动selinux、硬件服务等。如果你在日志里看到类似[Init] Parse init.cfg、[Init] start service说明已经进入用户态。此后出现的问题应该优先去查hilog、init日志、服务状态而不是继续改内核配置。顺带说一个关联很紧密的适配点OpenHarmony的HDIHardware Device Interface就是在这个阶段加载的。比如相机相关的camera_host进程会通过HDI调用vendor分区里的硬件驱动实现。如果内核启动参数少了某些挂载项vendor分区没有被正确挂载HDI服务就会注册失败表现为相机服务起不来或反复重启。这也是我在后面第4章会展开的问题之一。2. 驱动内核启动的三大关键要素设备树、bootargs和init框架2.1 设备树先让串口能出字后面的事情才有意义标准系统普遍采用设备树DTB描述硬件Linux内核在启动早期就要解析DTB匹配machine描述初始化内存、中断、时钟和串口。对于调试来说最致命的就是console串口配置不对内核本身可能已经正常跑起来但你什么都看不到只能干瞪眼。以RK3568为例设备树路径通常在kernel/linux/arch/arm64/boot/dts/rockchip/rk3568-evb.dts。你要确认两件事第一chosen节点里的bootargs和stdout-path是否指向正确串口第二对应的uart节点status是否为okay波特率是否和外接设备一致。一个典型的最小配置片段如下chosen { bootargs consolettyFIQ0,115200 rootPARTUUID... rootfstypeext4 rw init/init; stdout-path serial0:115200n8; };如果你改开发板型号或者换了一颗SoC却忘了同步修改这里的串口别名就会遇到“内核似乎在跑但串口没日志”的经典问题。此时可以靠earlycon或者earlyprintk参数让内核在标准console初始化之前就通过指定物理寄存器地址输出日志。例如chosen { bootargs earlyconuart8250,mmio32,0xfe660000 consolettyFIQ0,115200 ...; };0xfe660000需要根据实际芯片串口寄存器地址填写这个方法在真正排查早期卡死时非常救命。2.2 bootargs逐参数拆解为什么改动一个参数会导致开机失败bootargs是内核启动的命令行OpenHarmony标准系统的bootargs由U-Boot环境变量传递也可以由设备树chosen节点覆盖。下面这张表是我整理的高频参数和实际含义参数作用踩坑点consolettyFIQ0,115200指定内核调试串口和波特率RK3568常用ttyFIQ0Hi3516可能用ttyAMA0rootPARTUUIDxxx指定根文件系统所在分区分区编号对不上会报VFS panicrootfstypeext4强制根文件系统类型有的平台是f2fs写错挂不上rw以读写方式挂载根fs生产固件可能用ro调试建议rwinit/init指定内核执行的第一个用户态程序路径或权限不对会起不来loglevel8放大内核日志等级发布版建议降回4ohos.boot.required_mountOpenHarmony init要求挂载的关键分区列表缺失后system/vendor起不来卡在init早期androidboot.hardwarerk3568标明硬件平台供init和HDI选型写错会加载错误硬件配置ohos.boot.required_mount是OpenHarmony特有的参数格式类似/vendor/vendor或/system/systeminit进程启动后会根据这个参数逐一检查挂载点。XTS认证测试时尤其要检查这个参数是否齐全否则很多用例会因为挂载点不存在而直接fail。这里还想提一个和通用Linux经验互通的点如果你用过Ubuntu大概知道修改默认启动内核要去改GRUB配置。OpenHarmony设备上没有GRUB对应的就是U-Boot环境变量bootargs和bootcmd。调试过程中临时切换到另一个内核镜像也完全可以靠修改U-Boot的启动命令完成不需要每次重新烧录整个系统。2.3 HDF和HDI内核启动的“最后一公里”并不在内核内核启动完成后系统要能真正干活还需要硬件驱动框架HDFHardware Driver Foundation和对应的HDI接口正常工作。HDF负责统一管理内核态和用户态驱动HDI则是对上层系统服务暴露的稳定硬件接口。比如相机服务看到的是ICameraDevice底层的sensor、ISP、闪关灯等驱动则是厂商在vendor分区用HDI方式实现。HDF驱动会在init阶段被加载加载顺序和配置由hdf相关配置文件决定。如果某个驱动在启动时一直初始化失败可能的表现包括init启动卡住、某个服务反复重启、或者hilog里出现HdfDeviceObject、HdfDriverEntry之类的报错。我在调试camera相关问题时遇到过camera_host进程反复创建和退出最后发现是vendor分区里的HDI库和内核中的sensor驱动版本对不上接口返回版本号不匹配导致服务主动退出。所以在理解内核启动时不要把视野局限在vmlinux和DTB上。内核启动只负责把CPU、内存和基础总线跑通真正让设备可用还要看OpenHarmony的init是否顺利挂载vendor分区、HDF能否枚举到设备、HDI是否注册到服务框架。这三层环环相扣任何一个环节出问题开机表现都可能像“内核挂了”。3. 实操从源码编译到抓取一份完整的内核启动日志3.1 编译OpenHarmony标准系统的内核镜像如果你拿到了OpenHarmony源码最常见的整体编译方式是使用hb或build.sh。以3.2/4.0版本为例选择产品后执行hb set -p rk3568 hb build -f编译生成的镜像会在out/rk3568/下其中与内核直接相关的是boot.img。如果只想单独编译内核可以使用build.sh的kernel目标./build.sh --product-name rk3568 --build-target kernel --ccache这个流程的本质是调用build/kernel/linux/build_kernel.sh它会根据产品配置选择内核版本和defconfig然后生成内核Image、DTB并打包到boot.img。第一次编译时工具链下载和缓存可能会占很长时间建议给电脑留足磁盘空间并提前设置好repo同步。编译产物通常会出现在out/kernel/src_tmp/linux-5.10/arch/arm64/boot/Image.gz out/kernel/src_tmp/linux-5.10/arch/arm64/boot/dts/rockchip/rk3568-evb.dtb out/rk3568/packages/phone/images/boot.img拿到Image.gz和dtb之后你可以用U-Boot的fastboot boot临时加载也可以重新打包boot.img再烧录。需要注意的是不同产品配置对应的defconfig不一样比如要打开某个camera sensor驱动通常要在内核dts和defconfig里同时加。3.2 用串口抓日志先解决Windows调试器占用串口的问题启动日志主要通过调试串口输出。开发板上一般会引出UART0或者一个DEBUG口通过USB转串口模块连接到电脑。Linux下我用minicom或picocomWindows下用PuTTY等工具。连接时三件事必须确认波特率一般是115200、地线共地、不要开RTS/CTS硬件流控。这里务必说一下Windows下的一个常见坑设备管理器里串口设备状态显示“此设备已为 Windows 内核调试程序预留以便在此启动会话持续期间使用。 (代码 53)”。这个提示的意思是系统把串口资源分配给了Windows内核调试器普通串口工具自然打不开。解决办法很简单用管理员权限打开命令行关闭内核调试bcdedit /debug off然后重启电脑。重启后代码53就会消失串口可以被正常打开。这个问题和OpenHarmony无关纯粹是开发环境配置但我见过好几个同事在抓日志当天卡在这一步所以值得单独写出来。3.3 在U-Boot里手动调整启动参数并观察差异正常启动时U-Boot会从环境变量里读取bootargs。如果遇到启动异常最好的办法是先打印当前环境变量再做最小化修改。以RK3568为例进入U-Boot命令行后printenv bootargs printenv bootcmd如果bootcmd指向eMMC中的boot分区那么执行boot就会继续加载内核。想要临时换成一组新的bootargs可以这样setenv bootargs consolettyFIQ0,115200 rootPARTUUIDxxxxxxxx rootfstypeext4 rw init/init loglevel8 ohos.boot.required_mount/vendor/vendor saveenv bootsaveenv会把环境变量写入存储如果不希望永久修改就不要执行saveenv重启后恢复原样。这个技巧和“临时启动一个内核”的思路保持一致是日常调板最常用的手段。抓取日志的时候建议同时在PC端保存一份完整串口log并从BootROM或U-Boot阶段开始记录不要只录内核阶段。因为U-Boot日志里会打印加载地址、内存大小、分区信息这些对判断后续问题非常有价值。3.4 用fastboot boot启动内核镜像做A/B快速调试如果你编译生成了新的boot.img想在不烧写的情况下快速验证能不能启动可以用fastbootfastboot flash boot out/rk3568/packages/phone/images/boot.img fastboot reboot如果想更“轻”某些平台支持fastboot boot boot.img直接把镜像加载到内存并启动不影响eMMC上的现有boot分区。这个方法很适合在内核启动阶段反复测试一旦调好再固化。不过需要注意fastboot boot对boot.img的打包格式有要求必须和U-Boot约定的加载方式一致否则会报地址错误或者“wrong image format”。4. 常见启动问题与排查技巧实录4.1 串口完全没有输出先别急着改内核遇到串口没日志我的排查顺序是第一看电源指示灯和复位按键确认板子真的在上电第二看串口工具和模块排除接线、波特率、驱动占用第三才考虑固件和内核。Windows下先看设备管理器如果出现代码53就用前面说的方式关掉内核调试如果串口模块识别不到换一个USB口或者重新插拔。硬件确认没问题后U-Boot阶段应该能看到至少几行信息。如果连U-Boot日志都没有可能是BootROM启动介质选择不对比如eMMC里没有有效的U-Boot或者拨码开关让板子进入了下载模式。遇到这种情况先检查启动介质和烧录工具是否能识别设备再尝试重新烧录U-Boot。4.2 卡在“Starting kernel ...”之后重点查DTB加载地址U-Boot打印“Starting kernel ...”说明它已经把控制权交给了内核但内核可能因为DTB地址不合法、DTB格式损坏或内存参数不对而起不来。RK3568平台U-Boot通常会用一个固定的dtb加载地址比如0x...如果和内核Image解压地址冲突或超出内存范围就会黑屏无日志。此时可以重新加载一个干净的DTB或者在U-Boot命令行手动load内核Image和dtb到不同地址再通过booti启动。调试早期我把bootargs简化到最短只保留console和root排除其它参数干扰setenv bootargs consolettyFIQ0,115200 rootPARTUUIDxxxx rootfstypeext4 rw init/init loglevel8 boot如果这样能出日志再逐步加回ohos相关参数判断是哪个参数引起的卡死。4.3 内核日志最后提示“VFS: Unable to mount root fs”这句日志说明内核已经成功跑到挂载根文件系统的一步但找不到或读不了root分区。最常见的原因是root参数里的分区编号、PARTUUID和实际烧录分区不一致。排查办法是先在U-Boot的mmc list、part list里看分区表确认boot/system/vendor分别对应哪个分区号和UUID再去改bootargs。另一个原因是根文件系统类型不对比如分区格式是f2fs但bootargs写成了ext4。OpenHarmony标准系统在不同产品上会选不同文件系统查清楚product配置再改参数。还有一种情况是rootfs镜像本身损坏重新烧录system分区或userdata分区即可。4.4 init阶段不断重启先看required_mount和SELinux如果日志已经出现Run /init并且能打印[Init]相关日志但系统反复重启问题基本不在内核而在用户态初始化。最常见的是ohos.boot.required_mount缺少vendor或sys_prod等关键挂载点导致init的first_stage初始化失败进程直接abort。检查/init.cfg或/system/etc/init里的挂载规则并对照U-Boot环境变量里的required_mount参数。如果SELinux处于enforcing模式还要看avc denied日志可能是权限标签不对导致某个驱动或服务起不来。调试阶段可以临时加androidboot.selinuxpermissive等系统稳定后再恢复。4.5 camera和HDI相关启动异常日志指向vendor驱动加载失败当内核和init都正常但相机无法打开或camera服务自动退出可以从hilog看到类似Load libcamera_xxx.z.so fail或HdfDeviceBind失败。这类问题的根源一般在vendor分区HDI实现库缺失、内核sensor驱动没配、设备树里camera节点status不是okay、或者HDI库的依赖库版本不匹配。排查时我会按顺序走先确认内核日志里sensor驱动是否成功probe再确认/vendor/lib64/hw/下有没有对应HDI库然后确认/vendor/etc/camera/下配置文件的sensor名称是否和设备树匹配。启动阶段如果HDF加载顺序太早、依赖的外设时钟还没准备好也会出现偶发性失败可以在init配置文件里给camera服务加上延时或依赖属性。4.6 XTS认证失败可能与启动参数有关XTS兼容性测试会检查系统在标准启动流程下是否符合OpenHarmony兼容性定义。我遇到过几个case失败最后追到是和bootargs有关。比如测试要求system、vendor等分区必须按规范挂载如果ohos.boot.required_mount参数不完整init会跳过某些挂载点然后对应测试模块就直接fail。另外测试过程中如果串口console被不断输出日志也有可能影响一些高并发case的稳定性。正式跑XTS前建议把loglevel改为4并确认不含调试用的earlycon。但不要误删required_mount否则会引发更多失败。4.7 启动问题速查表现象可能原因快速定位手段串口设备被占用代码53Windows内核调试占用bcdedit /debug off后重启完全无U-Boot日志BootROM启动介质错误检查拨码开关、烧录U-Boot卡在Starting kernelDTB地址或Image格式错误用fastboot boot临时启动VFS含Unable to mount root fsroot参数或文件系统类型错误在U-Boot part list核对分区反复重启required_mount缺失或SELinux拦截查看[Init]日志和hilogcamera_host退出HDI库加载失败或sensor未probe查dmesg和hilogXTS部分case失败启动参数不合规对比兼容性测试要求逐项检查我在实际调板过程中最深的体会是内核启动问题不要一上来就认为是内核源码bug。先把串口打通再把bootargs简化到最小确认U-Boot和内核链路通了再逐步加参数和驱动。大多数“卡死”都能定位到DTB、分区、root参数或Windows调试器占用这几类可复现的原因。尤其是代码53这种环境问题一次就能浪费半天养成检查设备管理器的习惯后反而成了最快解决的问题。另外建议你在调一个新板子时先做一次“最小启动验证”只用console、root、init三个必要的bootargs不加载vendor、不开SELinux、不启动各种服务。等内核和串口日志稳定后再一步步加回OpenHarmony特有的挂载参数和HDI驱动。这个方法看上去麻烦其实是处理标准系统启动问题最高效的路径。我也习惯在每个板子上保留一份自己整理的bootargs注释表避免下次重新踩同样的坑。
返回列表