第 3 章 嵌入式软件系统移植
本课题中嵌入式系统正常工作的前提是嵌入式软件系统完整且能正常工作, 以便为之后的软件开发提供一个能够正常工作的平台。引导程序 PMON 需要完成 内核引导,嵌入式 Linux 内核需要具有完备的功能且能够正常使用,根文件系统要 完成启动过程中初始化工作。 3.1 PMON 启动流程与编译 3.1.1 PMON 目录结构及主要命令 PMON 目录结构如表 3-1 所示,PMON 目录下包含处理器相关代码和一些基 本的设备驱动,Targets 目录下有针对不同板卡详细的配置文件[39]。

PMON 主要命令包括常用指令和对设备的操作指令,例如设置网络设备 IP 的 命令 ifaddr,网络连接命令 ping 和查看设备命令 devls 等。 3.1.2 PMON 启动流程分析 龙芯 2K1000 处理器通过 SPI 总线接口连接 NOR Flash,PMON 镜像文件存储 在 NOR Flash 中,NOR Flash 的物理地址起始地址为 0x1fc00000,该地址为处理器 的 SPI 总线控制器。但是程序主要在内存中运行,因此需要的改地址映射的虚拟地 址起始地址为 0xbfc00000。处理器上电后,直接从 0xbfc00000 开始运行 PMON 的 代码。 PMON 有两部分代码,PMON 镜像文件开头部分为第一部分汇编代码,这部 分代码上电后直接运行,因此没有压缩,执行完毕会执行第二部分经过压缩的 C 代 码。PMON 分为两部分代码的原因在于系统初始阶段并没有准备好 C 语言运行的 环境,因此需要汇编代码来完成一些处理器和内存等设备的初始化,完成准备工作 [40]。

PMON 启动流程如图 3-1 所示,准备工作完成后,start.s 将第二部分压缩的 C 代码解压,之后 C 代码就在内存中运行。start.s 是 PMON 镜像文件的开头,因此 这部分代码存放在虚拟地址 0xbfc00000,这段代码初始化寄存器、内存、串口、网 口之后,将 PMON 代码搬运到内存中运行,之后开始执行 initmips 函数。initmips 函数在 Targets\LS2K\ls2k\tgt_machdep.c 文件中,该函数首先执行 tgt_cpufreq 函数 来探测 CPU 频率,之后执行 dbginit 函数对命令、文件系统、可执行文件和环境变 量进行初始化,初始化 PCI 总线和设备,加载串口、网络接口、视频接口和 USB 接口等设备的驱动,构建一个可以进行人机交互的命令行。 3.1.3 PMON 编译 1)下载 PMON 源代码,并解压。 2)获取编译 PMON 的 gcc 编译器 3)安装编译 PMON 必要的依赖工具 #sudo apt-get install xutils-dev #sudo apt-get install bison flex build-essential patch #cd /home/troy/pmon-loongson3-20200728/tools/pmoncfg #make pmoncfg #cp pmoncfg /usr/bin 4)在 PMON 源码存放目录下,更改相关文件配置编译所需环境变量 #vi .bashrc 在文件末尾增加三行 pmon 交叉编译工具链和编译器的路径信息 export LD_LIBRARY_PATH=/home/troy/pmongcc/opt/gcc-4.4-gnu/lib: exportCROSS_COMPILE=/home/troy/pmongcc/opt/gcc-4.4-gnu/bin/mipsel-linux export PATH=/home/troy/pmongcc/opt/gcc-4.4-gnu/bin/:$PATH #source .bashrc 使设置的环境变量生效 5)进入 pmon 源码目录进行编译 #cd /home/troy/pmon-loongson3-20200728/zloader.ls2k #make cfg #make all tgt=rom #make dtb 如图 3-2 所示编译完成后在 pmon 源码目录下的 zloader.ls2k/目录下生成 gzrom.bin 和 gzrom-dtb.bin。 如果更改了 Targets/LS2K/conf/ls2k 目录下的文件源代码或者参数, 则在编译 前要执行 make cfg,使得更改生效,如果普通编译没有更改配置,则每次无需都执 行 make cfg 命令。执行 make dtb 可以将设备树 dtb 和 gzrom.bin 结合生成 gzrom dtb.bin,此命令可以在上面编译完成后执行。
3.2 嵌入式 Linux 内核移植
3.2.1 Linux 内核简介 本课题中应用程序、Linux 内核、驱动程序、硬件关系如图 3-3 所示,Linux 内 核是应用程序和硬件设备之间的媒介,内核将应用程序的请求和命令通过底层程 序传递给硬件设备实现对硬件的操作。Linux 内核根据功能分为五个内核子系统, 分别为进程调度、内存管理、文件系统、进程通信和网络。进程调度是内核的核心 功能,多个进程需要使用 CPU 有限的资源,但是 CPU 一次只能处理一个进程,进 程调度会通过合理切换多进程让宏观上多进程并行,无论何时都有进程运行,使 CPU 资源得到最有效利用。内存管理使用一块内存区域给多个进程使用,节省内 存资源。虚拟文件系统屏蔽了不同文件的细节,让用户可以使用一种方式通过例如 read 和 write 等统一接口实现对不同类型文件的访问,如果没有虚拟文件系统,用 户访问文件只能直接访问存储设备上文件存放的位置,这显然对于用户访问文件 非常不方便。进程通信是指内核支持多进程之间通过信号量、共享内存等方式,实 现互斥利用资源以及进程间消息传递,随着软件功能越来越复杂,目前几乎没有单 进程的软件,因此进程调度属于内核中非常重要的功能。网络子系统包含各种网络 协议以及对网络设备的驱动程序,可以让系统实现网络传输功能[41]。
3.2.2 Linux 内核的配置与编译 对于龙芯 2K1000 处理器,考虑到软件的可互换性和可移植性,本课题选择龙 芯官方获得支持 MIPS 架构的嵌入式 Linux 源代码,在 ubuntu 虚拟机中对 Linux 内 核进行裁剪和交叉编译。主要工作流程如下: 1)获取 Linux 源码 从龙芯官方 ftp 下载内核源码压缩包并解压,生成对应的内核源码目录。 2)配置环境变量 #vi .bashrc 在文件最后一行增加交叉编译工具链的绝对路径 #export PATH=/home/troy/opt/gcc-4.9.3-64-gnu/bin:$PATH #source .bashrc 使配置的环境变量生效 3)内核裁剪配置 Linux 内核代码中提供使用图形化界面进行内核裁剪的工具,本课题不直接修 改 Linux 的编译文件代码,而是使用图形界面工具进行内核裁剪,命令如下: #make ARCH=mips CROSS_COMPILE=mips64el-linux- menuconfig 运行该命令之后会弹出如图 3-4 所示的内核配置界面。
由于本课题使用龙芯官方维护 Linux 内核,需要根据龙芯 2K1000 处理器实际 情况对内核进行一些裁剪: (1)增加帧缓冲区驱动 Device Driver—> Graphics support—> Support for frame buffer devices—> Loongson Frame Buffer Support (2)增加龙芯 2K1000 处理器 SPI 总线控制驱动 Device Driver—> SPI support—> <*>Loongson SPI Controller Support (3)增加龙芯 2K1000 处理器 VGA 驱动 Device Driver—> Graphics support—> <*>Loongson VGA DRM [ ] use platform device (4)将自己开发的网络驱动等编译进内核 (5)删除如邮箱、无线网络等不需要使用的驱动程序和功能 4)编译内核 #make ARCH=mips CROSS_COMPILE=mips64el-linux-,对内核进行编译。如 图 3-5 所示编译完毕之后在内核目录下生成 vmlinux 为可移植的 Linux 内核镜像, vmlinuz 为可移植的 Linux 内核压缩镜像。由于嵌入式系统一般资源有限,所以使 用 vmlinuz 作为系统内核镜像。

3.3 根文件系统制作 3.3.1 Linux 根文件系统 内核启动后首先挂载根文件系统,根文件系统除了有文件系统存储和管理数 据的功能,也有普通文件系统没有的功能。根文件系统中一般在/etc 目录下存放着 许多初始化脚本和配置文件,负责系统启动过程中的初始化功能。根文件系统在内 核启动最后阶段被挂载到根目录/,其他目录再挂载到根目录[42]。 虽然根文件系统负责 Linux 内核启动过程中的初始化工作,但在嵌入式系统 中它与内核一般是分开的,这是由于内核负责一些进程调度等操作系统的基本工 作,这些基本工作在所有处理器平台下基本相同。但是根文件系统负责配置工作, 不同的处理器配置方法和具体内容不同,因此根文件系统和内核分离,可以让嵌入 式软件系统的可移植性更好。 3.3.2 根文件系统制作 制作根文件系统常用工具是 BusyBox 和 Buildroot[43],由于 BusyBox 使用较为 繁琐,因此本课题使用 Buildroot 工具来制作根文件系统。Buildroot 包含了 Makefile 和 Kconfig,可以用来构建内核、引导程序和根文件系统,在嵌入式开发中主要用 来构建根文件系统。使用 Buildroot 制作根文件系统镜像步骤如下: 1)下载并配置 Buildroot 下载并解压 Buildroot 之后,使用龙芯 2k1000 对应的 config 文件替换之前默 认 config 文件,具体命令如下: #wget http://ftp.loongnix.org/embedd/ls2h/Buildroot/ #sudo tar -zxvf Buildroot.tar.gz #cp ls2k_docker-systemd-gcc-4.9.3.config .config 2)创建自动编译脚本 通过使用可执行脚本,将编译根文件系统步骤放在一个脚本文件中,减少操作 复杂度,具体命令如下: #touch cmd.sh #vi cmd.sh 脚本内容如下: #! /bin/bash export LANG=C export PATH=/home/troy/opt/gcc-4.9.3-64-gnu/bin/:$PATH make ARCH=mips CROSS_COMPILE=mips64el-linux- -j2 #chmod 777 cmd.sh 3)使用图形化界面对 Buildroot 进行配置 #make menuconfig Buildroot 图形化配置界面如图 3-6 所示,具体配置步骤如下:

(1)Target options —> 根据龙芯 2K1000 处理器实际参数配置 CPU 参数为小端 MIPS64 (2)Toolchain —> 设置工具链 gcc-4.9.3-64-gun 的路径,根据虚拟机中工具链实际的目录来填写 (3)System configuration —> 包含了开机之后系统的欢迎语和用户名及其密码 (4)Run a getty (login prompt) after boot —> 配置串口的波特率,由于本课题中不使用串口而是直接外接屏幕来显示,因此 该项可以不进行设置。 (5)Kernel —> 由于制作不附带内核的根文件系统,该项为空 (6)Target packages —> 根据实际需求选择附带的嵌入式软件,本课题中只需要构建一个最基本的根 文件系统,因此不需要进行选择。 4)开始编译 buildroot 在 buildroot 的顶层目录下执行如下命令: #./cmd.sh 执行自动编译脚本进行编译。 编译成功后,如图 3-7 所示/buildroot/output/images/目录下会生成 cpio、 cpio.gz、 ubi、ubifs 以及 yaffs2 五种类型的根文件系统镜像。由于系统使用 SSD 卡作为存储 介质,因此选择除 ubifs 之外格式的文件系统。
