[Linux服务器] 建议收藏万字长文!嵌入式Linux系统移植原理与方法总结

97 0
Honkers 前天 23:27 | 显示全部楼层 |阅读模式

Linux系统移植总结

摘要

本文是对整个Linux系统移植的讲解,适宜有一定基础的初学者进行复习,基本可以自己制作PCB之后自己根据这个方法烧写Linux系统,不涉及U-Boot与Linux的源码和编译流程的讲解(这东西后面再学没事的),只讲最实用的方法,如果你有跟着烧写过一遍Linux系统,那么本文会让你重新复习一遍整个流程,加深对Linux系统移植的理解与应用。

​ OK!移植 Linux之前我们需要先移植一个 bootloader 代码,这个 bootloader 代码用于启动 Linux 内核, bootloader有很多,常用的就是 U-Boot。移植好 U-Boot 以后再移植 Linux 内核,移植完 Linux 内核以后Linux 还不能正常启动,还需要再移植一个根文件系统(rootfs)。三个一起构成了一个完整的Linux系统,一个可以使用、功能完善的Linux系统。

一、U-Boot

1.简介

​ 一般直接编译一个移植好的U-Boot,然后烧写到SD卡里面启动。Linux系统要启动就必须要一个bootloader程序,这段bootloader程序会先初始化DDR等外设,然后将Linux内核从flash(NAND, NOR FLASH, SD, MMC等)拷贝到DDR中,最后启动Linux内核。

​ bootloader的实际工作很复杂,但它最主要的工作就是启动Linux内核,很庆幸我们有很多现成的bootloader可以使用比如U-Boot、vivi、RedBoot等等,为了方便书写我们后面都把U-Boot写为uboot。

​ uboot是一个遵循GPL协议的开源软件,是一个裸机代码,可以看作一个裸机综合例程,现在的uboot已经支持液晶屏、网络、USB等高级功能。uboot 官网为 http://www.denx.de/wiki/U-Boot/ 。

​ 一般官网里都是原汁原味的源码文件。但是我们一般不会使用uboot官方的源码,而是去自己的SOC芯片半导体厂商下载uboot,这个版本的uboot相当于是它们定制的,对自家的芯片支持会很全而且维护更好。NXP 就 维 护 的 2016.03 这 个 版 本 的 uboot , 下 载 地 址 为 :http://git.freescale.com/git/cgit.cgi/imx/uboot-imx.git/tag/?h=imx_v2016.03_4.1.15_2.0.0_ga&id=rel_imx_4.1.15_2.1.0_ga 。我们一般下载tar.bz2的压缩包。基本支持了NXP当前所有可以跑Linux的芯片,而且支持各种启动方式,比如EMMC、NAND、NOR FLASH等,这些都是uboot官方不支持的。

​ 但是我们自己做板子的话就需要修改NXP官方的uboot,使其支持我们自己做的板子。

2.编译uboot

​ 首先在 Ubuntu 中安装 ncurses 库, 否则编译会报错,安装命令如下:

  1. sudo apt-get install libncurses5-dev
复制代码

​ 在ubuntu中创建存放uboot的目录,然后存放自己的uboot源码。使用FileZilla软件将windows下源码放到自己的ubuntu中。然后对其进行解压缩:

  1. tar -vxjf uboot-imx-2016.03-2.1.0-g8b546e4.tar.bz2
复制代码

​ 1、如果使用512MB(DDR3)+8GB(EMMC)的核心板,则使用如下命令编译对应uboot:

  1. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
  2. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_defconfig
  3. make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
复制代码

​ ARCH=arm设置目标为arm架构, CROSS_COMPILE 指定所使用的交叉编译器。第一条命令是清除工程。第二条指令配置uboot。第三条命令使用12核编译uboot。

​ 编译完成以后uboot源码就多了一些文件,其中u-boot.bin就是编译出来的uboot二进制文件。uboot是个裸机程序,因此需要在其前面加上头部才能在SOC上运行,u-boot.imx文件就是我们最终要烧写到开发板中的uboot镜像文件。

​ 每次编译都要输入上面一长串命令,可以建立个shell脚本,新建mx6ull_emmc.sh脚本文件然后在上面代码的第一行插入 “#!/bin/bash” 放入shell中就行。使用chmod给予脚本可执行权限,然后就可以使用脚本来重新编译uboot:./mx6ull_emmc.sh

​ 2、如果用的 256MB+512MB 的 NAND 核心板 ,只需要将上面第二行的配置文件emmc改为nand即可。

3.烧写与启动

​ uboot编译好以后就可以烧写到自己的板子上咯,将uboot烧写到SD卡然后通过SD卡来启动运行uboot。使用imxdownload软件烧写,命令如下:

  1. chmod 777 imxdownload
  2. ./imxdownload u-boot.bin /dev/sdd //不能烧写到/dev/sda或sda设备里!
  3. //sdd具体参考自己的存储设备
复制代码

​ 以前在学习单片机的时候编译完代码以后可以直接通过MDK下载到内部flash中,但SOC内部只有96K的ROM而且只对芯片厂家使用不向我们开放。为此一般的SOC支持外置的NOR Flash、NAND Flash、SD/EMMC等存储介质中启动。但NXP对烧写代码至SD卡中有严格规定,不能简单的通过电脑拖动文件进sd卡那样操作,否则是运行不起来的,一般在《芯片参考手册》中的System Boot中专门讲解启动方式。

​ imxdownload就是专门将编译出来的.bin文件烧写到SD卡中的,只能在ubuntu下使用,需要放在工程目录下。然后需要确定要烧写的SD卡,ubuntu下所有的设备文件都在目录“/dev”里,ls /dev/sd*可以查看存储设备。

​ OK,介绍完了如何将uboot烧写到SD卡中后,就可以将SD卡插入开发板,BOOT设置从SD卡启动,使用USB线将USB_TTL和电脑连接,打开SecureCRT,在倒计时按回车进入uboot命令行模式,如果倒计时之后uboot就会使用默认参数来启动Linux内核了。

​ 进入命令行模式,就可以看到“=>”标志,下面全部是SOC的信息。

4.uboot裁剪与移植

​ 复习了一下uboot启动流程,对uboot有个初步的了解。这里就学习如何将芯片原厂(这里以NXP官方为例)官方的uboot移植到我们自己的板子上,学习如何在uboot中添加我们自己的板子~

​ 半导体厂商会将 uboot 移植到他们自己的原厂开发板上,测试好以后就会将这个 uboot 发布出去,这就是大家常说的原厂 BSP 包。我们一般做产品就会参考原厂的开发板做硬件,然后在原厂提供的BSP包上做修改,将uboot或者linux kernel移植到我们的硬件上。这就是uboot移植的一般流程:

​ ①、在uboot中找到参考的开发平台,一般是原厂的开发板
​ ②、参考原厂开发板移植uboot到我们自己的板子上

​ 在移植之前,我们先编译一下NXP官方开发板对应的uboot首先是配置uboot,configs目录下有很多跟你的SOC相关的配置,我们用的I.MX6ULL,所以使用mx6ull_14x14_evk_emmc_defconfig作为默认配置文件。

编译NXP官方开发板对应的uboot:

​ 找到NXP官方开发板对应的默认配置文件,以后就可以使用如下命令编译uboot:

  1. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_emmc_defconfig
  2. make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
复制代码

​ 也可以在顶层Makefile中直接给ARCH和CORSS_COMPILE赋值,修改如下:

  1. 245 #set default to nothing for native builds
  2. 246 ifeq ($(HOSTARCH),$(ARCH))
  3. 247 CROSS_COMPILE ?=
  4. 248 endif
  5. 249
  6. 250 ARCH = arm
  7. 251 CROSS_COMPILE = arm-linux-gnueabihf-
复制代码

​ 这样我们就可以使用简短的命令来编译uboot了:

  1. make mx6ull_14x14_evk_emmc_defconfig
  2. make V=1 -j16
复制代码

​ 还可以创建sheill脚本,这里不详细介绍。

​ 编译好以后就会生成u-boot.bin、u-boot.imx等文件,这些文件都是NXP官方开发板能不能用到我们自己的开发板上呢?试一试!

烧写验证与驱动测试

​ 将imxdownload软件拷贝到uboot源码根目录下,然后将uboot.bin烧写到SD卡中。烧写完以后将SD卡插入我们自己的开发板中,设置开发板从SD卡启动,打开SecureCRT设置好开发板使用的串口并打开。可以发现uboot启动正常,虽然我们使用的是NXP官方的开发板但是在我们自己的开发板上是可以正常启动的。

​ 1.SD卡和EMMC驱动检查:使用命令mmc list列出当前的MMC设备,可以看到两个MMC设备,先检查MMC设备0,输入以下命令:

  1. mmc dev 0
  2. mmc info
复制代码

​ 可以看出mmc设备0是SD卡,容量为14.8GB,说明SD卡驱动正常。再检查设备1,即把mmc dev 0改为1。

​ 2.LCD驱动检查

​ 如果uboot中的LCD驱动正确的话,启动uboot以后LCD上会显示NXP的logo,如果使用其他分辨率的LCD就需要修改LCD驱动,这里我们先不修改LCD驱动了,我们只需要记得uboot的LCD需要修改就行。

​ 3.网络驱动,ping不了ubuntu主机,网络驱动也需要修改。

环境变量bootcmd和bootargs

bootcmd 保存着 uboot 默认命令, uboot 倒计时结束以后就会执行 bootcmd 中的命令。这些命令一般都是用来启动 Linux 内核的,比如读取 EMMC 或者 NAND Flash 中的 Linux 内核镜像文件和设备树文件到 DRAM 中,然后启动 Linux 内核。

  1. setenv bootcmd 'mmc dev 1; fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ullalientek-emmc.dtb; bootz 80800000 - 83000000;'
复制代码

bootargs 保存着 uboot 传递给 Linux 内核的参数.

  1. mmcargs=setenv bootargs console= ttymxc0, 115200 root= /dev/mmcblk1p2 rootwait rw
复制代码

5.uboot启动Linux测试

​ uboot 已经移植好了, bootcmd 和 bootargs 这两个重要的环境变量也讲解了,接下来就要测试一下 uboot 能不能完成它的工作:启动 Linux 内核。我们测试两种启动 Linux 内核的方法,一种是直接从 EMMC 启动,一种是从网络启动。

1.从EMMC启动Linux系统

​ 从EMMC启动也就是将编译出来的Linux镜像文件zImage和.dtb文件保存在EMMC中,uboot从EMMC读取这两个文件并启动,这也是产品最终的启动方式。在zImage和.dtb文件烧写到了EMMC中后,检查以下EMMC的分区1中有没有zImage和.dtb文件,输入“ls mmc 1:1”即可。

如果有则设置bootargs和bootcmd两个环境变量:

  1. setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
  2. setenv bootcmd 'mmc dev 1; fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ull-alientek-emmc.dtb; bootz 80800000 - 83000000;'
  3. saveenv
复制代码

​ 然后可以直接输入boot启动linux内核。

2.从网络启动Linux系统

​ 从网络启动 linux 系统的唯一目的就是为了调试!这样就不用频繁的烧写EMMC,加快开发速度。我们可以通过nfs或者tftp从ubuntu下载zImage文件和设备树文件,根文件系统可以通过nfs挂载,后面再说。设置环境变量:

  1. setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
  2. setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-alientek-emmc.dtb; bootz
  3. 80800000 - 83000000'
  4. saveenv
复制代码

6,uboot图形化配置

uboot可以通过defconfig来配置,或者通过对应SOC文件mx6ull_…emmc.h来配置uboot。还有一种配置uboot的方法就是图形化配置。

​ uboot或Linux内核可以通过输入“make menuconfig”来打开图形化配置界面,menuconfig使一套图形化的配置工具,需要ncurses库支持,它提供了一系列的API函数供调用者生成基于文本的图形界面,需先在ubuntu下安装ncurses库:

  1. sudo apt-get install build-essential
  2. sudo apt-get install libncurses5-dev
复制代码

​ 打开图形化配置界面之前,需要对uboot进行一次默认配置,只需要一次即可。如果使用了make clean就要重新配置。进入uboot根目录输入命令:

  1. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_alientek_emmc_defconfig
  2. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
复制代码

如果已经在uboot顶层Makefile定义了ARCH和CROSS_COMPILE值那么可以简化为:

  1. make mx6ull_alientek_emmc_defconfig //你自己的defconfig文件
  2. make menuconfig //打开图形化配置界面
复制代码

​ 当我们修改了uboot配置以后,使用如下命令编译uboot:

  1. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
复制代码

​ 编译完成后烧写到SD卡中。

以后就可以通过图形化来配置uboot,这样就不需要导出找命令的配置宏,然后在配置文件里面去定义,舒服!

OK!uboot移植到此结束,简单再总结以下uboot移植过程:

①、自己做开发板基本都是参考半导体原厂的demo板,而厂商会在自己的开发板上移植好uboot、linux kernel和rootfs,最终制作好BSP包提供给用户。我们可以在官方提供的BSP包的基础上添加我们的板子,也就是移植
②、我们做开发板一般不会原封不动抄半导体厂商的demo板,都会根据实际情况来修改,既然有修改就必然涉及到uboot下驱动的移植
③、一般uboot中需要解决串口、NAND、EMMC或SD卡、网络和LCD驱动,因为uboot主要目的就是启动Linux内核,所以不需要考虑太多的外设驱动。
④、在uboot中添加自己的板子信息,根据自己板子的实际情况来修改uboot中的驱动

二、Linux内核

1.内核获取+编译

​ Linux 官网为 https://www.kernel.org ,想获得最新的Linux版本都可以下载。而NXP会下载某个版本的Linux内核,然后将其移植到自己的CPU上,测试成功以后就会将其开放给NXP的CPU开发者,开发者下载NXP提供的Linux内核,然后将其移植到自己的产品上。

​ 我们将Linux源码放到ubuntu下然后解压,示例:

  1. tar -vxjf linux-imx-4.1.15-2.1.0-g8a006db.tar.bz2
复制代码

​ 编译内核之前需要现在ubuntu上安装lzop库,否则内核会编译失败!

  1. sudo apt-get install lzop
复制代码

​ 以EMMC核心板为例,看看如何编译出对应的Linux镜像文件。新建shell脚本“mx6ull_emmc.sh”:

  1. #!/bin/sh
  2. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
  3. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfig
  4. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
  5. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16
复制代码

​ 然后给脚本可执行权限:chmod 777 ./mx6ull_emmc.sh。最后运行shell脚本:./mx6ull_emmc.sh开始编译。编译的时候会弹出Linux图形配置界面,不需要做任何配置,直接退出即可,等待编译完成。

​ 编译完成以后就会在 arch/arm/boot 这个目录下生成一个叫做 zImage 的文件, zImage 就是我们要用的 Linux 镜像文件。另外也会在 arch/arm/boot/dts 下生成很多**.dtb** 文件,这些.dtb 就是设备树文件。

2.内核移植

​ 然后就是将Linux内核移植到我们自己的开发板上了。掌握如何将半导体厂商提供的BSP包移植到我们自己的平台上。

NXP官方开发板Linux内核编译:

1.修改顶层Makefile

​ 直接在顶层 Makefile 文件里面定义 ARCH 和 CROSS_COMPILE 这两个的变量值为 arm 和 arm-linux-gnueabihf-

2.配置并编译Linux内核

​ 每个板子都有对应的默认配置文件,配置文件保存在arch/arm/configs目录中。我们使用imx_v7_mfg_defconfig这个默认配置文件。进入ubuntu中的Linux源码根目录,执行命令配置Linux内核:

  1. make clean //先清理一下
  2. make imx_v7_mfg_defconfig //配置Linux内核
复制代码

​ 然后就可以编译了,使用如下命令编译Linux内核:

  1. make -j16
复制代码

​ 编译完成后就会在 arch/arm/boot目录下生成zImage镜像文件,如果使用设备树就会在 arch/arm/boot/dts目录下生成对应的.dtb文件。

启动测试

​ 确保uboot中环境变量bootargs内容如下:

  1. console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw
复制代码

​ 然后将zImage 和 .dtb文件复制到ubuntu的tftp目录下,因为我们要在uboot中使用tftp命令将其下载到开发板中:

  1. cp arch/arm/boot/zImage /home/book/linux/tftpboot/ -f
  2. cp arch/arm/boot/dts/imx6ull-14x14-evk.dtb /home/book/linux/tftpboot/ -f
复制代码

​ 进入uboot命令行模式,将俩文件下载到开发板中并启动:

  1. tftp 80800000 zImage
  2. tftp 83000000 imx6ull-14x14-evk.dtb
  3. bootz 80800000 - 83000000
复制代码

然后会报错根文件系统缺失错误,说明Linux内核移植完成。

3.Linux中添加自己的开发板

​ 将arch/arm/configs目录下的imx_v7_mfg_defconfig重新复制一份,命名为imx_mine_emmc_defconfig,就可以当作自己的开发板默认配置文件了:

  1. cd arch/arm/configs
  2. cp imx_v7_mfg_defconfig imx_mine_emmc_defconfig
复制代码

以后就可以使用如下命令来配置自己的开发板对应的Linux内核:

  1. make imx_mine_emmc_defconfig
复制代码

添加开发板对应的设备树文件

​ 添加适合自己开发板的设备树文件,进入arch/arm/boot/dts中,复制一份你的开发板对应的soc型号的设备树文件,比如我用的就是imx6ull-14x14-evk.dts,将其复制并重命名为imx6ull-mine-emmc.dts即可。

​ 创建好.dts后还需要修改文件 arch/arm/boot/dts/Makefile,找到"dtb-$(CONFIG_SOC_IMX6ULL)"配置项,加入自己重命名对应的.dtb文件名 “imx6ull-mine-emmc.dtb” 。这样编译Linux的适合就可以从.dts编译出.dtb文件了。

编译测试

​ 现在Linux内核已经添加了自己的配置文件和.dts文件,创建一个shell脚本imx6ull_mine_emmc.sh:

  1. #!/bin/sh
  2. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
  3. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihfimx_mine_emmc_defconfig#注意这个地方
  4. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
  5. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16
复制代码

然后给予脚本可执行权限并运行编译Linux内核:

  1. chmod 777 imx6ull_alientek_emmc.sh
  2. ./imx6ull_mine_emmc.sh
复制代码

​ 编译完成以后就会在目录arch/arm/boot下生成zImage镜像文件。arch/arm/boot/dts目录下生成.dtb文件。将这两个文件拷贝到tftp目录下,然后重启开发板进入uboot命令模式中使用tftp命令下载文件并启动:

  1. tftp 80800000 zImage
  2. tftp 83000000 imx6ull-mine-emmc.dtb
  3. bootz 80800000 - 83000000
复制代码

4.CPU主频和网络驱动修改(附)

​ 这需要先确保emmc中的根文件系统可用,先要移植好根文件系统。重启开发板进入终端后输入命令查看cpu信息: cat /proc/cpuinfo 。我们可以通过BogoMIPS大概地判断CPU性能。

目录/sys/bus/cpu/devices/cpu0/cpufreq中,记录了CPU频率等信息。

查看当前CPU频率:cat cpuinfo_cur_freq

​ 如果开发板做一些高负载的工作,比如播放视频等操作那么CPU频率就会提升少去,查看stats目录下的time_in_state文件可以看CPU在各频率下的工作事件,命令:cat/sys/bus/cpu/devices/cpu0/cpufreq/stats/time_in_state

​ 如果想让CPU一直工作在高频怎么办?很简单,配置Linux内核,将调频策略修改为performance高性能模式。或者修改之前提到的defconfig默认配置文件,文件中有下面几行:

  1. 41 CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
  2. 42 CONFIG_CPU_FREQ_GOV_POWERSAVE=y
  3. 43 CONFIG_CPU_FREQ_GOV_USERSPACE=y
  4. 44 CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
复制代码

第一个是默认调频策略,后面三个是其他的调频策略。将示例代码中的第 41 行屏蔽掉,然后在 44 行后面添加:CONFIG_CPU_FREQ_GOV_ONDEMAND=y 。修改完成以后重新编译Linux内核,编译之前清理一下工程。编译完以后我们再次查看/sys/devices/system/cpu/cpu0/cpufreq/ cpuinfo_cur_freq 文件的值 ,当前CPU频率就为792MHz了

​ 除此之外还可以用linux内核的图形化配置界面:输入make menuconfig。进入路径CPU Power Management-> CPU Frequency scaling-> Default CPUFreq governor 打开默认调频策略选择界面就能选择不同调频策略了。

使能8线EMMC驱动

​ 直接修改设备树即可,打开自己的.dts文件找到usdhc2设备,修改成如下代码:

  1. 734 &usdhc2 {
  2. 735 pinctrl-names = "default", "state_100mhz", "state_200mhz";
  3. 736 pinctrl-0 = <&pinctrl_usdhc2_8bit>;
  4. 737 pinctrl-1 = <&pinctrl_usdhc2_8bit_100mhz>;
  5. 738 pinctrl-2 = <&pinctrl_usdhc2_8bit_200mhz>;
  6. 739 bus-width = <8>;
  7. 740 non-removable;
  8. 741 status = "okay";
  9. 742 };
复制代码

​ 使用make dtbs重新编译设备树。

修改网络驱动

​ 略了吧.

三、根文件系统

1.BusyBox构建根文件系统

​ 这是一个将一堆可执行文件和其他文件收集并打包,然后供我们开发者直接使用的软件。这是一个继承了大量Linux命令和工具的软件,一般下载BusyBox的源码,然后配置BusyBox选择自己想要的功能最后编译即可。官网地址为: https://busybox.net/ 。左侧的“Get BusyBox”栏有一行“Download Source” 。准备好以后就可以构建根文件系统了。

编译BusyBox构建rootfs

​ 一般我们在linux驱动开发的时候都是通过nfs挂载根文件系统的,当产品最终成型才会将rootfs烧写到emmc或nand中,所以在之前nfs服务器目录中创建一个名为rootfs的子目录,比如我的路径就是"/home/book/linux/nfs/rootfs",将下载的busybox的tar.bz2压缩包发送到ubuntu中,压缩包位置随便放,然后使用如下命令将其解压:

  1. tar -vxjf busybox-1.29.0.tar.bz2
复制代码

​ 解压完成后进入到目录中:

1.修改Makefilie,添加编译器

  1. 164 CROSS_COMPILE ?= /usr/local/arm/gcc-linaro-4.9.4-2017.01-
  2. x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
  3. ......
  4. 190 ARCH ?= arm
复制代码

2.busybox中文字符支持

​ 如果默认编译busybox在使用SecureCRT的时候中文字符是显示不正常的。所以需要修改busybox源码,取消对busybox对中文显示的限制,打开busybox1.29.0/libbb/printable_string.c,找到函数printable_string,缩减后的内容如下:

  1. 12 const char* FAST_FUNC printable_string(uni_stat_t *stats, const char
  2. *str)
  3. 13 {
  4. 14 char *dst;
  5. 15 const char *s;
  6. 16
  7. 17 s = str;
  8. 18 while (1) {
  9. 19 unsigned char c = *s;
  10. 20 if (c == '\0') {
  11. ......
  12. 28 }
  13. 29 if (c < ' ')
  14. 30 break;
  15. 31 /*if (c >= 0x7f)
  16. 32 break;*/
  17. 33 s++;
  18. 34 }
  19. 35
  20. 36 #if ENABLE_UNICODE_SUPPORT
  21. 37 dst = unicode_conv_to_printable(stats, str);
  22. 38 #else
  23. 39 {
  24. 40 char *d = dst = xstrdup(str);
  25. 41 while (1) {
  26. 42 unsigned char c = *d;
  27. 43 if (c == '\0')
  28. 44 break;
  29. 45 /*if (c < ' ' || c >= 0x7f)*/
  30. if( c < ' ')
  31. 46 *d = '?';
  32. 47 d++;
  33. 48 }
  34. ......
  35. 55 #endif
  36. 56 return auto_string(dst);
  37. 57 }
复制代码

接着打开文件 busybox-1.29.0/libbb/unicode.c,修改成如下内容:

  1. 1003 static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t
  2. *stats, const char *src, unsigned width, int flags)
  3. 1004 {
  4. 1005 char *dst;
  5. 1006 unsigned dst_len;
  6. 1007 unsigned uni_count;
  7. 1008 unsigned uni_width;
  8. 1009
  9. 1010 if (unicode_status != UNICODE_ON) {
  10. 1011 char *d;
  11. 1012 if (flags & UNI_FLAG_PAD) {
  12. 1013 d = dst = xmalloc(width + 1);
  13. ......
  14. 1022 /* 修改下面一行代码 */
  15. 1023 /* *d++ = (c >= ' ' && c < 0x7f) ? c : '?'; */
  16. 1024 *d++ = (c >= ' ') ? c : '?';
  17. 1025 src++;
  18. 1026 }
  19. 1027 *d = '\0';
  20. 1028 } else {
  21. 1029 d = dst = xstrndup(src, width);
  22. 1030 while (*d) {
  23. 1031 unsigned char c = *d;
  24. 1032 /* 修改下面一行代码 */
  25. 1033 /* if (c < ' ' || c >= 0x7f) */
  26. 1034 if(c < ' ')
  27. 1035 *d = '?';
  28. 1036 d++;
  29. 1037 }
  30. 1038 }
  31. ......
  32. 1044 return dst;
  33. 1045 }
  34. ......
  35. 1047
  36. 1048 return dst;
  37. 1049 }
复制代码

3.配置busybox

​ 有以下几种配置选项:

  1. defconfig,缺省配置,也就是默认配置选项
  2. allyesconfig,全选配置,选中busybox所有功能
  3. allnoconfig,最小配置
复制代码

​ 我们一般使用默认配置即可,busybox也支持图形化配置,使用make menuconfig。

进入路径Location:-> Settings-> Build static binary (no shared libs) ,不要选中Build static binary。

Location:-> Settings-> vi-style line editing commands ,选中vi-style line editing commands 。

Location:-> Linux Module Utilities-> Simplified modutils ,不要选中Simplified modutils

Location:-> Linux System Utilities-> mdev (16 kb) 确保这个下面全部选中,默认就是全选。

Location:-> Settings-> Support Unicode(选中)-> Check $LC_ALL, $LC_CTYPE and $LANG environment variables(选中)

4.编译busybox

我们可以指定编译结果的存放目录,我们肯定要将编译结果存放到前面创建的rootfs目录中,输入命令:

  1. make
  2. make install CONFIG_PREFIX=/home/book/linux/nfs/rootfs
复制代码

​ 然后可以进入rootfs目录,发现有bin、sbin和usr这三个目录,以及linuxrc这个文件夹。busybox的工作就完成了,但是此时的根文件系统还不能使用,还需要继续完善。

2.向rootfs添加lib库

1.向rootfs的“/lib”目录添加库文件

​ Linux 中的应用程序一般都是需要动态库的, 当然你也可以编译成静态的,但是静态的可执行文件会很大。如果编译为动态的话就需要动态库,所以我们需要向根文件系统中添加动态库。在 rootfs 中创建一个名为“lib”的文件夹

​ lib库文件从交叉编译器中获取,前面我们搭建交叉编译器的时候将其存放到了“/usr/local/arm/”目录中。交叉编译器里有很多的库文件,我们后续会学习这些库文件具体是什么以及对其裁剪,这里我们直接全部存放到我们的根文件系统中:

​ 进入如下路径对应的目录:

  1. /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/armlinuxgnueabihf/libc/lib
复制代码

此目录下有很多的 *so* 和 .a 文件,这些就是库文件,将此目录所有的这些文件拷贝到rootfs/lib中!

  1. cp *so* *.a /home/book/linux/nfs/rootfs/lib/ -d
复制代码

​ -d表示拷贝符号链接,这里有个特殊的库文件:ld-linux-armhf.so.3。此文件也是个符号链接,输入"ls ld-linux-armhf.so.3 -l"查看此文件详细信息,后面有个“->”表示这是个软连接文件,即“快捷方式”。我们需要重新复制ld-linux-armhf.so.3:先将rootfs/lib中的这个文件删掉,然后重新单独拷贝这个文件:“zp ld-linux-armhf.so.3 /home/book/linux/nfs/rootfs/lib/”。

​ 然后继续进入目录:/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/lib :此目录下也有很多的 *so* 和 .a 库文件,将这些拷贝到rootfs/lib目录中:“cp so *.a /home/book/linux/nfs/rootfs/lib/ -d”

2.向rootfs的“usr/lib”目录添加库文件

​ 在rootfs的usr目录下创建lib目录,进入目录/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib。同上:cp so *.a /home/book/linux/nfs/rootfs/usr/lib/ -d 。

​ 至此,根文件系统的库文件就全部添加好了,可以使用“du”命令查看rootfs/lib和rootfs/usr/lib这两个目录的大小:du ./lib ./usr/lib/ -sh 。可以看到两个文件夹大小分别为57MB和67MB,不裁剪还是挺大的

3.创建其他文件夹

​ 在根文件系统中创建其他文件夹,如 dev、 proc、 mnt、 sys、 tmp 和 root 等 。

3.根文件系统测试

​ 测试方法就是使用NFS挂载,uboot里面的bootargs环境变量会设置“root”值,所以我们将root的值改为NFS挂载即可。Linux内核源码里面有相应文档讲解如何设置:Documentation/filesystems/nfs/nfsroot.txt。

bootcmd:

  1. setenv bootcmd 'tftp 80800000 zImage;tftp 83000000 imx6ull-alientek-emmc.dtb;bootz 80800000 - 83000000;'
复制代码

bootargs要设置为:

  1. setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.5.11:/home/book/linux/nfs/rootfs,proto=tcp rw ip=192.168.5.89:192.168.5.11:192.168.5.1:255.255.255.0::eth0:off'
  2. saveenv
复制代码

​ 设置好以后可以使用boot命令成功启动linux内核,但现在还没完善。

创建/etc/init.d/rcS

​ rcS是个shell脚本,Linux内核启动以后需要启动一些服务,而这就是规定启动哪些文件的脚本。在rootfs中创建此文件然后输入如下内容:

  1. #!/bin/sh
  2. PATH=/sbin:/bin:/usr/sbin:/usr/bin:$PATH
  3. LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib
  4. export PATH LD_LIBRARY_PATH
  5. mount -a
  6. mkdir /dev/pts
  7. mount -t devpts devpts /dev/pts
  8. echo /sbin/mdev > /proc/sys/kernel/hotplug
  9. mdev -s
复制代码

​ PATH环境变量保存着bin文件可能存在的目录;LD_LIBRARY_PATH保存库文件所在目录;export来导出上面的环境变量,相当于声明为“全局变量”;mount命令挂载所有的文件系统,由/etc/fstab来指定这些文件系统;然后是创建目录/dev/pts,将devpts挂载到/dev/pts目录中;最后使用mdev来管理热插拔设备,通过这两行Linux内核可以在/dev目录下自动创建设备节点。

​ 然后给予/etc/init.d/rcS可执行权限:chmod 777 rcS

创建/etc/fstab文件

​ 在rootfs中创建/etc/fstab文件,开机以后自动配置哪些需要自动挂载的分区。文件内容:

  1. #<file system> <mount point> <type> <options> <dump> <pass>
  2. proc /proc proc defaults 0 0
  3. tmpfs /tmp tmpfs defaults 0 0
  4. sysfs /sys sysfs defaults 0 0
复制代码

创建/etc/inittab文件

​ init程序会读取/etc/inittab这个文件,inittab由若干条指令组成格式为:::

创建一个/etc/inittab,内容:

  1. #etc/inittab
  2. ::sysinit:/etc/init.d/rcS
  3. console::askfirst:-/bin/sh
  4. ::restart:/sbin/init
  5. ::ctrlaltdel:/sbin/reboot
  6. ::shutdown:/bin/umount -a -r
  7. ::shutdown:/sbin/swapoff -a
复制代码

​ 系统启动以后会运行/etc/init.d/rcS这个脚本文件;第3行将console作为控制台;第4行重启的话运行/sbin/init;第5行按下ctrl+alt+del组合键就运行/sbin/reboot,重启系统;第6行,关机执行/bin/umount卸载各个文件系统;第7行关机执行/sbin/swqpoff关闭交换分区。

​ 至此,根文件系统就已全部完成。接下来就要对根文件系统进行其他测试,比如我们自己编写的软件运行是否正常、是否支持APP开启自启动、中文支持是否正常以及能不能链接等。

软件运行测试

​ 我们使用Linux的目的就是运行我们自己的软件,我们编译的应用软件一般都使用动态库,使用动态库的话APP的体积就很小,但是得提供库文件,库文件我们已经添加到了根文件系统中。我们编写一个小小的测试软件来测试以下库文件是否工作正常,在rootfs下创建一个drivers的文件夹,在ubuntu下使用vim编辑器新建一个hello.c文件:

  1. #include <stdio.h>
  2. int main(void)
  3. {
  4. while(1) {
  5. printf("hello world!\r\n");
  6. sleep(2);
  7. }
  8. return 0;
  9. }
复制代码

因为我们是要在ARM芯片上运行的,所以要用交叉编译器去编译,命令:

  1. arm-linux-gnueabihf-gcc hello.c -o hello
复制代码

将编译出来的hello文件拷贝到rootfs/drivers目录下,然后在开发板中输入命令来执行:

  1. cd /drivers
  2. ./hello
复制代码

​ APP运行正常就说明我们的根文件系统中的共享库是没问题的。让APP在后台运行 在运行软件时加上&即可,ps查看后台进程ID,“kill -9 PID”可以用来删除后台应用程序。

中文字符测试

​ 1.设置SecureCRT使用UTF-8编码

​ 因为Linux使用的编码格式为UTF-8,因此要设置SecureCRT的编码格式。打开Options->Session Options,选择左侧的的Appearance,然后在右侧的Character encoding:选择UTF-8编码。

​ 2.创建中文文件

​ 在ubuntu中向rootfs目录新建一个名为“中文”的文档,然后在SecureCRT下查看中文名能不能显示正确。

开启自启动

​ 我们一般做好产品以后都是需要开机自启动相应的软件,进入根文件系统的时候会运行/etc/init.d/rcS这个shell脚本,因此我们可以在这个脚本里面添加自启动相关内容,添加完成后rcS文件内容如下:

  1. 1 #!/bin/sh
  2. 2 PATH=/sbin:/bin:/usr/sbin:/usr/bin
  3. 3 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib
  4. 4 runlevel=S
  5. 5 umask 022
  6. 6 export PATH LD_LIBRARY_PATH runlevel
  7. 7
  8. 8 mount -a
  9. 9 mkdir /dev/pts
  10. 10 mount -t devpts devpts /dev/pts
  11. 11
  12. 12 echo /sbin/mdev > /proc/sys/kernel/hotplug
  13. 13 mdev -s
  14. 14
  15. 15 #开机自启动
  16. 16 cd /drivers
  17. 17 ./hello &
  18. 18 cd /
复制代码

​ 第16行进入drivers目录,因为我们把hello软件存放在drivers下。第17行和我们在命令行运行软件一样。第18行返回根目录。

网络测试

​ 在rootfs中新建文件/etc/resolv.conf,然后输入以下内容:

  1. nameserver 114.114.114.114
  2. nameserver 192.168.1.1
复制代码

这就是设置域名解析地址,可以设置为所处网络的网关地址(第二个),也可以设置为运营商的域名解析服务器地址(第一个)。

​ 然后ping 一下baidu试试。

​ 至此!我们的根文件系统就彻底制作完成,这个根文件系统最好打包保存一下,防止以后不小心破坏了根文件系统功亏一篑,又得从头制作根文件系统。

四、系统烧写

​ 移植好了uboot、Linux kernel和.dtb,制作好了rootfs。接下来就将这四个文件烧写到板子上的EMMC、NAND或QSPI Flash等其他存储设备上,这样我们的产品就可以正常运行了。我们通过NXP官方提供的MfgTool工具通过USB OTG来烧写系统。

​ 此软件在Windwos下使用,“without-rootfs”和“with-rootfs”,一个是不带rootfs和一个是带rootfs的,我们肯定选择with的,对其解压可以得到一个文件夹,文件夹就包含我们需要的烧写工具。进入目录mfgtools-with-rootfs\mfgtools 中,在此目录下有几个文件夹和很多的.vbs文件。

​ 我们只关注Profiles这个文件夹,因为后面要烧写文件就放到这个文件夹中。MfgTool2.exe就是烧写软件,但是不会直接打开这个软件烧写,烧写之前要进行配置,指定烧写的是什么芯片,烧写到哪里去。下面的这些众多的.vbs 文件就是配置脚本,烧写的时候通过双击这些.vbs 文件来打开烧写工具。根据处理器的不同与存储芯片的不同选择.vbs文件。我们假如要向I.MX6U烧写系统,就有这些相关的.vbs文件:

脚本文件描述
mfgtool2-yocto-mx-evk-emmc.vbsEMMC 烧写脚本。
mfgtool2-yocto-mx-evk-nand.vbsNAND 烧写脚本
mfgtool2-yocto-mx-evk-qspi-nor-n25q256a.vbsQSPI Flash 烧写脚本,型号为 n25q256a
mfgtool2-yocto-mx-evk-sdcard-sd1.vbs如果 SD1 和 SD2 接的 SD 卡,这两个文件分 别向 SD1 和 SD2 上的 SD 卡烧写系统。
mfgtool2-yocto-mx-evk-sdcard-sd2.vbs同4

​ 其他的.vbs烧写脚本都用不到可以删除。我们选择EMMC烧写脚本。

1.MfgTool工作原理

​ 1.连接USB到USB OTG接口

​ 2.选择到USB下载模式。(注意弹出TF卡否则电脑识别不出USB)准备就绪以后按下开发板的复位键,此时就会进入USB模式,一旦第一次设置好设备以后,后面每次连接就不会有任何提示了至此,开发板与电脑连接完毕,可以开始烧写系统

​ 连接好以后双击“mfgtool2-yocto-mx-evk-emmc.vbs”,如果出现“符合 HID 标准的供应商定义设备”就说明连接正常,可以进行烧写。

​ 烧写之前我们要把那四个文件放在MfgTool能找到的地方,进入如下目录:

  1. L4.1.15_2.0.0-ga_mfg-tools/mfgtools-with-rootfs/mfgtools/Profiles/Linux/OS Firmware
复制代码

​ 文件夹“OS Firmware”看名字就知道是存放系统固件的,我们重点关注 files、 firmware 这两个文件夹,以及ucl2.xml这个文件。MfgTool的烧写原理:先通过 USB OTG 先将 uboot、 kernel 和.dtb(设备树)三个文件下载到开发板的 DDR 中,注意不需要下载 rootfs。就相当于直接在开发板的 DDR上启动 Linux 系统,等 Linux 系统启动以后再向 EMMC 中烧写完整的系统,包括 uboot、 linuxkernel、 .dtb(设备树)和 rootfs。

1.firmware文件夹

​ 打开 firmware 文件夹,里面有很多的.imx 结尾的 uboot 文件、一个 zImage 镜像文件、很多.dtb结尾的设备树文件。这些文件都是NXP官方开发板使用的,不同的板子使用不同的文件,我们只需要关心只有三个文件:

脚本文件描述
zImageNXP 官方 I.MX6ULL EVK 开发板的 Linux 镜像文件。
u-boot-imx6ull14x14evk_emmc.imxNXP 官方 I.MX6ULL EVK 开发板的 uboot 文件。
zImage-imx6ull-14x14-evk-emmc.dtbNXP 官方 I.MX6ULL EVK 开发板的设备树

我们将我们的这三个文件替换掉这三个文件,而且名字必须改为一致。

2.files文件夹

​ 一样的只关心四个文件:

脚本文件描述
zImageNXP 官方 I.MX6ULL EVK 开发板的 Linux 镜像文件。
u-boot-imx6ull14x14evk_emmc.imxNXP 官方 I.MX6ULL EVK 开发板的 uboot 文件。
zImage-imx6ull-14x14-evk-emmc.dtbNXP 官方 I.MX6ULL EVK 开发板的设备树
rootfs_nogpu.tar.bz2根文件系统,注意和另外一个 rootfs.tar.bz2 根文件系 统区分开。 nogpu 表示此根文件系统不包含 GPU 的内 容, I.MX6ULL 没有 GPU,因此要使用此根文件系统

​ 如果要烧写我们自己编译出来的系统,就需要用我们编译出来的 zImage、 u-boot.imx 和imx6ull-alientek-emmc.dtb 和 rootfs 这四个文件替换掉表 39.2.2.2 中这四个文件。名字一样要改为上面一样的。

3.ucl2.xml文件

​ files 和 firmware 目录下有众多的 uboot 和设备树,那么烧写的时候究竟选择哪一个呢?这个工作就是由ucl2.xml文件来完成的。

最后打开烧写软件“mfgtool2-yocto-mx-evk-emmc.vbs”,点击“Start”按钮开始烧写,由于我们自己制作的 rootfs 比较小,因此烧写相对来说会快一点。烧写完成以后设置开发板从EMMC 启动,启动我们刚刚烧写进去的系统,测试有没有问题。

至此!系统移植到最后的烧写全部讲解完毕,希望对大家有所帮助,对整个操作流程和原理有更深刻的了解。

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Honkers

特级红客

关注
  • 3005
    主题
  • 36
    粉丝
  • 0
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行