说实话,BMC(基板管理控制器)固件开发这个领域,有个很现实的特点:代码问题反而不是最难的,麻烦的是没用对工具链的人,在环境配置这一步就被劝退了一大半。我见过不少从应用层转过来做服务器底软的同学,第一次面对 OpenBMC(OpenBMC 是一个开源的 BMC 固件软件栈)那套基于 Yocto 的构建体系时,第一反应都是“这什么东西,怎么这么多仓库”,然后就被仓库同步、依赖解析、镜像构建一顿暴揍。
这篇“OpenBMC 开发 2:构建开发环境”其实是我自己踩坑过程的记录。整套环境从零到能出固件镜像,我前后折腾了不少时间,中途经历过仓库断流、磁盘撑爆、内存不足导致编译进程被直接杀死,各种状况。这篇文章我想整理出一条最稳的路径,把为什么这么做也说清楚,而不是单纯丢给你几行命令。不管你是刚入手 OpenBMC 的新手,还是想在另一台机器上重建开发环境的老手,这篇文章的完整流程、参数选择和避坑清单,应该都能帮你节省至少两到三天的摸索时间。
1. 为什么 OpenBMC 的环境搭建这么“劝退”
1.1 它不是单个仓库,而是一整棵依赖树
第一次打开 OpenBMC 官方仓库的人,大概率会有点懵。明明是一个固件项目,但代码仓库的根目录里没有一堆 .c 和 .h 文件,取而代之的是 submodule 列表、bitbake 层目录和一堆 OpenEmbedded 相关的配置文件。如果你把 OpenBMC 理解成“一个固件”,那方向就错了。
它的本质是一套定制化 Yocto 发行版。Yocto 是一套 Linux 嵌入式系统构建框架,通过 BitBake 任务调度器,把内核、引导程序、根文件系统、应用软件包按照预设的规则拉取、编译、打包成最终固件镜像。OpenBMC 的所有功能模块,比如 webui、dbus-sensors、phosphor-state-manager,全部以“软件包”的形式集成在这套框架里。所以你构建出来的固件,其实是一个完整的、精简的嵌入式 Linux 系统,而不只是一个管理程序。
这就解释了环境搭建为什么难:因为你本质上要搭建的不只是“用来编译 OpenBMC 的编译器”,而是一个完整的 Yocto 构建流水线。这个流水线要能访问互联网拉取源码,要能处理不同软件包之间的依赖关系,还要有足够的存储空间和内存来承载 CPU 密集的编译任务。
另外一个常见的误区是把 OpenBMC 的所有仓库手动 git clone 到本地,指望靠硬拼把代码凑齐。如果只拉 openbmc/openbmc 主仓是不够的,它还有很多子模块和依赖,分布在 meta-openbmc-mods、openbmc-meta-phosphor、linux-aspeed、u-boot 等大量不同仓库中。人工逐个拉取不仅费时,还极容易出现版本不匹配的问题。正确的做法是用 repo(Google 的多仓库管理工具)结合 manifest 文件把整棵依赖树一次性拉到正确状态。
1.2 构建全量镜像需要多“重”的资源
OpenBMC 构建对机器配置的要求,比一般嵌入式 Linux 项目要高很多。我个人的体会是,如果你指望在一台 8GB 内存的笔记本上完整构建 OpenBMC,能把机器用出“服务器”的感觉——风扇狂转,整个系统卡到鼠标都飘。
原因要从 BitBake 的工作机制说起。BitBake 在做全量编译时,会解析所有 layer 的 metadata,然后按照任务依赖关系逐个执行 do_fetch、do_unpack、do_configure、do_compile、do_install、do_package、do_image。这里面最吃内存的是解析阶段和并行编译阶段。OpenBMC 的依赖树很大(包括 openssl、systemd、glibc、kernel 等),在构建高峰期,BitBake 会启动几十个并行任务,每个任务都要加载工具链和头文件。官方文档其实没有特别严格的最低要求,但从实际经验看,16GB 内存是一个及格线。如果少于 16GB,一定要限制 BB_NUMBER_THREADS,否则内存耗尽后 BitBake 进程会被内核直接 OOM kill,构建失败还没什么提示信息,看起来像是一堆随机语法错误。
磁盘空间也是新手经常忽略的点。OpenBMC 的构建目录(build)在完整构建后,膨胀速度远超预期。所有下载的源码包放在 build/downloads,临时编译产物放在 build/tmp,生成的镜像放在 build/tmp/deploy/images。一套完整的 qemu-x86 目标构建,结束后整个 build 目录占到 80GB 到 120GB 属于正常范围。如果你同时开了多个目标机型的构建,磁盘很轻松就会突破 200GB。我建议给构建目录至少预留 200GB 的可用空间,并且优先使用 SSD。NVMe 固态硬盘对构建速度的影响非常明显,机械硬盘上同样一次构建可能要多花大半天的等待时间。
网络环境也要提前评估。OpenBMC 构建过程中,Yocto 会从上游抓取大量开源软件源码包(从 GNU 镜像站、GitHub Release、内核官网等),如果源站访问不稳定,build 过程会在 do_fetch 阶段反复失败。这个问题在国内开发环境里尤其突出,后面我会提供几种相对务实的做法。
1.3 本地环境构建是最灵活的切入点
OpenBMC 的构建环境,理论上可以有几种选择:本地物理机直接构建、Docker 容器内构建、或者依赖 CI 远端构建。很多企业会自己搭 CI 流水线,但作为个人开发者或者初级接触者,我还是建议从本地环境构建开始。
原因很实在:OpenBMC 这个项目在开发过程中需要频繁执行增量编译、修改 kernel 配置、单独 build 某个软件包、甚至修改 meta-layer 里,如果没有本地全量环境,这些操作全部需要提交远程触发,一个循环下来可能就是半小时一小时。本地环境虽然首次构建慢,但进入增量开发状态后,效率反而是最高的。
Docker 也是一个推荐的选择,尤其在团队协作或需要复现构建环境时很有价值。OpenBMC 官方提供了 Dockerfile 和构建辅助脚本,能省去很多折腾宿主机依赖的时间。但对新手来说,我不建议一上来就走 Docker 路线,因为容器会掩盖掉很多系统层面的细节,比如依赖库、全局工具链、路径权限等问题,后续你在容器外复现别人问题时还是会卡住。最好是先能在物理机原生环境成功构建一次,再考虑容器化或 CI 化。
2. 构建前的基本准备与方案选型
2.1 宿主机操作系统选型与依赖安装
OpenBMC 的构建,官方长期验证支持的系统是 Ubuntu。这个不是玄学,Yocto 社区对不同发行版的依赖兼容性测试,基本都集中在主流的 LTS 版本上。我个人的建议是直接用 Ubuntu 22.04 LTS,理由很直接:OpenBMC 社区、Yocto 社区以及大量开发者踩坑记录,大部分都基于这个版本。你遇到依赖缺失时,网上的现成答案也最多。
用全新系统的话,需要先安装一堆基础依赖包。这里有一个点要提醒:不要只装一个 build-essential 就觉得完事,Yocto 体系对 Python、Perl、make、diffstat、texinfo 等构建工具有一套完整的依赖清单。OpenBMC 官方文档里有一个命令清单,我这里贴一个基于 Ubuntu 22.04 的版本:
sudo apt update sudo apt install -y \ build-essential \ chrpath \ cpio \ debianutils \ diffstat \ file \ gawk \ gcc \ git \ iputils-ping \ libacl1 \ libdata-dump-perl \ libssl-dev \ libtool \ python3 \ python3-dev \ python3-pip \ python3-pexpect \ repo \ rsync \ sed \ socat \ subversion \ texinfo \ unzip \ wget \ xz-utils \ zstd注意,上面我用了 sudo apt install repo 直接安装 repo 工具的方式。但很多系统源里自带的 repo 版本比较旧,如果你在新版系统上遇到 repo 自检失败,可以考虑直接下载官方发布的 repo 脚本:
mkdir -p ~/bin curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo chmod a+x ~/bin/repo export PATH=~/bin:$PATH然后记得把 PATH 写进 ~/.bashrc,省得每次都重新 export。网络上关于下载 repo 的教程五花八门,这个官方链接其实是最稳妥的入口。
Git 配置也要提前看一眼。OpenBMC 仓库对提交信息的 user.name 和 user.email 有要求,而且 repo 同步过程中也会用到 git 的 SSH 或 HTTPS 配置。建议提前设置:
git config --global user.name "Your Name" git config --global user.email "your@email.com"构建工具链方面,OpenBMC 当前主线的 Yocto 版本对宿主机的 gcc 和 python 版本有要求。Ubuntu 22.04 默认的 gcc-11 和 python3.10 是满足要求的,不需要额外处理。这点相比旧版 OpenBMC(基于 Yocto 的 sumo/zeus 分支时代)来说要省心很多,那时候还要处理宿主机的 multilib 兼容问题。
2.2 独立构建目录的用户权限与路径规划
OpenBMC 的构建工具链有一个不算隐蔽但是很坑的脾气:它在构建过程中会创建大量符号链接,并且对路径中的特殊字符极其敏感。如果你的用户名或者项目路径里有空格、中文、或者特别长的路径,都可能在构建中途引发奇怪的问题。
所以项目放哪里是有讲究的。我建议在根目录或者用户目录下建一个干净、无空格的路径,比如:
mkdir -p /workspace/openbmc而不是把它放到什么~/Documents/My Projects/BMC下。虽然“放哪都能构建”在某些情况下也成立,但一旦出问题,路径相关的排查非常浪费时间。与其赌它不出问题,不如一开始就规避。
还有一个权限问题。很多新手喜欢直接 su 到 root 去跑构建,理由是省得 chown 文件。这个习惯在 Yocto 构建里强烈不推荐。BitBake 系统会拒绝以 root 身份运行部分任务,因为它有安全保护机制,防止构建产物被 root 污染后导致后续在普通用户下无法清理。另外以 root 运行时,后续一层层的临时文件和缓存文件全部归 root 所有,你想删除或者修改权限都很麻烦。
正确做法是建一个普通用户,在源码目录和构建目录下保证用户可写,然后用普通用户执行所有构建命令即可。如果你非要 docker,那另说,但原生环境就按这个来。
磁盘分区也要规划一下。Yocto 构建过程会产生大量的中间文件,临时文件系统如果空间不够,最直接的表现就是 do_compile 阶段报“No space left on device”,但这类报错经常会跟编译错误混在一起,误导排查方向。我建议构建前用 df -h 确认一下目标分区的剩余空间,如果项目放在根目录分区,至少保证 150GB 以上。如果空间紧,可以考虑把 build 目录放到独立挂载的大容量数据盘上,用软链接指过去,这个做法不会影响构建正确性。
2.3 源码同步:用 repo 还是直接 git clone
OpenBMC 的源码获取方式,我一直建议用 repo。虽然它也是一个 git 工具链里的东西,但它的定位是“多仓库统一管理”。OpenBMC 项目根目录有一个 default.xml manifest 文件,里面详细列出了所有需要拉取的 git 仓库、分支和位置。repo init 和 repo sync 配合这个 manifest,可以保证所有子仓库版本完全对齐。
手动 git clone 不是不行,但是在 OpenBMC 这个项目里尤其不推荐。因为 OpenBMC 的代码不仅在 openbmc/openbmc 这个主仓里,还包括 openbmc/meta-phosphor、openbmc/phosphor-dbus-interfaces、openbmc/phosphor-state-manager、openbmc/phosphor-host-ipmid 等等几十个仓库。你用 git clone 主仓后,还要手动去拉几十个子模块,而且它们需要基于特定分支和 commit 组合才能正确构建。repo 的存在,就是为这种大规模多仓协作开发的场景设计的。
不过 repo 对网络的要求也比较高,因为它本质上是并发执行多个 git fetch 操作。如果你的网络不稳定,经常会出现同步到一半卡住、或者某个仓库 fetch 失败的情况。这个问题的应对方案我放在后面的“常见问题”里详细说,这里先提醒一下:
repo sync 失败时不要慌,它支持断点续传。你只需要检查失败原因,然后重新执行 repo sync,它会接着之前完成的进度继续同步,不会从头开始。
2.4 理解 OpenBMC 的分支策略:在主线比在 tag 上更容易起步
OpenBMC 项目设计中,主线分支(master)是持续集成的开发主线,每天都有大量 commit 合入。对普通开发者来说,直接基于主线开发虽然代码变化快,但好处是文档更新及时、社区支持也大多针对主线。
一些刚接触 OpenBMC 的人会下意识去找 release tag,比如 2.10.0、2.13.0 之类的。这不能算错,但对“构建开发环境”这个目的来说,分支版本的兼容性资料往往不如主线全。而且主线通常已经修复了旧版的大量构建问题。我做开发时默认选择 master 分支,如果你需要对接某个特定版本的硬件 BSP,再考虑切换分支。
repo init 的时候可以用 -b 指定分支,比如 master。如果你的项目基于其它版本,比如 2.14.0,只需要:
repo init -u https://github.com/openbmc/openbmc.git -b 2.14.0这里的 -b 指定的是 manifest 所在的分支,而不是每个子仓库都硬切到该分支,但 manifest 里会定义对应每个子仓库的 revision,最终会保证整体一致性。
3. 完整实操:把 OpenBMC 开发环境跑起来
3.1 repo 初始化与同步细节
整个环境搭建,第一步是初始化 repo。假设你的项目目录是 /workspace/openbmc,那么:
cd /workspace/openbmc repo init -u https://github.com/openbmc/openbmc.git -b master执行这条命令后,repo 会拉取 OpenBMC 的 manifest 文件,在当前目录生成一个 .repo 目录。成功的话,会有类似repo init done的输出。
然后执行同步:
repo sync这一步是真正开始拉取代码的时候。几十个仓库的总数据量接近 2GB,取决于网速,等待时间从十分钟到一小时不等。repo 在执行时会显示每个仓库的进度,比如:
Fetching projects: 45% (23/51) phosphor-dbus-interfaces这里要提醒:repo sync 过程中尽量不要中断。如果中途 ctrl-c 或者断网,虽然可以重新执行 repo sync 续传,但部分仓库状态可能处于中间态,后面容易出现 git 对象不完整的错误。更稳妥的做法是让它在无人打扰的情况下跑完。
同步完成后,可以看到当前目录下出现大量子仓库目录。这些目录名和 manifest 里对应,每棵树都是完整的 git 仓库。如果你要修改 OpenBMC 源码里的某个模块,可以直接在这个模块对应的子仓库里改。这个结构本身就体现了 OpenBMC 的分层思想:每个独立仓库只负责自身的一个或一组功能模块,模块之间通过 dbus 接口通信,而不是通过代码级别的函数调用。
在 repo sync 之后,建议执行一次 git status 检查所有仓库是否干净:
repo forall -c "git status --short"如果输出为空,说明所有仓库都处于干净的 detached HEAD 状态,可以继续后续步骤。这个命令在排查环境问题时非常有用,它能把所有子仓库的状态一次性列出来,省得一个个目录切换过去看。
3.2 初始化构建环境:oe-init-build-env 与 TEMPLATECONF
源码同步完成后,还不能直接开始构建,要先初始化 BitBake 的构建环境。这一步的核心是两个文件:setup 脚本和模板配置文件。
OpenBMC 会根据你准备构建的目标平台,生成对应的 conf/local.conf 和 conf/bblayers.conf。这些配置文件决定了 BitBake 要解析哪些 layer、启用哪些 machine、构建哪些 image。默认配置如果没有制定 machine,BitBake 会报错告诉你没有指定 target。
我们以 qemu-x86 这个模拟目标为例,它是 OpenBMC 官方提供的,用来在没有真实硬件时验证系统功能的虚拟目标。设置 TEMPLATECONF 指定模板路径:
cd /workspace/openbmc TEMPLATECONF=meta-evb/meta-evb-x86/meta-qemux86/conf \ source oe-init-build-env build-qemu这里的逻辑拆开解释一下:
meta-evb/meta-evb-x86/meta-qemux86/conf是 qemu-x86 机器配置模板的物理路径。TEMPLATECONF 环境变量让 oe-init-build-env 知道去哪里找初始的 local.conf 模板。source oe-init-build-env才是核心动作,它会把 BitBake 等工具链路径加入 PATH,并跳转到你指定的构建目录。build-qemu是你要创建的构建目录名。如果目录不存在,脚本会自动创建。后续的构建产物、临时文件、下载缓存都会在这个目录下。- 如果你后面要为另一个目标平台构建,比如 ast2500-evb,需要建一个新的构建目录(比如 build-ast2500),因为构建目录绑定了一套 machine 配置,切换起来比较麻烦。一个构建目录对应一个目标,是 Yocto 的约定俗成做法。
成功执行后,当前工作目录会切换到 build-qemu,确认一下当前机器的配置:
grep MACHINE conf/local.conf可以看到类似MACHINE ??= "qemux86"的内容。如果什么都没输出,你需要手动在 local.conf 里加一行:
MACHINE ??= "qemux86"不过按 TEMPLATECONF 方式初始化时,normal case 下模板里已经写好了 MACHINE 变量,不需要再手动加。
3.3 local.conf 里的关键参数调优
local.conf 是 Yocto 项目里面向用户的全局配置文件。OpenBMC 默认生成的 local.conf 已经考虑了大部分常规场景,但我建议在开始构建前做几项调整,以便后续开发提速。
首先是 CPU 并行度。默认配置里 BB_NUMBER_THREADS 和 PARALLEL_MAKE 可能为空或者仅设了一个合理初值,但更好的是按宿主机 CPU 资源手动设置:检查你机器的 CPU 核心数:
nproc然后编辑 conf/local.conf,把并行参数设为实际核心数的一半或四分之三,比如 16 核的机器上设为 8:
BB_NUMBER_THREADS = "8" PARALLEL_MAKE = "-j 8"这里不要莽。设成和核心数一样并不一定更快,因为每个编译任务本身还会派生子进程,总并发很可能翻倍,内存会瞬间被打满。机器内存 16GB 的话,建议并行度控制在 4 到 8,否则容易触发 OOM。32GB 内存再考虑更高。
其次是磁盘缓存设置。Yocto 的构建系统会把所有下载的压缩包统一放到 downloads 目录,默认情况下就是在构建目录里的 downloads。如果机器上有多个构建目录共享同一份源码集,可以开启共享下载缓存:
DL_DIR = "/workspace/downloads"这个设置对多目标开发场景非常有用。比如你分别给 qemu-x86 和 ast2500-evb 建了构建目录,如果把 DL_DIR 指向同一个共享路径,两个构建目录拉过的源码包就可以复用,不会重复下载几 GB 的内容。不少人为了省事会把 downloads 放构建目录里,结果切换目标时又得重新下,那是浪费。
然后是 SSTATE_DIR 的设置。sstate 缓存是 Yocto 体系里一个强大的加速机制,它把构建产物(比如编译好的 .o、打包好的 .rpm)按 hash 缓存下来,如果后续构建时任务的签名没变,就直接用缓存的产物跳过重编。OpenBMC 默认开启了 local sstate,不需要额外配置,但如果你有多个构建目录共享同一份源码,可以同样配置一个共享 sstate 目录:
SSTATE_DIR = "/workspace/sstate-cache"这个配置能让“第二个目标”的构建速度提升非常多,因为大量公共组件(glibc、openssl、systemd 等)都可以直接从 sstate 里捞现成的,而不需要重新编译。我实测过,第一次完整构建需要几小时,设置共享 sstate 后,一个新的目标机型构建可能十几分钟到半小时就能出镜像。
3.4 构建 Fireware 镜像:bitbake 的过程与等待
环境初始化完成后,执行构建:
bitbake obmc-phosphor-imageobmc-phosphor-image 是 OpenBMC 的标准镜像目标,它把 OpenBMC 运行时的所有核心服务、WebUI、总线服务等打包进根文件系统,最后生成可烧录的完整固件镜像。
第一次构建的执行时间和机器配置、网络速度、下载缓存命中率关系很大。在配置合适的机器上,一份全新构建耗时大约在 1.5 小时到 4 小时之间。构建过程中,终端会滚动输出大量任务状态,比如:
Currently 4 running tasks (693 of 2200) 100% |############| 74917.2/s看到这个不要慌,它是 BitBake 的任务进度条。你需要注意的不是刷屏速度,而是有没有ERROR:开头的行。真正的构建错误会以 ERROR 形式打出,同时任务进度会停在某个百分比不动。
构建完成后,镜像文件路径在:
build-qemu/tmp/deploy/images/qemux86/在这个目录下,你至少能看到:
obmc-phosphor-image-qemux86.static.mtd:这个就是可烧录进 NOR Flash 的完整镜像文件。obmc-phosphor-image-qemux86.wic:WIC(WIC 是 OpenEmbedded 的镜像打包格式)格式的完整磁盘镜像,适合用 QEMU 加载或写入存储介质。u-boot-qemux86.bin:U-Boot 引导程序。- 以及一系列核心镜像文件。
如果你只关心机器能不能启动,qemu 项目还可以直接用 QEMU 模拟运行:
../../meta-evb/meta-evb-x86/meta-qemux86/scripts/qemu-run这个脚本会按 OpenBMC 的 QEMU 配置自动拉起虚拟机,并把串口重定向到当前终端。等看到 OpenBMC 的 U-Boot 启动日志和内核解压日志,就说明镜像构建成功且能正常引导。这时你可以在 QEMU 里通过串口登录 OpenBMC 系统,默认管理员账号root,默认密码0penBmc(字母 o 是数字 0,B 大写、m 小写)。实测这个默认凭据在新版中可以直接登录。
登录后执行:
busctl tree能看到当前启用的 dbus 服务列表,这是验证 OpenBMC 用户态服务是否正常拉起的重要手段。能看到 phosphor-mapper、xyz.openbmc_project.State.Host 等服务的 bus 名称,就说明 OpenBMC 核心服务基本起来了。
3.5 变更代码后如何做增量构建
环境搭好之后,真正的开发循环不是反复全量 bitbake,而是“改代码后增量构建”。
如果你修改了某个 Yocto recipe 对应的源码(比如改了一个 C++ 服务),最直接的增量方法是使用 BitBake 针对该组件的专属构建语法。比如修改了 phosphor-state-manager,可以单独重新构建这个包:
bitbake phosphor-state-manager -c compile -f bitbake phosphor-state-manager第一行强制重新编译(-f 是 force 的缩写),第二行将该组件重新打包。注意如果不加 -f,BitBake 可能因为“任务签名没变”而跳过编译,导致你的改动没有生效。
这里的关键点在于:“任务签名没变”这套机制对源码更改本身是敏感的——如果你在源码目录里改了文件,BitBake 的 hash 检测会识别出代码变动,进而自动触发重编,不需要 -f。但当你修改的是配置、补丁文件、或者从 Yocto metadata 层面做了调整时,签名可能不会自动变化,这时候就要 -f 强制。
只改代码不需要重编所有东西。这也是为什么 OpenBMC 开发要本地环境的核心原因:你在本地可以做到改动一个 dbus 服务,几分钟内就验证效果;走 CI 的话,一个循环至少等半小时。
4. 常见问题与排查技巧实录
4.1 repo sync 卡住或重复失败
repo sync 最让人头疼的问题,是同步到一半卡住。常见原因有三个:网络抖动、某个远端仓库访问慢、以及本地仓库状态损坏。
第一个建议:不要反复把整个目录删掉重来。repo 是支持断点续传的,同步中断后直接重新执行 repo sync,它会接着已有进度继续,不会从头开始。这个机制对网络不稳定的情况非常友好。
第二个建议:如果某个仓库反复失败,而其他仓库都正常,先看看是不是因为本地该仓库的 .git 目录处于异常状态。可以用下面命令单独同步失败的仓库:
repo sync -f <失败仓库名>-f 参数强制同步,它会在仓库失败后继续尝试其他仓库,并在最后汇总失败列表。如果加 -f 还是没用,再考虑删除该仓库本地目录:
rm -rf <失败仓库目录> repo syncrm 之后 repo 会重新拉取该仓库,虽然耗时,但能解决大部分本地仓库状态损坏问题。
还有一个会导致 repo sync 反复失败的情况:manifest 文件本身在你 init 时就指定了某个 tag,而这个 tag 下部分子仓库的 commit 已经被远端做 gc 清理。这种问题多半出现在你执行过repo sync --force-sync或长期不更新后。解决办法是切换到更新一点的 tag:
repo init -u https://github.com/openbmc/openbmc.git -b master repoh sync如果你想省时间,也可以临时给 repo 加-j4降低并发。默认 repo 的并发拉取任务数取决于 cpu 核数,网络很差时 16 个并发同时拉取容易造成大量失败。调低到 4 往往能明显提高成功率:
repo sync -j44.2 BitBake 阶段提示找不到下载文件或 URL
构建过程中报找不到某个源文件,通常是 do_fetch 阶段的问题。比如经常有人遇到类似:
ERROR: xxx-1.0-r0 do_fetch: Fetcher failure: Unable to fetch URL from any source.这种错误分为两种情况:一种是 URL 本身过期;另一种是网络连接失败。如果你确定代码版本没有问题,优先检查网络。
前面说过,OpenBMC 会从上游源码站下载源码包。由于 OpenBMC 的很多源码来自 github.com 和 GNU 镜像源,对这些源连接不稳定时,fetch 失败率会非常高。务实的做法有几套:
- 预先在本地搭一个缓存服务器,把常见源码包手动准备好放进 DL_DIR。这个适合反复多次构建的场景。
- 直接配置一个下载代理或者调整 git 的下载配置,把 FETCHCMD_git 改走镜像站。
- 对于手头能访问的实验室网络,有时候强制用 IPv4 也能缓解问题。
但不管怎样,我不建议把“下载失败就跳过该包”当成解决办法。Yocto 的构建依赖是完整的,缺一个包后面必然出现编译错误,而且错误提示可能会怪到另一个包头上,误导排查。遇到这类问题,最稳妥的思路就是:分析 URL,检查当前网络对该 URL 的可达性,然后对症处理。
实际开发中,不少团队会把 DL_DIR 提前用 archive.z 或镜像站填充好。比如在构建服务器上下载完整后,再用 rsync 把 downloads 目录同步到离线机器。这是离线开发环境的标准做法,值得参考。
4.3 内存不足导致的 OOM Kill
内存不足是 OpenBMC 初次构建最常见的隐性故障。它的表现不是直接报“out of memory”,而是构建到某个任务时,终端闪现一个Killed字样,然后 BitBake 报出一堆看起来无关的错误。很多新手把这类错误定位成编译错误,翻源码查到天荒地老,其实只是系统把编译进程杀了。
排查方法很简单:系统日志里看 OOM 痕迹:
sudo dmesg | grep -i "killed process"如果你在输出里看到某个 cc1plus 或者 bitbake 进程被 OOM 杀死的记录,基本可以确定问题根因就是内存不够。
解决办法按先后顺序递减优先级:
- 降低并行度,设置 BB_NUMBER_THREADS 和 PARALLEL_MAKE,比如 4。
- 关闭无关的桌面程序和浏览器,释放系统内存。
- 增加 swap,但这治标不治本,编译任务需要的是连续可用的物理内存,频繁 swap 会让整机卡死。
- 最彻底的办法是加内存条或者换配置更高的构建机器。
实际开发中,16GB 内存构建 qemu-x86 是可行的,但要严格控制并行度。如果你手头只有 8GB,即便设置了低并行度,构建速度也会慢得让人崩溃,而且很容易在高峰期还是被 OOM。建议要么考虑先试着构建一个更小的目标镜像(比如某个具体组件,而不是全量 image),要么就换台机器。
4.4 构建后镜像无法启动
还有一种比较常见的情况:构建成功,但 QEMU 启动后卡在 U-Boot 阶段,或者内核 panic。
如果是 QEMU 目标,优先确认你运行的是官方配套的 qemu-run 脚本,因为这涉及 qemu 机器类型、CPU 型号、内存大小、串口参数等细节。如果自己手敲 qemu 命令,漏掉某些参数(比如没有正确传 dtb 文件或者 flash 镜像参数),启动失败非常正常。
如果是真实硬件目标(比如 ast2500-evb),构建完镜像后还需要按硬件需求确认 DDR 初始化、flash 大小、mac 地址等。真实硬件的情况要复杂很多,这在下一节的硬件移植里详细说。
另一种常见现象是镜像启动后串口没有任何输出。这种问题在 OpenBMC 中多半是 CONFIG_CMDLINE 或 U-Boot 配置里没有打开串口控制台,或者 U-Boot 环境变量里的 bootargs 没有正确配置 console=ttyS0,115200。如果你确认自己用的是标准 qemu-x86,一般不会遇到这种问题。
4.5 常见问题速查表
| 现象 | 常见原因 | 处理建议 |
|---|---|---|
| repo sync 拉取一半卡住 | 网络抖动、源站慢 | 重新执行 repo sync,可加 -j4 降低并发 |
| BitBake 报 do_fetch 失败 | 下载源不可达、URL 过期 | 检查网络,或配置共享 DL_DIR 提前缓存 |
| 编译中途进程被 Killed | 内存不足,OOM 触发 | 降低并行度,扩充物理内存 |
| bitbake 没有执行修改过的代码 | 任务签名没有触发变化 | 使用-c compile -f强制重编 |
| 构建成功但 QEMU 起不来 | qemu 参数错误/镜像不对 | 使用官方 qemu-run 脚本 |
| 登录默认账号失败 | 密码大小写、版本差异 | 核对 root / 0penBmc,注意数字 0 和字符 o |
| 构建目录占用磁盘过大 | 未清理中间缓存 | 用 bitbake -c cleanall 清理和定期清理 tmp |
5. 从“能构建”到“能移植”:OpenBMC 硬件移植思路
5.1 为什么硬件移植绕不开 Yocto BSP
环境构建固然重要,但 OpenBMC 真正面向“实际产品”的开发场景,往往是针对某块自定义板卡的硬件移植。这也就是那类“openbmc 硬件移植”关键词背后大家真正关心的问题。
硬件移植的核心目标,是让 OpenBMC 在目标硬件上能启动、能跑通管理功能。而它本质上是 Yocto BSP(Board Support Package,板级支持包)工作。Yocto BSP 的定义很清晰:它是把特定主板的 kernel、u-boot、设备树、flash 布局等知识封装成可复用的 layer,只要构建系统选中了该 layer,就能生成对应板卡的固件。
从那张构建环境图就能看出来:bitbake 构建时通过 bblayers.conf 找到各 layer,通过 MACHINE 变量锁定要构建的板卡,通过 local.conf 里的配置影响全局。硬件移植做的事情,就是在这一套体系里新增一块板卡的“身份”。
5.2 新建 meta-xxx 的套路与 MACHINE 配置
Yocto BSP 开发的常规起点,是参考现有 BSP 模板新增一个 layer。OpenBMC 里现成的示例在 meta-evb/meta-evb-x86 和 meta-evb/meta-evb-aspeed 下,前者是 x86 常见评估板的示例,后者是 aspeed 系列的示例。
新建 layer 时,你需要创建以下几样关键东西:
- 一个独立 layer 目录,比如
meta-myboard/。 - 该目录下的
conf/layer.conf,声明 layer 的路径、优先级(BBFILE_PRIORITY)、依赖关系。 conf/machine/myboard.conf,定义目标板卡的 MACHINE 相关变量,包括 kernel 选择(PREFERRED_PROVIDER_virtual/kernel)、U-Boot 配置、flash 大小、设备树名称等。- 一个
recipes-kernel/linux/linux-aspeed_%.bbappend之类的追加配方,用来定制 kernel 配置,比如把默认配置文件换成自己裁剪的 defconfig。 - 设备树源文件(dts/dtsi),或者通过 kernal 仓库内的 dts 路径方式覆盖。
- 一个
recipes-bsp/u-boot/u-boot_%.bbappend之类的追加配方,用于定制 U-Boot 行为。
这些文件的具体内容,不同板卡差异很大,但整体的结构都是同一个套路。新手可能容易陷入“我要从头写一套 BSP”的误区,实际上 90% 的情况是在现有 BSP 基础上拷贝、裁剪、修改。比如你要做一块 aspeed 2600 芯片的板卡,那就从 meta-evb/meta-evb-aspeed 下对应 eval board 的配置文件开始改,完全不必要从零生成 defconfig。
5.3 设备树、内核配置与固件烧写
板级硬件移植在代码层面主要涉及三个部分:
设备树(DTS)的编写与修改。BMC SoC(不同芯片)通常需要根据主板的实际外设连接,比如 I2C 总线上挂了哪些传感器、PWM 风扇接在哪个通道、NCSI 网络如何连接,来修改设备树节点。设备树写错了,轻则某个传感器无法上报,重则整个系统在启动阶段 hang 住。
内核配置裁剪。OpenBMC 的 Linux 内核配置需要启用 IPMI(智能平台管理接口)、I2C、GPIO、WATCHDOG 等基础功能。不同 BMC 芯片厂商在 OpenBMC 社区有对应的内核分支,比如 linux-aspeed。你在自定义板卡时,通常会在内核的 defconfig 基础上 CONFIG 增删,并通过 bbappend 覆盖应用。
固件烧写与启动方式。OpenBMC 镜像生成后,会以static.mtd文件或.wic镜像的形式输出。在开发阶段,你通常需要在 U-Boot 或者烧录器层面把镜像写入 NOR Flash。常用烧写方式是:先启动到 U-Boot 命令行,通过 tftp 把镜像传到板卡内存,然后用 flashcp 写入:
flashcp /tmp/obmc-phosphor-image-myboard.static.mtd /dev/mtd0在 U-Boot 阶段也可以用 tftp + nand erase/fwrite 烧写。这部分属于启动流程的底层细节,如果你还没有真实的板卡在手,可以先在 qemu 上把整个“编译镜像 -> 启动镜像 -> 验证服务”的链路走通,再考虑硬件阶段的移植。
5.4 硬件移植的验证闭环:不止能开机
对硬件移植来说,能开机只是起点。真正的移植完成标准,至少包括:
- 串口控制台能输出 OpenBMC 启动日志并能登录。
- D-Bus 上出现关键的 phosphor-* 服务,比如 phosphor-mapper、state manager、sensor 服务。
- IPMI 命令能正常执行,比如
ipmitool sensor list能读到 BMC 收集的传感器数据。 - 网络接口(eth0/NCSI)能拿到 IP,能在局域网内访问 WebUI。
- 电源控制脚本能对受管主机执行上电、下电、重启操作。
所以做硬件移植,环境构建只是第一步。真正让我觉得 OpenBMC 复杂但有魅力的地方,也恰恰在这里:它是一个完整嵌入式 Linux 系统,又带了一套非常规范的软件总线机制,所有外设、状态、控制都通过 dbus 和服务的方式呈现出来。你修改一块板卡的 BSP,最终会以这些可观测的接口体现出来,验证起来非常清晰,不像很多嵌入式项目,点个灯就算完成。
6. 我给新手的一点实在建议
构建开发环境这件事,在 OpenBMC 开发流程里看似基础,但它决定了你后续所有开发效率的下限。环境没配好就急急忙忙去看源码,大概率会陷入“编译失败 -> 查资料 -> 环境问题 -> 重新编译”的死循环里。
我自己实际的体验是:在 Ubuntu 22.04 上按照本文的步骤完整走一遍,第一次全量构建出 qemu-x86 镜像,即便网络状况中等,也基本可以在三到四小时内跑通。这之后再进行任何源码修改和增量构建,效率就完全在掌控内了。
最后分享一个小技巧:如果你打算长期做 OpenBMC 开发,先把DL_DIR和SSTATE_DIR规划好,并第一时间跑通一次共享缓存组合。后续每接入一个新板卡的 BSP,构建时间可以从“几小时”直接压缩到“十几分钟到半小时”。很多老手效率高,不是因为他编译速度快,而是他把该缓存的、该复用的都提前安排好了。这一步做好了,你的 OpenBMC 开发才算是真正进入了正轨。