OpenHarmony源码树解剖—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】

编译命令还没敲,先认路。今天把源码树摊开:你改的文件会进哪张镜像。改错目录、刷错分区,表现都是「我改了,板上没反应」。

改了一行设备树,./build.sh 退出码 0,镜像也刷进去了,板上 /proc/device-tree 还是昨天的内容。再改一份 init 脚本,这次连镜像时间戳都没动。第三回改触摸的 HCS,只刷了 vendor,坐标照样歪。

这三件事看起来像三只不同的虫子,其实是同一件事:你改的文件,和你刷的那张分区,不在一条管子上。 开源鸿蒙这棵树不是「一个 rootfs 打进去就算数」。内核、设备树、用户态驱动配置、系统应用、init、fstab,各自进不同的 img。编译系统还在中间做了一份拷贝。你改源码目录,不等于改到了那份拷贝,更不等于改到了板子正在读的分区。

产品名按源码写:rk3568_evb。板级路径 device/board/rk/rk3568_evb/。内核 Linux 5.10 aarch64,用户态 32 位 musl。你工程里产品名不同,对着换。

官方仓库布局可以对照:源码获取。下面按「改哪里 → 进哪张镜像 → 怎么验」摊开,不按仓库 README 的字母表走。


1. 先把现象钉死:exit 0 只说明构建脚本没崩

全量编完,日志最后一行经常是 build success 之类。它的意思是 ninja 认为该跑的 target 都跑完了。它不保证

  • 你改的那个 .dts 被编进了 dtb
  • 你改的那个 .hcs 被编进了内核 Image 或 vendor
  • 你刷进去的那张 img,就是这次刚生成的那张
  • 板子启动时读的,就是你刷的那张

分层看,失败通常停在其中一层:

bash 复制代码
你改的文件
    ↓  构建系统看见了吗?(GN sources / checkpoint)
out/ 里的中间拷贝
    ↓  真的重编了吗?(时间戳、hcb、.config)
packages/phone/images/*.img
    ↓  打包脚本同步了吗?(pack_update 里可能是旧文件)
你刷进板子的分区
    ↓  启动时读的是这块介质吗?(SD / eMMC,resource vs boot_linux)
板上正在跑的那份

后面几篇分别写编译、打包、刷机、启动。这篇只回答一个问题:源码树里每个目录,默认会流进哪张镜像。认路比认命令值钱。


2. 整棵树只需要记住这几块

源码根目录下面东西很多。日常改板,真正会进镜像的,集中在这几处:

bash 复制代码
OpenHarmony/
├── kernel/linux/
│   ├── linux-5.10/                  # 内核源,aarch64
│   ├── patches/linux-5.10/
│   │   └── rk3568_patch/            # 芯片补丁,构建时打进 linux-5.10
│   └── config/linux-5.10/           # 碎片 defconfig,不是你 menuconfig 的那份
├── device/board/rk/rk3568_evb/
│   ├── kernel/                      # 板级 DTS、板级驱动、build_kernel.sh、logo
│   ├── cfg/                         # init / fstab
│   ├── loader/                      # MiniLoader、打包三件套、空分区占位
│   ├── camera/                      # 相机 IQ / 板级 camera 部件
│   └── audio/                       # 音频策略、板级 json
├── vendor/rk/rk3568_evb/
│   └── hdf_config/
│       ├── khdf/                    # 编进内核 Image
│       └── uhdf/                    # 打进 vendor.img
├── device/product/rk/rk3568_evb/    # 产品 json、要编哪些部件
├── drivers/hdf_core/                # HDF 框架,khdf 链进内核
├── drivers/peripheral/              # 显示 / 输入 / 音频 / 相机 HAL,多半进 vendor
├── base/                            # 系统服务、部分系统应用,进 system.img
├── foundation/                      # 窗口、图形、电话一类,进 system.img
├── applications/                    # 桌面、设置、部分预装,进 system.img
└── out/
    ├── kernel/
    │   ├── src_tmp/linux-5.10/      # 打过补丁的内核工作树
    │   ├── OBJ/linux-5.10/          # KBUILD_OUTPUT,.config / Image / dtb
    │   └── checkpoint/              # 「内核变没变」的判决书
    └── rk3568_evb/packages/phone/images/   # 各分区镜像,打包的输入

还有 prebuilts/(clang、hap-sign-tool)、build/(GN 规则)、third_party/。那些你很少改。改了 third_party 里某个 so,产物进 system 还是 vendor,看那个部件的 install_images,不要猜。

用户态是 32 位 musl。板上动态链接器是 /system/lib/ld-musl-arm.so.1,没有 aarch64 的 ld-linux。你在内核目录里用 aarch64 clang 编 .ko 没问题;你在用户态目录里编一个「随便写的诊断程序」,必须走 armv7-unknown-linux-ohos-clang,并且 --dynamic-linker=/system/lib/ld-musl-arm.so.1。编成 64 位 ELF,hdc file send 能送上去,一执行 No such file or directory------那是解释器路径不存在,不是文件没送过去。


3. 内核三份目录,别改错那一份

内核不是「一个 linux-5.10 目录」这么简单。构建时会拷、会打补丁、会另开 OBJ。三份目录职责不同。

3.1 kernel/linux/linux-5.10/:上游 + 开源鸿蒙合入的那份

这是 git 里的内核源。GN 的内核 action 把 sources 指到这里。你在这里改 drivers/xxx.c,ninja 看得到 ,下次 ./build.sh 会认为内核变了。

不要在这里改板级设备树。板级 dts 不从这里出发,从 device/board/rk/rk3568_evb/kernel/ 出发,构建脚本拷进来。你在 linux-5.10/arch/arm64/boot/dts/rockchip/ 里直接改,下次 copy_and_patch 一跑,板级那份会盖过来,或者反过来你的改动被当成「内核源改动」进了 git 脏状态,两头都不干净。

3.2 kernel/linux/patches/linux-5.10/rk3568_patch/:芯片补丁

构建把 linux-5.10 拷到 out/kernel/src_tmp/ 之后,按清单把这里的 patch 打进去。RK3568 的 DRM、ISP、PCIE PHY、一些 staging 网卡,经常以补丁形式存在,而不是直接合进 git 源。

bash 复制代码
ls kernel/linux/patches/linux-5.10/rk3568_patch | head
# 典型名字像:
# 0001-drm-rockchip-vop2-....patch
# 0012-net-usb-cdc_ncm-....patch

改补丁和改源码等效,但有两个坑。第一,补丁是对「拷贝后的树」打的,你若已经在 src_tmp 里手改过同一文件,patch 会 reject,构建停在 patch -p1。第二,cdc_ncm.c 这类文件,开源鸿蒙补丁从 struct usbnet 里删了 rx_speed / tx_speed,公版驱动还在访问这两个字段。板级 overlay 通常另给一份改过的 cdc_ncm.c 覆盖进去。补丁没打上、覆盖没做,内核编到 USB 网卡就报:

bash 复制代码
error: no member named 'rx_speed' in 'struct usbnet'

这不是网卡没插,是编译期。

自己加补丁,编号接在现有最大号后面,让 build_kernel.sh 的循环能扫到。打补丁的代码大致是:

bash 复制代码
# build_kernel.sh 里常见的形态,路径按你树里的为准
PATCH_DIR="$ROOT/kernel/linux/patches/linux-5.10/rk3568_patch"
cd "$SRC_TMP/linux-5.10"
for p in $(ls "$PATCH_DIR"/*.patch 2>/dev/null | sort); do
    echo "apply $(basename $p)"
    patch -p1 --forward --dry-run < "$p" >/dev/null 2>&1 || {
        echo "REJECT: $p"
        exit 1
    }
    patch -p1 --forward < "$p"
done

src_tmp 是打补丁后的工作树。下次构建若判定「内核没变」,这份树会留着,补丁不会重打。这就是为什么你改了 patch 目录,有时镜像里还是旧内核------checkpoint 说没变,拷贝和打补丁整段被跳过。

3.3 out/kernel/src_tmpout/kernel/OBJ:干活的地方

路径 是什么 能手改吗
`out/kernel/src_tmp/linux-5.10` 拷贝 + 打过补丁 + 拷过板级 dts/驱动的工作树 只为了「这一次增量 make」,下次 copy 会盖
`out/kernel/OBJ/linux-5.10` `KBUILD_OUTPUT`,`.config`、`vmlinux`、`Image`、dtb、`certs/` 不要当权威源。menuconfig 改它,下次合成会丢
`out/kernel/checkpoint` `last_build.info` 一类,给 `is_kernel_change` 用 删它 = 强制认为内核变了

增量编内核、编单个 .ko,命令都在 src_tmp 上跑,输出进 OBJ:

bash 复制代码
ROOT=/path/to/OpenHarmony
export PATH=$ROOT/prebuilts/clang/ohos/linux-x86_64/llvm/bin:$PATH
export KBUILD_OUTPUT=$ROOT/out/kernel/OBJ/linux-5.10
cd $ROOT/out/kernel/src_tmp/linux-5.10

make LLVM=1 LLVM_IAS=1 CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 \
     Image -j$(nproc)

编完,Image 在 OBJ 里:

bash 复制代码
ls -l $KBUILD_OUTPUT/arch/arm64/boot/Image
# 再打进 boot_linux.img,不是到此就能刷

不要在 OBJ 里改源码。 OBJ 是 out-of-tree 编译目录,.c 不在这里。改 dts 要改板级那份,再拷进 src_tmp,或者改 src_tmp 里对应文件后立刻 make dtbs。src_tmp 里的 dts 是副本,权威在 device/board/rk/rk3568_evb/kernel/


4. defconfig 有四层,menuconfig 只动最上面那层

「我 menuconfig 勾过了」和「下次全量还在」是两件事。四层从底到顶:

bash 复制代码
层 1  kernel/linux/config/linux-5.10/.../arch/arm64_defconfig
      git 里的产品碎片,copy 的起点

层 2  src_tmp 里合成出来的 rockchip_linux_defconfig
      merge_config / 拷贝 / 补丁改过的那份「即将 make defconfig 的源」

层 3  OBJ/.config
      这一次 make 真正读的。menuconfig 改的是它

层 4  device/board/rk/rk3568_evb/kernel/build_kernel.sh
      MERGED_CFG 段。每次内核重建都会再跑一遍的幂等注入
      这才是「下次合成还会在」的那一层

只改层 3,下次 make defconfig 或脚本重新 merge,勾掉的又回来,勾上的又没了。只改层 1 不改层 4,脚本若按自己的清单覆盖,你加的 CONFIG_FOO=y 会被冲掉。只改层 4 不删 checkpoint,脚本根本不跑,OBJ 继续用旧 .config

写入层 4 要幂等,已经是 =y 不要再 append 出两行:

bash 复制代码
# device/board/rk/rk3568_evb/kernel/build_kernel.sh  MERGED_CFG
CFG="$KERNEL_SRC/arch/arm64/configs/rockchip_linux_defconfig"

set_y() {
    local k="$1"
    if grep -q "^${k}=y" "$CFG"; then
        return 0
    fi
    if grep -q "^# ${k} is not set" "$CFG"; then
        sed -i "s/^# ${k} is not set/${k}=y/" "$CFG"
    else
        echo "${k}=y" >> "$CFG"
    fi
}

set_y CONFIG_GPIO_WATCHDOG
set_y CONFIG_CFI_PERMISSIVE
set_y CONFIG_I2C_CHARDEV
set_y CONFIG_USB_VIDEO_CLASS

编完对三处,缺一处就是没进:

bash 复制代码
# 层 1 碎片
grep GPIO_WATCHDOG kernel/linux/config/linux-5.10/*/arch/arm64_defconfig

# 层 3 真正编译用
grep '^CONFIG_GPIO_WATCHDOG' out/kernel/OBJ/linux-5.10/.config

# 符号在不在 Image 里(=y 才有;=m 在 .ko 里)
strings out/kernel/OBJ/linux-5.10/vmlinux | grep -i gpio_wdt | head

.config=yvmlinux 里没有符号:合成了但没重链。删 checkpoint 再编。.config 仍是 is not set:层 4 没跑,或 Kconfig 本身 depends on 不允许 =y

GN 的内核 action 不跟踪 device/board/。只改 build_kernel.sh、只改板级 dts,ninja 可能判定内核没变,层 4 整段跳过。强制重跑:

bash 复制代码
rm -rf out/kernel/checkpoint
./build.sh --product-name rk3568_evb --ccache

5. 板级目录:一个产品真正和公版分道的地方

device/board/rk/rk3568_evb/ 是你最常改的树。子目录和镜像的对应,比记 GN target 名有用。

bash 复制代码
device/board/rk/rk3568_evb/
├── kernel/
│   ├── rk3568-evb-linux.dts         # 板级设备树(文件名按你树里的为准)
│   ├── *.c / *.h                    # 板级驱动,构建时拷进 src_tmp
│   ├── build_kernel.sh              # 拷贝、打补丁、合成 defconfig、编 Image、打 boot_linux / resource
│   ├── logo.bmp / logo_kernel.bmp   # 进 resource.img,不是进 boot_linux 才亮
│   └── certs/                       # 模块签名永久钥匙
├── cfg/
│   ├── init.rk30board.cfg           # 生效的 init,后缀必须是 rk30board
│   ├── init.rk30board.usb.cfg
│   ├── fstab.rk30board              # 二阶段挂载,进 vendor
│   ├── fstab.required               # 一阶段挂载,进 ramdisk
│   └── BUILD.gn
├── loader/
│   ├── MiniLoaderAll.bin
│   ├── parameter.txt                # 有的树放 images 构建产物,有的放这里当源
│   ├── afptool / rkImageMaker / package-file
│   └── prebuilt-images/             # misc / bootctrl / chip_ckm / eng_chipset 空镜像
├── camera/                          # IQ、module 信息,多半进 vendor
├── audio/                           # 板级音频 json,多半进 vendor
└── BUILD.gn                         # part_name、install_images

build_kernel.sh 才是板级内核的总开关。它通常会做这些事(具体行号按你的脚本,逻辑一样):

bash 复制代码
# 伪代码,对着你的 build_kernel.sh 看
copy_kernel_source          # linux-5.10 → src_tmp
apply_rk3568_patch          # patches/linux-5.10/rk3568_patch
copy_board_dts_and_drivers  # device/board/.../kernel/* → src_tmp 对应位置
merge_and_inject_defconfig  # 层 1 + MERGED_CFG → rockchip_linux_defconfig
make Image dtbs             # 输出到 OBJ
pack_boot_linux_ext2        # Image + extlinux.conf + ramdisk → boot_linux.img
pack_resource               # rk-kernel.dtb + logo*.bmp → resource.img
cp boot_linux.img resource.img MiniLoaderAll.bin uboot.img \
   → out/rk3568_evb/packages/phone/images/

板级驱动不要直接丢进 git 的 kernel/linux/linux-5.10/drivers/。放 device/board/rk/rk3568_evb/kernel/,让脚本拷。理由很具体:内核源每次 copy_and_patch 会从干净树重新来一份,你手改 linux-5.10 要么进了 git 变成「对所有产品生效」,要么下次 copy 被冲掉。

cfg/ 进哪张镜像看 BUILD.gninstall_images

bash 复制代码
ohos_prebuilt_etc("fstab_required") {
  source = "fstab.required"
  install_images = [ "ramdisk" ]
  part_name = "rk3568_evb"
}

ohos_prebuilt_etc("fstab_rk30board") {
  source = "fstab.rk30board"
  install_images = [ "chipset_base_dir" ]   # 即 vendor
  part_name = "rk3568_evb"
}

ohos_prebuilt_etc("init_rk30board") {
  source = "init.rk30board.cfg"
  install_images = [ "chipset_base_dir" ]
  part_name = "rk3568_evb"
}

part_name 必须是这个产品在 json 里登记过的部件名,乱写一个 product_rk3568_evb,构建期可能过,安装期文件不进 vendor。改完 init 不必清内核;ninja 跟踪 ohos_prebuilt_etc 的 source。


6. ohos.boot.hardware=rk30board,只认这个后缀

设备树 cmdline 里有:

bash 复制代码
chosen {
    bootargs = "console=ttyS2,1500000n8 androidboot.hardware=rk30board \
                hardware=rk30board ...";
};

跑起来之后:

bash 复制代码
# param get ohos.boot.hardware
rk30board

init 用这个字符串拼文件名。system/etc/init.cfg 里类似:

bash 复制代码
import /vendor/etc/init.${ohos.boot.hardware}.cfg
mount_fstab /vendor/etc/fstab.${ohos.boot.hardware}

展开就是:

bash 复制代码
/vendor/etc/init.rk30board.cfg
/vendor/etc/fstab.rk30board

init.rk3568.cfgfstab.rk3568init.rk3568_evb.cfg 都可以在源码树里活得好好的,开机不理它们 。这不是文档写错,是这颗产品从公版 SD 方案继承了 rk30board 这个 hardware 名,一直没改 bootargs。

改 console、改 chmod、改开机自启服务,文件名必须带 rk30board。验源码:

bash 复制代码
grep -n 'rk30board' device/board/rk/rk3568_evb/cfg/*
grep -n 'ohos.boot.hardware' system/core/init/init.cfg \
    device/board/rk/rk3568_evb/cfg/ -r

验板上:

bash 复制代码
# ls /vendor/etc/init.rk30board.cfg /vendor/etc/fstab.rk30board
# grep start /vendor/etc/init.rk30board.cfg | head

你若在 init.rk3568.cfg 里加了 start console,串口能看 dmesg、敲回车没人理,就是这个后缀。


7. HDF 配置:khdf 进内核,uhdf 进 vendor

vendor/rk/rk3568_evb/hdf_config/ 分成两棵树,长得很像,进的镜像完全不同。

bash 复制代码
vendor/rk/rk3568_evb/hdf_config/
├── khdf/
│   ├── hdf.hcs                 # 顶层,几乎全是 #include
│   ├── device_info/
│   ├── input/
│   ├── lcd/
│   ├── audio/
│   ├── camera/
│   └── platform/
└── uhdf/
    ├── device_info.hcs
    └── ...                     # 用户态 host 读的

khdf 经 hc-gen 变成 C 数组,链进 vmlinux,最后进 boot_linux.img。触摸芯片驱动、部分音频、部分 panel 入口,开机读的是这份。

uhdf 变成 hdf_default.hcb,放进 /vendor/etc/hdfconfig/,进 vendor.img。composer、部分用户态 host 读这份。

改 khdf 只刷 vendor,等于没改。改 uhdf 只刷 boot_linux,同样没改。触摸分辨率写在 khdf 的 input_config.hcs 里,这是最常见的「我改了 HCS 怎么没反应」。

khdf 的 make 依赖只盯顶层 hdf.hcs。你改 input_config.hcs,顶层一行不动,make 认为没变,继续链上次的 hex。板级 build_kernel.sh 编内核前应该无条件删:

bash 复制代码
rm -f vendor/rk/rk3568_evb/hdf_config/khdf/hdf.hcb
rm -f vendor/rk/rk3568_evb/hdf_config/khdf/hdf_hcs.hcb
find out/kernel -name 'hdf_hcs_hex.c' -o -name 'hdf_hcs_hex.o' \
    | xargs --no-run-if-empty rm -f

uhdf 是另一对,别删混:

bash 复制代码
rm -f out/rk3568_evb/gen/vendor/rk/rk3568_evb/hdf_config/uhdf/hdf_default.hcb
rm -f out/rk3568_evb/packages/phone/vendor/etc/hdfconfig/hdf_default.hcb
rm -f out/rk3568_evb/packages/phone/images/vendor.img

编完在 Image 里搜字符串,搜到才算 khdf 真进去了:

bash 复制代码
grep -a "HDF_TOUCH_GT911" out/kernel/OBJ/linux-5.10/arch/arm64/boot/Image | head
grep -a "solutionX"       out/kernel/OBJ/linux-5.10/arch/arm64/boot/Image | head

HDF 的模型和 HCS 语法,配置那篇写。这里只要记住分流。


8. 改什么,进哪张镜像

这张表是这篇的中心。刷机之前对一下,少刷一张没用的、少漏一张有用的。

你改了什么 权威路径 进哪张镜像 板上谁读 还要刷
板级 dts `device/board/rk/rk3568_evb/kernel/*.dts` resource.img 里的 `rk-kernel.dtb`;boot_linux 里也有一份 toybrick.dtb,开机不认它当主 dtb U-Boot 从 **resource** 取出 dtb 传给内核 **resource + boot_linux**,SD 启动时 eMMC 的 p4/p5 也建议写
内核 `.c` / Kconfig / defconfig `kernel/linux/linux-5.10` 或板级 kernel/ + MERGED_CFG boot_linux.img 内核 boot_linux;改显示相关 dts 仍要 resource
khdf HCS `vendor/rk/rk3568_evb/hdf_config/khdf/` boot_linux.img(链进 Image) 内核态 HDF boot_linux
uhdf HCS、vendor 脚本、相机 IQ、音频 json `vendor/rk/...`、`device/board/.../camera` `audio` vendor.img 用户态 host / init 从 /vendor 读 vendor
`fstab.required` `device/board/rk/rk3568_evb/cfg/` ramdisk.img(也常被打进 boot_linux 的 extlinux/) 一阶段 init ramdisk / 整包;只刷 vendor 不够
`fstab.rk30board`、`init.rk30board.cfg` 同上 cfg/ vendor.img 二阶段 init vendor
系统应用、foundation、powermgr json `applications/` `foundation/` `base/` system.img BMS / 各 SA system;首启签名校验,单刷有时跳过
MiniLoader / DDR 固件 `device/board/rk/rk3568_evb/loader/` MiniLoaderAll.bin,在整包最前面 BootROM 整包;SDDiskTool 会覆盖卡上 loader 区
U-Boot 板级 u-boot 或预置 uboot.img uboot.img MiniLoader 之后 uboot 分区或整包
logo.bmp 板级 kernel/ resource.img U-Boot resource,不必动内核
空分区 misc 等 loader/prebuilt-images/ 对应 img,打包时补齐 U-Boot 看 misc 是否全零 整包;misc 非零可能进 recovery

显示相关的 dts 为什么强制刷 resource:U-Boot 的 bootrkpresource 分区 取出 rk-kernel.dtb 和 logo。boot_linux 是一个 ext2,里面有 Image、extlinux.conf、有时还有一份 dtb,那份 dtb 不是开机生效的那份。只刷 boot_linux,/proc/device-tree 可以一动不动。设备树那篇把缓存和反编译写完。这里先建立映射。


9. out/.../images/ 才是打包的输入

全量编完,分区镜像落在:

bash 复制代码
out/rk3568_evb/packages/phone/images/
├── boot_linux.img
├── resource.img
├── system.img
├── vendor.img
├── ramdisk.img
├── userdata.img
├── MiniLoaderAll.bin
├── uboot.img
├── parameter.txt
├── updater.img
├── sys_prod.img
├── chip_prod.img
├── eng_system.img
├── misc.img              # 构建不一定生成,缺了从 loader/prebuilt-images 补
├── bootctrl.img
├── chip_ckm.img
└── eng_chipset.img

./build.sh 等于可刷整包已经躺在桌面上。它停在这些零散 img。pack_update_img.sh 再把它们合成 RKFW。打包脚本若从 images/ 拷到 pack_update/Image/,你只更新了 images/boot_linux.img、忘了拷,打出来的整包还是上一份 boot_linux。

验时间戳,不要只信文件名:

bash 复制代码
IMG=out/rk3568_evb/packages/phone/images
ls -l --time-style=long-iso $IMG/boot_linux.img $IMG/resource.img \
    $IMG/vendor.img $IMG/system.img $IMG/ramdisk.img
stat $IMG/boot_linux.img | grep Modify

时间是两小时前、你十分钟前才改完 dts,这份 boot_linux 就不是这次的。回去看 checkpoint 和 build_kernel.sh 有没有跑。


10. 用户态 32 位,内核 64 位,产物别混

这不是题外话,是「改了 native 目录却进不了板」的常见原因。

bash 复制代码
# 板上
# uname -m
aarch64
# ls /system/lib/ld-musl-arm.so.1
/system/lib/ld-musl-arm.so.1
# ls /system/lib64 2>/dev/null | head
# file /system/bin/init
init: ELF 32-bit LSB ... ARM, ... interpreter /system/lib/ld-musl-arm.so.1

内核和 .ko:aarch64,clang 的 aarch64-linux-gnu 或 LLVM ARCH=arm64。 用户态 bin / so / hap 里的 .so:armv7,musl,动态链接器必须是板上那条路径。

自己写一个丢 /data 的小工具:

bash 复制代码
# 错:宿主机 gcc,或 aarch64-linux-gnu-gcc 静态/动态混用
# 对:
OHOS_SDK=/path/to/OpenHarmony
CC=$OHOS_SDK/prebuilts/clang/ohos/linux-x86_64/llvm/bin/clang
$CC --target=arm-linux-ohos -march=armv7-a \
    -fuse-ld=lld \
    -Wl,--dynamic-linker=/system/lib/ld-musl-arm.so.1 \
    -o gpio_dump gpio_dump.c
file gpio_dump
# ELF 32-bit LSB executable, ARM, EABI5 ... interpreter /system/lib/ld-musl-arm.so.1

file 看到 ARM aarch64 或 interpreter 指向 /lib/ld-linux-aarch64.so.1,送上去必挂。这个工具本身不进任何官方镜像,但你若把它写进 device/board/...BUILD.gnohos_executable,GN 会按产品的用户态 abi 编,那才省事。


11. 一次对照:改 dts 之后,源码、out、镜像、板,四份要一致

假设你在板级 dts 里把 &hdmi { status = "disabled"; }; 改成了 okay。期望板上 /proc/device-tree 里对应节点是 okay。对照顺序:

bash 复制代码
BOARD=device/board/rk/rk3568_evb/kernel
# 1) 权威源
grep -n -A2 '&hdmi' $BOARD/*.dts

# 2) src_tmp 副本,构建脚本应拷过来
grep -n -A2 '&hdmi' out/kernel/src_tmp/linux-5.10/arch/arm64/boot/dts/rockchip/*.dts

# 3) 编出来的 dtb,反编译看 status
DTB=$(find out/kernel/OBJ/linux-5.10 -name '*rk3568*evb*.dtb' | head -1)
dtc -I dtb -O dts "$DTB" 2>/dev/null | grep -A6 'hdmi@' | head -20

# 4) resource.img 里那份 rk-kernel.dtb 才是 U-Boot 交给内核的
mkdir -p /tmp/res && cd /tmp/res
# resource_tool 若在树里:
resource_tool --unpack --image=$OLDPWD/out/rk3568_evb/packages/phone/images/resource.img
dtc -I dtb -O dts rk-kernel.dtb 2>/dev/null | grep -A6 'hdmi@' | head -20

四份里只要有一份还是 disabled,刷进去也是 disabled。最常见的是 1 改了、2 没拷(checkpoint 跳过)、或者 2、3 对了,4 是旧 resource------你只把 boot_linux 刷进了 p5。

板上最后一问:

bash 复制代码
# cat /proc/device-tree/hdmi@fe0a0000/status
# 或找实际节点名
# find /proc/device-tree -name status | xargs grep -l okay | grep -i hdmi

/proc/device-tree当前内核拿到的 dtb,不是源码,不是 out。它才是生效证据。


12. 失败判断:改了却没进镜像,按这张单子过

ninja 很快结束,images 时间戳旧。 GN 没看见你的文件。板级 dts / build_kernel.sh / khdf 子 hcs 都不在内核 action 的 sources 里。删 out/kernel/checkpoint,必要时 rm -rf out/kernel/src_tmp/linux-5.10/boot_linux

exit 0,Image 里 grep 不到你加的字符串。 khdf 缓存,或 .c 没链进。strings vmlinux | grep 你的符号。没有就别刷。

Image 对了,板上 /proc/device-tree 不对。 刷了 boot_linux 没刷 resource,或刷了 SD 的 p4、U-Boot 从 eMMC 的 p4 取 dtb。

vendor 时间戳新,触摸还是旧分辨率。 分辨率在 khdf,不在 uhdf。刷错分区。

init 改了没反应。 改的是 init.rk3568.cfg。hardware 是 rk30board

自己编的小工具 No such file or directory。 32/64 位或动态链接器。file 看 interpreter。

干净机 rm -rf out 之后打包缺 afptool / misc.img。 这三件套和空分区不在 ./build.sh 的产物里,在 device/board/rk/rk3568_evb/loader/。打包那篇写脚本怎么同步。

改了 applications/ 下某个系统应用,单刷 system 后桌面图标没变。 BMS 数据库还在 userdata。整包或清 data 才按新 hap 装。那是应用签名那条线,和源码树映射无关,但常被当成「system.img 没编进去」。先 debugfsmd5sum 对镜像里的 hap,再怀疑 BMS。

对镜像里的 hap:

bash 复制代码
debugfs -R 'ls /app' out/rk3568_evb/packages/phone/images/system.img
# 或
debugfs -R 'dump /app/com.ohos.settings/Settings.hap /tmp/Settings.hap' \
    out/rk3568_evb/packages/phone/images/system.img
md5sum /tmp/Settings.hap

和板上 /system/app/.../Settings.hap 的 md5 比。镜像里已经是新的、板上是旧的,是刷机介质刷错;镜像里就是旧的,是构建没跟踪到应用源码(系统应用很多是 prebuilt,ninja 只 COPY)。


改文件之前先问两句:它进哪张 img?板上启动时读哪张分区?两句都有答案,再动手。答案来自这张树,不来自文件名看起来像不像「内核」。


系列第 7 篇 · 芯片:瑞芯微 RK3568 · OpenHarmony 4.1(API 11) · Linux 5.10

相关推荐
星火102416 分钟前
【从 0 到 1 动手造 Agent】03、给 Agent 装上操作系统——MemGPT/Letta 内存分层与自我演化
人工智能·后端·agent
深入云栈18 分钟前
一文搞懂Netty 4.2六大核心概念:Channel/EventLoop/Selector/ByteBuf 如何关联
java·后端
天才熊猫君20 分钟前
通用虚拟滚动表格行拖拽排序:从思路到完整实现
前端·javascript
hh95022 分钟前
Agent Plan × DeepSeek Harness:角色 Prompt 驱动的 Agent 分工优化与协作质量实验
java·前端·人工智能·prompt·adg·agent plan·adg成都社区
MetaLite24 分钟前
SpringBoot接口通用返回对象Resp设计
java·spring boot·后端
姚杨24 分钟前
聊了三年 DDD,代码里全是贫血模型:老陈一句话点破,落地先过这几关
后端·orm
专业抄代码选手25 分钟前
05|把递归渲染拆成 Fiber:让一棵大树可以暂停
前端·javascript·react.js
像我这样帅的人丶你还28 分钟前
🚀苹果的液态玻璃咋做?🚀
前端·webgl·three.js
天才熊猫君31 分钟前
Vue 3 插槽机制深度解析:两条线与三层树
前端·javascript