1. 为什么要在Rock3A上折腾OpenBMC
手里这块Rock3A开发板是瑞芯微RK3568的方案,四核A55,主频最高2.0GHz,带NPU和双千兆网口,社区支持也还算活跃。我最初拿到它的目的是做边缘计算网关,跑了一段时间Ubuntu之后发现一个挺实际的问题:设备部署在无人值守的机房里,一旦系统崩溃或者内核panic,只能跑现场去重启。这种场景下,带外管理能力就成了刚需。
OpenBMC正好解决这个痛点。它是一套开源的BMC固件框架,核心思路是在主处理器之外维护一个独立的管理通道,即使主系统完全挂掉,你依然可以通过网络访问BMC,查看传感器数据、控制电源、挂载虚拟介质、查看系统日志。传统上OpenBMC跑在ASPEED的AST2500/AST2600这类专用BMC芯片上,但这些芯片价格不便宜,而且和主SoC是分离的两套系统。Rock3A的RK3568本身性能足够强,如果能让它同时承担主业务和BMC管理两个角色,硬件成本能省下一大块。
这个想法听起来有点“歪门邪道”,因为OpenBMC的官方支持列表里根本没有RK3568。但OpenBMC的架构本身是高度可移植的,它基于Yocto构建,底层是Linux,上层是phosphor系列守护进程和D-Bus消息总线。只要能把Yocto的BSP层跑通,理论上任何能跑Linux的ARM板子都有机会。我花了大概三周时间,从零开始把OpenBMC移植到了Rock3A上,中间踩了不少坑,也积累了一些性能数据。这篇文章就把整个过程拆开来讲,包括方案选型、设备树适配、内核配置、用户空间裁剪、性能对比,以及那些文档里不会写的避坑经验。
适合谁来读?如果你手上有RK3568或者类似ARM开发板,想了解BMC固件的移植流程,或者你正在做带外管理的方案选型,这篇内容应该能帮你省下不少试错时间。即使你之前没接触过OpenBMC,只要对嵌入式Linux和Yocto有基本概念,跟着思路走也能理解整个链路。
2. 移植前的整体方案设计与选型考量
2.1 为什么选OpenBMC而不是自己写一套管理程序
有人可能会问:不就是远程看个传感器、控制个电源吗,自己写个Python脚本加个Web界面不就行了?我一开始也这么想过,但实际评估下来,自己造轮子的成本远高于预期。
OpenBMC提供的不只是几个API,它有一套完整的、经过工业场景验证的框架。phosphor-dbus-interfaces定义了标准化的传感器、电源、日志、固件更新等接口,phosphor-hwmon负责传感器采集,phosphor-state-manager管理电源状态机,bmcweb提供Redfish兼容的REST API。这些组件之间的解耦做得很好,你可以按需裁剪,但接口规范是统一的。如果自己写,光是Redfish协议兼容性这一项就够喝一壶的,更别说后续的固件更新、日志持久化、多用户权限管理这些功能。
另一个考虑是社区生态。OpenBMC背后有Facebook、IBM、Intel等公司在推,代码质量和文档虽然不算完美,但至少有人在维护。遇到问题去邮件列表或者Gerrit上搜,大概率能找到类似案例。自己写的方案出了问题只能自己扛。
2.2 RK3568作为BMC主控的可行性分析
RK3568跑OpenBMC,最大的疑问是资源够不够。OpenBMC在AST2600上的典型内存占用是512MB左右,而Rock3A标配2GB或4GB LPDDR4,完全够用。存储方面,OpenBMC的rootfs大概200-300MB,加上日志和配置分区,16GB eMMC绰绰有余。CPU性能更是碾压AST2600,四核A55跑D-Bus消息总线毫无压力。
真正需要关注的是外设接口。BMC的核心功能包括:I2C传感器采集、GPIO电源控制、UART串口重定向、网络管理、PWM风扇控制。RK3568的I2C控制器有6路,GPIO数量充足,UART也有多路,PWM支持8通道。从硬件接口层面看,完全能满足BMC的基本需求。唯一需要注意的是,RK3568的I2C控制器在Linux下的驱动是i2c-rk3x,需要确认它支持从机模式(如果要做IPMB的话),不过对于本地传感器采集,主机模式就够了。
2.3 Yocto BSP层的选择:用官方还是自己搭
OpenBMC的构建系统基于Yocto,官方提供了meta-phosphor层和几个参考机器的BSP。对于RK3568,没有现成的meta层可用。我有两个选择:一是基于Rockchip官方的Yocto BSP(meta-rockchip)来改,二是从OpenBMC的meta-aspeed或者meta-arm参考层出发,自己写一个meta-rock3a。
我最终选了第二条路。原因是Rockchip官方的Yocto BSP版本比较老,而且和OpenBMC的Yocto版本(kirkstone)不一定对得上。从meta-arm出发,我可以复用ARM通用的内核配置和工具链,只需要针对RK3568写一个小的BSP层,包含机器配置、内核配方、设备树和u-boot配方。这样层次更清晰,后续升级也方便。
具体来说,我创建了meta-rock3a层,目录结构如下:
meta-rock3a/ ├── conf/ │ ├── machine/ │ │ └── rock3a.conf │ └── layer.conf ├── recipes-bsp/ │ ├── u-boot/ │ │ └── u-boot-rock3a.bb │ └── trusted-firmware-a/ │ └── trusted-firmware-a-rock3a.bb ├── recipes-kernel/ │ └── linux/ │ ├── linux-rock3a_5.15.bb │ └── linux-rock3a/ │ ├── defconfig │ └── rock3a.dts └── recipes-phosphor/ └── ... (OpenBMC相关配方覆盖)机器配置里关键的是指定内核设备树、串口控制台、以及OpenBMC需要的几个特性开关。这部分后面会详细展开。
2.4 启动链路的规划:从BootROM到BMC用户空间
RK3568的启动流程是BootROM -> TPL/SPL -> U-Boot -> Linux Kernel -> Init。OpenBMC的用户空间由systemd管理,phosphor服务通过D-Bus互相通信。移植的关键是确保每个阶段都能正确加载。
我规划的分区方案是:
| 分区 | 大小 | 内容 |
|---|---|---|
| uboot | 4MB | U-Boot + SPL |
| boot | 64MB | Kernel Image + DTB |
| rootfs | 512MB | OpenBMC rootfs (squashfs) |
| data | 剩余空间 | 日志、配置、持久化数据 |
rootfs用squashfs只读挂载,data分区用ext4可读写。这样即使rootfs损坏,恢复也容易。U-Boot环境变量里设置bootargs指向正确的分区和console。
3. 核心细节解析与实操要点
3.1 设备树适配:让内核认识Rock3A的硬件
设备树是移植中最琐碎但也最关键的部分。Rock3A的硬件布局和Rockchip官方的EVB板有差异,不能直接套用。我需要根据原理图确认几个关键外设的引脚和总线地址。
首先是I2C。Rock3A引出了I2C1、I2C3、I2C5等几路,我选了I2C3接一颗LM75温度传感器做测试。在设备树里需要配置:
&i2c3 { status = "okay"; clock-frequency = <100000>; pinctrl-names = "default"; pinctrl-0 = <&i2c3m0_xfer>; lm75: lm75@48 { compatible = "national,lm75"; reg = <0x48>; }; };这里有个坑:RK3568的I2C引脚有多种mux选项,i2c3m0和i2c3m1对应的物理引脚不同。我一开始没注意,用了默认的mux,结果传感器读不到数据。后来对着原理图确认了实际用的是m0组引脚,改成i2c3m0_xfer才正常。
GPIO部分,我需要控制一个电源使能引脚。在设备树里定义:
gpio-power { compatible = "gpio-leds"; power_en { gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>; default-state = "off"; label = "power-en"; }; };然后在OpenBMC的phosphor-gpio-monitor配置里引用这个GPIO,实现电源状态监控。
UART方面,Rock3A的调试串口是UART2,对应ttyS2。在chosen节点里设置:
chosen { stdout-path = "serial2:1500000n8"; bootargs = "console=ttyS2,1500000n8 earlycon"; };注意波特率是1500000,不是常见的115200。这个在Rockchip的板子上很常见,如果设错了串口会输出乱码。
3.2 内核配置裁剪:只保留BMC需要的功能
OpenBMC的内核不需要桌面环境、声卡、GPU这些,但需要确保I2C、GPIO、PWM、hwmon、watchdog、网络这些驱动都编译进去。我基于multi_v7_defconfig和defconfig做了一份精简配置,关键选项如下:
CONFIG_I2C=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_RK3X=y CONFIG_GPIO_SYSFS=y CONFIG_GPIO_DWAPB=y CONFIG_PWM=y CONFIG_PWM_ROCKCHIP=y CONFIG_SENSORS_LM75=y CONFIG_WATCHDOG=y CONFIG_DW_WATCHDOG=y CONFIG_NET=y CONFIG_STMMAC_ETH=y CONFIG_PHYLIB=y CONFIG_EXT4_FS=y CONFIG_SQUASHFS=y CONFIG_OVERLAY_FS=y裁剪的时候要注意,phosphor-hwmon依赖sysfs接口读取传感器,所以CONFIG_HWMON和CONFIG_SENSORS_*必须开。另外,OpenBMC的日志服务需要CONFIG_TMPFS和CONFIG_TMPFS_POSIX_ACL。
内核大小方面,裁剪后Image大概8MB左右,加上DTB不到9MB,boot分区64MB完全够用。启动时间实测从U-Boot到用户空间login提示大约12秒,比AST2600的方案快不少。
3.3 U-Boot适配:从SPL到内核的跳转
Rock3A的U-Boot需要支持从eMMC加载内核。我用的U-Boot版本是2022.07,Rockchip的SPL框架已经比较成熟。关键配置在rock3a_defconfig里:
CONFIG_ARM=y CONFIG_ARCH_ROCKCHIP=y CONFIG_SPL=y CONFIG_SPL_ROCKCHIP_BACK_TO_BROM=y CONFIG_TARGET_ROCK3A=y CONFIG_DEFAULT_DEVICE_TREE="rk3568-rock3a" CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR=0x4000 CONFIG_SPL_PAYLOAD="u-boot.bin"CONFIG_SPL_ROCKCHIP_BACK_TO_BROM=y这个选项很重要,它让SPL加载完U-Boot后回到BootROM,由BootROM跳转到U-Boot。如果不设这个,SPL会直接跳转,在某些DDR配置下可能不稳定。
U-Boot的环境变量里设置bootcmd:
bootcmd=mmc dev 0; ext4load mmc 0:2 ${kernel_addr_r} /Image; ext4load mmc 0:2 ${fdt_addr_r} /rk3568-rock3a.dtb; booti ${kernel_addr_r} - ${fdt_addr_r}这里假设boot分区是mmc 0的第2个分区。实际部署时可以用part uuid来定位,更稳妥。
3.4 OpenBMC用户空间裁剪:去掉不需要的配方
OpenBMC默认构建出来的镜像包含了很多面向服务器主板的功能,比如IPMI、Redfish、WebUI、固件更新等。对于Rock3A这种轻量级场景,有些可以裁掉。
我在local.conf里通过IMAGE_INSTALL:remove去掉了一些重量级组件:
IMAGE_INSTALL:remove = "bmcweb phosphor-ipmi-host phosphor-ipmi-net"但保留了phosphor-hwmon、phosphor-state-manager、phosphor-logging、phosphor-dbus-interfaces这些核心服务。裁剪后rootfs从原来的380MB降到了210MB左右,启动时间也缩短了约3秒。
需要注意的是,phosphor-hwmon的配置文件需要根据实际传感器路径来写。在/etc/phosphor-hwmon/下创建conf文件:
TEMP=/sys/class/hwmon/hwmon0/temp1_input然后systemd服务会自动读取并发布到D-Bus上。可以用busctl introspect xyz.openbmc_project.Hwmon /xyz/openbmc_project/sensors/temperature/TEMP来验证。
4. 实操过程与核心环节实现
4.1 构建环境搭建与Yocto配置
构建OpenBMC需要一台Linux主机,Ubuntu 20.04或22.04都可以。依赖包安装:
sudo apt install gawk wget git diffstat unzip texinfo gcc build-essential \ chrpath socat cpio python3 python3-pip python3-pexpect xz-utils debianutils \ iputils-ping python3-git python3-jinja2 libegl1-mesa libsdl1.2-dev \ pylint xterm python3-subunit mesa-common-dev zstd liblz4-tool file locales然后拉取OpenBMC的代码:
git clone https://github.com/openbmc/openbmc.git cd openbmc git checkout kirkstone把之前写好的meta-rock3a层放到meta-rock3a目录下,然后在bblayers.conf里添加:
BBLAYERS += "${TOPDIR}/../meta-rock3a"在local.conf里设置机器:
MACHINE ??= "rock3a"构建命令:
source oe-init-build-env bitbake obmc-phosphor-image第一次构建大概需要2-3小时,取决于主机性能和网络。建议挂个代理加速下载,但注意不要用任何违规工具,直接用国内镜像源即可。Yocto的PREMIRRORS和SSTATE_MIRRORS可以配置成本地缓存,第二次构建就快很多。
4.2 镜像烧录与首次启动
构建完成后,镜像在tmp/deploy/images/rock3a/下。我用的是obmc-phosphor-image-rock3a.wic,可以直接dd到SD卡或者eMMC。
烧录到SD卡:
sudo dd if=obmc-phosphor-image-rock3a.wic of=/dev/sdX bs=4M status=progress sync把SD卡插入Rock3A,拨码开关设置成SD卡启动。上电后串口应该能看到U-Boot和内核的启动日志。第一次启动时,systemd会做一些初始化工作,比如生成SSH密钥、初始化D-Bus,大概需要30秒左右。
启动完成后,串口会显示登录提示。默认用户名是root,密码是0penBmc(注意是数字0)。登录后可以用systemctl status查看服务状态。
4.3 传感器与电源管理功能验证
登录BMC后,先验证传感器是否正常:
busctl tree xyz.openbmc_project.Hwmon如果配置正确,应该能看到/xyz/openbmc_project/sensors/temperature/TEMP这个路径。读取数值:
busctl get-property xyz.openbmc_project.Hwmon \ /xyz/openbmc_project/sensors/temperature/TEMP \ xyz.openbmc_project.Sensor.Value Value返回的应该是一个double类型的温度值。
电源控制方面,我通过GPIO模拟了一个电源按钮。在/etc/systemd/system/下创建一个服务:
[Unit] Description=Power Button Monitor After=phosphor-gpio-monitor.service [Service] ExecStart=/usr/bin/gpio-monitor -g power-button -t both -e power-button-pressed Restart=always [Install] WantedBy=multi-user.target然后在D-Bus上监听power-button-pressed事件,触发电源状态切换。这部分需要和phosphor-state-manager配合,实际实现时我写了一个小的Python脚本桥接GPIO事件和D-Bus方法调用。
4.4 网络配置与远程访问
Rock3A有两个千兆网口,我配置了eth0作为管理口,静态IP:
ip addr add 192.168.1.100/24 dev eth0 ip link set eth0 upOpenBMC默认启用了dropbear SSH服务,可以直接远程登录。如果需要Web界面,可以单独构建bmcweb,但我觉得对于调试阶段,SSH加busctl已经够用了。
远程访问的稳定性实测下来不错,连续运行72小时没有掉线。网络吞吐方面,用iperf3测试,BMC管理通道的带宽大概在800Mbps左右,足够传输日志和固件。
5. 性能对比与实测数据
5.1 启动时间对比
我把Rock3A OpenBMC和一台AST2600参考板的启动时间做了对比:
| 阶段 | Rock3A (RK3568) | AST2600参考板 |
|---|---|---|
| U-Boot到内核 | 1.8s | 2.5s |
| 内核到init | 3.2s | 4.1s |
| init到D-Bus就绪 | 4.5s | 6.8s |
| D-Bus到SSH可用 | 2.5s | 3.2s |
| 总计 | 12.0s | 16.6s |
Rock3A快的主要原因在于CPU主频高(2.0GHz vs 1.2GHz)和eMMC读写速度快。不过AST2600的方案在功耗上更有优势,典型功耗只有3W左右,而Rock3A满载大概5-6W。
5.2 内存与CPU占用
OpenBMC核心服务在Rock3A上的资源占用:
| 服务 | 内存占用 | CPU占用(空闲) |
|---|---|---|
| systemd | 12MB | 0.3% |
| dbus-daemon | 8MB | 0.1% |
| phosphor-hwmon | 6MB | 0.2% |
| phosphor-state-manager | 5MB | 0.1% |
| phosphor-logging | 10MB | 0.5% |
| dropbear | 4MB | 0.1% |
| 其他 | 15MB | 0.5% |
| 总计 | 60MB | 1.8% |
2GB内存的Rock3A跑这些服务绰绰有余,剩余内存可以全部用作tmpfs缓存。CPU占用也很低,四核A55基本处于 idle 状态。
5.3 传感器采集延迟
用LM75传感器测试,从温度变化到D-Bus属性更新,延迟大概在200-300ms。这个延迟主要来自phosphor-hwmon的轮询周期,默认是1秒。如果需要更快的响应,可以调整/etc/phosphor-hwmon/下的配置,把轮询间隔改成500ms或更短。但要注意,轮询太频繁会增加CPU占用。
6. 常见问题与排查技巧实录
6.1 串口无输出或乱码
这是移植初期最常见的问题。排查顺序:
- 确认波特率。Rockchip的板子通常是1500000,不是115200。如果U-Boot阶段有输出但内核阶段乱码,检查bootargs里的console参数。
- 确认引脚mux。UART2的引脚可能被其他功能占用,检查设备树里的pinctrl配置。
- 确认电平。Rock3A的调试串口是3.3V TTL,如果用了RS232转接板,电平不匹配会导致乱码。
6.2 I2C传感器读不到数据
我遇到过一次,传感器地址正确但i2cdetect扫不到。后来发现是上拉电阻的问题。Rock3A的I2C总线内部有弱上拉,但LM75模块自带的上拉电阻阻值偏大(10K),导致上升沿太慢。换成4.7K的上拉电阻后正常。如果不想改硬件,可以在设备树里降低I2C时钟频率到50KHz试试。
6.3 D-Bus服务启动失败
OpenBMC的服务之间依赖关系比较复杂,如果某个服务启动失败,可能导致连锁反应。排查方法:
systemctl --failed journalctl -u phosphor-hwmon@0.service -n 50常见原因是配置文件路径不对或者D-Bus接口名拼写错误。phosphor-hwmon的配置文件必须放在/etc/phosphor-hwmon/下,文件名对应D-Bus对象路径。比如/etc/phosphor-hwmon/temp0.conf对应/xyz/openbmc_project/sensors/temperature/temp0。
6.4 镜像烧录后无法启动
如果dd烧录后板子没反应,先检查拨码开关是否在正确的启动模式。Rock3A的启动模式由拨码开关决定,SD卡启动和eMMC启动的拨码位置不同。另外,wic镜像的分区表是GPT,有些老的烧录工具可能不识别,建议直接用dd。
6.5 常见问题速查表
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 串口无输出 | 波特率错误 | 改为1500000 |
| 串口乱码 | 引脚mux冲突 | 检查pinctrl配置 |
| I2C扫描不到设备 | 上拉电阻过大 | 换4.7K上拉 |
| D-Bus服务失败 | 配置文件路径错误 | 检查/etc/phosphor-hwmon/ |
| 镜像无法启动 | 拨码开关错误 | 确认启动模式 |
| 网络不通 | PHY驱动未加载 | 检查CONFIG_STMMAC_ETH |
| 启动卡在U-Boot | 环境变量错误 | 检查bootcmd和分区号 |
7. 实操心得与后续扩展思路
移植过程中最大的体会是:OpenBMC的框架设计确实考虑了很多工业场景的需求,但它的默认配置是面向服务器主板的,直接搬到开发板上会有很多冗余。裁剪的时候要大胆,但核心的D-Bus接口和phosphor服务不能动,否则后续扩展会很麻烦。
另一个心得是设备树的调试要耐心。RK3568的引脚mux选项很多,同一个功能可能有多个引脚组可选,一定要对着原理图确认。我在这上面浪费了两天时间,最后发现是I2C的mux选错了。
后续如果想继续扩展,有几个方向可以考虑:一是把bmcweb加回来,提供Redfish接口,方便和上层管理平台对接;二是实现固件更新功能,通过phosphor-software-manager支持A/B分区升级;三是把NPU利用起来,做一些本地的异常检测,比如根据温度趋势预测风扇故障。这些都需要在现有基础上继续裁剪和适配,但核心的移植流程已经跑通了。
最后分享一个小技巧:构建OpenBMC的时候,把SSTATE_DIR和DL_DIR配置到本地大容量硬盘上,第二次构建能省很多时间。另外,bitbake -c cleansstate和bitbake -c cleanall要慎用,除非你确定要重新编译整个组件,否则增量构建就够了。