找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

852

积分

0

好友

120

主题
发表于 19 小时前 | 查看: 1| 回复: 0

NVIDIA Jetson Orin 设备出厂预装的官方内核默认未开启 BTF (BPF Type Format) 功能,而 BTF 是 eBPF 相关功能(如 CO-RE)得以正常运行的关键依赖。因此,若想在 Orin 上开发和运行 eBPF 程序,我们需要在 x86_64 的 Ubuntu 主机上,为 ARM64 架构的 Orin 重新编译并烧录一个开启了 BTF 支持的定制化内核。以下为完整的操作步骤,涵盖环境准备、源码获取、配置修改、交叉编译及最终烧录的全流程。

一、烧录支持BTF的内核

1.1 环境准备

  • 系统版本:Ubuntu 20.04 或更高版本
  • 存储空间:建议预留至少 100GB 可用空间,用于存放源码和编译中间文件。

1.2 下载编译链

首先,安装编译内核所需的基础依赖包,确保编译环境完整:

sudo apt update
sudo apt install -y \
  build-essential \
  gcc \
  make \
  binutils \
  libncurses-dev \
  flex \
  bison \
  libssl-dev \
  libelf-dev \
  bc \
  dwarves \
  cpio \
  rsync

1.3 下载内核及相关文件

访问 NVIDIA 官方开发者网站,获取对应版本的内核及配套文件。本文以 R35.1.0 版本为例。

# 进入以下网站下载相关资料
https://developer.nvidia.com/embedded/jetson-linux-r351

需要下载以下四个文件(重要:建议直接在 Ubuntu 编译主机上使用 Firefox 等浏览器下载,避免在 Windows 系统下载后拷贝至 Ubuntu 导致后续解压错误):

NVIDIA Jetson Linux R35.1.0 下载页面,高亮显示Driver Package (BSP)和Bootlin Toolchain

1.3.1 解压核心文件

按顺序解压下载的内核包、根文件系统、源码包及显示驱动源码,确保目录结构正确:

# 解压Jetson Linux核心包
tar -xjf Jetson_Linux_R35.1.0_aarch64.tbz2

# 进入rootfs目录,此时目录下为空,将压缩包Tegra_Linux_Sample-Root-Filesystem_R35.1.0_aarch64.tbz2放到此目录下并用命令进行解压
cd Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/rootfs
sudo tar -xjf Tegra_Linux_Sample-Root-Filesystem_R35.1.0_aarch64.tbz2

# 在Jetson_Linux_R35.1.0_aarch64的同级目录下解压public_sources.tbz2 会自动解压到Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/source/public下 然后在解压public下的 kernel_src.tbz2 就能得到kernel.5.10的源码和nvbuild.sh脚本,但是这个脚本会默认强制使用英伟达的tegra_defconfig,导致你后面改menuconfig,然后去跑脚本时候会重置你自己的配置。
tar -xjf public_sources.tbz2
cd Linux_for_Tegra/source/public
tar –xjf kernel_src.tbz2
# 解压NVIDIA显示模块源码
tar -xjvf nvidia_kernel_display_driver_source.tbz2

1.3.2 修改nvbuild.sh脚本(避免重置自定义配置)

原厂提供的 nvbuild.sh 脚本在每次执行时会强制加载 tegra_defconfig,这将导致我们手动定制的内核配置被重置。为了解决这个问题,我们需要用以下脚本内容替换原有的 nvbuild.sh,其逻辑是:如果输出目录中已存在 .config 文件,则直接使用它,否则才加载默认配置。

将以下内容保存为 nvbuild1.sh(或直接覆盖原文件):

#!/bin/bash

# Copyright (c) 2019-2021, NVIDIA CORPORATION.
# All rights reserved.

# This script builds kernel sources in this directory.
# Modified behavior:
#   - If .config already exists, DO NOT reset via tegra_defconfig

set -e

SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
SCRIPT_NAME="$(basename "$0")"

source "${SCRIPT_DIR}/nvcommon_build.sh"

function usage {
        cat <<EOM
Usage: ./${SCRIPT_NAME} [OPTIONS]
This script builds kernel sources in this directory.
OPTIONS:
        -h              Displays this help
        -o <outdir>     Creates kernel build output in <outdir>
EOM
}

# parse input parameters
function parse_input_param {
while [ $# -gt 0 ]; do
case ${1} in
            -h)
                usage
                exit 0
                ;;
            -o)
                KERNEL_OUT_DIR="${2}"
                shift 2
                ;;
            *)
                echo "Error: Invalid option ${1}"
                usage
                exit 1
                ;;
        esac
    done
}

function build_arm64_kernel_sources {
    kernel_version="${1}"
    echo "Building kernel-${kernel_version} sources"

    source_dir="${SCRIPT_DIR}/kernel/kernel-${kernel_version}"
    config_file="tegra_defconfig"
    tegra_kernel_out="${source_dir}"

    if [ -n "${KERNEL_OUT_DIR}" ]; then
        O_OPT=(O="${KERNEL_OUT_DIR}")
        tegra_kernel_out="${KERNEL_OUT_DIR}"
    else
        O_OPT=()
    fi

    CONFIG_PATH="${tegra_kernel_out}/.config"

    echo "Kernel source dir : ${source_dir}"
    echo "Kernel output dir : ${tegra_kernel_out}"

# --------------------------------------------------
# CONFIG stage
# --------------------------------------------------
    if [ -f "${CONFIG_PATH}" ]; then
        echo "[nvbuild] Existing .config found"
        echo "[nvbuild] >>> Using user-provided config (NOT running tegra_defconfig)"
    else
        echo "[nvbuild] No .config found"
        echo "[nvbuild] >>> Generating default config: ${config_file}"

        "${MAKE_BIN}" -C "${source_dir}" ARCH=arm64 \
            LOCALVERSION="-tegra" \
            CROSS_COMPILE="${CROSS_COMPILE_AARCH64}" \
            "${O_OPT[@]}" \
            "${config_file}"
    fi

# Ensure config dependencies are resolved
    "${MAKE_BIN}" -C "${source_dir}" ARCH=arm64 \
        CROSS_COMPILE="${CROSS_COMPILE_AARCH64}" \
        "${O_OPT[@]}" \
        olddefconfig

# --------------------------------------------------
# Build Image
# --------------------------------------------------
    "${MAKE_BIN}" -C "${source_dir}" ARCH=arm64 \
        LOCALVERSION="-tegra" \
        CROSS_COMPILE="${CROSS_COMPILE_AARCH64}" \
        "${O_OPT[@]}" -j"${NPROC}" \
        --output-sync=target Image

# --------------------------------------------------
# Build DTBs
# --------------------------------------------------
    "${MAKE_BIN}" -C "${source_dir}" ARCH=arm64 \
        LOCALVERSION="-tegra" \
        CROSS_COMPILE="${CROSS_COMPILE_AARCH64}" \
        "${O_OPT[@]}" -j"${NPROC}" \
        --output-sync=target dtbs

# --------------------------------------------------
# Build modules
# --------------------------------------------------
    "${MAKE_BIN}" -C "${source_dir}" ARCH=arm64 \
        LOCALVERSION="-tegra" \
        CROSS_COMPILE="${CROSS_COMPILE_AARCH64}" \
        "${O_OPT[@]}" -j"${NPROC}" \
        --output-sync=target modules

    image="${tegra_kernel_out}/arch/arm64/boot/Image"
    if [ ! -f "${image}" ]; then
        echo "Error: Missing kernel image ${image}"
        exit 1
    fi

    echo "Kernel sources compiled successfully."
    echo "Kernel Image: ${image}"
}

parse_input_param "$@"
build_arm64_kernel_sources "5.10"

1.3.3 解压编译工具链

为交叉编译创建独立的工具链目录,避免与主机系统工具冲突:

# 创建工具链目录
mkdir toolchain
# 解压工具链包
tar -xzvf aarch64--glibc--stable-final.tar.gz -C ./toolchain/

1.4 添加BTF模块并编译内核

1.4.1 设置全局环境变量

配置交叉编译工具链路径及架构参数,确保编译时能正确调用工具链(请根据工具链实际存放路径修改):

export CROSS_COMPILE_AARCH64=/home/njxm/Desktop/toolchain/bin/aarch64-buildroot-linux-gnu-
export CROSS_COMPILE=$CROSS_COMPILE_AARCH64
export ARCH=arm64
export CROSS_COMPILE_AARCH64_PATH=/home/njxm/Desktop/toolchain

1.4.2 内核编译前配置

按步骤清理源码树、加载默认配置、开启 BTF 及所有必要依赖项,确保内核配置满足 eBPF 功能需求:

PUBLIC=/mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/source/public
KERNEL=$PUBLIC/kernel/kernel-5.10
OUT=$PUBLIC/kernel_out

# 1) 清理源码树
cd "$KERNEL"
make ARCH=arm64 mrproper

# 2) 准备输出目录
rm -rf "$OUT"
mkdir -p "$OUT"

# 3) 加载官方默认配置
make -C "$KERNEL" ARCH=arm64 O="$OUT" tegra_defconfig

# 4) 开启BTF/BTF相关依赖配置
"$KERNEL/scripts/config" --file "$OUT/.config" \
  -e BPF \
  -e BPF_SYSCALL \
  -e BPF_JIT \
  -e BPF_EVENTS \
  -e BPF_TRAMPOLINE \
  -e DEBUG_INFO \
  -e DEBUG_INFO_DWARF4 \
  -e DEBUG_INFO_BTF \
  -e FTRACE \
  -e FUNCTION_TRACER \
  -e TRACEPOINTS \
  -e FTRACE_SYSCALLS \
  -e KPROBES \
  -e KPROBE_EVENTS \
  -e UPROBE_EVENTS \
  -e KALLSYMS \
  -e KALLSYMS_ALL

# 5) 对齐配置依赖
make -C "$KERNEL" ARCH=arm64 O="$OUT" olddefconfig

# 6) 执行编译
cd "$PUBLIC"
sudo -E env NPROC=$(nproc) ./nvbuild1.sh -o "$OUT"

1.5 替换内核文件并安装模块

1.5.1 执行apply_binaries.sh脚本

回到 Linux_for_Tegra 根目录,运行 apply_binaries.sh 脚本。这个脚本的作用是将 NVIDIA 提供的闭源/预编译用户态组件、驱动、固件、内核模块、内核头文件及配置 等“二进制包”,安装/解包到指定的目标根文件系统目录中。

cd /mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra
sudo ./apply_binaries.sh

1.5.2 替换核心内核文件

将我们刚刚编译好的内核镜像、设备树二进制文件(DTBs)以及关键的 GPU 驱动模块,替换到 Linux_for_Tegra 目录下的对应位置,覆盖原厂文件。

sudo cp /mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/source/public/kernel_out/drivers/gpu/nvgpu/nvgpu.ko /mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/rootfs/usr/lib/modules/5.10.104-tegra/kernel/drivers/gpu/nvgpu &&
sudo cp /mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/source/public/kernel_out/arch/arm64/boot/dts/nvidia/* /mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/kernel/dtb &&
sudo cp /mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/source/public/kernel_out/arch/arm64/boot/Image /mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/kernel

1.5.3 安装内核模块

进入编译输出目录,将编译生成的所有内核模块安装到目标根文件系统的模块目录中,确保系统启动时能正确加载这些模块。

cd /mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/source/public/kernel_out &&
sudo -E make INSTALL_MOD_STRIP=1 LOCALVERSION="-tegra" ARCH=arm64 modules_install INSTALL_MOD_PATH=/mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/rootfs modules_install

1.6 编译NVIDIA可视化模块

1.6.1 补齐内核配置生成文件

为了编译 NVIDIA 的专有显示模块,需要先在 kernel_out 目录中生成完整的配置头文件。在 source/public 目录下执行:

cd /mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/source/public
# 重新设置全局变量(若终端会话已重置)
export CROSS_COMPILE_AARCH64=/home/njxm/Desktop/toolchain/bin/aarch64-buildroot-linux-gnu-
export CROSS_COMPILE=$CROSS_COMPILE_AARCH64
export ARCH=arm64
export CROSS_COMPILE_AARCH64_PATH=/home/njxm/Desktop/toolchain

PUBLIC=/mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/source/public
KERNEL=$PUBLIC/kernel/kernel-5.10
OUT=$PUBLIC/kernel_out

# 确认配置文件存在
ls -l $OUT/.config

# 生成配置依赖文件
make -C "$KERNEL" ARCH=arm64 O="$OUT" olddefconfig prepare

执行后可以验证文件是否生成:

ls -l $OUT/include/config/auto.conf $OUT/include/generated/autoconf.h

1.6.2 编译NVIDIA显示/开源模块

指定内核源码路径、编译输出目录及交叉编译工具链,编译 NVIDIA 的开源显示驱动模块。

cd /mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/source/public/NVIDIA-kernel-module-source-TempVersion

make modules \
  SYSSRC=/mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/source/public/kernel/kernel-5.10 \
  SYSOUT=/mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/source/public/kernel_out \
  O=/mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/source/public/kernel_out \
  CC="${CROSS_COMPILE}gcc" \
  LD="${CROSS_COMPILE}ld.bfd" \
  AR="${CROSS_COMPILE}ar" \
  CXX="${CROSS_COMPILE}g++" \
  OBJCOPY="${CROSS_COMPILE}objcopy" \
  TARGET_ARCH=aarch64 \
  ARCH=arm64

1.6.3 替换显示模块ko文件

将编译好的显示驱动模块文件(.ko)复制到根文件系统的指定模块目录。

cd /mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/source/public/NVIDIA-kernel-module-source-TempVersion
sudo cp kernel-open/*.ko /mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra/rootfs/usr/lib/modules/5.10.104-tegra/extra/opensrc-disp

1.7 进入Recovery模式并烧录内核

1.7.1 进入Recovery模式

将 Jetson Orin 设备进入强制恢复模式(Force Recovery Mode),有两种方法:

  • 设备未开机状态:长按设备上的 Force Recovery 键(通常标记为②),然后插入电源线通电,待设备保持黑屏且白色指示灯常亮,即表示进入恢复模式。
  • 设备已开机状态:长按 Force Recovery 键(②),接着按下 Reset 键(③),先松开 Reset 键(③),再松开 Force Recovery 键(②)。

NVIDIA Jetson Orin 设备外观图,侧面标记有1-9数字,指示不同接口和按键位置

1.7.2 烧录内核

使用双头 Type-C 数据线连接 Orin 设备与作为烧录主机的 Ubuntu 电脑。在主机上通过 lsusb 命令确认设备已被识别(应出现 NVIDIA Corp. 相关设备)。然后,在 Linux_for_Tegra 目录下执行烧录命令:

cd /mnt/data/Jetson_Linux_R35.1.0_aarch64/Linux_for_Tegra
sudo ./flash.sh jetson-agx-orin-devkit mmcblk0p1

烧录过程会自动进行,完成后设备将自动重启。此时,Orin 设备运行的就是我们刚刚编译的、支持 BTF 的新内核了。

二、eBPF程序测试

eBPF (Extended Berkeley Packet Filter) 是一种革命性的内核技术,它允许开发者在不修改内核源码、不加载传统内核模块的前提下,动态地向运行中的内核注入安全、高效的自定义监控和分析逻辑。

2.1 下载eBPF编译链及依赖

在已烧录新内核的 Orin 设备上(通过 SSH 或直接操作),安装编译和运行 eBPF 程序所需的工具链和库。

sudo apt update
sudo apt install make
sudo apt install -y libelf-dev pkg-config
sudo apt install -y clang-12 llvm-12 lld-12

# 下载并编译bpftool
git clone --recurse-submodules https://github.com/libbpf/bpftool.git
sudo apt install -y libssl-dev
cd ~/bpftool/src
make -j"$(nproc)"
# 可将生成的bpftool复制到系统路径,例如 /usr/local/bin/
sudo cp bpftool /usr/local/bin/

2.2 安装libbpf-bootstrap测试

libbpf-bootstrap 是 eBPF 程序开发的优秀脚手架项目。通过运行其示例程序,可以快速验证我们的内核 eBPF 环境是否正常工作。

# 递归克隆项目
cd ~
git clone --recurse-submodules https://github.com/libbpf/libbpf-bootstrap.git

# 进入C语言示例目录并编译
cd /home/orin/libbpf-bootstrap/examples/c
make

# 运行示例工具,观察输出
sudo ./bootstrap

运行 ./bootstrap 后,如果能看到类似以下实时打印的进程执行与退出事件,则证明 eBPF 程序已在内核中成功加载并运行,我们的内核编译与烧录工作圆满成功

orin@orin:~/libbpf-bootstrap/examples/c$ sudo ./bootstrap
[sudo] password for orin:
TIME     EVENT COMM             PID     PPID    FILENAME/EXIT CODE
14:27:29 EXEC  git              5573    4959    /usr/bin/git
14:27:29 EXIT  git              5573    4959    [0] (1ms)
14:27:29 EXEC  git              5574    4959    /usr/bin/git
14:27:29 EXEC  git              5575    5574    /usr/lib/git-core/git
14:27:29 EXIT  git              5575    5574    [0] (1ms)
14:27:29 EXIT  git              5574    4959    [0] (3ms)
14:27:29 EXEC  git              5576    4959    /usr/bin/git

整个过程涉及对内核源码的深度定制和交叉编译,是嵌入式 Linux 系统开发中一项颇具代表性的开源实战任务。希望这份详细的指南能帮助你在 NVIDIA Jetson Orin 平台上顺利开启 eBPF 开发之旅。如果在实践过程中遇到问题,欢迎在 云栈社区 与更多开发者交流探讨。




上一篇:开发者必备的2个免费JSON工具:json4u.cn与JSON Hero实战测评
下一篇:Docker网络核心模式全解析:从Bridge到Overlay,打通容器互联
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-27 19:31 , Processed in 0.383122 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表