首页>>论坛 >>技术社区 >>OpenHW社区论坛 >>ZYNQ及嵌入式操作系统
|
我要发帖  | 我要投票  | 我要回复  | 收藏
1

ArmLinux bootloader 全程详解(1)

版主: Jerry Fan  玄剑  jennyzheng  XUPteam 
ArmLinux bootloader 全程详解(1)
 

网上关于Linux的BOOTLOADER文章不少了,但是大都是vivi,blob等比较庞大的程序,读起来不太方便,编译出的文件也比较大,而且更多的是面向开发用的引导代码,做成产品时还要裁减,这一定程度影响了开发速度,对初学者学习开销也比较大,在此分析一种简单的BOOTLOADER,是在三星公司提供的2410 BOOTLOADER上稍微修改后的结果,编译出来的文件大小不超过4k,希望对大家有所帮助.

1.几个重要的概念

COMPRESSED KERNEL and DECOMPRESSED KERNEL

压缩后的KERNEL,按照文档资料,现在不提倡使用DECOMPRESSED KERNEL,而要使用COMPRESSED KERNEL,它包括了解压器.因此要在ram分配时给压缩和解压的KERNEL提供足够空间,这样它们不会相互覆盖.

当执行指令跳转到COMPRESSED KERNEL后,解压器就开始工作,如果解压器探测到解压的代码会覆盖掉COMPRESSED KERNEL,那它会直接跳到COMPRESSED KERNEL后存放数据,并且重新定位KERNEL,所以如果没有足够空间,就会出错.

Jffs2 File System

可以使armlinux应用中产生的数据保存在FLASH上,我的板子还没用到这个.

RAMDISK

使用RAMDISK可以使ROOT FILE SYSTEM在没有其他设备的情况下启动.一般有两种加载方式,我就介绍最常用的吧,把COMPRESSED RAMDISK IMAGE放到指定地址,然后由BOOTLOADER把这个地址通过启动参数的方式ATAG_INITRD2传递给KERNEL.具体看代码分析.

启动参数(摘自IBM developer)

在调用内核之前,应该作一步准备工作,即:设置 Linux 内核的启动参数。Linux 2.4.x 以后的内核都期望以标记列表(tagged list)的形式来传递启动参数。启动参数标记列表以标记 ATAG_CORE 开始,以标记 ATAG_NONE 结束。每个标记由标识被传递参数的 tag_header 结构以及随后的参数值数据结构来组成。数据结构 tag 和 tag_header 定义在 Linux 内核源码的include/asm/setup.h 头文件中.

在嵌入式 Linux 系统中,通常需要由 BOOTLOADER 设置的常见启动参数有:ATAG_CORE、ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_INITRD等。

(注)参数也可以用COMMANDLINE来设定,在我的BOOTLOADER里,我两种都用了.

2.开发环境和开发板配置:

CPU:S3C2410,BANK6上有64M的SDRAM(两块),BANK0上有32M NOR FLASH,串口当然是逃不掉的.这样,按照数据手册,地址分配如下:

0x4000_0000开始是4k的片内DRAM.

0x0000_0000开始是32M FLASH 16bit宽度

0x3000_0000开始是64M SDRAM 32bit宽度

注意:控制寄存器中的BANK6和BANK7部分必须相同.

0x4000_0000(片内DRAM)存放4k以内的BOOTLOADER IMAGE

0x3000_0100开始存放启动参数

0x3120_0000 存放COMPRESSED KERNEL IMAGE

0x3200_0000 存放COMPRESSED RAMDISK

0x3000_8000 指定为DECOMPRESSED KERNEL IMAGE ADDRESS

0x3040_0000 指定为DECOMPRESSED RAMDISK IMAGE ADDRESS

开发环境:Redhat Linux,armgcc toolchain, armlinux KERNEL

如何建立armgcc的编译环境:建议使用toolchain,而不要自己去编译armgcc,偶试过好多次,都以失败告终.

先下载arm-gcc 3.3.2 toolchain

将arm-linux-gcc-3.3.2.tar.bz2 解压到 /toolchain

# tar jxvf arm-linux-gcc-3.3.2.tar.bz2

# mv /usr/local/arm/3.3.2 /toolchain

在makefile 中在把arch=arm CROSS_COMPILE设置成toolchain的路径

还有就是INCLUDE = -I ../include -I /root/my/usr/local/arm/3.3.2/include.,否则库函数就不能用了

3.启动方式:

可以放在FLASH里启动,或者用Jtag仿真器.由于使用NOR FLASH,根据2410的手册,片内的4K DRAM在不需要设置便可以直接使用,而其他存储器必须先初始化,比如告诉memory controller,BANK6里有两块SDRAM,数据宽度是32bit,= =.否则memory control会按照复位后的默认值来处理存储器.这样读写就会产生错误.

所以第一步,通过仿真器把执行代码放到0x4000_0000,(在编译的时候,设定TEXT_BAS

E=0x40000000)

第二步,通过 AxD把linux KERNEL IMAGE放到目标地址(SDRAM)中,等待调用

第三步,执行BOOTLOADER代码,从串口得到调试数据,引导armlinux

4.代码分析

讲了那么多执行的步骤,是想让大家对启动有个大概印象,接着就是BOOTLOADER内部的代码分析了,BOOTLOADER文章内容网上很多,我这里精简了下,删除了不必要的功能.

BOOTLOADER一般分为2部分,汇编部分和c语言部分,汇编部分执行简单的硬件初始化,C部分负责复制数据,设置启动参数,串口通信等功能.

BOOTLOADER的生命周期:

1. 初始化硬件,比如设置UART(至少设置一个),检测存储器= =.

2. 设置启动参数,这是为了告诉内核硬件的信息,比如用哪个启动界面,波特率 = =.

3. 跳转到Linux KERNEL的首地址.

4. 消亡


当然,在引导阶段,象vivi等,都用虚地址,如果你嫌烦的话,就用实地址,都一样.

我们来看代码:

2410init.s

.global _start//开始执行处

_start:

//下面是中断向量

b reset @ Supervisor Mode//重新启动后的跳转

……

……

reset:

ldr r0,=WTCON /WTCON地址为53000000,watchdog的控制寄存器 */

ldr r1,=0x0 /*关watchdog*/

str r1,[r0]


ldr r0,=INTMSK

ldr r1,=0xffffffff /*屏蔽所有中断*/

str r1,[r0]


ldr r0,=INTSUBMSK

ldr r1,=0x3ff /*子中断也一样*/

str r1,[r0]

/*Initialize Ports...for display LED.*/

ldr r0, =GPFCON

ldr r1, =0x55aa

str r1, [r0]

ldr r0, =GPFUP

ldr r1, =0xff

str r1, [r0]

ldr r0,=GPFDAT

ldr r1,=POWEROFFLED1

str r1,[r0]

/* Setup clock Divider control register

* you must configure CLKDIVN before LOCKTIME or MPLL UPLL

* because default CLKDIVN 1,1,1 set the SDMRAM Timing Conflict

nop

* FCLK:HCLK:PCLK = 1:2:4 in this case

*/

ldr r0,=CLKDIVN

ldr r1,=0x3

str r1,[r0]


/*To reduce PLL lock time, adjust the LOCKTIME register. */

ldr r0,=LOCKTIME

ldr r1,=0xffffff

str r1,[r0]

/*Configure MPLL */

ldr r0,=MPLLCON

ldr r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV) //Fin=12MHz,Fout=203MHz

str r1,[r0]

ldr r1,=GSTATUS2

ldr r10,[r1]

tst r10,#OFFRST

bne 1000f

//以上这段,我没动,就用三星写的了,下面是主要要改的地方

/* MEMORY C0NTROLLER(MC)设置*/

add r0,pc,#MCDATA - (.+8)// r0指向MCDATA地址,那里存放着MC初始化要用到的数据

ldr r1,=BWSCON // r1指向MC控制器寄存器的首地址

add r2,r0,#52 // 复制次数,偏移52字


1: //按照偏移量进行循环复制

ldr r3,[r0],#4

str r3,[r1],#4

cmp r2,r0

bne 1b

.align 2


MCDATA:

.word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))

上面这行就是BWSCON的数据,具体参数意义如下:


需要更改设置DW6 和DW7都设置成10,即32bit,DW0 设置成01,即16bit

下面都是每个BANK的控制器数据,大都是时钟相关,可以用默认值,设置完MC后,就跳到调用main函数的部分

.word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))

.word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))

.word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))

.word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))

.word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))

.word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))

.word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))

.word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))

.word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)

.word 0xB2 /* REFRESH Control Register */

.word 0x30 /* BANKSIZE Register : Burst Mode */

.word 0x30 /* SDRAM Mode Register */


.align 2

.global call_main //调用main函数,函数参数都为0

call_main:

ldr sp,STACK_START

mov fp,#0 /* no previous frame, so fp=0*/

mov a1, #0 /* set argc to 0*/

mov a2, #0 /* set argv to NUL*/

bl main /* call main*/

STACK_START:

.word STACK_BASE

undefined_instruction:

software_interrupt:

prefetch_abort:

data_abort:

not_used:

irq:

fiq:

/*以上是主要的汇编部分,实现了时钟设置,串口设置watchdog关闭,中断关闭功能(如果有需要还可以降频使用),然后转入main*/

 
相关主题
一个人之所以快乐,不是因为他得到的多,而是因为他计较的少
回复 链接 收藏
 
RE:ArmLinux bootloader 全程详解(1)
 
ding !!!!!!!!!!!!!!!!
 
回复 链接 收藏
 
RE:ArmLinux bootloader 全程详解(1)
 
 
坚持就是胜利
回复 链接 收藏
 
我要发帖  | 我要投票  | 我要回复  | 收藏
1