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

相关推荐
苏三说技术3 分钟前
如何看待GPT-6在UP主众测中碾压夺冠?它是现在最强大模型吗?
后端
mldong22 分钟前
一份 JSON,一条能跑的审批流:把报销流程送上工作流引擎
后端·架构
wno70423 分钟前
Spring Boot WebFlux增删改查
java·spring boot·后端
Captaincc26 分钟前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
aramae1 小时前
模拟实现strlen()函数 (C语言)
c语言·开发语言·后端
计算机魔术师2 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen2 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程
IT_陈寒3 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
分支预测失败3 小时前
RISC-V 时间子系统深度专题:mtime 访问路径、SBI 定时器与 Linux tickless 协同
后端
65岁退休Coder3 小时前
PI Agent 开发一个生产级 Harness
后端·node.js·agent