跳转到内容
新建笔记

i.MX8MP SDK build.sh:依赖、错误与产物核对

这份笔记按构建依赖解读早期 OK8MP / i.MX8MP SDK 的 build.sh 片段:U-Boot、内核和模块、M7 固件、应用及文件系统如何进入最终镜像。OK8MP-C_defconfig 和文中的目录名都是具体 SDK 的约定,不能推广为所有 i.MX8MP BSP 的统一接口。

**资料边界:**本次能核对的是原笔记保存的片段,未取得与其逐字对应的完整 SDK build.sh。因此,文中把「片段直接显示的行为」「需要查看完整调用链才能确认的行为」和「建议的改造示例」分别标明;没有执行厂商构建、烧录或熔丝写入。飞凌的 Linux 5.4.70 官方实例 可以印证该 SDK 的环境初始化与 ./build.sh all 使用方式,但不能证明下文每个历史分支的实现完全一致。

原笔记中的步骤输入或前提预期产物/写入位置
uboot板卡与 DDR 配置、AArch64 工具链SPL、U-Boot、DTB 和组合启动镜像
freertos_demoM7 工具链与示例配置固件汇总目录,随后进入 rootfs 的 lib/firmware/
kernel内核配置、匹配工具链Image、DTB、模块,以及引用 M7 固件的 boot.img
apps目标 Qt/库和安装路径rootfs 内的 Qt 与命令行应用
ramdisk对应文件树与打包工具RAM disk 产物
mkfw前面各组件与文件系统元数据rootfs/最终固件
all / full全部依赖;full 在原记录中还包含清理多个目录都会被改写

顺序尤其影响 boot.img:若它要复制 hello_world.bin,该固件就必须先存在。原笔记的分发器摘录里,freertos_demo 有环境初始化调用,而 all 中只显示固件构建调用;不能只凭这个简化片段保证 all 的所有环境前提都已满足。

工作目录与工具链环境

跳转到“工作目录与工具链环境”

原片段用当前工作目录确定 SDK 路径:

终端窗口
SDK_PATH=$(pwd)
DESTDIR=$(realpath OK8MP-linux-fs/rootfs)
export SDK_PATH DESTDIR
UBOOT_DIR=${SDK_PATH}/OK8MP-linux-uboot
KERNEL_DIR=${SDK_PATH}/OK8MP-linux-kernel
ROOTFS_DIR=${SDK_PATH}/OK8MP-linux-fs/rootfs
IMAGES_DIR=${SDK_PATH}/images

因此,从别的目录调用 /path/to/sdk/build.sh 仍可能得到错误的根路径。改造时可以使用 BASH_SOURCE[0] 定位脚本,再明确进入该目录;仅改变一个变量、不调整后续相对路径仍不完整。

DESTDIR 在此处指向待安装的目标文件系统,不是开发主机的 /。工具链环境应按具体 SDK 手册初始化,确认 aarch64-poky-linux-gcc、sysroot 和目标 Qt 工具来自匹配环境。MAKE_JOBS 也要检查:原来的 -j${MAKE_JOBS} 在变量为空时可能退化为不限制任务数的 -j。

下面是新写的独立预检查脚本,保存为 SDK 根目录中的 build-preflight.sh,用 Bash 执行。它只检查目录、语法与变量,不调用厂商构建函数,也不证明所有配置正确:

#!/usr/bin/env bash
set -euo pipefail
SDK_PATH="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
cd -- "$SDK_PATH"
DESTDIR="$SDK_PATH/OK8MP-linux-fs/rootfs"
MAKE_JOBS="${MAKE_JOBS:-$(nproc)}"
[[ "$MAKE_JOBS" =~ ^[1-9][0-9]*$ ]] || {
printf 'MAKE_JOBS must be a positive decimal integer\n' >&2
exit 2
}
for directory in OK8MP-linux-uboot OK8MP-linux-kernel OK8MP-linux-fs/rootfs images; do
[[ -d "$SDK_PATH/$directory" ]] || {
printf 'missing directory: %s\n' "$directory" >&2
exit 1
}
done
[[ -f "$SDK_PATH/build.sh" ]] || {
printf 'missing build.sh\n' >&2
exit 1
}
bash -n "$SDK_PATH/build.sh"
for tool in aarch64-poky-linux-gcc make mkfs.vfat mcopy fakeroot; do
command -v "$tool" >/dev/null || {
printf 'missing tool: %s\n' "$tool" >&2
exit 1
}
done
export SDK_PATH DESTDIR MAKE_JOBS
printf 'SDK: %s\nRootfs: %s\nBuild jobs: %s\n' "$SDK_PATH" "$DESTDIR" "$MAKE_JOBS"

这是一个独立进程,里面的 export 不会把环境永久设置到父 shell。它是预检查示例,不应声称运行一次就已经替后续新终端配置好了工具链。

原记录的模式是在关键命令后接 || exit_on_fail "$LINENO",并由函数打印行号后退出。$LINENO 在调用点展开,能帮助定位失败;但保护仅覆盖实际加上该处理的命令。一个 cp 失败时,若既没有显式检查,也不处于有效的 set -e 退出条件中,脚本可能继续运行。

set -Eeuo pipefail 和 ERR trap 可以作为改造的一部分,却不是对任意脚本都成立的「任意失败自动退出」开关:条件列表、&&/||、部分管道和函数调用上下文都有规则,命令替换也有继承差异。需要分别验证失败返回、清理与旧产物处理。Bash 的 Set Builtin 和 Bourne Shell Builtins 说明了退出和 trap 条件。

U-Boot:DDR 配置与输出文件

跳转到“U-Boot:DDR 配置与输出文件”

原片段列出了这些 DDR 参数映射:

传给构建函数的参数片段选择的配置
_4GOK8MP-C_defconfig
_NY4GOK8MP-C_NANYA_4G_defconfig
_NY1GOK8MP-C_NANYA_1G_defconfig
_NY2GOK8MP-C_NANYA_2G_defconfig
其他拼接为 OK8MP-C<参数>_defconfig

原记录的分发器会给第二参数加 _;是否如此必须以手头脚本为准。不能因为容量相同就交换不同 DDR 器件的配置,也不能从可拼接字符串推断对应配置一定存在。

片段中的构建命令使用 ARCH=arm 与 CROSS_COMPILE=aarch64-poky-linux-。在 U-Boot 中,AArch64 平台仍属于 arm 架构目录;Linux 内核的对应参数则通常为 ARCH=arm64。配置后预期得到 u-boot-nodtb.bin、arch/arm/dts/OK8MP-C.dtb 和 spl/u-boot-spl.bin,再复制到 images/uboot/。目标目录须预先存在,每次复制都应检查返回值。

imx-boot 与 ROM 可加载镜像

跳转到“imx-boot 与 ROM 可加载镜像”

原笔记保留的合成片段为:

终端窗口
pushd tools/imx-boot/
make SOC=iMX8MP flash_evk
popd
cp tools/imx-boot/iMX8M/flash.bin "images/imx-boot$1.bin"

这是已有 SDK 环境中的历史摘录,不是独立可运行函数;$1 的来源以及目录中输入文件如何准备,要追到完整调用链。NXP 的 imx-mkimage 按平台规则组合启动组件,具体目标所需的 SPL、U-Boot、设备树、ATF 和 DDR 固件等由该版本构建文件决定。仅有几个 cp 片段,不能证明所有输入都已更新。

验证应记录组件来源与校验值,并在目标板串口检查 DRAM 初始化、U-Boot 版本、设备树与后续启动。时间戳、大小和 cp 成功只能帮助排查文件流程,不能证明镜像可启动。

熔丝与 EEPROM 宏必须单独审查

跳转到“熔丝与 EEPROM 宏必须单独审查”

原记录出现以下源码修改:

路径宏原记录描述
board/forlinx/ok8mp-c/fuse/fuse.cWRITE_FUSE用 sed 将注释宏改为定义,生成特殊镜像
board/forlinx/ok8mp-c/eep.cEEP_FORCE_UPDATE启用强制 EEPROM 更新
images/secret/imx-boot-secret<DDR>.bin不适用以文件是否存在决定是否复用特殊产物

这里要分清在主机编译包含写入逻辑的镜像与在板上执行实际写入。编译通常不会直接编程目标熔丝,但会产生以后启动时可能改变设备状态的固件。宏名本身不足以确定写入哪个字、什么值或何时触发,必须审查对应源码及所有调用条件。

不要仅因缓存文件存在就认定它适合当前板卡、DDR 或安全配置。特殊镜像与日常启动镜像应有明确产物标识、输入版本和独立使用流程。熔丝的可编程方向与不可逆性由具体器件规定;EEPROM 更新也可能覆盖板级参数。

原文用 git checkout -- <文件> 作为退出时恢复示例,这会丢弃这些文件已有的未提交修改,不能当成通用回滚。更合适的改造是在独立构建副本中做临时变更;若仍要修改原目录,应在变更前保存精确原内容,并在正常退出、失败和中断路径恢复,再核对差异。本文不提供或执行熔丝值与烧录命令。

内核、模块和 rootfs 要来自同一构建

跳转到“内核、模块和 rootfs 要来自同一构建”

原记录的内核命令次序如下,依赖已初始化的工具链和有效 MAKE_JOBS / DESTDIR:

终端窗口
make OK8MP-C_defconfig ARCH=arm64
make ARCH=arm64 -j"$MAKE_JOBS" CROSS_COMPILE=aarch64-poky-linux- dtbs Image
make ARCH=arm64 -j"$MAKE_JOBS" CROSS_COMPILE=aarch64-poky-linux- modules
make ARCH=arm64 CROSS_COMPILE=aarch64-poky-linux- \
modules_install INSTALL_MOD_PATH="$DESTDIR"

每次重新执行 defconfig 可能覆盖此前的 .config 改动,产品配置应另行保存。modules_install 通常写入 $DESTDIR/lib/modules/<kernel-release>/,要让最终 rootfs 与刚构建的内核匹配;安装路径不等于内核实际加载路径已正确配置。

原记录还涉及 extra/vvcam/v4l2/、Basler 相机、cryptodev 和 Jailhouse 等外部模块。仅在它们的目录执行 make,是否选择了正确内核和架构取决于各自 Makefile。检查日志、构建目录、模块架构与 vermagic;vermagic 相同也不替代符号版本和实际加载测试。标准外部模块接口使用 make -C <内核构建目录> M=<模块绝对目录> ... modules,但厂商包装脚本是否支持直接替换要另行确认。Linux 外部模块文档 说明了该接口及配置前提。

FAT boot.img 包含哪些文件

跳转到“FAT boot.img 包含哪些文件”

原记录使用:

终端窗口
mkfs.vfat -n "Boot OK8MP" -S 512 -C images/boot.img 85184
mcopy -i images/boot.img images/kernel/Image ::/Image
mcopy -i images/boot.img images/kernel/OK8MP-C.dtb ::/OK8MP-C.dtb
mcopy -i images/boot.img "$DESTDIR/lib/firmware/hello_world.bin" \
::/forlinx_m7_tcm_firmware.bin

mkfs.vfat -C 创建普通文件,末尾的 85184 以 1024 字节为单位,所以镜像容量为 87,228,416 字节,即 83.1875 MiB,不是 85,184 个 512 字节扇区。该数值只属于这段记录。dosfstools 文档 明确区分块数和扇区大小。

mcopy -i 把后续的 ::/ 路径解释为镜像内路径,无需 loop mount。使用 mdir -i images/boot.img ::/ 可列出文件;进一步从镜像读回并比较校验值,才能确认放入的是预期内容。重建前还要处理已有输出:-C 并不意味着自动安全覆盖任何旧 boot.img。

第三个输入必须先存在,复制后的名称还需与 M7 加载方式匹配。把它改名为 forlinx_m7_tcm_firmware.bin 不会改变内部链接地址,也不能把 DDR 固件转换为 TCM 固件。

M7 工具链、示例和应用

跳转到“M7 工具链、示例和应用”

原片段在环境中未找到 ARMGCC_DIR 时设置工具链目录。若该目录为发行包根目录,GCC 可执行文件通常在 bin/ 下,追加到 PATH 的应是 "$ARMGCC_DIR/bin"。env | grep 只查看已导出的变量,无法判断当前 shell 中未导出的同名变量,也不能证明变量指向有效目录。

应检查 "$ARMGCC_DIR/bin/arm-none-eabi-gcc" 是否可执行,并确认 CMake 或示例脚本实际读取了这个目录。这个 M7 bare-metal 工具链与 A53 Linux 的 aarch64-poky-linux- 工具链作用不同。

原记录依次构建 Hello World、GPIO LED、RPMsg、软件定时器示例,把产物聚合到 OK8MP_RTOS_BIN,再复制到 lib/firmware/。这里应逐项记录 TCM/DDR 链接版本和固件文件名,避免 release/* 混入旧文件。lib/firmware/ 是 Linux 固件加载的常见搜索位置,具体名字和加载方式仍由驱动、remoteproc 或启动配置决定。

应用分支中的 qmake → make → make install 依赖目标 Qt 环境和项目安装规则。先检查构建日志的编译器、库与安装前缀;make install 不会因为脚本运行在 SDK 中就自动保证写入目标 rootfs。命令行应用也要核对同样的工具链与路径条件。

版本记录、命令分发和清理

跳转到“版本记录、命令分发和清理”

原 build_git 把 SDK、U-Boot、内核和文件系统仓库的分支及最近提交记录写入 rootfs 的 /etc/forlinx,再复制到 images/git.txt。这是追溯来源的一部分,但还应记录脏工作树、配置与产物校验值。[ -d "$dir/.git" ] 会漏掉 .git 为文件的 Git worktree;可以用 Git 自身命令查询,再核对返回的仓库根路径,避免把父仓库误当成独立子仓库。

原笔记还出现 build_git || check_return $?,但保存的片段没有 check_return 定义。只有读完整脚本及其 source 文件,才能判断它确实缺失,还是由其他文件提供。相同地,未知命令是否静默成功、all 是否初始化 M7 环境、full 调用哪些清理函数,都需要检查完整分发器,不能把手写的 case 概览称作「等价实现」。

旧 clean 记录删除 GPU SDK、Pylon、LTP、头文件、内核模块、U-Boot/内核构建目录及 images/release。它们并非天然都可随时删除:rootfs 中可能有手工添加的内容,通配符也可能匹配多套模块。改造清理逻辑时应使用已解析和检查过的目标目录,仅移除可再生的明确产物,并保存必要的源码与配置。

日常使用应先完成只读预检查和完整脚本审阅,确认普通构建不会隐式生成或替换特殊镜像,再按实际依赖分组件构建,最后检查 boot.img、模块、rootfs 和版本记录。烧录及板上测试采用该板卡已确认的流程;本篇提供的是脚本解读与检查方法,未声称完整 SDK 或目标启动已在本次复现。