arm linux启动

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Uboot
Universal Boot Loader
分析cpu/arm920t/start.s文件
打开cpu/arm920t/start.s文件
s3c2440复位之后,pc指针会指向0x0地址。

在u-boot代码中,该0x0地址是一个向量表,第一条指令跳转branch到复位代码
start_code。

位于cpu/arm920t/start.S汇编语言文件最开始的地方: .globl _start /*定义一个全局变量,_start,也就是程序的入口点*/ _start: bstart_code /*程序入口点是一条跳转指令,跳转到
start_code 入口执行*/
/*定义中断向量表*/
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
按照上面定义的异常模式,当发生异常时,执行
cpu/arm920t/interrupts.c中定义的中断处理函数。

复位指令跳转之后标号为start_code处开始执行,开始执行
arm920t处理器的基本初始化。

首先修改当前程序状态寄存器CPSR,使处理器进入Supervisor|32 bit ARM模式,
并关闭ARM9TDMI中断和快速中断,这是通过设置CPSR相应掩码实现的:
start_code: /* * set the cpu to SVC32 mode */ mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
紧接着,将S3C2410特有的WTCON寄存器清零,此举仅为关闭看门狗,因为系统启动时一般不需要看门狗的支持。

ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
然后将S3C2410中断控制器INTMSK寄存器置为全1,INTSUBMSK置为0x7ff,禁止全部中断源。

S3C2410手册中对此有详细描述:
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442) || \ defined(CONFIG_S3C2443) ldr r1, =INTSUBMSK_val
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
接下来,访问arm920t控制寄存器CP15,并置位最高两位
[31,30]。

此两位置为0b11后,处理器时钟被设置为异步模式,允许处理器异步访问总线:
mrc p15, 0, r1, c1, c0, 0
orr r1, r1, #0xc0000000
mcr p15, 0, r1, c1, c0, 0
至此arm920t相关的配置完成。

在上一篇文章中,我们介绍了u-boot启动的时候汇编语言的部分,当时我们进行了一些简单的初始化,并且为C语言的执行建立的环境(堆栈),现在我们看看当从汇编语言转到C语言的时候执行的第一个函数(start_armboot (),在lib_arm\board.c中),该函数进行了一系列的外设初始化,然后调用main_loop (),根据配置来选择是直接加载Linux内核还是进入等待命令模式。

void start_armboot (void)
{main_loop (),
依次是cpu_init、board_init、interrupt_init、env_init、init_baudrate、serial_init、dram_init、flash_init等
我用的板子是基于三星的2410,三星的内存片选有8个bank,这里所谓的bank我开始也不是很清楚,在网上搜了一通也不知所云,但是当我看了2410的用户手册后才有点明白,这里的bank就是片选,一个片选就是一个bank,在U-Boot中,配制的时候要配制SDRAM和FLASH的bank数,那么如果你的SDRAM或者FLASH就接了一个片选的时候,就定义为1就可以了,其他的类推。

下面是2410的内存映射图,2410和其他的大部分的处理器一样,支持NorFlash和NANDFlash启动,而这两种启动方式内存所映射的地址不怎么相同,我的板子没有NANDFlash,所以就以从NorFlash启动为例子了:
==============================================<-------0xFFFF_ FFFF
| NOT USED
==============================================<-------0x6000_ 0000
| SFR Area (各个接口的控制寄存器)
==============================================<-------0x4800_ 0000
==============================================<-------0x4000_ 0FFF
| BootSRAM (4KBytes)
==============================================<-------0x4000_ 0000
| SROM/SDRAM nGCS7 (bank7)
==============================================<-------0x3800_ 0000
| SROM/SDRAM nGCS6 (bank6)
==============================================<-------0x3000_
0000
| SROM nGCS5 (bank5)
==============================================<-------0x2800_ 0000
| SROM nGCS4 (bank4)
==============================================<-------0x2000_ 0000
| SROM nGCS3 (bank3)
==============================================<-------0x1800_ 0000
| SROM nGCS2 (bank2)
==============================================<-------0x1000_ 0000
| SROM nGCS1 (bank1)
==============================================<-------0x0800_ 0000
| SROM nGCS0 (bank0)
==============================================<-------0x0000_ 0000
上面的每个bank最大支持128M,除了bank0,其它的每个bank都支持8/16/32位操作,bank0只支持16/32位操作,在我的板子上,Flash接bank0,一共8M,16位,SDRAM接bank6,一个32M,32位。

所以启动的时候,CPU从0x0000_0000开始执行,而在这上面的NorFlash,存放的是U-Boot,用于启动Linux的。

而我们目前所做的工作就是要把U-Boot移植成功,然后烧写到该地址,假设你的板子的供应商已经提供了少些的工具,等到我们少些成功,我们就可以通过网络来加载Linux,通过串口来调试了,至于怎么做的,我们慢慢来,以后的帖子会涉及到。

在上面的存储地址布局中,SFR Area就是各个接口控制器的地址,我们可以把它定义如下,来自华恒PPCboot上面的源代码中的头文件s3c2410.h:
本篇文章主要讨论u-boot,Linux内核以及文件系统在Flash以及SDRAM中的布局,我用的板子是华恒的爱好者学习板,基于S3C2410,所参考的也是华恒所给的文档。

通过u-boot命令flinfo,可以看出,所用的flash是intel TE28F640J3C120,flash一共有64个块,每个块有128K大小,其中u-boot就放在最前面的块中,下面是其中的分配布局,第二个是对应的当把内核以及文件系统搬到内存中时内存的布局。

128k= 0x20000
0x20000*64=8M
Flash:
============================<-----0x0000_0000
| U-BOOT
============================
|
============================<-----0x0004_0000
| Linux(zImage)
============================
|
============================<-----0x0014_0000
| 文件系统ramdisk.image.gz
============================
|
============================<-----0x0054_0000
| 文件系统cramfs
============================
|
============================<-----0x0074_0000
| 文件系统JFFS2
============================<-----0x0080_0000
SDRAM:
============================<-----0x3000_0000
|
============================<-----0x3000_8000
| Linux(zImage)
============================
|
============================<-----0x3080_0000
| 文件系统ramdisk
============================<-----0x3200_0000
现在是不是对我们将要做的工作很清楚了?
其实就是先把u-boot移植好,然后就用供应商提供的烧写工具把u-boot烧写到flash的前面,然后就由u-boot来接管一切,通过网络来下载内核和文件系统(TFTP),或者把主机上的目录挂在到目标板上(NFS),这样开发和调试将非常的方便,我以前用的板子是通过JTAG下载下去的,有时候很不稳定,一个不是技术上的错误都可能导致出错,郁闷至极。

好了,言归正传,下一篇文章将介绍u-boot的整体结构,以及启动代码(start.S中)
一、整体结构
首先下载u-boot的源代码(www.denx.de),解压缩,你可以看到下面的目录:
- board 目标板相关文件,主要包含SDRAM、FLASH驱动;
- common 独立于处理器体系结构的通用代码,如内存大小探测与故障检测;
- cpu 与处理器相关的文件。

如mpc8xx子目录下含串口、网口、LCD驱动及中断初始化等文件;
- driver 通用设备驱动,如CFI FLASH驱动(目前对INTEL FLASH支持较好)
- doc U-Boot的说明文档;
- examples可在U-Boot下运行的示例程序;如hello_world.c,timer.c;
- include U-Boot头文件;尤其configs子目录下与目标板相关的配置头文件是移植过程中经常要修改的文件;
- lib_xxx 处理器体系相关的文件,如lib_ppc, lib_arm目录分别包含与PowerPC、ARM体系结构相关的文件;
- net 与网络功能相关的文件目录,如bootp,nfs,tftp;
- post 上电自检文件目录。

尚有待于进一步完善;
- rtc RTC驱动程序;
- tools 用于创建U-Boot S-RECORD和BIN镜像文件的工具;
二、移植步骤
为了使U-Boot支持新的开发板,一种简便的做法是在U-Boot已经支持的开发板中选择一种和目标板接近的,并在其基础上进行修改。

代码修改的步骤如下:
1)在board目录下创建smdk2410目录,添加smdk2410.c、flash.c、memsetup.s、u-boot.lds和config.mk等;
2)在cpu目录下创建arm920t目录,主要包含start.s、interrupts.c、cpu.c、serial.c和speed.c等文件;
3)在include/configs目录下添加smdk2410.h,它定义了全局的宏定义等;
4)修改u-boot根目录下的Makefile文件:
smdk2410_config : unconfig@./mkconfig $(@:_config=) arm arm920t smdk2410
5)运行make smdk2410_config,如果没有错误,就可以开始进行与硬件相关的代码移植工作。

由于这部分代码与硬件紧密相关,所以要熟悉开发板的硬件配置,可参考各芯片的用户手册。

当然,这个是一般步骤,后面我们做的可能具体文件名还和这个不一样,等到那时候在交待,这里先介绍的目的是在开始的时候给个大概的思路,要不直接分析源代码,有点在原始森林的感觉,耐心的看吧:)
三、start.S分析
首先介绍start.S中的代码的具体作用,由于该代码是系统最开始执行的,这时,u-boot对系统一无所知,必须要初始化一些东西,比如设置异常的入口地址和异常处理函数;配置PLLCON寄存器,确定系统的主频;屏蔽看门狗和中断;初始化I/O寄存器;关闭MMU功能;调用/board/smdk2410中的memsetup.s,初始化存储器空间,设置刷新频率;将U-Boot的内容复制到SDRAM中;设置堆栈的大小, 然后ldr pc, _start_armboot,跳到C函数
///////////////////////////////////////////////////////////////////////////////
u-boot的启动过程
系统启动的入口点。

既然我们现在要分析u-boot的启动过程,就必须先找到u-boot最先实现的是哪些代码,最先完成的是哪些任务。

另一方面一个可执行的image必须有一个入口点,并且只能有一个全局入口点,所以要通知编译器这个入口在哪里。

由此我们可以找到程序的入口点是在/board/lpc2210/u-boot.lds中指定的,其中ENTRY(_start)说明程序从_start开始运行,而他指向的是cpu/arm7tdmi/start.o文件。

因为我们用的是ARM7TDMI的cpu架构,
在复位后从地址0x00000000取它的第一条指令,所以我们将Flash映射到这个地址上,这样在系统加电后
资料来源:学网(),原文地址:/itedu/200707/121835.html cpu将首先执行u-boot程序。

u-boot的启动过程是多阶段实现的,分了两个阶段。

依赖于cpu体系结构的代码(如设备初始化代码等)通常都放在stage1中,而且通常都是用汇编语言来实现,以达到短小精悍的目的。

而stage2则通常是用C语言来实现的,这样可以实现复杂的功能,而且代码具有更好的可读性和可移植性。

资料来源:学网(),原文地址:/itedu/200707/121835.html
-boot的启动过程是多阶段实现的,分了两个阶段。

依赖于cpu体系结构的代码(如设备初始化代码等)通常都放在stage1中,而且通常都是用汇编语言来实现,以达到短小精悍的目的。

而stage2则通常是用C语言来实现的,这样可以实现复杂的功能,而且代码具有更好的可读性和可移植性。

下面我们先详细分析下stage1中的代码,如图2所示:
此主题相关图片如下:
图2 Start.s程序流程
代码真正开始是在_start,设置异常向量表,这样在cpu发生异常时就跳转到/cpu/arm7tdmi/interrupts中去执行相应得中断代码。

在interrupts文件中大部分的异常代码都没有实现具体的功能,只是打印一些异常消息,其中关键的是reset中断代码,跳到reset入口地址。

reset复位入口之前有一些段的声明。

在reset中,首先是将cpu设置为svc32模式下,并屏蔽所有irq和fiq。

在u-boot中除了定时器使用了中断外,其他的基本上都不需要使用中断,比如串口通信和网络等通信等,在u-boot中只要完成一些简单的通信就可以了,所以在这里屏蔽掉了所有的中断响应。

初始化外部总线。

这部分首先设置了I/O口功能,包括串口、网络接口等的设置,其他I/O口都设置为GPIO。

然后设置BCFG0~BCFG3,即外部总线控制器。

这里bank0对应Flash,设置为16位宽度,总线速度设为最慢,以实现稳定的操作;Bank1对应DRAM,设置和Flash 相同;Bank2对应RTL8019。

接下来是cpu关键设置,包括系统重映射(告诉处理器在系统发生中断的时候到外部存储器中去读取中断向量表)和系统频率。

文章整理:学网 (本站) [1] [2] [3] [4]
lowlevel_init,设定RAM的时序,并将中断控制器清零。

这些部分和特定的平台有关,但大致的流程都是一样的。

下面就是代码的搬移阶段了。

为了获得更快的执行速度,通常把stage2加载到RAM空间中来执行,因此必须为加载Boot Loader的stage2准备好一段可用的RAM空间范围。

空间大小最好是memory page大小(通常是4KB)的倍数,一般而言,1M的RAM空间已经足够了。

flash中存储的u-boot可执行文件中,代码段、数据段以及BSS段都是首尾相连存储的,所以在计算搬移大小的时候就是利用了用BSS段的首地址减去代码的首地址,这样算出来的就是实际使用的空间。

程序用一个循环将代码搬移到0x81180000,即RAM底端1M 空间用来存储代码。

然后程序继续将中断向量表搬到RAM的顶端。

由于stage2通常是C语言执行代码,所以还要建立堆栈去。

在堆栈区之前还要将malloc分配的空间以及全局数据所需的空间空下来,他们的大小是由宏定义给出的,可以在相应位置修改。

基本内存分布图:资料来源:学网(),原文地址:/itedu/200707/121835.html
图3 搬移后内存分布情况图
接下来是u-boot启动的第二个阶段,是用c代码写的,这部分是一些相对变化不大的部分,我们针对不同的板子改变它调用的一些初始化函数,并且通过设置一些宏定义来改变初始化的流程,所以这些代码在移植的过程中并不需要修改,也是错误相对较少出现的文件。

在文件的开始先是定义了一个函数指针数组,通过这个数组,程序通过一个循环来按顺序进行常规的初始化,并在其后通过一些宏定义来初始化一些特定的设备。

在最后程序进入一个循环,main_loop。

这个循环接收用户输入的命令,以设置参数或者进行启动引导。

本篇文章将分析重点放在了前面的start.s上,是因为这部分无论在移植还是在调试过程中都是最容易出问题的地方,要解决问题就需要程序员对代码进行修改,所以在这里简单介
绍了一下start.s的基本流程,希望能对大家有所帮助。

资料来源:学网(),原文地址:/itedu/200707/121835_2.html
资料来源:学网(),原文地址:/itedu/200707/121835_2.html。

相关文档
最新文档