嵌入式Linux之我行——u-boot-2009.08在2440上的移植详解(六)嵌入式Linux之我行,主要讲述和总结了本人在学习嵌入式linux中的每个步骤。
一为总结经验,二希望能给想入门嵌入式Linux的朋友提供方便。
如有错误之处,谢请指正。
•共享资源,欢迎转载:一、移植环境•主机:VMWare--Fedora 9•开发板:Mini2440--64MB Nand,Kernel:2.6.30.4•编译器:arm-linux-gcc-4.3.2.tgz•u-boot:u-boot-2009.08.tar.bz2二、移植步骤上接:u-boot-2009.08在2440上的移植详解(五)10)u-boot利用tftp服务下载内核和利用nfs服务挂载nfs文件系统。
知识点:1.tftp服务的安装与配置及测试;2.nfs服务的安装与配置及测试;3.u-boot到kernel的参数传递(重点)。
我们知道使用tftp下载内核和使用nfs挂载文件系统的好处是,当我们重新编译内核或文件系统后不用重新把这些镜像文件再烧录到flash上,而是把这些镜像文件放到开发主机的tftp或nfs服务的主目录下,通过网络来加载他们,不用频繁的往flash上烧,这样一可以保护flash的使用寿命,二可以方便的调试内核或文件系统,提高开发效率。
可见,让u-boot实现这个功能是一件很有意义的事情。
实现这样的功能很简单,网上也有很多资料。
但有很多细节的东西如果稍不注意就导致失败,这里就结合本人实现的过程进行讲述和一些问题的分析。
•tftp服务的安装与配置及测试要使用tftp服务及测试它要安装两个软件包,一个就是tftp服务器,另外一个就是tftp客户端,这里安装客户端只是用于在主机本地测试tftp服务器是否正常运行的,来确保u-boot能够访问tftp服务(u-boot中已有tftp客户端的功能,其实在前面几篇中都已经使用了tftp下载内核或文件系统到开发板上,如果那里都做到了,这里就可以直接跳过)。
首先使用rpm命令查看你的主机上是否已经安装了tftp服务器和客户端,如果没有安装就去下载这两个软件包进行安装或者可以使用yum命令进行在线安装,yum会自动的去搜索适合你主机平台的最新软件包进行下载安装,如果主机已经安装了,则会提示软件包已经安装了最新的版本。
如下图所示:配置tftp服务器,主要是配置tftp的主目录及访问权限。
因tftp服务依赖于xinetd服务,所以一般tftp服务安装好后其配置文件一般会在/etc/xinetd.d/目录下:[root@localhost home]# vi /etc/xinetd.d/tftpservice tftp{disable =nosocket_type = dgramprotocol = udpwait= yesuser = rootserver =/usr/sbin/in.tftpdserver_args =-s /home/tftp-root -c //主要是修改这里,指定tftp服务器的主目录,-c选项是指可以创建文件per_source = 11cps = 100 2flags = IPv4}创建刚才指定的tftp服务器主目录,也要注意主目录的可读可写的权限:[root@localhost home]#mkdir /home/tftp-root[root@localhost home]#chmod 777 /home/tftp-root启动和测试tftp服务:[root@localhost home]#service xinetd restart //重启xinetd服务就会启动其下的所有服务,也包括tftp服务[root@localhost home]#service iptables stop //关闭防火墙[root@localhost home]#tftp 主机IP地址tftp>get 要下载的文件tftp>put 要上传的文件tftp>q[root@localhost home]#•nfs服务的安装与配置及测试以root的身份在控制台输入setup,在系统服务选项中选中nfs服务,如下图:配置NFS服务器的共享主目录,也要注意权限问题:[root@localhost home]# vi /etc/exports //如果没有这个文件就创建它,添加下面一行配置信息,注意格式一定要正确,否则导致服务不正常/home/filesystem *(rw,no_root_squash,sync)注释:“/home/filesystem”是NFS服务器的主目录,注意目录的权限“*”表示所有的IP都可以访问NFS主目录“rw”表示可读可写”no_root_squash“表示登入到NFS主机的用户如果是ROOT用户,他就拥有ROOT的权限“sync”表示同步[root@localhost home]# service nfs restart //重新启动NFS服务,使配置文件生效测试NFS服务是否正常。
将事先准备好的文件系统放到NFS主目录下,如下:[root@localhost home]# ls /home/filesystem/bin dev home lib mnt root sum100 tmp vardebug etc hostname linuxrc proc sbin sys usr[root@localhost home]#//在主机本地测试NFS服务,将NFS主目录下的文件系统挂载到/mnt目录下,192.168.1.101是主机的IP[root@localhost home]#mount -o nolock -t nfs 192.168.1.101:/home/filesystem /mnt可以看到/mnt目录下的内容和NFS主目录/home/filesystem下的内容完全一致,说明NFS 服务正常:•u-boot到kernel的参数传递我们知道,在kernel配置选项Boot options中有一个Default kernel command string参数项,而在u-boot参数中也有一个bootargs参数项,他们都是供内核启动用的,那他们又有什么区别呢,内核启动时到底是用哪一个呢?两种参数项分别如下图所示(kernel中的参数指定是从开发板Flash分区上挂载文件系统,u-boot中的参数指定的是从NFS挂载文件系统):实际上,内核中的参数项是内核默认提供的,在内核配置时去指定,而u-boot提供的则在u-boot启动时传递到内核中取代内核提供的参数。
所以当u-boot没有提供bootargs参数时,内核启动就是用内核配置时指定的参数,当u-boot提供了bootargs参数时就使用u-boot的参数。
那么,u-boot是如果将参数信息传递到内核中的呢?而内核又是怎么接收u-boot传递过来的参数呢?这就涉及到一点点ARM寄存器的知识了。
我们知道,ARM有7种工作模式和37个寄存器(31个通用寄存器和6个状态寄存器),如下图:ARM工作模式之间的转换就是利用这些寄存器进行,而u-boot参数的传递也利用了三个通用寄存器R0、R1和R2。
关于ARM工作模式和寄存器在这里就不做讲叙了,以后再讲,这里你就理解成u-boot在启动的时候把参数存放到这三个寄存器中,到内核启动时再把寄存器中的参数取出,当然,他们并不是就这样简单的操作。
下面我们看代码一一分析。
首先,我们来分析一下u-boot是怎样处理和发送要传递的参数,而u-boot要传递的参数又有哪些呢?除了我们最容易知道的bootargs(即内核commandline)参数项外,要传递的参数还有MACH_TYPE(即我们所说的机器码)、系统根设备信息(标志,页面大小)、内存信息(起始地址,大小)、RAMDISK信息(起始地址,大小)、压缩的RAMDISK根文件系统信息(起始地址,大小)。
由此可见要传递的参数很多,这时候,u-boot就提供一种叫做参数链表(tagged list)的方式把这些参数组织起来,链表结构体定义在:include/asm-arm/setup.h中,而实现链表的组织在lib_arm/bootm.c中:我们可以看到,链表的组织是由一系列函数实现,u-boot规定,链表必须以ATAG_CORE标记开始,以ATAG_NONE标记结束,中间就是一些参数标记项,这点从代码中可以体现出来。
那么在这些函数中有一个bd的参数是至关重要的,它是一个bd_info类型的结构体,定义在include/asm-arm/u-boot.h中,而这个结构体又被一个global_data类型的结构体所引用,定义在include/asm-arm/global_data.h中,如下:那么,那个bd参数到底是做什么用的呢?从定义中可以得知,bd记录了机器码、u-boot参数链表在内存中的地址等信息,那又问,它在什么地方进行记录的呢?它就在我们自己开发板初始化代码中记录的,如:board/samsung/my2440/my2440.c中注意:bd_t被gd_t所引用,而在global_data.h中我们可以看到,u-boot定义了一个gd_t的全局指针变量*gd,所以在这里就可以直接使用gd来设置bd了。
好了,我们还是接着分析这个参数链表是如何被传递的,组织参数链表的系列函数在一个叫do_bootm_linux的函数中被调用的,还是定义在lib_arm/bootm.c中从这个函数中我们可以看到,要使参数传递生效必须需要CONFIG_SETUP_MEMORY_TAGS和CONFIG_CMDLINE_TAG这两个宏的支持,所以需要在include/configs/my2440.h中定义它们。
原来我就是没定义它们,在使用NFS挂载文件系统时就出现问题。
同时,theKernel这个函数指针是u-boot参数传递的至关点,我们知道,函数在内存中执行的时候其实就是一个地址,而在代码中首先将这个函数指针指向kernel的入口地址,最后还将0、机器码和u-boot参数项在内存中的地址带给这个入口地址,故执行这个入口地址的时候即kernel启动的时候可以有这三个参数进行接收。
那么,这个入口地址(kernel启动地址或者说kernel入口地址)是怎么来的是谁指定的,又是多少呢?看代码,是从一个bootm_headers_t类型的结构体的成员ep取得的,而这个结构体是从调用do_bootm_linux的地方传递过来的。
bootm_headers_t定义在include/image.h 中,do_bootm_linux在common/cmd_bootm.c中被调用,如下:从代码中可以清楚的看到对bootm_headers_t的成员ep进行了赋值,但是还是不够直观这个入口地址到底是多少?只知道是使用image_get_ep函数从bootm_headers_t中的legacy_hdr_os_copy上取得的,那它在什么地方被赋值的呢?原来在image_set_ep函数中,定义在tools/mkimage.c中,如下:我们再想想,这个mkimage.c是做什么用的?原来是用它来制作u-boot格式的内核——uImage,还记得怎样使用mkimage来制作uImage吧,在“u-boot-2009.08在2440上的移植详解(四)”中讲到,如下:mkimage [-x]-A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image选项:-A:set architecture to 'arch'//用于指定CPU类型,比如ARM-O:set operating system to 'os'//用于指定操作系统,比如Linux-T:set image type to 'type'//用于指定image类型,比如Kernel-C:set compression type 'comp'//指定压缩类型-a:set load address to 'addr'(hex)//指定image的载入地址-e:set entry point to 'ep'(hex)//内核的入口地址,一般为image的载入地址+0x40(信息头的大小)-n:set image name to 'name'//image在头结构中的命名-d:use image data from 'datafile'//无头信息的image文件名-x:set XIP (execute in place)//设置执行位置例如:mkimage -n 'linux-2.6.30.4'-A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -d zImage uImage.img呵呵,相信此时的你拨云见日,茅塞顿开了吧!这个入口地址就是0x30008000,这也正是为什么u-boot一定要使用uImage的格式来启动内核的原因之一。