VisionFive 2 RISC-V - 构建 Linux 内核





5.00/5 (4投票s)
本文讨论了在 x86 Ubuntu Linux 下使用交叉编译工具为 VisionFive 2 RISC-V SBC 构建 Linux 内核。
目录
引言
Linux 社区多年来一直在致力于 RISC-V 版本的 Linux 内核和其他软件,并且可以追溯到 2015 年的 Stackoverflow 帖子,其中有人尝试使用 RISC-V gcc 编译 Linux。
随着编译器等工具的成熟,构建 RISC-V 版本的 Linux 内核变得越来越容易。Linux 内核开发社区支持许多 RISC-V 设备,制造商也在努力将他们的 RISC-V 设备纳入支持范围。StarFive 的 VisionFive 2 尚未完全支持,因此 StarFive 拥有自己的 Github 仓库,开发团队正从中向 Linux 开发团队推送更新。
StarFive 的 Github 区域 starfive-tech · GitHub,包含几个仓库,其中包含 VisionFive 2 SBC 的构建材料的仓库是 GitHub - starfive-tech/VisionFive2。VisionFive2 仓库包含 VisionFive 2 的 Linux 内核、BusyBox、U-Boot 加载器和其他组件的软件组件,VisionFive 2 使用 JH7110 SOC。
背景
如果您几乎没有经验,我强烈建议您通过了解基本过程来为构建 Linux 内核的体验做好准备。我做的第一件事是花些时间观看 YouTube 上关于该过程的各种视频。VisionFive 2 Linux 构建过程使用 Buildroot,因此了解该镜像构建工具的作用很重要。以下是一些我觉得有用的列表:
- Buildroot:轻松构建嵌入式 Linux 系统
- 从零开始制作简单的 Linux 发行版
- 了解 Linux 内核:初学者指南)
- Linux 内核编程 01:编译与启动
- 嵌入式 Linux 启动过程
如果您对制作镜像的内部工作原理以及 Linux 设备从开机到出现登录屏幕的过程中发生的事情感兴趣,以下是一些可能有用的文档:
- Linux 内核新手 (大量文档和 Linux 社区)
- Scott Chacon 和 Ben Straub 撰写的 Pro Git 书籍
- Buildroot 用户手册 (Buildroot 用于构建其中一个镜像)
- 使用 Linux 和 u-boot (embeddedartists.com)
以下是一些关于设备驱动程序和模块的视频和文档,当我们将目光投向为 VisionFive 2 构建 WiFi 设备驱动程序时,它们可以帮助理解该过程。
- Linux 内核驱动程序如何工作? (来自 Linux 设备驱动程序一书的简要概述)
- Linux 设备驱动程序,第三版 (章节为 PDF 格式)
- 如何避免为嵌入式 Linux 编写设备驱动程序
我发现构建 Linux 内核的概念上相当令人生畏,但如果按照正确的步骤执行,实际上相当简单。一旦您开始修改过程,您就需要更好地理解内部原理。最重要的一步是确保您已安装所有适用于 RISC-V 架构的交叉编译和构建工具。
要记住的一点是,您很可能不需要对 Linux 内核本身进行任何实质性的更改。构建并部署镜像到设备后,可以通过标准的包管理功能添加应用程序和设备驱动程序。构建自己的镜像允许您添加或删除功能,例如,精简镜像以仅包含基本要素,以便在资源受限的设备上运行。
我建议您了解 Git 版本控制系统。Scott Chacon 和 Ben Straub 撰写的 Pro Git 书籍 有一个免费的电子版本,涵盖了使用 Git 所需的一切。
我遇到的最常见问题是在执行步骤时发生复制粘贴错误。我从 Windows 浏览器中显示的文档中选择并复制文本,然后将其粘贴到 WSL 虚拟机中的 Ubuntu 终端窗口命令行中。
我只尝试过构建安装在 microSD 卡上的镜像。我没有尝试使用 M.2 NVMe 卡,也没有为这种卡构建镜像。我的理解是,为 SD 卡构建的镜像也是 eMMC 和 NVMe 驱动器的镜像的基础。一旦您理解了 SD 卡镜像的步骤,您基本上就理解了该设备的任何版本的构建过程。
可以通过 StarFive Debian 仓库 从 仓库的 Releases 标签页 获取每月的 Debian 构建版本。在 Assets 列表中,有许多适用于 FireFox 等应用程序的 Debian 包文件。实际的 Debian 镜像文件可以从 debian.starfivetech.com 的两个存储区域之一下载。使用 OneDrive 链接时,当它要求我进行身份验证时,我只需重新加载页面再试一次,它似乎无需身份验证即可工作。
构建和测试环境
我使用的是 Windows 11 Pro 桌面,通过 Windows Subsystem for Linux (WSL) 运行 Ubuntu。我有一个安装了 Subversion 的 Ubuntu Linux 服务器,但我不想用 Linux 构建的工具和源代码弄乱它,因为我认为我可以轻松犯错,而销毁 WSL 虚拟机并从头开始是解决这些错误的最佳方法。
我使用的 PC 具有以下硬件:
- i5-11400 第 11 代 Intel 处理器
- 24 GB RAM (之前是 32 GB,后来一个内存条坏了)
- 1 TB NVMe 固态硬盘
- LG 46 英寸 4K 显示器
我在 Amazon.com 上购买了一个 VisionFive 2 套件,其中包含以下部件:
- VisionFive 2 版本 1.2A SBC
- USB WiFi 适配器
- TTY 转 USB 转接线
- 64 GB microSD 卡
- USB 转 microSD 卡适配器
强烈建议使用 TTY 转 USB 转接线来访问 VisionFive 2 40 针连接器上的 UART 引脚。当桌面不可用或我需要额外的日志和其他调试信息时,我使用终端模拟器以 root 用户身份查看正在发生的事情。终端模拟器通常具有某种日志记录功能,允许您保留一个文本日志以供以后查看,或在寻求帮助时包含在论坛帖子中。
通过一些对测试 Linux 内核构建的实验,我曾让 VisionFive 2 无头运行,放在 OrangePi 5 Plus SBC 旁边,我已将 TTY 转 USB 转接线连接到 OrangePi 5 Plus。OrangePi 5 Plus 使用了我通常用于 VisionFive 2 的键盘、鼠标和 HDMI 显示器。在这些情况下,我正在 OrangePi 5 Plus 上使用 gtkterm 应用程序通过端口 /dev/ttyUSB0 连接到 VisionFive 2 Linux 控制台,并使用 rsh 应用程序作为终端窗口。我确实遇到了 brltty 服务占用 /dev/ttyUSB0 设备的问题,直到我禁用了该服务(请参阅下面的 由于 brltty 服务导致的 gtkterm 连接问题 部分的讨论)。
我一直在 Windows 11 上使用 Windows Subsystem for Linux (WSL) 进行构建。我只遇到过一个问题,一个 PATH
环境变量问题,但很容易解决。在 WSL 中,我使用 Ubuntu,并在终端窗口中从命令行进行所有操作。
我尝试将 TTY 转 USB 转接线与 Windows 10 笔记本电脑一起使用,但 Windows 未能正确设置设备驱动程序,因此无法工作。但是该适配器在 Linux 下似乎工作正常。在一次在线讨论中,有人推荐使用 Linux,说 USB/TTY 转换器可能使用了 Windows 无法识别的芯片,而 Linux 通常对奇怪的或中国山寨芯片相当友好。
由于构建文档使用的是命令行界面,因此带 WSL 的终端窗口可以正常工作。除了 WSL 之外,我使用的 Windows 应用程序只有 Chrome 网络浏览器、Raspberry Pi Imager 工具和 PuTTY 应用程序套件(包括用于传输文件的 PSFTP 工具)。我发现使用 Windows 文件管理器在 WSL 文件空间和 Windows 文件空间之间移动文件很顺畅。
我可以在 psftp 工具的 lcd (本地更改目录或本地 cd) 命令中使用 WSL 路径名。我做的做法是,通过文件浏览器导航到 WSL 文件空间,然后一旦导航到要传输文件的文件夹,我就会复制文件浏览器窗口导航栏中显示的路径,然后在构建 lcd 命令行的 psftp 窗口中粘贴它。
当 Windows 启动 Ubuntu 虚拟机时,PATH
环境变量包含来自 Windows 的包含空格的路径名,例如 /mnt/c/Program Files/Microsoft SQL Server/110/Tools/Binn/。由于这种差异,当我第一次尝试制作内核时,我收到了以下错误消息:
Your PATH contains spaces, TABs, and/or newline (\n) characters.
This doesn't work. Fix you PATH.
为了解决这个问题,我将 $PATH
变量 echo 到一个文件,然后创建了一个简单的 shell 脚本,其中我使用 export
命令设置我的 PATH
环境变量,并修改了路径,删除了所有带空格的路径名。然后我从命令行使用 source
命令将我的 PATH
设置为新值。
在获取源代码和组件时,您将使用几个应用程序。
- 一个网络浏览器,用于浏览 StarFive Github 仓库和访问各种文档
apt
工具或其他包管理器,用于安装软件工具- git 源代码管理工具,
sudo apt install git
- git 大文件工具,
sudo apt install git-lfs
curl
工具,sudo apt install curl
wget
工具,sudo apt install wget
两个替代构建的文档提供了设置构建环境所需的 apt
命令。这两种方法有许多共同的工具,也有一些不共有的工具,因此为了避免浪费时间追踪由于缺少工具而导致的错误,请花时间正确设置您的构建环境。
源码树克隆与更新
我同时拥有两个构建环境,每个镜像的源代码都位于不同的文件层次结构中。这样,我可以通过转到该镜像文件层次结构的根文件夹然后执行相应过程来构建任一版本。
由于需要下载大量源代码,并且距离我上次 git clone
各种仓库已经过去了几周,我研究了一个保持本地 Git 仓库更新的程序。
两种不同的替代镜像构建使用 Git 的方式不同。StarFive VisionFive2 仓库的本地版本是通过一个 git clone
命令创建的,然后它会拉取几个链接的仓库,包括物理上存储在 StarFive GitHub 上的单独仓库中的 Linux 源代码。从零开始构建 Debian 镜像 的本地版本是通过使用几个 git clone
命令以及几个 wget
命令手动构建包含源代码和其他构成 Debian 桌面镜像组件的目录和文件树来构建的。
这个 Stackoverflow 帖子 "git pull --all" 可以更新我所有本地分支吗? 描述了管理涉及分支的复杂 Git 结构的一些困难。由于 Linux 构建源码树中可能存在多个 Git 仓库,因此识别所有这些仓库可能会有点挑战。我的做法是使用 find 命令来查找包含 .git 文件的目录,因为该目录将是 Git 仓库的顶部。
对于 StarFive VisionFive2 仓库,我目前使用的保持更新的程序是转到我 git clone
VisionFive2 仓库的树的根目录,然后执行 git pull
来更新该树,然后使用 git submodule foreach 'git pull'
来更新作为子模块的链接仓库。
要更新手动构建的 从零开始构建 Debian 镜像 的树,我 cd 到目录树的顶部并运行一个 bash
脚本,该脚本会进入几个已克隆的 Git 仓库并在此目录中执行 git pull
。
两种构建方案
到目前为止,我发现有两种不同的文档化方法可以使用 StarFive VisionFive 材料进行 Linux 构建。我将在下面提供我在这两种方法上的经验。
第一种方法使用了 StarFive VisionFive2 仓库 中 README.md 文件中指定的程序。该程序使用 Buildroot 构建一个带有 BusyBox 作为用户界面的 Linux 内核。此版本无法启动桌面。该过程生成的镜像需要使用连接到 40 针连接器 UART 引脚的 TTY 转 USB 转接线,并在另一台设备上使用终端模拟器,以 root 用户(而不是 user 用户)身份访问命令行。
我认为第一种版本的情况是,Linux 没有启动到运行级别 5,而是停在运行级别 3,因此您必须通过 TTY 转 USB 转接线使用 Linux 控制台,因为图形显示管理器未运行。有关运行级别的一些信息,请参见 init - 如何查看或更改默认运行级别?- Ask Ubuntu。然而,第二种方法是,Linux 处于运行级别 5,图形显示管理器正在运行,HDMI 也正常工作。
第二种方法记录在 构建 Debian 镜像 中。该过程需要各种手动步骤来下载文件,使用 cat
和 echo
命令创建文件,并使用循环设备手动创建镜像文件。第二种方法耗时较长且相当繁琐,但结果是在连接到 VisionFive 2 的 HDMI 屏幕上显示的桌面。此方法有两个用户,root
和 user
(可以使用 sudo
提升权限),用户名为 user
是登录提示的默认值。
在本文的其余部分,我将第一种方法称为 VisionFive2 README.md 版本,第二种方法称为 Debian 从零开始构建。
此过程中有几个步骤可能需要很长时间才能完成,因此请准备好运行一些命令,然后去做其他事情几个小时。
当心复制粘贴错误!
在花费数天时间安装丢失的软件包并与构建错误作斗争后,我发现当我将 README.md 文件中建议的 app install
列表与我已安装的列表进行比较时,我意识到 README.md 文件中的 app install
列表包含其中大部分。当我通过从 README.md 文件复制粘贴文本来重试使用相同的列表时,我发现粘贴操作插入了某种换行符,当我在 Ubuntu 命令行中运行粘贴的 apt install 时,apt 命令处理的软件包列表不是我认为我指定的完整列表。
另一方面,我遇到了另一个错误,抱怨 GPU 驱动程序文件的 sha256 校验和与预期不符。错误似乎是由于 GPU 驱动程序文件不正确,但实际上是由于在执行 git clone
https://github.com/starfive-tech/VisionFive2.git 之前未安装 Git LFS。我认为我已经完成了安装 Git LFS 的步骤,但实际上只下载了软件包但没有用 apt install
安装它。
我遇到的另一个问题是,错误可能不会立即停止构建,任何错误消息或警告都会迅速滚出终端窗口的可视部分。还有一些内核源代码文件在编译时会显示各种警告,但仍然会生成一个可工作的系统。终端窗口中快速滚动的有时大量的文本会使识别错误变得困难。
使用代码
两种方法,两种不同的产物
这两种不同方法产生的产物是不同的。第一种方法,VisionFive2 README.md 方法,似乎创建了一个带有 BusyBox 的 Linux 内核,没有官方 StarFive Debian 构建的大部分软件内容,截至本文撰写时,其最新版本是 202310。第二种方法,Debian 从零开始构建,似乎创建了 202310 构建的完整副本,包含该构建的所有软件。
两者之间的第一个主要区别是,第一种方法需要一个终端,通过 TTY 转 USB 转接线将终端连接到 VisionFive 2 40 针连接器上的 UART 引脚。第二种方法提供了完整的桌面,可以使用连接到 VisionFive 2 设备端口的 HDMI 显示器以及键盘和鼠标。
请注意,这两种方法使用的工具链略有不同。一个重要的区别是,第一种方法使用的编译器是与第二种方法使用的编译器不同版本的 gcc。当我尝试添加一个设备驱动程序来支持 USB WiFi 适配器时,这个区别是我遇到的一个问题。在为第一种,VisionFive2 README.md 方法编译设备驱动程序源代码时,我必须使用 Buildroot 使用的工具链中的编译器。
为 README.md 方案设置构建工具
VisionFive2 仓库中的 README.md 文件包含一个相当不错的步骤列表。但是,在我第一次进行内核构建时,我遇到了几个缺少软件包的问题,导致构建失败。我后来发现这些软件包丢失是因为我自己在复制粘贴过程中对 apt
命令产生了错误。这是我由于有些粗心和过于自信而犯下的几个错误之一。
VisionFive2 目录下的 README.md 文件提到了 Git LFS (Large File Support),用于下载一些第三方组件,如 GPU 驱动程序。我看到的是,没有安装 Git LFS 会导致 sha256 校验和不匹配等错误,这些错误似乎与 Git LFS 的不可用无关。
当我运行 curl
命令获取 Git LFS 时,它运行了很长时间,似乎卡住了。我看到的唯一输出是打印的一条消息,提到了我正在使用的 Ubuntu 版本。我最终按下了键盘上的回车键,它似乎完成了。您可以使用 apt-cache search git-lfs
来双重检查 git-lfs
是否可供安装。然后运行 apt install
。
克隆和设置 VisionFive2 Linux 构建源码树需要先安装 Git 和 Git LFS
。在使用 git
命令克隆源码树之前,您必须同时安装它们,否则将获得不完整的源代码和不完整的第三方组件,而构建将很少出现错误来指示问题。
VisionFive2 仓库 README.md 文件指定了许多步骤,包括用于设置交叉编译环境的 apt
命令行。源代码的实际编译很简单,一个简单的 make 命令,然后需要数小时才能运行。结果是一个在 make 命令成功完成后神奇出现的镜像文件,就像早晨出门发现玫瑰花蕾在夜间绽放一样。
为从头构建方案设置构建工具
在使用 VisionFive2 仓库中的 Buildroot 系统后,我改用了从头构建文档中的程序。这需要我安装 sudo apt install gcc-riscv64-linux-gnu
,因为该过程需要这个工具,它似乎与 VisionFive2 仓库程序使用的编译器不同。我认为这是唯一的区别,直到我遇到了另一个缺失的应用程序,那时我做了我一开始就应该做的事情,复制粘贴了从头构建文档中指定的工具所需的 sudo apt install
,并完成了导出环境变量 VF2_WORK_DIR
以用于构建工作目录的步骤。
在我逐步进行时,创建 Debian Root 文件系统 部分中的创建 bootstrap rootfs 步骤,使用 debootstrap
命令耗时很长,在开始缓慢显示进度行之前似乎卡住了。在我第一次尝试使用该命令时,我收到了 "gpg 可能不正确或已过时" 的错误。按照文档中的说明,该文档提到了这个可能的错误,我然后用建议的 --no-check-gpg
参数选项重试了该命令,它运行正常,尽管耗时很长,如一整夜的检索和验证。
在大多数情况下,从头构建过程的其余部分是一个有些繁琐的复制粘贴文本的过程,确保复制时选择了所有内容,并且粘贴是复制的文本。在大多数情况下,我的做法是逐行选择复制和粘贴,以确保文本正确。
在某个点,使用 cat 命令将终端命令行窗口中输入的文本复制并放入文件来创建或追加文本行。大多数是短文件,可以用一个复制/粘贴完成,但有几个需要分段复制/粘贴。
在您进行获取源代码和构建文件的繁琐过程时,请记住,一旦完成,只有当您清除正在创建的镜像构建源时,您才需要再次执行它。
我建议您学习一些关于 Linux 系统管理以及用于管理 Linux 系统的命令的知识,这将使其中一些步骤更容易理解,不那么机械化。花时间查找各种命令(如 chroot 等)的文档,以及这些步骤创建的一些文件,以更好地理解正在做什么。
两点重要说明
第一点重要说明是关于 Build SD Image 部分的步骤,其中命令中提到了 /dev/loop30 设备。您必须将 /dev/loop30 的文本替换为 losetup
命令显示的特定循环设备。在终端窗口中,我使用了程序中写的命令,而不是用我的设备 /dev/loop0 替换 /dev/loop30,这导致了错误。请参阅以下摘录。
rick@rchamber2:~/Documents/starfive/github/VF2/create_sd_image$ sudo losetup --partscan --show --find starfive-jh7110-SD-minimal-desktop.img
/dev/loop0
rick@rchamber2:~/Documents/starfive/github/VF2/create_sd_image$ mkdir boot rootfs
rick@rchamber2:~/Documents/starfive/github/VF2/create_sd_image$ sudo mkfs.vfat /dev/loop30p3
mkfs.fat 4.2 (2021-01-31)
mkfs.vfat: unable to open /dev/loop30p3: No such file or directory
rick@rchamber2:~/Documents/starfive/github/VF2/create_sd_image$ sudo mkfs.vfat /dev/loop0p3
mkfs.fat 4.2 (2021-01-31)
rick@rchamber2:~/Documents/starfive/github/VF2/create_sd_image$ sudo mkfs.ext4 -m 0 -L root /dev/loop0p4
mke2fs 1.46.5 (30-Dec-2021)
Discarding device blocks: done
Creating filesystem with 996091 4k blocks and 249488 inodes
Filesystem UUID: ff03fc59-02e2-4ade-b517-779a498d4c63
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736
Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done
rick@rchamber2:~/Documents/starfive/github/VF2/create_sd_image$
其次,选择正确的镜像构建程序,以适应您使用的存储设备类型 - SD 卡、eMMC 或 NVMe 卡。我使用了 eMMC 程序,而我本应使用 SD 卡程序,并在 SD 卡启动测试时发现错误,镜像未能启动到桌面。
当我使用 eMMC 程序(并通过额外的 sed
命令修改存储设备名称)而不是 SD 卡镜像程序创建的第一个镜像启动时,HDMI 显示器显示了以下消息(这只是显示错误的最后一部分):
Begin: Running /scripts/local-block Begin: Running /scripts/local-block… done. done. Gave up waiting for root file system device. Common problems: Boot args (cat /proc/cmdline) Check rootdelay= (did the system wait long enough?) - Missing modules (cat /proc/modules: ls /dev) ALERT! /dev/mmcblk0p4 does not exist. Dropping to a shell!
幸运的是,TTY 接口显示了 initramfs
提示,我得以通过 TTY 接口进行一些查看。当我在 initramfs
提示符下使用 ls /dev/m*
在 /dev 中查看时,我看到了 mmcblk1
和每个预期的分区 mmcblk1p4
等,但没有 mmcblk0
。
我查看了使用 sed
命令修改 eMMC 程序中的 mmcblk1
为 mmcblk0
的三个文件。文件 /etc/fstab 除了一个注释外没有其他内容。另外两个文件有引用 mmcblk0
的指令,所以我将它们改回 mmcblk1
。然后我重新执行了 SD 卡镜像构建程序,跳过了 sed
命令步骤,创建了一个镜像,然后使用 Raspberry Pi Imager 工具将其刻录到 microSD 卡上。
我将 microSD 卡插入 VisionFive 2 驱动器,然后按电源按钮启动设备。短暂的延迟后,消息开始在显示屏上滚动,然后屏幕清空,暂停一下,然后桌面出现并显示登录提示。
我登录并设置了 WiFi,然后尝试了 FireFox。看起来与 202310 Debian 构建相似。
向 Debian 镜像添加应用程序
查看文档创建 Debian Root 文件系统部分复制应用程序步骤中的 wget
命令中使用的路径名,似乎您可以使用这些应用程序的最新版本,方法是在 wget
命令中使用 Assets 中的路径名,来自StarFive Debian 仓库 Releases 的 StarFive Debian 仓库 Releases。在 wget
命令。
可用的一些 Assets 可能无法正常工作,这可能是由于依赖项中一个尚未纠正的问题。例如,截至本文撰写时(2023 年 12 月 2 日),Chromium 无法正常工作,也无法正确打开。
作为构建包含我创建的应用程序的镜像的调查的一部分,我编写了一个简单的 HelloWorld.c 应用程序。我交叉编译了源文件,并将可执行文件放入 Linux 镜像区域的 /usr/bin 文件夹。然后我构建了镜像并在 VisionFive 2 上进行了测试,程序就在那里并且可以运行。
我还尝试将 Chromium 从 Debian 仓库 2023 年 10 月构建的 Assets 列表中包含到我的镜像中。这包括使用 wget
从 Debian Releases 链接 拉取一个 Asset。Chromium 出现在我的桌面上的应用程序列表中,当我启动镜像时,但遗憾的是,截至 2023 年 12 月 4 日,目前有一个 bug 导致 Chromium 崩溃。
由于我正在修改一个已有的镜像构建区域,我使用了以下从从头构建 Debian 程序改编的程序来添加 Chromum
sudo chroot $VF2_WORK_DIR/riscv-chroot-snapshots
cd /opt
wget https://github.com/starfive-tech/Debian/releases/download/v0.11.0-engineering-release-wayland/chromium-103.0.5060.114.deb
dpkg -i chromium-103.0.5060.114.deb
rm chromium-103.0.5060.114.deb
apt clean
history -c
sync
exit
我没有尝试过,但我认为您可以对任何您想添加到 Debian 构建中的附加软件包使用类似的程序,只要该软件包是为 StarFive VisionFive 2 和 RISC-V 编译器或交叉编译器编译的 Debian 软件包。
编译设备驱动程序
您可能希望添加自己的设备驱动程序来支持某些设备。我有一个旧的 Tenda U12 USB 转 WiFi 适配器,我想用在我的 VisionFive 2 上。我发现的一件事是,似乎有一个标准的方法和实践,关于用于编译设备驱动程序的 Makefile 以及 make 过程如何与设备驱动程序所需的各种组件(如 Linux 头文件)进行交互。正确完成时,Makefile 将允许在各种环境中编译设备驱动程序,包括不同的处理器、不同的架构以及外部组件(如 Linux 头文件)的不同位置。
有关编写设备驱动程序的信息,请参阅
- 动手编写 Linux 驱动程序系列 (约 37 个视频,从基本的 HelloWorld 驱动程序开始)
- Linux 设备驱动程序编程系列 (约 21 个视频,使用 Beagle Bone 上的 Linux)
- 驱动程序实现者 API 指南 — The Linux Kernel documentation
- Linux 设备驱动程序:Linux 驱动程序开发教程
- GNU make
- 构建 Linux 内核模块:独立开发指南 (fastbitlab.com)
您将在 VisionFive 2 设备本身上需要几个与 Linux 模块一起使用的工具。这些工具位于 /sbin 目录中,该目录不是默认 PATH 环境变量的一部分。
您可能需要安装 usbutils 包,sudo apt install usbutils
,在 VisionFive 2 设备上,因为它包含有用的 lsusb 工具,该工具显示连接的 USB 设备列表,并包含一些详细信息,如供应商 ID。
我想在 VisionFive 2 上使用一个我手头的 Tenda U12 USB WiFi 适配器。我购买的套件附带的 USB WiFi 适配器仅支持 2.4 Ghz WiFi,而 Tenda U12 同时支持 5 Ghz 和 2.4 Ghz。然而,VisionFive 2 可用的 Linux 内核没有 Tenda U12 的驱动程序。
制造商提供了 Linux 驱动程序以及 Windows 和 MacOS 的驱动程序,但我假设它们是为 x86 Linux 而非 RISC-V 处理器设计的。基于这个假设,我直到后来才下载并查看它们,当我出于好奇短暂查看时。那时我才发现下载包包含源代码。我的失误。我计划稍后进一步查看该软件包。
失败,失败,失败,但至少我有所收获
对于我的新 USB WiFi 驱动程序,我在 Github 上找到了一个看似合适的驱动程序源代码,GitHub - cilynx/rtl88x2bu: 针对当前内核更新的 rtl88x2bu 驱动程序。,但实际上并非如此。不过,我还是继续尝试,即使它是错误的,我也能从失败中学习很多,直到遇到无法逾越的障碍。我使用 git clone 来获取源代码的副本,即 88x2bu,并在我的 VisionFive 2 上进行编译。我遇到的第一个问题是 Makefile 没有为 RISC-V 目标编译提供任何支持,只支持 x86 和 ARM。这意味着我必须修改 Makefile 才能编译驱动程序源代码。
在尝试修改 Makefile 时出现的一个问题是处理器是大端还是小端。VisionFive 2 是小端序,如下面的 Stackoverflow 帖子中的简短 C 程序所示 riscv - 使用 C 代码检查 RISC-V 机器的字节序 - Stack Overflow。
// test the endiness type for the architecture
//
#include <stdio.h>
int main()
{
unsigned long x = 1;
unsigned char* p = (unsigned char*)&x;
printf(" if 1 then little-endian %d\n",(unsigned long)*p);
#if defined(__BYTE_ORDER__)
#if defined(__ORDER_BIG_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
printf(" __ORDER_BIG_ENDIAN__ defined\n");
#elif defined(__ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
printf(" __ORDER_LITTLE_ENDIAN__ defined\n");
#else
printf(" defines not set.\n");
#endif
#else
printf(" No __BYTE_ORDER__ define found.\n");
#endif
return 0;
}
您将需要您的 Linux 发行版和构建的 Linux 头文件。如果您没有正确的 Linux 头文件,您仍然会因为版本魔术不匹配而难以安装驱动程序。请参阅这个 Stackoverflow 帖子 arm - 交叉编译内核模块:无效模块格式 - Stack Overflow。我曾使用 dmesg 命令配合 grep 工具查找有关模块加载失败的更多信息。使用 uname 命令 配合 -r 选项可以找出内核版本。
我遇到的另一个编译错误在此 StackExchange Ubuntu 帖子中有描述 kernel - 缺少 asm/types.h 文件 - Ask Ubuntu。在我对该问题的回答中,我描述了我如何使用符号链接,以便在编译器查找 asm/types.h 时,它会找到一个指向实际位置 asm-generic/types.h 的符号链接,这似乎是已进行但未反映在旧源代码中的更改。
当我编译 VisionFive 2 上的 USB WiFi 驱动程序时,Linux 头文件不正确,导致我在尝试加载它时出现错误。下面的示例显示了如何使用 dmesg 命令配合 grep 来查找关于错误的更多详细信息以及 Linux 版本。然后,我使用 modinfo 命令显示 ch341 芯片组的 USB 串行驱动程序的 vermagic 或版本魔术,该驱动程序已内置到 Linux 中,并且具有正确的 vermagic 标签。
user@starfive:~/Documents$ dmesg | grep 88x2bu
[184363.434896] 88x2bu: version magic '6.0.0-6-riscv64 SMP mod_unload modversions riscv' should be '5.15.0-starfive SMP mod_unload riscv'
user@starfive:~/Documents$ uname -r
5.15.0-starfive
user@starfive:~/Documents$ /sbin/modinfo /lib/modules/5.15.0-starfive/kernel/drivers/usb/serial/ch341.ko
filename: /lib/modules/5.15.0-starfive/kernel/drivers/usb/serial/ch341.ko
license: GPL v2
alias: usb:v9986p7523d*dc*dsc*dp*ic*isc*ip*in*
alias: usb:v4348p5523d*dc*dsc*dp*ic*isc*ip*in*
alias: usb:v1A86p7523d*dc*dsc*dp*ic*isc*ip*in*
alias: usb:v1A86p7522d*dc*dsc*dp*ic*isc*ip*in*
alias: usb:v1A86p5523d*dc*dsc*dp*ic*isc*ip*in*
alias: usb:v1A86p5512d*dc*dsc*dp*ic*isc*ip*in*
depends: usbserial
intree: Y
name: ch341
vermagic: 5.15.0-starfive SMP mod_unload riscv
user@starfive:~/Documents$
设备驱动程序的 Linux 头文件
由于编译设备驱动程序需要您的 Linux 发行版和 Linux 构建的 Linux 头文件,那么您从哪里获取它们呢?
从头构建程序在文件树层次结构中有几个文件,其中一个是 linux-headers-5.15.0-starfive_5.15.0-starfive-1_riscv64.deb。我使用 PuTTY PSFTP 工具将该文件传输到我的 VisionFive 2 设备,然后使用 dpkg 命令安装该软件包,sudo dpkg -i linux-headers-5.15.0-starfive_5.15.0-starfive-1_riscv64.deb
。然后使用 apt list --installed
,我能够检查并看到 linux-headers-5.15.0-starfive/now 5.15.0-starfive-1 riscv64 [installed,local] 已安装。
但是,我仍然无法构建完整的设备驱动程序,因为 Linux 头文件不是设备驱动程序构建所需的唯一源。
此时,我意识到我遇到了另一个障碍,是时候思考一下了。所以我删除了包含驱动程序 git clone 的目录,撤销了我为使其能够编译而对 /lib/modules 所做的更改,并决定第二天重新开始。
思考,因为有时答案就在眼前
重新开始,我找到了这个 GitHub 仓库集合,里面有各种 USB WiFi 设备驱动程序,morrownr · GitHub,并决定再次尝试构建一个支持 VisionFive 2 上的 Tenda U12 的设备驱动程序。
然而,由于有几个不同的驱动程序仓库,每个仓库都支持不同的 RealTek 芯片组,我必须研究 Tenda U12 中使用了哪种芯片组。这比我想象的要困难一些。我无法从制造商的网站上找到任何关于芯片组的信息。我终于找到了这个网页 Tenda U12 - WikiDevi.Wi-Cat.RU,它告诉我它是 Realtek RTL8812au。
接下来,我意识到我在 Windows Subsystem for Linux 的 Ubuntu 会话中拥有 VisionFive 2 Linux 桌面构建的完整交叉编译环境,并且我构建驱动程序可能需要的一切都在里面。而且,在从头构建的程序中,有一个构建 vpu 驱动程序的步骤,它展示了为内核构建设备驱动程序的步骤。
我在 ~/Documents/starfive/github/VF2/soft_3rdparty 目录旁边创建了一个新目录 ~/Documents/starfive/github/VF2/soft_extra,并在该新目录中执行了 git clone https://github.com/morrownr/8812au-20210629
以下载源代码。
我花了一些时间阅读 Makefile,努力理解各种定义的常量如何用于设置构建环境。我意识到的一件事是,~/Documents/starfive/github/VF2/linux 文件夹中的 linux 文件树有几个目录,这些目录与 VisionFive 2 上的 /lib/modules/5.15.0-starfive 中的目录相对应,这些目录是设备驱动程序编译所必需的。继续阅读,似乎标签 KSRC 应该指向 Linux 编译的所有源代码所在的位置。所以我使用以下 make 命令编译了驱动程序,make CROSS_COMPILE=riscv64-linux-gnu- ARCH=riscv KSRC=$VF2_WORK_DIR/linux
,make 成功完成。
然后我使用 PuTTY PSFTP 将文件 8812au.ko 放入 VisionFive 2。
在 VisionFive 2 上,我使用 /sbin/insmod 来安装驱动程序。我换掉了我一直在使用的 USB WiFi 适配器,并插入了 Tenda U12。在几秒钟的漫长等待后,我检查了 dmesg,看起来驱动程序已启用并正在工作。然后我进入设置,现在我可以看到我的 2.4 Ghz WiFi 和 5 Ghz WiFi。我在 Setup 工具中进行了身份验证,并能够使用 FireFox 观看 YouTube 视频。
下一个问题是我是否可以使用相同的方法来构建第一个镜像,即 README.md 替代方案,该方案仅构建 Linux 内核和 BusyBox,不包含桌面。我将 soft_extra 目录复制到了 VisualFive2 仓库树中。第一个问题是设备驱动程序构建所需的 Linux 内核材料放在哪里。起初,我认为它在根目录下的 linux 文件树中,即 ~/Documents/starfive/github/VisionFive2/linux,然而使用该目录编译失败,并出现缺少内核配置文件错误。然后我意识到内核构建的输出在 ~/Documents/starfive/github/VisionFive2/work/linux 中,这似乎工作了几秒钟,直到我收到一个新的错误,cc1: error: incompatible gcc/plugin versions
。
rick@rchamber2:~/Documents/starfive/github/VisionFive2/soft_extra/8812au-20210629$ make CROSS_COMPILE=riscv64-linux-gnu- ARCH=riscv KSRC=~/Documents/starfive/github/VisionFive2/work/linux
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- -C /home/rick/Documents/starfive/github/VisionFive2/work/linux M=/home/rick/Documents/starfive/github/VisionFive2/soft_extra/8812au-20210629 modules
make[1]: Entering directory '/home/rick/Documents/starfive/github/VisionFive2/work/linux'
CC arch/riscv/kernel/vdso/vgettimeofday.o
cc1: error: incompatible gcc/plugin versions
cc1: error: failed to initialize plugin ./scripts/gcc-plugins/structleak_plugin.so
make[2]: *** [/home/rick/Documents/starfive/github/VisionFive2/linux/scripts/Makefile.build:277: arch/riscv/kernel/vdso/vgettimeofday.o] Error 1
make[1]: *** [/home/rick/Documents/starfive/github/VisionFive2/linux/arch/riscv/Makefile:120: vdso_prepare] Error 2
make[1]: Leaving directory '/home/rick/Documents/starfive/github/VisionFive2/work/linux'
make: *** [Makefile:2510: modules] Error 2
rick@rchamber2:~/Documents/starfive/github/VisionFive2/soft_extra/8812au-20210629$
这表明我使用的编译器与编译构建镜像源所使用的编译器不同。我记得模糊地认为两种替代镜像构建使用了不同的工具。这个 Stackoverflow 帖子 如何检索用于编译给定 ELF 可执行文件的 GCC 版本? 提供了一种检查方法,我使用帖子中的方法下载并构建了一个名为 ELFinfo 的工具,该工具表明 Debian 镜像构建正在使用 GCC 11.4.0
,而 VisionFive2 仓库镜像构建正在使用 GCC 12.2.0
。
经过一些调查,我发现 Buildroot 系统在构建 Linux 镜像的 VisionFive2 版本时会拉取自己的工具链。Buildroot 使用的编译器名称也与 从头构建 Debian 桌面构建使用的名称略有不同。从头构建 使用的名称是安装在 WSL 的 Ubuntu 中的交叉编译器的名称,即 riscv64-linux-gnu-gcc,而 Buildroot 工具链位于目录 ./work/buildroot_initramfs/host/bin 中,并且名称为 riscv64-buildroot-linux-gnu-gcc。
我将路径 ~/Documents/starfive/github/VisionFive2/work/buildroot_initramfs/host/bin 添加到 $PATH 环境变量中,以使正确的工具链可用。然后我 cd 到包含 8812au 驱动程序的目录进行构建。然后使用 make CROSS_COMPILE=riscv64-buildroot-linux-gnu- ARCH=riscv KSRC=~/Documents/starfive/github/VisionFive2/work/linux 来编译驱动程序。
现在的问题是如何将此或任何其他产物提供给 VisionFive2 镜像构建过程,以便将其包含在镜像中。这仍然是我正在弄清楚的事情。
修改 Linux 内核并更新 VisionFive 2
虽然我还没有实际更改 Linux 内核,但一个问题是如何在不触及其他任何内容的情况下更新内核?
我实际上没有什么要更改的,但我认为我可以通过重新编译 Linux 内核来假装做出更改。首先,我需要弄清楚如何安装一个新的 Linux 内核,仅仅是内核。我发现 VisionFive 2 镜像构建过程创建了两个 .deb 文件:linux-headers-5.15.0-starfive_5.15.0-starfive-5_riscv64.deb、linux-image-5.15.0-starfive_5.15.0-starfive-5_riscv64.deb 和 linux-libc-dev_5.15.0-starfive-5_riscv64.deb。如果我再次重新编译,文件名中的 -starfive-5_riscv64 部分将变为 -starfive-6_riscv64。
然后我找到了一篇帖子,描述了使用 sudo apt install
和 .deb 包,并决定尝试用我新构建的 Linux 内核来尝试。但是当我尝试时,sudo apt install
失败了,并出现关于更改 /boot/System.map-5.15.0-starfive 的错误。我意识到我并不真正理解我在做什么。我花了一些时间阅读,然后决定通过在 Makefile 中将变量 SUBLEVEL
从值 0 更改为值 11,使用 vi 文本编辑器将内核版本从 5.15.0 更改为 5.15.11,并且使用生成的 linux-image-5.15.11-starfive_5.15.11-starfive-8_riscv64.deb 文件,sudo apt install
成功了。
我将一个问题发布到 Unix & Linux StackExchange 论坛 更新我构建的测试内核的 Linux 内核过程,随着我阅读更多内容并得到评论的帮助,该问题已经经过多次编辑。
这是我目前使用的基本过程:
- 使用
vi
编辑 Linux 内核Makefile
,将变量EXTRAVERSION =
更改为EXTRAVERSION = 1
或其他数字,而不是我最初更改的SUBLEVEL
变量 - 重新构建内核以创建一个文件名类似 linux-image-5.15.01-starfive_5.15.01-starfive-8_riscv64.deb 的文件(文件名中的 -starfive-8_riscv64 部分每次构建内核时都会更改)
- 使用
psftp
工具将文件传输到 VisionFive 2 SBC - 使用
PuTTY
打开命令 shell 并导航到我放置.deb
文件的文件夹 - 使用
sudo apt install ./linux-image-5.15.01-starfive_5.15.01-starfive-8_riscv64.deb
- 使用
sudo reboot
重启 VisionFive 2 以使用新内核重启设备
这似乎工作得很好。uname -a
命令表明我正在运行我的测试构建 Linux 内核。
启动与 u-boot
VisionFive 2 与许多其他 SBC 设备一样,使用 u-boot 引导加载程序而不是 GRUB。镜像构建包含一份 u-boot 副本到镜像中。u-boot 引导加载程序负责加载实际的 Linux 内核并开始执行,u-boot 允许您拥有多个内核并选择要引导哪个内核,或者如果您在几秒钟内未做出选择,将加载并启动默认内核。
VisionFive 2 有一个目录 /boot,其中包含每个内核的各种内核镜像和 System.map 文件。我发现我必须使用 sudo ls /boot
才能看到目录内容,因为当我未使用 sudo 时,我收到了错误。当您对 Linux 内核 .deb 文件运行 sudo apt install
时,/boot 目录就是 Linux 内核放置的位置。
为了能够访问 u-boot shell 以便选择内核版本或执行其他 u-boot 任务,您必须能够连接到 Linux 控制台。这需要使用 TTY 转 USB 转接线以及运行终端应用程序的 PC。当我进行这些关于引导我自己的内核的实验时,我使用了一台运行 Linux 的 OrangePi 5 Plus SBC,并使用了 gtkterm 应用程序。我还使用 rsh Linux 应用程序远程 shell 到 VisionFive 2,因为我可以无头运行 VisionFive 2(无需显示器或键盘),并将两个设备并排放置在餐桌上。
由于 brltty 服务导致的 gtkterm 连接问题
当我进行这些关于引导我自己的内核的实验时,我使用了一台运行 Linux 的 OrangePi 5 Plus SBC,并使用了 gtkterm 应用程序。然而,当我尝试连接 gtkterm 时,端口菜单没有显示我预期的 /dev/ttyUSB0 设备,因为我将 TTY 转 USB 转接线的 USB 连接器插入了 OrangePi。
通过研究这个问题,我找到了一个 reddit 帖子,该帖子引导我找到了这个 Stackoverflow 帖子 如何连接到 Ubuntu 10.10 (Maverick Meerkat) 上的串行到 USB 设备终端?,其中有一个答案引导我解决了我的连接问题。在那个答案的基础上,我更新了我找到的有用答案,它太简短而无益。
基本上,问题是一个针对听障人士的服务,brltty 或 Braile TTY,它占用了 /dev/ttyUSB0 端口,因此无法与 gtkterm 一起使用。命令 dmesg | grep tty
显示了以下日志中的问题:
[ 1298.811838] usb 4-1: ch341-uart converter now attached to ttyUSB0
[ 1299.440590] usb 4-1: usbfs: interface 0 claimed by ch341 while 'brltty' sets config #1
[ 1299.442800] ch341-uart ttyUSB0: ch341-uart converter now disconnected from ttyUSB0
我使用了两个命令来首先停止 brltty 服务,然后禁用它以防止它重新启动。
orangepi@orangepi5plus:~$ sudo systemctl disable --now brltty brltty-udev
orangepi@orangepi5plus:~$ sudo systemctl mask brltty brltty-udev
Created symlink /etc/systemd/system/brltty.service → /dev/null.
Created symlink /etc/systemd/system/brltty-udev.service → /dev/null.
orangepi@orangepi5plus:~$
移除测试 Linux 构建
由于我的测试 Linux 构建现在是默认构建,每次我重启设备时,我的测试 Linux 内核都会运行。我决定恢复到原始镜像 Linux 内核。
首先,我想用原始 Linux 内核启动,以防止在移除我的测试内核时出现问题。使用命令 sudo /sbin/reboot now
重启 VisionFive 2,并连接控制台。在 OrangePi 5 Plus 上的 gtkterm 会话中观察控制台输出,直到 u-boot 暂停片刻以便我更改要加载的镜像,我按下空格键,然后选择原始 5.15.0 内核加载。
现在原始内核已运行,我使用 rsh 连接到 VisionFive 2,并使用 uname -a
命令验证我运行的是原始 Linux 内核而不是我的测试构建。然后我使用 sudo apt remove
移除测试构建 Linux 镜像包,从而移除我的测试构建内核。
为了确保我使用了正确的包名,我使用了命令 apt list "*starfive*"
来查看已安装的 Linux 内核列表,以确保我输入了正确的包名。移除完成后,我检查了 /boot 目录,sudo ls -l /boot
,测试内核不再存在。我重启并等待 u-boot 内核列表显示,然后测试内核不再列出,当它加载默认并重启时,运行的 Linux 内核是原始内核。
值得关注的要点
我很高兴地发现,我可以在 Windows PC 上使用 Windows Subsystem for Linux 来执行 VisionFive 2 镜像构建。虽然过程中遇到了一些障碍,例如包含空格的 PATH
环境变量以及一些导致构建失败的复制粘贴错误,但最终一切都很好。
当我第一次尝试构建镜像时,我没有使用连接到 VisionFive 2 40 针连接器上的 TTY 转 USB 转接线来查看系统终端输出。因此,我在 HDMI 显示器上看到的是镜像启动失败,而实际上我构建的镜像不支持桌面。一旦我连接了一个带有 TTY 转 USB 转接线的 Linux 平板电脑到 VisionFive 2,并能够看到启动过程中发生的情况,我就可以推断出错误的设备指定并纠正了配置文件,然后重试。
关于构建 Linux 内核的 YouTube 视频非常有帮助,它们让我对预期有所了解。这些视频还帮助我认识到我正在构建的第一个镜像没有用于 HDMI 显示器的桌面,它只是带有 BusyBox 的 Linux 内核,必须通过系统终端使用 TTY 转 USB 转接线访问。
我不得不更多地学习 Git 版本控制系统,并很高兴找到了免费的在线书籍。
我对 Linux 系统管理有基本的了解,在花时间阅读从头构建程序中用于构建带桌面的 Debian 镜像的命令文档后,我的了解得到了扩展。
在拉取用于构建镜像的源代码时,可能需要很长时间,尤其是对于 Debian 镜像构建。在执行构建镜像的过程中,有几次需要长时间等待而没有反馈,表明实际上没有任何事情在发生。
当我成功构建 Tenda U12 USB 转 WiFi 适配器的设备驱动程序时,我感到很高兴,并在测试成功时感到一种真正的成就感。我期待着弄清楚如何为 VisionFive2 精简镜像和 Debian 桌面镜像构建设备驱动程序。
当我弄清楚了如何仅更新 Linux 内核的过程后,我感到相当有成就感。我现在对 Linux 引导序列有了更好的理解。
历史
- 2023 年 12 月 5 日:初始版本
- 2023 年 12 月 7 日:添加了指向一些附加资源的链接,重新排列了部分文本以改善流程,添加了一些细节,添加了 源码树拉取与克隆 部分
- 2023 年 12 月 9 日:添加了标题为 编译设备驱动程序 的部分,添加了目录
- 2024 年 2 月 21 日:添加了新部分 修改 Linux 内核并更新 VisionFive 2,并更新了关于 构建和测试环境 的部分