嵌入式Linux实验手册——基于ARM9 S3C2410实验平台.doc

2019-07-12 17:25发布

  嵌入式Linux实验手册 基于ARM9 S3C2410 实验平台   1. 实验概述... 1 2. 实验环境配置... 1 2.1 开发主机配置... 1 2.2 实验板介绍... 1 2.3 实验板配置准备... 4 3. Linux预备知识... 5 3.1 Linux常用命令... 5 3.2 Makefile的使用常识... 8 3.3 嵌入式Linux系统引导过程... 12 3.4 U-boot启动过程... 13 3.5 Linux内核启动过程... 22 4. 实验内容... 31 4.1 建立交叉开发环境... 31 4.2 编译调试应用程序... 34 4.3 移植u-boot 36 4.4 编译Linux内核... 39 4.5 移植Linux内核... 47 4.6 调试Linux内核... 50 4.7 制作Linux文件系统... 52 4.8 部署Linux系统... 55

1. 实验概述

对于嵌入式系统,目标板一般只有很小的存储空间,处理器频率也很低。而且没有可以预装的Linux系统,直接在这样的硬件上建立Linux系统非常困难。嵌入式Linux交叉开发环境可以很好地解决这个难题。 所谓交叉开发,就是在开发主机上编辑编译源程序,在目标板上运行可执行程序。通常通过以太网接口传输Linux内核影像到目标板内存,让目标板的Linux挂接NFS的文件系统。这样的交叉开发环境可以非常方便地进行嵌入式Linux开发调试以及集成。 本实验以S3C2410 ARM920T处理器的实验板为例,建立嵌入式Linux交叉开发环境,完成嵌入式Linux开发的全过程。 通过本实验,可以掌握嵌入式Linux基本开发流程,熟悉u-boot、Linux内核、应用程序以及Linux文件系统的配置开发。从而能够在具体的工程项目中应用嵌入式Linux系统。

2. 实验环境配置

2.1 开发主机配置

建议开发主机硬件配置高一些,这样编译的速度快一些,尤其是编译Linux内核。 推荐使用X86 PC配置: 主频:>1GHz 内存:>256MB 安装Redhat Linux 9操作系统作为开发环境。可以在PC上安装Windows和Linux双操作系统,也可以在Windows安装vmware。 安装过程中,建议完全安装所有软件包,这样可以使用Redhat Linux提供的一些Linux工具和服务,方便开发。如果磁盘存储空间有限,安装过程可以附加选择一些软件包,或者Linux启动后再安装这些rpm包。 这些软件包包括:tftp, tftp-server等。 在开发时所需其他服务,请参考手册试验部分4.1内容:建立交叉开发环境。

2.2 实验板介绍

实验板的硬件特点:
  • SAMSUNG ARM9 S3C2410处理器,主频可达203MHz。
  • 64MB SDRAM,有2片K4S561632构成,工作在32位模式
  • 2MB NOR FLASH,型号为SST39VF1601,工作在16位模式
  • 64MB NAND FLASH,型号为K9F1208,可以兼容16MB、32MB和128MB
  • 通过跳线可以设置系统从NOR FLASH或者NAND FLASH启动。
  • 10M以太网接口,采用CS8900Q3芯片,带传输和连接状态指示灯
  • LCD和触摸屏接口。
  • 二个USB HOST接口,遵守USB1.1标准。
  • 一个USB Device接口,遵守USB1.1标准
  • Audio音频接口:音频模块由S3C2410的IIS音频总线和UDA1341音频编解码器组成,板上带一个麦克。
  • 2路RS232接口,波特率可达115200bps。
  • RTC接3V锂电池供电
  • SD卡接口,兼容SD Memory Card 1.0和SDIO Card Protocol 1.0
  • I2C接口的EEPROM:可通过CPU的I2C接口实现对EEROM中的数据读写,数据掉电不丢失。
  • 提供国际标准20针ICE JTAG接口,提供配套的Flash编程下载线。
  • 16个按键
  • 蜂鸣器,4个LED
  • 开关电源

2.3 实验板配置准备

  • 实验板与开发主机之间连接串口线,以太网线和JTAG接口线。
表2.3.1 连线接口说明 连接线 实验板 开发主机 串口线 UART1 串口 以太网线 10M网口 网口 JTAG电缆线 JTAG接口 并口 注意:切勿带电拔插JTAG电缆和并口线,否则很容易损坏芯片。
  • 设置启动方式,从NOR FLASH启动。
核心板上的跳线JP1:连接短路线插,设置为NAND FLASH启动; 断开短路插,设置为NOR FLASH启动。 注意:断开短路插时,要把短路插子插在一个脚上,不要取下,防止丢失。
  • 烧写FS2410BIOS到FLASH
使用sjf2410.exe程序把FS2410_BIOS_I.bin烧写到FLASH中: 1) 复制“Flash烧写工具”目录到主机上,双击“安装驱动.exe”,安装GIVEIO驱动。 2) 连接JTAG接口和并口线。20针扁平电缆连接JTAG接口和JTAG小板的JP3,并口连接主机和JTAG小板。确认核心板上的JP1调线断开。 3) 进入“Flash烧写工具”目录,双击执行sjf2410_bios.bat,显示烧写提示信息 4) 根据烧写提示信息,选择3:SST39VF160 Prog,再输入Input target offset: 0,开始烧写通过并口烧写到NOR FLASH 5)烧写完成后,复位即可启动BIOS 具体参考光盘提供的FS2410用户手册和烧写文档: 2.3节 FS2410的BIOS功能说明 2.6节 用SJF2410工具将BIOS烧写到NAND FLASH 2.7节 用SJF2410工具将BIOS烧写到NOR FLASH FLASH烧写说明文档_sjf2410_v4.pdf
  • 烧写u-boot到FLASH
通过BIOS烧写u-boot.bin到NOR FLASH。 目标板上电,BIOS启动,在DNW显示启动信息并提示命令选项。 通过串口下载影像文件,选择1: Uart download file,输入1,显示等待串口接收传输文件。 打开DNW的Serial Port菜单,选择发送文件:Tx file 在对话框中浏览选择u-boot.bin的文件,确认后,开始传输,下载到目标板的SDRAM中缓存。 传输完成,提示是否立刻执行,输入n,不立刻执行 然后,回到显示命令选项,选择5: Write NOR FLASH with download file,SDRAM中内容就自动烧写到NOR FLASH的0地址了。 烧写完成后,复位实验板,串口终端应该显示出现u-boot的启动信息。

3. Linux预备知识

3.1 Linux常用命令

       本课程要求学员对Linux基本操作命令有一定了解和掌握。下面列出的一些常用命令作为参考。最好针对每一个都能亲自练习、掌握。 ---------------------------------------------------------------------- ls                  以默认方式显示当前目录文件列表
ls –a              显示所有文件包括隐藏文件
ls –l              显示文件属性,包括大小,日期,符号连接,是否可读写及是否可执行
----------------------------------------------------------------------
cd dir            切换到当前目录下的dir目录
cd ..              切换到到上一级目录
cd ~              切换到用户目录,比如是root用户,则切换到/root下
----------------------------------------------------------------------
rm file           删除某一个文件
rm -rf dir              删除当前目录下叫dir的整个目录
----------------------------------------------------------------------
cp source target                          将文件source 复制为 target
cp –av soure_dir target_dir          将整个目录复制,两目录完全一样
cp –fr source_dir target_dir         将整个目录复制,并且是以非链接方式复制,当source目录带有符号链接时,两个目录不相同
----------------------------------------------------------------------
mv source target                         将文件或者目录source更名为target
----------------------------------------------------------------------
diff dir1 dir2                              比较目录1与目录2的文件列表是否相同,但不比较文件的实际内容,不同则列出
diff file1 file2                            比较文件1与文件2的内容是否相同,如果是文本格式的文件,则将不相同的内容显示,如果是二进制代码则只表示两个文件是不同的
----------------------------------------------------------------------
echo message                              显示一串字符
cat file                                       显示文件的内容,和DOS的type相同
cat file | more                             显示文件的内容并传输到more程序实现分页显示,使用命令less file可实现相同的功能
more             分页命令,一般通过管道将内容传给它,如ls | more
----------------------------------------------------------------------
eject              umout掉CDROM并将光碟弹出,但cdrom不能处于busy的状态,否则无效
----------------------------------------------------------------------
du                               计算当前目录的容量
du -sm /root                 计算/root目录的容量并以M为单位
find -name /path file     在/path目录下查找看是否有文件file
grep -ir “chars”            在当前目录的所有文件查找字串chars,并忽略大小写,-i为大小写,-r为下一级目录
----------------------------------------------------------------------
vi file                          编辑文件file
vi原基本使用及命令:
vi分为编辑状态和命令状态。输入命令要先按ESC,退出编辑状态, 然后输入命令。 常用命令有: :x(退出) :x!(退出不保存) :w(保存文件) :w!(不询问方式写入文件) :r file(读文件file) :%s/oldchars/newchars/g(将所有字串oldchars换成newchars) i进入编辑插入状态 ESC退出编辑状态 ----------------------------------------------------------------------
man ls           读取关于ls命令的帮助
----------------------------------------------------------------------
reboot           重新启动计算机
halt                      关闭计算机
init 0             关闭所有应用程序和服务,进入纯净的操作环境
init 1             重新启动应用及服务
init 6             重新启动计算机
----------------------------------------------------------------------
tar xfzv file.tgz     将文件file.tgz解压
tar -zcvf file.tgz    将文件或目录压缩为file.tgz
gzip directory.tar          将覆盖原文件生成压缩的 directory.tar.gz
gunzip directory.tar.gz   覆盖原文件解压生成不压缩的 directory.tar。
----------------------------------------------------------------------
dmesg           显示kernle启动及驱动装载信息
uname -a        显示操作系统的类型
----------------------------------------------------------------------
strings file     显示file文件中的ASCII字符内容
---------------------------------------------------------------------
rpm -ihv program.rpm 安装程序program并显示安装进程
----------------------------------------------------------------------
su root                         切换到超级用户
chmod a+x file             将file文件设置为可执行,脚本类文件一定要这样设置一个,否则得用bash file才能执行
chmod 666 file             将文件file设置为可读写
chown user /dir            将/dir目录设置为user所有
----------------------------------------------------------------------
mknod /dev/hda1 b 3 1 创建块设备hda1,主设备号为3,从设备号为1,即master硬盘的的第一个分区
mknod /dev/tty1 c 4 1   创建字符设备tty1,主设备号为4,众设备号为1,即第一个tty终端
----------------------------------------------------------------------
touch /tmp/running       在/tmp下创建一个临时文件running,重新启动后消失
----------------------------------------------------------------------
fdisk /dev/hda                            就像执行了dos的fdisk一样
mount -t ext2 /dev/hda1 /mnt       把/dev/hda1装载到 /mnt目录
df                                             显示文件系统装载的相关信息
mount -t nfs 192.168.1.1:/sharedir /mnt 将nfs服务的共享目录sharedir加载到/mnt/nfs目录
umount /mnt         将/mnt目录卸载,/mnt目录必须处于空闲状态
sync                    刷新缓冲区,使内容与磁盘同步,
mkfs /dev/hda1      格式化/dev/hda1为ext2格式
----------------------------------------------------------------------
lilo                              运行lilo程序,程序自动查找/etc/lilo.conf并按该配置生效
lilo -C /root/lilo.conf    lilo程序按/root/lilo.conf配置生效
----------------------------------------------------------------------
dd if=/dev/fd0 of=floppy.fd         将软盘的内容复制成一个镜像
dd if=/dev/zero of=root.ram bs=1024,count=1024  生成一个大小为1M的块设备,可以把它当作硬盘的一个分区来用
----------------------------------------------------------------------
gcc hello.c -o hello                     将hello.c编译成名为hello的二进制执行文件
ldd program                               显示程序所使用了哪些库
----------------------------------------------------------------------
ps                        显示当前系统进程信息
ps –ef                   显示系统所有进程信息
kill -9 500            将进程编号为500的程序杀死
killall -9 netscape 将所有名字为netscape的程序杀死,kill不是万能的,对僵死的程序则无效。
top                显示系统进程的活动情况,按占CPU资源百分比来分
free               显示系统内存及swap使用情况
time program        在program程序结束后,将计算出program运行所使用的时间
----------------------------------------------------------------------
chroot .         将根目录切换至当前目录,调试新系统时使用
----------------------------------------------------------------------
ifconfig eth0 192.168.1.1 netmask 255.255.255.0      设置网卡1的地址192.168.1.1,掩码为255.255.255.0,不写netmask参数则默认为255.255.255.0
ifconfig eth0:0 192.168.1.2 捆绑网卡1的第二个地址为192.168.1.2
----------------------------------------------------------------------
telnet 192.168.1.1         登陆IP为192.168.1.1的telnet服务器
ftp 192.168.1.1            登陆到ftp服务器

3.1.2 GUN工具链常识

一般在完全安装的Redhat Linux上都会自动按照Linux GUN Toolchains。在/usr/bin目录下容易找到gcc,ld等工具。 为了对GUN工具链有一个初步印象,这里安排一个课余作业,在预习时完成。 课余作业: 通过编译一个简单的hello world对Linux编译开发环境有一个直观印象,学习使用编译工具。 1) 在主机上, 写好简单的“hello world” 例子程序。 # include main(int argc, char **argv) { int i; for ( i=0; i<10; I++) { printf("hello i=%d ", i);   } return 0; } 2) 编译可执行程序. $ gcc -o hello hello.c 3)使用如下命令运行程序: # ./hello 可以看到控制台有”hello world”输出。

3.2 Makefile的使用常识

下面将介绍怎样编写、编译一个复杂点的应用程序。 多个代码文件的复杂应用可以通过Makefiel来进行管理。实际上内核就是一个由多个文件组成的程序,也是通过多级的Makefile进行管理,最终通过编译、链接,生成一个内核映象。在理解后面内核编译过程之前需要先了解Makefile的使用常识。 make命令执行时,需要一个 Makefile 文件,以告诉make命令需要怎么样的去编译和链接程序。 l  Makefile里有什么? Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。 1、显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。 2、隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。 3、变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。 4、文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。 5、注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:“#”。 还值得一提的是,在Makefile中的命令,必须要以[Tab]键开始。 l  Makefile的文件名 默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件,找到了解释这个文件。在这三个文件名中,最好使用“Makefile”这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。最好不要用“GNUmakefile”,这个文件是GNU的make识别的。有另外一些make只对全小写的“makefile”文件名敏感,但是基本上来说,大多数的make都支持“makefile”和“Makefile”这两种默认文件名。 当然,你可以使用别的文件名来书写Makefile,比如:“Make.Linux”,“Make.Solaris”,“Make.AIX”等,如果要指定特定的Makefile,你可以使用make的“-f”和“--file”参数,如:make -f Make.Linux或make --file Make.AIX。 l  引用其它的Makefile 在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include的语法是: include filename可以是当前操作系统Shell的文件模式(可以保含路径和通配符) 在include前面可以有一些空字符,但是绝不能是[Tab]键开始。include和可以用一个或多个空格隔开。举个例子,你有这样几个Makefile:a.mk、b.mk、c.mk,还有一个文件叫foo.make,以及一个变量$(bar),其包含了e.mk和f.mk,那么,下面的语句: include foo.make *.mk $(bar) 等价于: include foo.make a.mk b.mk c.mk e.mk f.mk make命令开始时,会把找寻include所指出的其它Makefile,并把其内容安置在当前的位置。就好像C/C++的#include指令一样。如果文件都没有指定绝对路径或是相对路径的话,make会在当前目录下首先寻找,如果当前目录下没有找到,那么,make还会在下面的几个目录下找: 1、如果make执行时,有“-I”或“--include-dir”参数,那么make就会在这个参数所指定的目录下去寻找。
2、如果目录/include(一般是:/usr/local/bin或/usr/include)存在的话,make也会去找。 如果有文件没有找到的话,make会生成一条警告信息,但不会马上出现致命错误。它会继续载入其它的文件,一旦完成makefile的读取,make会再重试这些没有找到,或是不能读取的文件,如果还是不行,make才会出现一条致命信息。如果你想让make不理那些无法读取的文件,而继续执行,你可以在include前加一个减号“-”。如: -include 其表示,无论include过程中出现什么错误,都不要报错继续执行。和其它版本make兼容的相关命令是sinclude,其作用和这一个是一样的。 l  环境变量 MAKEFILES 如果你的当前环境中定义了环境变量MAKEFILES,那么,make会把这个变量中的值做一个类似于include的动作。这个变量中的值是其它的Makefile,用空格分隔。只是,它和include不同的是,从这个环境变中引入的Makefile的“目标”不会起作用,如果环境变量中定义的文件发现错误,make也会不理。 但是在这里我还是建议不要使用这个环境变量,因为只要这个变量一被定义,那么当你使用make时,所有的Makefile都会受到它的影响,这绝不是你想看到的。在这里提这个事,只是为了告诉大家,也许有时候你的Makefile出现了怪事,那么你可以看看当前环境中有没有定义这个变量。 l  make的工作方式 GNU的make工作时的执行步骤入下:(想来其它的make也是类似)     1、读入所有的Makefile。
    2、读入被include的其它Makefile。
    3、初始化文件中的变量。
    4、推导隐晦规则,并分析所有规则。
    5、为所有的目标文件创建依赖关系链。
    6、根据依赖关系,决定哪些目标要重新生成。
    7、执行生成命令。 1-5步为第一个阶段,6-7为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么,make会把其展开在使用的位置。但make并不会完全马上展开,make使用的是拖延战术,如果变量出现在依赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。 l  书写规则 规则包含两个部分,一个是依赖关系,一个是生成目标的方法。 在Makefile中,规则的顺序是很重要的,因为,Makefile中只应该有一个最终目标,其它的目标都是被这个目标所连带出来的,所以一定要让make知道你的最终目标是什么。一般来说,定义在Makefile中的目标可能会有很多,但是第一条规则中的目标将被确立为最终的目标。如果第一条规则中的目标有很多个,那么,第一个目标会成为最终的目标。make所完成的也就是这个目标。 书写规则举例: foo.o : foo.c defs.h       # foo模块
cc -c -g foo.c 看到这个例子,各位应该不是很陌生了,前面也已说过,foo.o是我们的目标,foo.c和defs.h是目标所依赖的源文件,而只有一个命令“cc -c -g foo.c”(以Tab键开头)。这个规则告诉我们两件事: 1、文件的依赖关系,foo.o依赖于foo.c和defs.h的文件,如果foo.c和defs.h的文件日期要比foo.o文件日期要新,或是foo.o不存在,那么依赖关系发生。
2、如果生成(或更新)foo.o文件。也就是那个cc命令,其说明了,如何生成foo.o这个文件。(当然foo.c文件include了defs.h文件) l  规则的语法 targets : prerequisites
command
... command是命令行,如果其不与“target:prerequisites”在一行,那么,必须以[Tab键]开头,如果和prerequisites在一行,那么可以用分号做为分隔。 prerequisites也就是目标所依赖的文件(或依赖目标)。如果其中的某个文件要比目标文件要新,那么,目标就被认为是“过时的”,被认为是需要重生成的。这个在前面已经讲过了。 如果命令太长,你可以使用反斜框(‘’)作为换行符。make对一行上有多少个字符没有限制。规则告诉make两件事,文件的依赖关系和如何成成目标文件。 一般来说,make会以UNIX的标准Shell,也就是/bin/sh来执行命令。 l  在规则中使用通配符 如果我们想定义一系列比较类似的文件,我们很自然地就想起使用通配符。make支持三各通配符:“*”,“?”和“[...]”。这是和Unix的B-Shell是相同的。 波浪号(“~”)字符在文件名中也有比较特殊的用途。如果是“~/test”,这就表示当前用户的$HOME目录下的test目录。而“~hchen/test”则表示用户hchen的宿主目录下的test目录。(这些都是Unix下的小知识了,make也支持)而在Windows或是MS-DOS下,用户没有宿主目录,那么波浪号所指的目录则根据环境变量“HOME”而定。 通配符代替了你一系列的文件,如“*.c”表示所以后缀为c的文件。一个需要我们注意的是,如果我们的文件名中有通配符,如:“*”,那么可以用转义字符“”,如“*”来表示真实的“*”字符,而不是任意长度的字符串。 还是先来看几个例子吧:     clean:
         rm -f *.o 上面这个例子我不不多说了,这是操作系统Shell所支持的通配符。这是在命令中的通配符。    objects = *.o 上面这个例子,表示了,通符同样可以用在变量中。并不是说[*.o]会展开,不!objects的值就是“*.o”。Makefile中的变量其实就是C/C++中的宏。如果你要让通配符在变量中展开,也就是让objects的值是所有[.o]的文件名的集合,那么,你可以这样: objects := $(wildcard *.o) 这种用法由关键字“wildcard”指出,wildcard是Makefile的关键字。 课余作业: 随便找一个Linux下的应用程序,提供源代码,tar.gz压缩格式的软件包。用上节介绍的命令解压。读该软件根目录下的Makefile文件。

3.3 嵌入式Linux系统引导过程

嵌入式Linux内核通常需要目标板上的固件引导。这些引导程序就是bootloader,在目标板上电的时候,完成板级初始化和Linux内核引导的任务。U-boot是一种常用的bootloader,本实验就是通过u-boot来引导的。 一般来说,嵌入式Linux系统启动过程都经过3个阶段,如下图所示: 嵌入式Linux系统启动过程 板子上电以后,首先执行bootloader,bootloader负责把Linux内核影像加载或者解压到RAM中。如果有ramdisk的话,也在这个阶段解压到RAM中。然后bootloader把控制权交给Linux内核。 Linux内核开始执行,初始化内存和硬件设备,挂接根文件系统,然后执行/sbin/init。启动第一个用户进程init,开始Linux用户空间的初始化过程。通常Linux内核影像也包含一段Linux bootloader的代码,完成zImage自解压功能。但是这不能完全替代bootloader固件。 Init进程根据inittab,执行系统初始化脚本,启动网络服务和X-windows等,并且管理用户登录。 这样Linux系统就完全启动起来了。

3.4 U-boot启动过程

S3C2410EP实验板上电后,执行u-boot的第一条指令,顺序执行下列函数或者子程序,本手册中以u-boot-1.1.2为例: _start:   --> reset:               --> cpu_init_crit --> memsetup               --> relocate:               --> stack_setup:               --> start_armboot( )  --> init_sequence[] --> … --> main_loop()        这样u-boot就可以执行go或者bootm命令,引导Linux内核启动了。详细分析一下启动过程代码。 l  cpu/arm920t/start.S _start:     b       reset        ldr   pc, _undefined_instruction        ldr   pc, _software_interrupt        ldr   pc, _prefetch_abort        ldr   pc, _data_abort        ldr   pc, _not_used        ldr   pc, _irq        ldr   pc, _fiq …  /* the actual reset code  */ reset:        /* set the cpu to SVC32 mode       */        mrs  r0,cpsr        bic   r0,r0,#0x1f        orr   r0,r0,#0xd3        msr  cpsr,r0 /* turn off the watchdog */          /*         * we do sys-critical inits only at reboot,         * not when booting from ram!         */ #ifdef CONFIG_INIT_CRITICAL        bl    cpu_init_crit #endif   relocate:                                    /* relocate U-Boot to RAM   */        adr   r0, _start                      /* r0 <- current position of code */        ldr   r1, _TEXT_BASE         /* test if we run from flash or RAM */        cmp     r0, r1           /* don't reloc during debug  */        beq     stack_setup          ldr   r2, _armboot_start        ldr   r3, _bss_start        sub  r2, r3, r2               /* r2 <- size of armboot   */        add  r2, r0, r2               /* r2 <- source end address */   copy_loop:        ldmia      r0!, {r3-r10}         /* copy from source address [r0] */        stmia       r1!, {r3-r10}         /* copy to   target address [r1] */        cmp r0, r2                           /* until source end addreee [r2] */        ble   copy_loop          /* Set up the stack                                         */ stack_setup:        ldr   r0, _TEXT_BASE                       /* upper 128 KiB: relocated uboot */        sub  r0, r0, #CFG_MALLOC_LEN      /* malloc area    */        sub  r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo   */ #ifdef CONFIG_USE_IRQ        sub  r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif        sub  sp, r0, #12             /* leave 3 words for abort-stack */ clear_bss:        ldr   r0, _bss_start         /* find start of bss segment */        ldr   r1, _bss_end          /* stop here   */        mov       r2, #0x00000000           /* clear */   clbss_l:str       r2, [r0]           /* clear loop...  */        add  r0, r0, #4        cmp r0, r1        bne  clbss_l          ldr   pc, _start_armboot   _start_armboot:      .word start_armboot   cpu_init_crit:        bl    memsetup   l  board/smdk2410/memsetup.S .globl memsetup memsetup:        /* memory control configuration */        /* make r0 relative the current location so that it */        /* reads SMRDATA out of FLASH rather than memory ! */        ldr     r0, =SMRDATA        ldr   r1, _TEXT_BASE        sub  r0, r0, r1        ldr   r1, =BWSCON      /* Bus Width Status Controller */        add     r2, r0, #13*4 0:        ldr     r3, [r0], #4        str     r3, [r1], #4        cmp     r2, r0        bne     0b          /* everything is fine now */        mov pc, lr   l  lib_arm/board.c void start_armboot (void) {        DECLARE_GLOBAL_DATA_PTR;        ulong size;        init_fnc_t **init_fnc_ptr;        char *s;        /* Pointer is writable since we allocated a register for it */        gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));        /* compiler optimization barrier needed for GCC >= 3.4 */        __asm__ __volatile__("": : :"memory");          memset ((void*)gd, 0, sizeof (gd_t));        gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));        memset (gd->bd, 0, sizeof (bd_t));          monitor_flash_len = _bss_start - _armboot_start;          for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {               if ((*init_fnc_ptr)() != 0) {                      hang ();               }        }          /* configure available FLASH banks */        size = flash_init ();        display_flash_config (size);          /* armboot_start is defined in the board-specific linker script */        mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);          /* initialize environment */        env_relocate ();          /* IP Address */        gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");          /* MAC Address */ …        devices_init ();       /* get the devices list going. */        jumptable_init ();        console_init_r ();    /* fully init console as a device */        /* enable exceptions */        enable_interrupts ();        /* Initialize from environment */        if ((s = getenv ("loadaddr")) != NULL) {               load_addr = simple_strtoul (s, NULL, 16);        }        /* main_loop() can return to retry autoboot, if so just run it again. */        for (;;) {               main_loop ();        }        /* NOTREACHED - no way out of command loop except booting */ }   init_fnc_t *init_sequence[] = {        cpu_init,               /* basic cpu dependent setup */   --cpu/arm920t/cpu.c        board_init,           /* basic board dependent setup */  --board/smdk2410/smdk2410.c        interrupt_init,      /* set up exceptions */        --cpu/arm920t/s3c24x0/interrupt.c        env_init,               /* initialize environment */         --common/cmd_flash.c        init_baudrate,      /* initialze baudrate settings */     --lib_arm/board.c        serial_init,            /* serial communications setup */        --cpu/arm920t/s3c24x0/serial.c        console_init_f,      /* stage 1 init of console */               --common/console.c        display_banner,    /* say that we are here */            --lib_arm/board.c        dram_init,            /*configure available RAM banks */ --board/smdk2410/smdk2410.c        display_dram_config,                                            --lib_arm/board.c        NULL, }; main_loop() { }  -- common/main.c   go 命令引导Linux内核的代码 l  common/cmd_boot.c int do_go (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) {        ulong      addr, rc;        int     rcode = 0;        if (argc < 2) {               printf ("Usage: %s ", cmdtp->usage);               return 1;        }        addr = simple_strtoul(argv[1], NULL, 16);        printf ("## Starting application at 0x%08lX ... ", addr);        /*         * pass address parameter as argv[0] (aka command name),         * and all remaining args         */        rc = ((ulong (*)(int, char *[]))addr) (--argc, &argv[1]);        if (rc != 0) rcode = 1;          printf ("## Application terminated, rc = 0x%lX ", rc);        return rcode; } U_BOOT_CMD(        go, CFG_MAXARGS, 1,      do_go,        "go      - start application at address 'addr' ",        "addr [arg ...]     - start application at address 'addr' "        "      passing 'arg' as arguments " );   bootm命令引导Linux内核的部分代码 l  common/cmd_bootm.c int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) {        ulong      iflag;        ulong      addr;        ulong      data, len, checksum;        ulong  *len_ptr;        uint  unc_len = 0x400000;        int   i, verify;        char *name, *s;        int   (*appl)(int, char *[]);        image_header_t *hdr = &header;          s = getenv ("verify");        verify = (s && (*s == 'n')) ? 0 : 1;        if (argc < 2) {               addr = load_addr;        } else {               addr = simple_strtoul(argv[1], NULL, 16);        }        SHOW_BOOT_PROGRESS (1);        printf ("## Booting image at %08lx ... ", addr);        /* Copy header so we can blank CRC field for re-calculation */        memmove (&header, (char *)addr, sizeof(image_header_t));        if (ntohl(hdr->ih_magic) != IH_MAGIC)        {               puts ("Bad Magic Number ");               SHOW_BOOT_PROGRESS (-1);               return 1;        }        SHOW_BOOT_PROGRESS (2);        data = (ulong)&header;        len  = sizeof(image_header_t);          checksum = ntohl(hdr->ih_hcrc);        hdr->ih_hcrc = 0;          if (crc32 (0, (char *)data, len) != checksum) {               puts ("Bad Header Checksum ");               SHOW_BOOT_PROGRESS (-2);               return 1;        }        SHOW_BOOT_PROGRESS (3);          /* for multi-file images we need the data part, too */        print_image_hdr ((image_header_t *)addr);          data = addr + sizeof(image_header_t);        len  = ntohl(hdr->ih_size);        if (verify) {               puts ("   Verifying Checksum ... ");               if (crc32 (0, (char *)data, len) != ntohl(hdr->ih_dcrc)) {                      printf ("Bad Data CRC ");                      SHOW_BOOT_PROGRESS (-3);                      return 1;               }               puts ("OK ");        }        SHOW_BOOT_PROGRESS (4);          len_ptr = (ulong *)data; ……        switch (hdr->ih_os) {        default:                 /* handled by (original) Linux case */        case IH_OS_LINUX:            do_bootm_linux  (cmdtp, flag, argc, argv,                           addr, len_ptr, verify);            break;        …… } U_BOOT_CMD(       bootm,    CFG_MAXARGS, 1,    do_bootm,       "bootm   - boot application image from memory ",       "[addr [arg ...]]     - boot application image stored in memory "       " passing arguments 'arg ...'; when booting a Linux kernel, "       " 'arg' can be the address of an initrd image " );   l  lib_arm/armlinux.c void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],                    ulong addr, ulong *len_ptr, int verify) {        DECLARE_GLOBAL_DATA_PTR;        ulong len = 0, checksum;        ulong initrd_start, initrd_end;        ulong data;        void (*theKernel)(int zero, int arch, uint params);        image_header_t *hdr = &header;        bd_t *bd = gd->bd;   #ifdef CONFIG_CMDLINE_TAG        char *commandline = getenv ("bootargs"); #endif        theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);        /*         * Check if there is an initrd image         */        if (argc >= 3) {               SHOW_BOOT_PROGRESS (9);               addr = simple_strtoul (argv[2], NULL, 16);               printf ("## Loading Ramdisk Image at %08lx ... ", addr);               /* Copy header so we can blank CRC field for re-calculation */               memcpy (&header, (char *) addr, sizeof (image_header_t));               if (ntohl (hdr->ih_magic) != IH_MAGIC) {                      printf ("Bad Magic Number ");                      SHOW_BOOT_PROGRESS (-10);                      do_reset (cmdtp, flag, argc, argv);               }               data = (ulong) & header;               len = sizeof (image_header_t);               checksum = ntohl (hdr->ih_hcrc);               hdr->ih_hcrc = 0;               if (crc32 (0, (char *) data, len) != checksum) {                      printf ("Bad Header Checksum ");                      SHOW_BOOT_PROGRESS (-11);                      do_reset (cmdtp, flag, argc, argv);               }               SHOW_BOOT_PROGRESS (10);               print_image_hdr (hdr);               data = addr + sizeof (image_header_t);               len = ntohl (hdr->ih_size);               if (verify) {                      ulong csum = 0;                      printf ("   Verifying Checksum ... ");                      csum = crc32 (0, (char *) data, len);                      if (csum != ntohl (hdr->ih_dcrc)) {                             printf ("Bad Data CRC ");                             SHOW_BOOT_PROGRESS (-12);                             do_reset (cmdtp, flag, argc, argv);                      }                      printf ("OK ");               }               SHOW_BOOT_PROGRESS (11);               if ((hdr->ih_os != IH_OS_LINUX) ||                   (hdr->ih_arch != IH_CPU_ARM) ||                   (hdr->ih_type != IH_TYPE_RAMDISK)) {                      printf ("No Linux ARM Ramdisk Image ");                      SHOW_BOOT_PROGRESS (-13);                      do_reset (cmdtp, flag, argc, argv);               }               /*                * Now check if we have a multifile image                */        } else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) {               ulong tail = ntohl (len_ptr[0]) % 4;               int i;               SHOW_BOOT_PROGRESS (13);               /* skip kernel length and terminator */               data = (ulong) (&len_ptr[2]);               /* skip any additional image length fields */               for (i = 1; len_ptr[i]; ++i)                      data += 4;               /* add kernel length, and align */               data += ntohl (len_ptr[0]);               if (tail) {                      data += 4 - tail;               }               len = ntohl (len_ptr[1]);        } else {               /*                * no initrd image                */               SHOW_BOOT_PROGRESS (14);               len = data = 0;        }        if (data) {               initrd_start = data;               initrd_end = initrd_start + len;        } else {               initrd_start = 0;               initrd_end = 0;        }        SHOW_BOOT_PROGRESS (15);        debug ("## Transferring control to Linux (at address %08lx) ... ",               (ulong) theKernel);   #if defined (CONFIG_SETUP_MEMORY_TAGS) ||     defined (CONFIG_CMDLINE_TAG) ||     defined (CONFIG_INITRD_TAG) ||     defined (CONFIG_SERIAL_TAG) ||     defined (CONFIG_REVISION_TAG) ||     defined (CONFIG_LCD) ||     defined (CONFIG_VFD)        setup_start_tag (bd); #ifdef CONFIG_SERIAL_TAG        setup_serial_tag (¶ms); #endif #ifdef CONFIG_REVISION_TAG        setup_revision_tag (¶ms); #endif #ifdef CONFIG_SETUP_MEMORY_TAGS        setup_memory_tags (bd