ARM板移植Linux系统启动(四)配置Kernel

如今的Linux比uboot要成熟得更多,所有的模块都是可配置的。所以基本上对于kernel的移植,特别是这种已经有基础源码的项目,代码改动其实相当少,更多是时间花在调试和配置上。在所有工作中,最主要的困难恐怕就是怎么把系统跑进console了。

uImage & zImage

这个问题太基础,只是因为跟手动跑系统有关,所以稍微提一下。kernel编译后的成果是zImage,uImage只是添加了一个长度为0x40的头,用来记录给uboot的相关信息。虽然uImage是专门给uboot用的,但uboot的启动并不一定要uImage,它既可以接受uImage也可以接受zImage。可以说:uImage的头信息本质上是一种启动参数的传递形式。
制作uImage的工具是mkimage,它是来自于uboot。为了避免各种可能的版本问题,建议直接使用uboot编译后的uboot/tools/mkimage。而uImage的制作脚本则包含在kernel的编译脚本中,使用命令:
make uImage LOADADDR=0x80008000
即可编译获得uImage,前提是你已经将mkimage放入了编译环境的/bin/文件夹下。LOADADDR是kernel的启动地址(注意,这不是真正的kernel运行地址),uBoot会将kernel拷贝到此地址后(实际中也可能不拷贝)执行。关于uboot使用的几个内存地址的具体讲解也很多,这么些年也没什么变化,需要了解的可以自行去搜索。

根文件系统的制作

一个系统必须要有文件系统才能跑起来,即使是很小很小的文件系统。目前一个最基本的文件系统都是基于Busybox制作的,制作过程的教程很多也大同小异,我就不赘述了。有的厂商或者Linux系统发行商也会提供根文件系统的下载,不过大多太过冗余,需要自行裁剪。

你看到的是非授权版本!爬虫凶猛,请尊重知识产权!

转载请注明出处:http://conanwhf.github.io/2017/06/12/bootup-4-kernel/

访问原文「ARM板移植Linux系统启动(四)配置Kernel」获取最佳阅读体验并参与讨论


对于一块空板子,通常的做法是用一个能进入命令行的最小文件系统,然后将完整的文件系统和kernel等烧写到存储设备上。所以如何做一个能够启动的最小系统,就成了第一个步骤。由于这种最小系统启动时还不会用到存储装置(emmc or nand flash),文件系统是跑在RAM里的,有两种做法:将其包含在Kernel的镜像中,或者用uboot加载到内存后告诉kernel加载的内存地址。

带Ramdisk的Kernel镜像

menuconfig里面配置kernel:
General setup —> 选中Initial RAM filesystem and RAM disk (initramfs/initrd) support,将()Initramfs source file(s)填写为准备好的根文件系统路径

保存,编译后即可。

U-boot支持的rootfs镜像

另一种方法是使用单独的根文件系统镜像,这样不需要重新编译kernel,只需要把数据重新打包即可。

  1. 生成空白Image,可以稍微预留大一点,全0数据压缩后可以忽略:

    dd if=/dev/zero of=TEMP bs=1M count=5
    
  2. 将image挂载为loop文件系统

    losetup /dev/loop0 TEMP
    sudo mke2fs -m 0 /dev/loop0
    mkdir -p loop 
    sudo mount -t ext2 /dev/loop0 loop
    
  3. 将数据copy到挂载后的image中,然后卸载:

    sudo cp -raf initramfs/* loop
    sudo umount loop; rm -rf loop
    
  4. 压缩并制作成img:

    gzip -v9 TEMP
    mkimage -n 'YOUR_MARK ext2 uboot ramdisk' -A arm -O linux -T ramdisk -C gzip -d TEMP.gz ramdisk.img
    

    这里的mkimage就是上文提到的来自于uboot的工具,它同样也只是将已有的文件加了一个uboot可以识别的头信息。注意这里的-A arm必须和kernel的目标平台相对应,否则uboot会拒绝引导。

DTS(Device Tree Source)

在老的内核版本中,关于硬件的定义、配置和描述都是通过各种头文件和#define来实现的,而在新的内核中已经全部使用了DTS来描述硬件信息,从而实现了更加灵活的硬件支持和配置。DTS中的“硬件描述”,准确地说是设备描述,包括所有的硬件设备和软件设备节点,从CPU到外设,从型号描述到地址定义,具体到设备节点到硬件的链接等等,都可以定义。
DTS的具体表达文件是*.dtb,它们是由一组遵循特定语法的dts源文件编译而成,放在arch/$PLATFORM/boot/dts下,对于arm板来说就是arch/arm/boot/dts/。这个文件夹底下的文件有点像config file,每个*.dts文件对应一种配置,而dts文件又可以通过include某些$SLICON.dtsi来作为配置的基础,在此基础上添加、覆盖原有内容。一个dts一般只对应一种板子,但同一块板子可以使用几种不同的dts,例如可以通过指定不同的dtb文件,来选择启动时将HDMI还是LCD屏作为标准输出(lcdc0)。以BeagleBoneBlack举例,它的基本启动配置是am335x-boneblack.dts,而它又inlcude了am33xx.dtsi, am335x-bone-common.dtsi, am33xx-pruss-rproc.dtsi等文件。
查找编译所对应的dts file,可以查看arch/arm/boot/dts/Makefile,看看kernel config里定义的CONFIG_SOC_XXX所对应编译出来的dtb文件。一般来说一个芯片下有好几个dtb文件,它们就是对应了不同的应用场景,需要根据需要选择一个来作为启动配置。如果需要添加自己的dts文件,除了新建一个dts文件以外,还要记得在这个Makefile中把自己的target dtb添加进去。
关于DTS设备定义的语法,也不是这篇文章的重点,请允许我跳过。

使用DTS启动系统

之前我并没有使用过DTS,对它也只有耳闻,所以开始移植的时候并没有想到这个部分。做好了kernel在uboot中手动引导启动后,我发现系统停在了Starting kernel ...这样的打印信息之后。经过搜索资料,我发现很有可能是因为uboot和kernel的机器号(Mechine ID)不匹配,为了确定这个问题,我在kernel config中打开low-level的调试选项:

Kernel hacking —>
|- (X) Kernel low-level debugging functions (read help!)
-|— Kernel low-level debugging port (Kernel low-level debugging… —>
—- |- ( ) Kernel low-level debugging messages via OMAP36XX UART4
—- |- ( ) Kernel low-level debugging messages via OMAP4/5 UART4
—- |- ( ) Kernel low-level debugging messages via TI81XX UART1
—- |- ( ) Kernel low-level debugging messages via TI81XX UART2
—- |- ( ) Kernel low-level debugging messages via TI81XX UART3
—- |- (X) Kernel low-level debugging messages via AM33XX UART1
|- (X) Early printk

记得选对UART驱动,不然是看不到打印的。这个驱动不是在kernel的drivers里面配置对了就可以的,driver里面的驱动要等基本的项目初始化后才会跑,这里指的是提前初始化UART以提供打印的驱动程序。
然后再次尝试,看打印信息果然是这个原因,但奇怪的是,在aviliable mechine的list里面,只有一个0xFFFFFFFF的mechine ID。我跟踪了很久的kernel和uboot代码,还是对此一筹莫展,直到再次搜索了类似的情况,发现有人说使用新版本的kernel才会出现这种情况,才想到了是没有使用DTS的原因。
于是我添加了对dts文件的地址指定:

fatload usb 0:1 0x82000000 am335x-boneblack.dtb
fatload usb 0:1 0x83000000 uImage
bootm 0x83000000 - 0x82000000

这次uboot将dtb加载并传递了地址给kernel,kernel能够看到所需要的各种初始化信息,终于顺利地将系统跑了起来。

编译后的文件location

至此,系统已经能够跑进console,剩下的就是引导和烧写问题了。编译后的相关文件location如下,在制作文件系统时需要将它们更新至软件包中:

  • kernel: ./boot/zImage
  • dts: ./boot/dst/XXX.dtb
  • firmware: ./lib/firmware/
  • module:/lib/modules/$KERNEL_VERSION