系统移植概述

所谓Linux系统的移植,就是让Linux在自己的嵌入式板子上跑起来。但是不像STM32上移植个FreeRTOS那么简单,移植Linux还是挺复杂的,不仅包含了OS本身,还要移植bootloader和rootfs

为了让Linux系统能够运行,只需保证组成Linux最小系统的那些部分都被移植了就行了,具体地,包括以下几点(具体作用见另一个文档):

  • BootLoader:常见的有U-Boot,本质上就是一个裸机程序,用于初始化一些外设,然后将Linux内核从Flash拷贝到DDR启动,内核启动后U-Boot就结束运行了。就相当于PC上的BIOS
  • Linux内核:这里需要的不是Linux内核源码,而是编译好的内核镜像
  • 根文件系统:一个目录,包含了Linux运行必备的一些文件和程序

只要移植了这3部分,那么一个基础的Linux系统就能跑起来了,而Ubuntu那些的Linux发行版也包含了这几部分,并进行了一定的扩充。

各种配置文件

在编译移植完的uboot、内核、根文件系统时,大致流程都是:

  • 1.加载适配此板子的默认(default)配置文件:make xxx_defconfig,在根目录生成.config配置文件
  • 2.(可选)在根目录进行图形化配置:make menuconfig,对.config配置文件进一步更改
  • 3.交叉编译,顶层Makfile会读取.config文件,进行条件编译
  • 4.(可选)保存当前的配置文件,方便后续复用

由此我们可以看出,裁剪、移植的成功与否,关键在于能否得到一个正确的.config配置文件

下面介绍一下在移植的过程中会看到的各种配置文件:

  • xxx_defconfig:特定板子的配置文件模板,用于为特定的开发板或平台提供一套预定义的编译选项,该文件中只包含了不同于默认配置参数的选项,而不像.config包含了全部配置参数
  • .config:在编译前的最终配置文件,包含了系统的所有配置选项和参数,它是编译过程中被直接读取的文件
  • Kconfig:定义了配置菜单和选项的层次结构,用于配置系统的 menuconfig 或其他图形化配置工具

配置文件中的依赖关系

Kconfig定义的图形化配置,各个选项可能是树状的,包含了依赖关系,而在.config文件中,却都是扁平的

尽管 .config 文件是平面的,但配置项之间的依赖关系仍然会在生成 .config 文件时处理。例如,如果 CONFIG_NET 被禁用,那么依赖于它的 CONFIG_ETHERNETCONFIG_WIRELESS 选项也会被自动禁用,如下:

1
2
3
CONFIG_NET=n
# CONFIG_ETHERNET is not set
# CONFIG_WIRELESS is not set

如何获得配置文件模板

  • 方法1:在得到了最终的.config文件后,通过make savedefconfig可以在根目录生成一个defconfig文件,就是我们要的配置文件模板,后面把它放到对应位置并重命名就行了
  • 方法2:直接找一个现有的xxx_defconfig文件,在他的基础上改

移植前的准备工作

在移植uboot、kernel、nfs前,需要在宿主机上安装一些必要的服务,比如:tftpnfs,并需要正确的配置,这部分可以看正点原子的文档

BootLoader的移植

移植步骤

以U-Boot为例,移植大概分为以下几步:

1.下载U-Boot的源码到虚拟机中

2.利用适配自己板子的配置文件对U-Boot进行交叉编译

uboot源码的configs目录中有许多xxx_deconfig文件,就是预配置文件,uboot官方会对许多芯片原厂做的开发板写一些demo,到自己的板子可能需要再修改一下才能用,修改有多种方式,可以直接改源码,也有图形化的界面可以用

3.将U-Boot==烧写到SD卡等存设备==中并插到开发板上

4.开发板的boot设置从SD卡启动,此时上电开发板,其将自动运行U-Boot

5.PC上打开串口工具,即可通过命令行使用U-Boot了

Linux内核的移植

移植步骤

1.下载内核源码到虚拟机

这里我们不是去Linux官网下源码,而是去芯片原厂下源码。芯片原厂一般才会去Linux官网下载源码,他们会将Linux内核源码进行修改以适配自己的CPU并发布

2.利用适配自己板子的配置文件对Linux内核进行交叉编译(编译时也会打开一个图形界面,可以在图形界面中手动配置)

3.编译成功后会得到Linux内核镜像文件(叫zImage)和一堆设备树文件(.dtb)

4.将zImage和设备树文件放到网络文件系统中,或者将其下载到外部flash中

5.在U-boot中使用tftp协议将zImage和设备树文件从板子的外部存储介质(flash或者nfs)中下载到内存中的指定地址

对于I.MX6ULL,zImage放到内存地址0x80800000,设备树文件放到内存地址0x83000000,其实也不是必须的,可以改成别的

6.在U-boot中,使用bootz命令从内存中的指定地址启动Linux内核,就可以进入到Linux系统中了

1
bootz <kernel_addr> [<ramdisk_addr> <fdt_addr>]
  • <kernel_addr>:指定内核镜像在内存中的加载地址(此处为0x80800000)
  • <ramdisk_addr>:可选参数,指定 RAM Disk 的加载地址。如果没有 RAM Disk,则可以省略此参数
  • <fdt_addr>:指定设备树文件在内存中的加载地址(此处为0x83000000)

根文件系统的移植

移植步骤

1.下载Busy Box的源码到虚拟机

2.在虚拟机的nfs服务器的目录中创建根文件系统的目录rootfs(一般做Linux驱动开发时,都是通过nfs挂载根文件系统的,在最终版时才将根文件系统烧到flash中)

3.修改busy box的源码,接触其对中文显示的限制

4.对busy box进行配置,并进行交叉编译(可以通过修改配置文件的方式或者图形化界面的方式配置)

5.将编译的结果放到nfs的根文件系统目录rootfs中去

此时rootfs中只有bin、sbin、usr这3个目录,还需添加一些目录才能使用

6.向根文件系统添加lib库目录(本质上就是将交叉编译器中的一些库文件复制到这个目录中,所谓剪裁就是人为决定复制哪些库,不剪裁的话就把所有库都复制进去)

7.向根文件系统添加usr/lib库目录(本质上就是将交叉编译器中的一些库文件复制到这个目录)

8.创建其他文件夹,如dev、proc、mnt、sys、tmp和root等

9.创建/etc/init.d/rcS文件:这是一个shell脚本,用于开机时自动启动一些程序

10.创建/etc/fstab文件:其在Linux启动后自动配置需要挂载的一些分区

11.创建/etc/inittab文件:init程序会读取该文件,其中包含了一些控制指令

12.U-Boot中设置使用nfs挂载的根文件系统为系统的根文件系统

Linux系统的烧写

在移植并测试完上述几部分(uboot、Linux内核镜像、设备树、根文件系统)后,在产品发布前,需要将这几部分烧写到板载的FLASH(EMMC、NAND FLASH之类的)上,而不是一直使用SD卡 + nfs挂载的根文件系统。