镜像在手里了。今天专门写怎么刷:SD 卡、eMMC 线刷、只更新某一个分区。路径写错时工具会显示成功,分区却纹丝不动。 

dd 打印了 1+0 records inmd5sum 跟镜像一致,复位之后 /proc/version 还是三天前。下一次更干净:dd 报 0+1 records in,看起来也成功,resource 分区里的 dtb 纹丝不动。

这两次都不是板子坏了。第一次,of= 写到了 /dev/block/by-name/boot_linux,符号链接永远指向 eMMC,当时系统却跑在 SD 上。第二次,of=/dev/mmcblk1p4,这个节点在开源鸿蒙的 /dev 里经常不存在,dd 不会报「没有这个设备」,它在 tmpfs 上建一个普通文件,把镜像写进文件,md5 当然和源文件一样。

整包刷 SD、整包刷 eMMC、单分区用 hdc 丢进去再 dd,三条路都能把系统写上板。三条路共用同一张分区表,共用同一个「假成功」陷阱。刷之前只做一件事:cat /proc/partitions,看清楚当前内核看见的盘字母。不要用 X 当盘符,不要用 by-name 当捷径。

Windows 整包工具的用法,进桌面那篇写过。这篇把介质、分区号、dd 的完整命令写成能直接跑的两套:一套 eMMC(mmcblk0),一套 SD(mmcblk1)。


1. 先看盘,再说话

OTG 已经能 hdc list targets,或者串口已经有 #。先跑:

# cat /proc/partitions
major minor  #blocks  name
 179        0   15267840 mmcblk0
 179        1      16384 mmcblk0p1
 179        2       4096 mmcblk0p2
 ...
 179        4       6144 mmcblk0p4
 179        5      98304 mmcblk0p5
 179        7    1572864 mmcblk0p7
 179        8     307200 mmcblk0p8
 179       15    4194304 mmcblk0p15
 179       32   31166976 mmcblk1
 179       36       6144 mmcblk1p4
 179       37      98304 mmcblk1p5
 179       39    1572864 mmcblk1p7
 179       40     307200 mmcblk1p8
 179       47    4194304 mmcblk1p15

读法:

你看见 含义
只有 `mmcblk0` 内核只枚举到 eMMC。没插 SD,或 SD 没认出
还有 `mmcblk1` SD 卡在位。**在位不等于从 SD 启动**
没有 `mmcblk1p7` 卡在,但没按这张 GPT 分区。可能是一张普通数据卡

这块板的约定:

mmcblk0 = eMMC(焊在核心板上)
mmcblk1 = Micro SD(槽里那张)

再看根挂在哪:

# mount | grep ' on / '
# df -h / /system /vendor
# param get ohos.boot.hardware

vendor 若在 mmcblk1p8,当前从 SD 跑;在 mmcblk0p8,从 eMMC 跑。ohos.boot.hardware 必须是 rk30board,否则你刷的可能是另一份产品。

不要用 /dev/block/by-name 判断介质。 它永远指向 eMMC:

# ls -l /dev/block/by-name | head
boot_linux -> /dev/block/mmcblk0p5
resource   -> /dev/block/mmcblk0p4
system     -> /dev/block/mmcblk0p7
vendor     -> /dev/block/mmcblk0p8
userdata   -> /dev/block/mmcblk0p15

从 SD 启动时,这些箭头看起来一切正常。dd of=/dev/block/by-name/system 写的是 eMMC 的 p7,SD 上正在跑的 system 一字节都没动。下次复位还是老桌面。dd 成功,md5 对的是 eMMC 那份。

所以规则就三条:

  1. 刷前 cat /proc/partitions 和 df /vendor
  2. of= 写 /dev/block/mmcblk0pN 或 /dev/block/mmcblk1pN,按当前要写的那块盘选
  3. 禁止 /dev/mmcblk0pN 这种不带 block 的路径,禁止 by-name,禁止命令里出现盘符占位符

2. 分区号:两块盘的布局一样

parameter.txt 决定 GPT。SD 和 eMMC 用同一张表,所以 p 后面的数字相同。常用的就这几个:

分区 镜像 改什么时必刷
resource p4 resource.img DTS、logo、显示口切换。里面是 `rk-kernel.dtb`
boot_linux p5 boot_linux.img 内核、khdf、内置驱动、extlinux
ramdisk 有的产品打进 boot_linux,有的单独一分区 ramdisk.img `fstab.required`
system p7 system.img 系统应用、foundation、powermgr
vendor p8 vendor.img uhdf、init.rk30board、fstab.rk30board、芯片 so
userdata p15 userdata.img 应用数据、BMS 数据库。整包刷会清空

p1 附近是 uboot,p2 常是 misc。misc 必须全零,否则 U-Boot 可能进 recovery。日常单分区不要碰 uboot 和 misc,除非你知道自己在换 loader。

resource 大约 6 MB。开机第一张 logo、U-Boot 交给内核的 dtb,都从这里取。boot_linux 是 32~64 MB 的 ext2。system 1 GB 以上。vendor 几百 MB。userdata 看容量,parameter 里的偏移必须落在真实介质内部,8 GB eMMC 不要用按 16 GB 算的 userdata 起始扇区,否则 Could not find userdata part

cat /proc/partitions 的 #blocks 是 1K 块。p4 若是 6144,就是 6 MB,装得下 3~4 MB 的 resource.img。不要看见镜像 3.6 MB、自己心算「分区只有 4K」——那是把 6144 看成了扇区数。


3. 假成功:/dev/mmcblk1p4 是一个普通文件

开源鸿蒙的 /dev 是 tmpfs,节点由 ueventd 按规则建。块设备的正统路径在 /dev/block/ 下。/dev/mmcblk1p4 经常不存在

# ls -l /dev/block/mmcblk1p4
brw------- 1 root root 179, 36 ... /dev/block/mmcblk1p4

# ls -l /dev/mmcblk1p4
ls: /dev/mmcblk1p4: No such file or directory

此时你跑:

# dd if=/data/local/tmp/resource.img of=/dev/mmcblk1p4 bs=4M conv=fsync
0+1 records in
0+1 records out

成功。再 md5:

# md5sum /data/local/tmp/resource.img /dev/mmcblk1p4

两行一样。因为 /dev/mmcblk1p4 现在是 dd 刚建的普通文件,内容就是镜像本身。真正的分区 /dev/block/mmcblk1p4 没被打开过。

辨认:

# ls -l /dev/mmcblk1p4 /dev/block/mmcblk1p4
-rw-r--r--  1 root root 3670016 ... /dev/mmcblk1p4     ← 普通文件,假的
brw-------  1 root root 179, 36 ... /dev/block/mmcblk1p4  ← 块设备,真的

第一列是 - 不是 b,就是假的。删掉假文件,以免下次 ls /dev/mmcblk* 再骗你:

# rm -f /dev/mmcblk1p4 /dev/mmcblk0p4 /dev/mmcblk1p5 /dev/mmcblk0p5

echo foo > /dev/ttyS0 在 uart0 还没 probe 时会建同样的假节点,ls 看得到 ttyS0,tcgetattr 报 Not a tty。同一类坑。

还有一种假文件,名字更阴。有人写命令时用 mmcblkXp4 表示「0 或 1 你自己选」。接收的人逐字照抄:

# 错误示范,不要跑。X 不是通配符
dd if=/data/local/tmp/resource.img of=/dev/block/mmcblkXp4 bs=4M conv=fsync
0+1 records in
0+1 records out

/dev/block/ 可写。dd 创建名为 mmcblkXp4 的普通文件。打印成功。md5sum /dev/block/mmcblkXp4 和镜像一样。/proc/partitions 里没有这个设备。resource 分区一个字节没动。现场靠「这次该消失的旧字符串还在」才识破,不是靠 dd 的退出码。

# ls -l /dev/block/mmcblkXp4
-rw-r--r-- 1 root root 3670016 ... /dev/block/mmcblkXp4
# find /dev /dev/block -type f -name '*mmcblk*'
/dev/block/mmcblkXp4
# rm -f /dev/block/mmcblkXp4

发给别人的命令里不要出现这个字母。两套完整路径都写出来,让对方按 df | grep vendor 选一套跑。

正确路径永远带 /dev/block/ 写完读回校验,读的也是 block 路径:

# dd if=/data/local/tmp/resource.img of=/dev/block/mmcblk1p4 bs=4M conv=fsync
# dd if=/dev/block/mmcblk1p4 of=/data/local/tmp/resource.read bs=4M count=2
# md5sum /data/local/tmp/resource.img /data/local/tmp/resource.read

镜像比分区短,读回会多出分区里原来的尾巴,md5 可能不同。比前 N 字节:

# LEN=$(wc -c < /data/local/tmp/resource.img)
# dd if=/dev/block/mmcblk1p4 bs=1 count=$LEN of=/data/local/tmp/resource.head
# md5sum /data/local/tmp/resource.img /data/local/tmp/resource.head

两行相同才算写进去。bs=4M conv=fsyncbs=1M 在个别镜像末尾碎块上会报 No space left on device,分区明明够大,那是 dd 和分区大小对齐的假报错,换 4M 就消失。conv=fsync 让内核把缓存落到闪存再返回,否则 dd 结束了数据还在 page cache,立刻复位可能写残。


4. 改 DTS 必刷 resource,U-Boot 还可能去 eMMC 取

开机画面和生效 dtb 在 p4。内核 Image 在 p5。只刷 p5,logo 不变,/proc/device-tree 也可以不变。

U-Boot 找 resourceboot_linux 时,经常去 eMMC 上找,哪怕你拨码拨在 SD、内核根文件系统已经在 mmcblk1 上。新核心板 eMMC 空白,串口会打:

Cannot find partition 'boot_linux'
FATAL error: cannot load boot_linux image to ram

同一张刚刷好的 SD 卡,换到一块 eMMC 里曾经有过系统的核心板上,立刻能起。所以:

  • 新板先 USB 整包刷一次 eMMC,再谈 SD 启动
  • 改内核 / DTS:p4 和 p5,mmcblk0 和 mmcblk1 都写(卡在位的话)

两盘都写的成本是多两次 dd,收益是不再出现「我明明刷了 SD,U-Boot 还在用 eMMC 里那份 LVDS dtb」。


5. 主机到板:hdc file send,再在板上 dd

镜像在 Linux 编译机或 Windows 共享盘。板上没有 sshd、没有 wget。通道是 USB OTG 上的 hdcd。

先确认设备:

hdc kill
hdc start
hdc list targets

应打出一串十六进制。没有就换 OTG 口、确认不是 Host、板上 ps | grep hdcd。系统没进用户态时没有 hdcd,只能整包刷,不能走这条单分区路。

Windows 上 hdc 常见位置:

C:\Users\<you>\AppData\Local\OpenHarmony\Sdk\11\toolchains\hdc.exe

把 hdc 所在目录加进 PATH,下面的 bat 才能直接跑。

hdc file send 的远端路径用 /data/local/tmp/,这个目录可写、重启可清。不要 send 到 /system/vendor,只读挂载,send 报成功也不一定落盘。

大文件 send 可能中途断开。system.img 1.5 GB,USB2 要十几分钟。断了看板上文件大小,不够就重发。


6. 完整命令:Windows bat

先在板上查介质。下面这个 bat 只负责「问板上现在 vendor 在哪」:

@echo off
hdc shell "param get ohos.boot.hardware"
hdc shell "cat /proc/partitions"
hdc shell "df /vendor /system"
hdc shell "ls -l /dev/block/mmcblk0p4 /dev/block/mmcblk0p5 /dev/block/mmcblk0p7 /dev/block/mmcblk0p8"
hdc shell "ls -l /dev/block/mmcblk1p4 /dev/block/mmcblk1p5 /dev/block/mmcblk1p7 /dev/block/mmcblk1p8"

看输出决定走下面哪一套。镜像放 D:\ohos\,按你的实际路径改第一行。不要把 mmcblk0 改成 mmcblkX。

6.1 当前要从 eMMC 更新(vendor 在 mmcblk0p8)

@echo off
set IMG=D:\ohos
set HDC=hdc

%HDC% file send %IMG%\resource.img   /data/local/tmp/resource.img
%HDC% file send %IMG%\boot_linux.img /data/local/tmp/boot_linux.img

%HDC% shell "dd if=/data/local/tmp/resource.img   of=/dev/block/mmcblk0p4 bs=4M conv=fsync"
%HDC% shell "dd if=/data/local/tmp/boot_linux.img of=/dev/block/mmcblk0p5 bs=4M conv=fsync"

REM SD 卡在位时,U-Boot 仍可能读 eMMC;若你其实想让 SD 也带上这份,再写 mmcblk1
%HDC% shell "if [ -b /dev/block/mmcblk1p4 ]; then dd if=/data/local/tmp/resource.img   of=/dev/block/mmcblk1p4 bs=4M conv=fsync; fi"
%HDC% shell "if [ -b /dev/block/mmcblk1p5 ]; then dd if=/data/local/tmp/boot_linux.img of=/dev/block/mmcblk1p5 bs=4M conv=fsync; fi"

%HDC% shell "sync; reboot"

6.2 当前要从 SD 更新(vendor 在 mmcblk1p8)

@echo off
set IMG=D:\ohos
set HDC=hdc

%HDC% file send %IMG%\resource.img   /data/local/tmp/resource.img
%HDC% file send %IMG%\boot_linux.img /data/local/tmp/boot_linux.img

%HDC% shell "dd if=/data/local/tmp/resource.img   of=/dev/block/mmcblk1p4 bs=4M conv=fsync"
%HDC% shell "dd if=/data/local/tmp/boot_linux.img of=/dev/block/mmcblk1p5 bs=4M conv=fsync"

REM U-Boot 常从 eMMC 取 p4/p5,eMMC 也写上
%HDC% shell "dd if=/data/local/tmp/resource.img   of=/dev/block/mmcblk0p4 bs=4M conv=fsync"
%HDC% shell "dd if=/data/local/tmp/boot_linux.img of=/dev/block/mmcblk0p5 bs=4M conv=fsync"

%HDC% shell "sync; reboot"

6.3 只刷 vendor(uhdf、init、fstab.rk30board)

eMMC:

hdc file send D:\ohos\vendor.img /data/local/tmp/vendor.img
hdc shell "dd if=/data/local/tmp/vendor.img of=/dev/block/mmcblk0p8 bs=4M conv=fsync"
hdc shell "sync; reboot"

SD:

hdc file send D:\ohos\vendor.img /data/local/tmp/vendor.img
hdc shell "dd if=/data/local/tmp/vendor.img of=/dev/block/mmcblk1p8 bs=4M conv=fsync"
hdc shell "sync; reboot"

6.4 只刷 system(系统应用、power_suspend.json)

eMMC:

hdc file send D:\ohos\system.img /data/local/tmp/system.img
hdc shell "dd if=/data/local/tmp/system.img of=/dev/block/mmcblk0p7 bs=4M conv=fsync"
hdc shell "sync; reboot"

SD:

hdc file send D:\ohos\system.img /data/local/tmp/system.img
hdc shell "dd if=/data/local/tmp/system.img of=/dev/block/mmcblk1p7 bs=4M conv=fsync"
hdc shell "sync; reboot"

system.img 很大。send 失败先看:

hdc shell "ls -l /data/local/tmp/system.img"

大小和主机 dir D:\ohos\system.img 一致再 dd。不够就重 send。

单刷 system 不会清 userdata。BMS 数据库还在,预装应用的签名校验在「整包首启」和「单刷保留 data」上行为不同。应用装不上那条线另说。这里只保证分区字节换了。


7. 完整命令:Linux bash

编译机若直接插着板的 OTG,hdc 在 Linux 上也能用。更多时候 Linux 只出镜像,Windows 跑 hdc。下面假设当前 shell 能 hdc list targets

先定介质:

hdc shell 'param get ohos.boot.hardware; echo ---; cat /proc/partitions; echo ---; df /vendor'

按 vendor 所在盘选函数。改 DTS 时两盘 p4/p5 都写。

#!/usr/bin/env bash
set -euo pipefail
IMGDIR=/mnt/hgfs/share          # 改成你的镜像目录
hdc_ok() { hdc list targets | grep -v '^$' | grep -v 'Empty'; }

flash_res_boot() {
    local dev="$1"   # mmcblk0 或 mmcblk1
    hdc file send "$IMGDIR/resource.img"   /data/local/tmp/resource.img
    hdc file send "$IMGDIR/boot_linux.img" /data/local/tmp/boot_linux.img
    hdc shell "dd if=/data/local/tmp/resource.img   of=/dev/block/${dev}p4 bs=4M conv=fsync"
    hdc shell "dd if=/data/local/tmp/boot_linux.img of=/dev/block/${dev}p5 bs=4M conv=fsync"
}

# 用法:确认 partitions 里两块盘都在,再:
hdc_ok
flash_res_boot mmcblk0
flash_res_boot mmcblk1
hdc shell 'sync; reboot'

只刷 vendor,eMMC:

hdc file send /mnt/hgfs/share/vendor.img /data/local/tmp/vendor.img
hdc shell 'dd if=/data/local/tmp/vendor.img of=/dev/block/mmcblk0p8 bs=4M conv=fsync'
hdc shell 'sync; reboot'

只刷 vendor,SD:

hdc file send /mnt/hgfs/share/vendor.img /data/local/tmp/vendor.img
hdc shell 'dd if=/data/local/tmp/vendor.img of=/dev/block/mmcblk1p8 bs=4M conv=fsync'
hdc shell 'sync; reboot'

只刷 system,eMMC:

hdc file send /mnt/hgfs/share/system.img /data/local/tmp/system.img
hdc shell 'dd if=/data/local/tmp/system.img of=/dev/block/mmcblk0p7 bs=4M conv=fsync'
hdc shell 'sync; reboot'

只刷 system,SD:

hdc file send /mnt/hgfs/share/system.img /data/local/tmp/system.img
hdc shell 'dd if=/data/local/tmp/system.img of=/dev/block/mmcblk1p7 bs=4M conv=fsync'
hdc shell 'sync; reboot'

读回校验 resource(eMMC):

hdc shell 'dd if=/dev/block/mmcblk0p4 of=/data/local/tmp/p4.read bs=4M count=2'
hdc file recv /data/local/tmp/p4.read /tmp/p4.read
# 主机上比前 N 字节
python3 - <<'PY'
from pathlib import Path
src = Path('/mnt/hgfs/share/resource.img').read_bytes()
rd  = Path('/tmp/p4.read').read_bytes()[:len(src)]
print('match' if src == rd else 'DIFF', len(src), len(rd))
PY

8. 整包:SDDiskTool 和 RKDevTool 各管一块介质

单分区走不通时(hdcd 没起、分区表乱、新板 eMMC 空),回整包。

SD 卡:SDDiskTool,管理员,勾 SD 启动,固件头必须是 RKFW,文件名不含 emmc。 下拉框认容量,认不准就拔读卡器看哪一项消失。写的是读卡器里那张卡,不会隔空写 eMMC。刷完弹出再拔。拨码拨到 SD。

eMMC:RKDevTool「升级固件」,Maskrom 或 Loader,固件头 RKFW,文件名含 emmc。 点升级前清空日志,成功判据是本次打出的「下载固件成功」。上次那行还躺在窗口里不算。

SD 包刷进 eMMC、eMMC 包刷进 SD,工具都可以显示成功。内核起来之后 init 按 fstab 去等另一条控制器,16 秒或 58 秒 panic。串口关键字:

wait for file /dev/block/platform/fe2b0000.dwmmc/by-name/system
wait for file /dev/block/platform/fe310000.sdhci/by-name/system

等的那条和当前介质不一致,就是包拿错了。不要在这一步开始改内核。

整包会写 loader 区。4 GB 板必须用带 DDR V1.16 的 MiniLoader。SDDiskTool 从 0 扇区写,卡上旧的 V1.16 会被包里的 V1.10 覆盖。

新核心板顺序:先 USB 整包刷 eMMC(让 GPT 和 boot_linux 存在),再刷 SD、再拨 SD 启动。只刷 SD、eMMC 全空,U-Boot 可能找不到分区。

RKFW 头 4 字节只告诉工具「我是瑞芯微整包」,分不出该进 SD 还是 eMMC。两份包的差别在镜像里面的 fstab:SD 写 fe2b0000.dwmmc,eMMC 写 fe310000.sdhci。介质约定写在文件名上。

文件名 介质 唤起
含 `emmc`,例如 `emmc-hdmi.img` eMMC RKDevTool「升级固件」
含 `update-` 或头是 RKAF 方向像 eMMC,本机不要当主交付 改用含 `emmc` 的 RKFW
`sd_image*.img`,或其它不含 `emmc` 的 RKFW Micro SD SDDiskTool,勾选 SD 启动

有人做了一个刷机选择小工具:扫一个目录,按文件名含不含 emmc 判断该唤起哪个软件。没有这个小工具也没关系,你自己按上表选。小工具能防的是手滑——把 emmc-xxx.img 丢进 SDDiskTool,或把 sd_image.img 当 eMMC 包升级。

旧 exe 若「凡 RKFW 一律当 SD」,emmc-xxx.img 会被送进 SDDiskTool。规则改了必须重新编出 exe 再发,Windows 上还在跑昨天那份,选择器就会用旧规则把 eMMC 包当 SD 刷。fstab 和介质一旦错配,串口看起来像系统坏了,其实是挂载路径指错盘。

小工具不能替你做的事:认 C: 和 SD 卡的容量、判断 Maskrom 进没进、清空 RKDevTool 日志、在单分区 dd 时选 mmcblk0 还是 mmcblk1。成功判据仍然是本次「下载固件成功」;单分区仍然先 df | grep vendor


9. 刷完怎么证明写进去的是这份

内核:

# cat /proc/version
Linux version 5.10.xxx (builder@host) (clang ...) #1 SMP PREEMPT 2026-08-21 ...

日期应是你这次编 Image 的日期。还是上周,p5 没写上,或写到另一块盘了。

设备树:

# cat /proc/device-tree/model
# find /proc/device-tree -name status | xargs grep -n okay | grep -i uart | head

和这次 dts 对。不对,p4 没写上,或 U-Boot 取了另一块盘的 resource。

vendor 里的 fstab:

# grep -E 'dwmmc|sdhci' /vendor/etc/fstab.rk30board

系统应用:

# md5sum /system/app/com.ohos.settings/Settings.hap

和镜像里 debugfs dump 出来的比。

假文件排查:

# ls -l /dev/mmcblk* /dev/block/mmcblk0p4 /dev/block/mmcblk1p4 2>/dev/null

看见 /dev/mmcblk1p4 类型是 -rw,删掉它,不要再 dd 给它。


10. 失败判断

dd: /dev/block/mmcblk1p4: No such file or directory 没插卡,或卡没分区。先 cat /proc/partitions。不要改成 /dev/mmcblk1p4 碰运气。

0+1 records 且 md5 与镜像完全相同、复位后内容旧。 假文件。ls -l 看类型。路径若是 /dev/block/mmcblkXp4,那是占位符被当成文件名,find /dev -type f -name '*mmcblk*' 能挖出来。

records in/out 正常,/proc/version 不变。 写错盘。by-name 指向 eMMC,你从 SD 启动。或写了 mmcblk0,U-Boot 从 mmcblk1 起。两盘 p5 都写。

改了 dts,内核新、屏还是旧时序。 只刷了 p5。刷 p4。两盘都刷。

logo 不变。 同上,logo 在 p4。boot_linux 里即使有 /logo.bmp,开机不读。

No space left on device,分区比镜像大。 bs=1M 换 bs=4M。或假文件建在了空间很小的 tmpfs 根上——那正好说明 of 路径不在 block 下。

hdc file send 成功,板上 ls 没有文件。 远端路径写到了只读层,或 send 到了另一台设备(两个 hdc server 抢)。hdc list targets 只留一个。调试那篇写 HDC_SERVER_PORT

send 到一半断开,dd 仍在跑。 先 ls -l /data/local/tmp/*.img 看大小。不够就不要 dd。dd 一个截断的 system.img 进 p7,下次可能进不了桌面。

整包成功,单分区失败。 整包走 ROCKUSB,不靠 hdcd;单分区靠用户态。系统没起来时不要坚持 hdc。

刷了 p7,桌面图标还是旧的。 字节可能已经换了,BMS 数据库在 p15。整包或清 userdata。先 md5 hap,确认 p7 真换了,再查 BMS。

刷机命令写完自己读一遍:有没有 mmcblkX、有没有 by-name、有没有丢掉 /block/。有,就不要发给任何人,包括自己。


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

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐