泰晓科技 -- 聚焦 Linux - 追本溯源,见微知著!
网站地址:https://tinylab.org

儿童Linux系统,可打字编程学数理化
请稍侯

为 LicheePi 4A 开发板构建运行 Linux v6.5-rc1

王杰迅 创作于 2023/09/06

Corrector: TinyCorrect v0.2-rc1 - [tounix spaces header tables refs pangu autocorrect] Author: 王杰迅 wangjiexun@foxmail.com Date: 2023/07/26 Revisor: falcon falcon@tinylab.org Project: RISC-V Linux 内核剖析 Sponsor: PLCT Lab, ISCAS

前言

LicheePi 4A 是 Sipeed 公司推出的高性能 RISC-V Linux 开发板,是截止目前(2023Q2)为止最强的 RISC-V SBC。目前 Sipeed 官方基于 Linux v5.10.113 开发了官方 SDK —— RevyOS,对该开发板提供了较为完整的驱动支持。

Linux 主线在 v6.5-rc1 版本加入了 部分补丁,以提供对该开发板的支持。

本文介绍了为 LicheePi 4A 开发板构建并运行 Linux v6.5-rc1 的过程,实现的效果为:内核可以成功启动并进入 initramfs 命令行界面。

软件版本

SoftwareVersion
Linux6.5-rc1
U-Boot2020.01
OpenSBI0.9
Buildroot2023.02.2

构建 U-Boot、OpenSBI

U-Boot 和 OpenSBI 主要使用了 Sipeed 官方提供的版本, 见 RevyOS 文档 中构建 U-Boot 和 OpenSBI 的相关部分。

构建设备树

在尝试使用 Linux v6.5-rc1 自带的设备树进行启动时,系统会在初始化 PLIC 时报错:

[    0.000000] Oops - load access fault [#1]
[    0.000000] epc : __plic_toggle+0x6a/0x72

由于无法验证该设备树是否存在 Bug,所以使用了 Sipeed 官方 SDK 编译的设备树。 根据 RevyOS 文档 构建内核后,设备树文件为: arch/riscv/boot/dts/thead/light-lpi4a.dtb

构建 initramfs

使用 Buildroot 构建 initramfs 较为简单,只需进行简单的配置,即可自动编译生成。

配置处理器架构

在 Buildroot 目录下输入:

$ make menuconfig

选择架构为 RISC-V:

Target options  --->
  Target Architecture ()  --->
    (X) RISCV

配置文件系统类型

选择文件系统类型为 CPIO:

Filesystem images  --->
  [*] cpio the root file system(for use as an initial RAM filesystem)

配置内核

构建好 initramfs 后,需要在内核配置中使能 initramfs,并指定生成的 CPIO 文件的位置。

General Setup  --->
   [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
   () Initramfs source file(s)

构建内核

构建 Linux v6.5-rc1 内核的过程与 RevyOS 文档 中构建内核的过程基本一致,只是需要在配置内核时更改为使用 RISC-V 架构下的 defconfig:

$ make CROSS_COMPILE=riscv64-unknown-linux-gnu- ARCH=riscv defconfig

串口无法工作

在尝试使用直接编译的 Linux v6.5-rc1 内核在开发板上启动时,串口输出会卡住,不再继续显示,相关日志如下:

[    0.876673] Warning: unable to open an initial console.
[    0.884088] Freeing unused kernel image (initmem) memory: 3744K
[    0.895429] Run /init as init process

下面将介绍该问题的解决思路和解决方法。

解决思路

下面依次从根文件系统、内核启动参数、设备树、内核驱动这几个方面考虑可能出现问题的原因,并依次进行了排查。

根文件系统

针对该问题,网上最普遍的原因为:根文件系统中缺少 console 文件。因此需要在根文件系统的 /dev 目录下输入以下命令进行创建:

$ mknod -m 660 console c 5 1
$ mknod -m 660 null c 1 3

笔者打开 /dev 目录下发现并不缺少 console 文件,问题没能解决。

进行测试时发现:使用构建好的内核文件和 initramfs 在 QEMU 模拟器上运行时,QEMU 模拟器可以正常启动,进入到 initramfs 的命令行界面,因此推断构建的 initramfs 可以正常使用。

内核启动参数

在内核启动时,需要由 Bootloader 为其传递一些命令行参数。其中 console 参数指定了内核启动后使用的输出端口,通常设置为 console=ttyS0,115200。如果开发板串口提供的波特率不为 115200,则需要进行更改。

在使用了 U-Boot 作为 Bootloader 时,可以在启动倒计时按下回车键,进入 U-Boot 命令行界面, 输入 printenv 指令可以看到内核启动参数 bootargs,并可以通过 setenv 指令修改该参数。

在内核的启动日志中也会打印出启动参数,如:

[    0.000000] Kernel command line: console=ttyS0,115200 rootwait rw earlycon init=/lib/systemd/systemd

同时需要注意,在内核启动时使用的 bootconsole 并不受启动参数 console 的影响。

[    0.000000] earlycon: uart0 at MMIO32 0x000000ffe7014000 (options '115200n8')
[    0.000000] printk: bootconsole [uart0] enabled

经过检查,相关配置没有问题,问题没能解决。

设备树

随后考虑了设备树是否有问题,但使用的设备树为开发板厂家 Sipeed 提供的,并且可以成功启动厂家提供的 RevyOS(基于 Linux v5.10.113)。因此推断设备树可以正常使用。

内核驱动

其他因素已经排除的差不多,只剩内核相关的因素可能存在问题。而使用 QEMU 可以成功启动构建好的内核,因此基本将问题锁定在与开发板相关的因素。主要怀疑内核驱动与设备树的对应可能存在问题,最怀疑的是串口驱动。

重新分析启动日志后发现,存在下述输出:

[    0.674296] Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled

这句输出可以证明串口驱动被内核正常加载。而下述输出:

[   11.098262] platform ffe7014000.serial: deferred probe pending

基本宣告了问题出现的真正原因:串口设备加载失败。

Linux 内核驱动采用平台设备驱动模型,驱动和设备分别被加载进内核,再进行匹配。 因此驱动被成功加载,而串口加载失败,这种情况是可能的。

随后阅读了一篇 具有类似报错信息的文章,发现导致 deferred probe pending 的原因可能为:该设备依赖的驱动没有被内核正确加载。

随后分析设备树文件中串口相关部分:

uart0: serial@ffe7014000 { /* Normal serial, for C910 log */
    compatible = "snps,dw-apb-uart";
    ……
    interrupt-parent = <&intc>;
    ……
    clocks = <&clk CLKGEN_UART0_SCLK>;
    ……
};

intc: interrupt-controller@ffd8000000 {
    compatible = "riscv,plic0";
    ……
};

clk: clock-controller@ffef010000 {
    compatible = "thead,light-fm-ree-clk";
    ……
};

通过在代码仓库中搜索 compatible 属性的值,来判断内核中是否存在驱动缺失的情况。

  • 串口使用的驱动为:“snps,dw-apb-uart”
  • 串口依赖的中断控制器 intc 使用的驱动为:“riscv,plic0”
  • 串口依赖的时钟 clk 使用的驱动为:“thead,light-fm-ree-clk”

通过搜索发现串口依赖的时钟 clk 使用的驱动 “thead,light-fm-ree-clk” 缺失。 从 Sipeed 提供的内核中将相关驱动移植到 Linux v6.5-rc1 后,终于可以正常启动。

解决方法(驱动移植)

  1. 在 Sipeed 提供的内核中找到 drivers/clk/thead 文件夹,添加到 Linux v6.5-rc1 的 drivers/clk 文件夹下
  2. 将 thead 文件夹中的 Kconfig 文件进行修改:将其中所有 SOC_THEAD 替换为 ARCH_THEAD
  3. 修改 drivers/clk 下的 Kconfig 和 Makefile 文件,使之包含 thead 文件夹
  4. 在 Sipeed 提供的内核中找到 include/dt-bindings/clock 文件夹,添加以下头文件到 Linux v6.5-rc1 的相同文件夹下:
    • light-dspsys.h
    • light-fm-ap-clock.h
    • light-mpw-clock.h
    • light-visys.h
    • light-vosys.h
    • light-vpsys.h
  5. 修改 Linux v6.5-rc1 的 arch/riscv/configs/defconfig 文件,在最后添加
CONFIG_THEAD_CLK=y
CONFIG_CLK_LIGHT_FM=y

驱动移植完成后,再对内核重新编译即可在开发板上正常启动。 烧录的具体步骤见 Sipeed 官方提供的烧录文档

总结

本文介绍了为 LicheePi 4A 开发板构建并运行 Linux v6.5-rc1 的过程,并分析了在内核启动过程中无法得到串口输出的问题,提供了相应的分析思路和解决措施。

参考资料



Read Album:

Read Related:

Read Latest: