3. Marvell SDK 与开发环境搭建
Marvell SmartNIC(如 OCTEON 10 / OCTEON TX2 系列 DPU)的软件开发不是“装个驱动就能跑”的流程,而是一条从 SDK 获取、工具链准备、固件加载、板卡启动,到串口调试通道打通,最后跑通第一个数据面程序的完整链路。本节按这条链路的实际顺序展开,重点讲清楚每一步的输入、关键操作和结果判断依据。
一、SDK 获取与目录结构
Marvell 的 SmartNIC SDK 通常以 BSP(Board Support Package)加 SDK 源码包的形式发布,常见获取途径有三类:一是通过 Marvell Extranet / Customer Portal 下载对应板卡的 SDK 发布包(如 octeon-sdk-*.tar.gz 或 cnxk-sdk-*.tar.gz);二是从板卡供应商(如 OEM 厂商)获取配套的 BSP;三是基于 Marvell 公开的 GitHub 仓库(如 marvell/cn9130-*、marvell/octeon-*)拉取开源部分。生产环境建议使用官方发布包,避免版本错配。
解压后典型目录结构如下(以 OCTEON TX2 SDK 为例):
sdk/
├── build/ # 编译输出
├── components/ # 各功能组件源码
│ ├── kernel/ # Linux 内核补丁与配置
│ ├── u-boot/ # Bootloader
│ ├── atf/ # ARM Trusted Firmware
│ ├── bdk/ # Board Development Kit
│ └── sdk-*/ # 数据面库(如 CVMX、DPDK、PKB)
├── tools/ # 交叉编译工具链与辅助脚本
├── target/ # 目标板根文件系统
└── env-setup.sh # 环境变量脚本
关键动作是执行 source env-setup.sh,它会导出 CROSS_COMPILE、ARCH、PATH 等变量,后续所有编译都依赖这些变量。若跳过这一步,编译时会出现找不到 aarch64-marvell-linux-gnu-gcc 的错误。
二、交叉编译工具链
SmartNIC 的 CPU 核通常是 ARM64(Cortex-A72/A78 或 Marvell 自研核),因此需要在 x86_64 主机上使用 aarch64 交叉工具链。SDK 一般自带工具链,位于 tools/ 下,例如:
export PATH=$SDK/tools/bin:$PATH
export CROSS_COMPILE=aarch64-marvell-linux-gnu-
export ARCH=arm64
aarch64-marvell-linux-gnu-gcc --version
验证工具链是否可用,最直接的方式是编译一个最小 C 程序并检查 ELF 架构:
echo 'int main(){return 0;}' > t.c
$CROSS_COMPILE gcc t.c -o t
file t
# 期望输出:ELF 64-bit LSB executable, ARM aarch64
如果 file 显示 x86-64,说明 CROSS_COMPILE 未生效或 PATH 顺序错误。这一步是后续所有编译的基础,务必先确认。
三、固件加载与板卡启动
SmartNIC 的启动链通常是:BootROM → ATF(BL1/BL2/BL31)→ U-Boot → Linux 内核 → 根文件系统。固件加载有两种典型方式:
- SD 卡 / eMMC 启动:将
fip.bin(含 ATF + U-Boot)和Image、rootfs写入启动介质,板卡上电后自动加载。 - TFTP + 串口启动:在 U-Boot 阶段通过
tftpboot从主机加载内核与设备树,适合开发调试阶段快速迭代。
以 TFTP 方式为例,U-Boot 下的典型命令序列:
setenv serverip 192.168.1.100
setenv ipaddr 192.168.1.50
tftpboot 0x20000000 Image
tftpboot 0x30000000 cn9130-db.dtb
booti 0x20000000 - 0x30000000
判断启动是否成功的关键观察点:串口输出中应依次出现 ATF 版本信息、U-Boot 版本、内核解压日志、Starting kernel ...,最后进入登录提示符。若卡在 ATF 或 U-Boot 阶段,通常是 fip.bin 与板卡型号不匹配;若卡在内核阶段,多半是设备树或根文件系统路径错误。
四、串口与调试通道
串口是 SmartNIC 开发中最重要的调试通道,因为板卡启动早期没有网络和显示输出。典型连接参数为 115200 8N1,主机侧使用 minicom、picocom 或 screen:
picocom -b 115200 /dev/ttyUSB0
# 或
screen /dev/ttyUSB0 115200
除串口外,常用调试通道还包括:SSH(板卡启动完成后通过网络登录)、JTAG(用于 ATF/U-Boot 阶段断点调试)、以及 Marvell 提供的 octeon-debug 工具集。建议在开发初期同时保留串口和 SSH 两条通道,串口用于观察启动全过程,SSH 用于日常操作。
五、第一个数据面程序运行验证
环境打通后,用一个最小数据面程序验证整条链路是否可用。以基于 PKB(Packet Buffer)或 DPDK 的报文收发为例,程序逻辑是:初始化端口 → 从指定端口收包 → 打印报文长度和源 MAC → 回送或丢弃 → 统计计数。
#include <stdio.h>
#include <rte_eal.h>
#include <rte_ethdev.h>
int main(int argc, char **argv) {
int ret = rte_eal_init(argc, argv);
if (ret < 0) { printf("EAL init failed\n"); return -1; }
uint16_t port = 0;
struct rte_eth_conf conf = {0};
rte_eth_dev_configure(port, 1, 1, &conf);
rte_eth_dev_start(port);
struct rte_mbuf *bufs[32];
while (1) {
uint16_t n = rte_eth_rx_burst(port, 0, bufs, 32);
for (uint16_t i = 0; i < n; i++) {
printf("rx len=%u\n", bufs[i]->pkt_len);
rte_pktmbuf_free(bufs[i]);
}
}
return 0;
}
编译命令:
$CROSS_COMPILE gcc dp_demo.c -o dp_demo \
-I$SDK/components/sdk-dpdk/include \
-L$SDK/components/sdk-dpdk/lib -ldpdk
将 dp_demo 通过 scp 传到板卡,在板卡上以 root 运行:
./dp_demo -l 0-1 -n 4 -- -p 0x1
验证是否成功的判断依据有三条:一是程序启动后不报 EAL 初始化错误;二是从对端发包时,串口/SSH 能看到 rx len=... 输出且长度与发送报文一致;三是程序退出后 rte_eth_stats_get 或 ifconfig 显示的收发包计数与预期相符。若收不到包,优先检查端口绑定(dpdk-devbind.py --status)、对端链路状态和 VLAN 配置。
env-setup.sh 负责环境变量,build.sh 负责交叉编译,deploy.sh 负责 scp 和远程执行。这样每次修改数据面代码后,一条命令即可完成“编译—部署—运行—看日志”的闭环,避免手工操作引入的版本错配。
fip.bin、设备树和板卡型号三者匹配。不同 SKU 的 DDR 配置、SerDes 通道和端口映射可能不同,用错固件会导致板卡无法启动或端口不可用,且串口日志往往只显示到 ATF 阶段就停止,排查成本较高。