S3C2410 bootloader VIVI阅读笔记.docx
《S3C2410 bootloader VIVI阅读笔记.docx》由会员分享,可在线阅读,更多相关《S3C2410 bootloader VIVI阅读笔记.docx(19页珍藏版)》请在冰点文库上搜索。
S3C2410bootloaderVIVI阅读笔记
S3C2410bootloader----VIVI阅读笔记
建议读一读《嵌入式系统BootLoader技术内幕》(詹荣开著),google一下就会找到一片。
什么是Bootloader就不再这里废话了,看看上面的文章就明了了。
Bootloader有非常多种,如本文将要阅读的vivi,除此之外更有uboot,redboot,lilo等等。
Vivi是韩国mizi公司专门为三星s3c2410芯片设计的Bootloader。
先来看看vivi的源码树:
vivi-+-arch-+-s3c2410
|-Documentation
|-drivers-+-serial
|
‘-mtd-+-maps
|
|-nor
|
‘-nand
|-include-+-platform
| |-mtd
| ‘-proc
|-init
|-lib-+-priv_data
|-scripts-+-lxdialog
|-test
|-util
能google一下,搜到源码vivi.tar.gz。
前面提到的文件已系统的分析了bootloader的,这里就按原始码来具体说事。
vivi也能分为2个阶段,阶段1的代码在arch/s3c2410/head.S中,阶段2的代码从init/main.c的main函数开始。
1.阶段1
阶段1从程式arch/s3c2410/head.S开始,按照head.S的代码执行顺序,一次完成了下面几个任务:
u
1、关WATCHDOG(disablewatchdogtimer)
上电后,WATCHDOG默认是开着的
u
2、禁止所有中断(disableallinterrupts)
vivi中不会用到中断,中断是系统的事,bootloader可不能去干这事的(不过这段代码实在多余,上电后中断默认是关闭的)
u
3、初始化系统时钟(initialisesystemclocks)
启动MPLL,FCLK=200MHz,HCLK=100MHz,PCLK=50MHz,“CPUbusmode”改为“Asynchronousbusmode”。
u
4、初始化内存控制寄存器(memsetup)
S3c2410共有15个寄存器,在此开始初始化13个寄存器。
u
5、检查是否从掉电模式唤醒(Checkifthisisawake-upfromsleep)
若是,则调用WakeupStart函数进行处理。
u
6、点亮所有LED(AllLEDon)
点一下灯,通知外面的同志,告诉他们有情况发生。
u
7、初始化UART0(setGPIOforUART&InitUART)
a.设置GPIO,选择UART0使用的引脚
b.初始化UART0,设置工作方式(使用FIFO)、波特率1152008N1、无流控等。
这可是使用串口和s3c2410通信的条件啊,在终端也要如此设置。
u
8、跳到内存测试函数(simplememorytesttofindsomeDRAMflaults)
当然要定义了CONFIG_BOOTUP_MEMTEST这个参数才会跳到内存测试。
u
9、如果定义了以Nand
flash方式启动(#ifdef
CONFIG_S3C2410_NAND_BOOT),则此时要将vivi所有代码(包括阶段1和阶段2)从Nandflash复制到SDRAM中(因为在Nandflash中是不能执行程式的,他只能做为程式和数据的存储器,而Norflash可就不同了,Nor
flash能执行程式,但贵是他发展得瓶颈):
a.设置nandflash控制寄存器
b.设置堆栈指针
c.设置即将调用的函数nand_read_ll的参数:
r0=目的地址(SDRAM的地址),r1=源地址(nandflash的地址),r2=复制的长度(以字节为单位)
d.调用nand_read_ll进行复制
u
10、跳到bootloader的阶段2运行,亦即调用init/main.c中的main函数(getreadtocallCfunctions)
a.重新设置堆栈
b.设置main函数的参数
c.调用main函数
head.S有900多行,都是些arm汇编,看的云山雾罩,汇编看来是忘的差不多了,所以这部分代码也看的相当糙,只知道大概在干什么,至于个中缘由就不是非常了解。
先学学arm汇编再回来看。
2.阶段2
从init/main.c中的main函数开始,终于步入C语言的世界了。
Main函数总共有8步(8steps),先看看原始码:
intmain(intargc,char*argv[])
{
intret;
/*
Step1:
*/
putstr("\r\n");
putstr(vivi_banner);
//vivi_banner是vivi执行开始的显示信息,vivi_banner在文件version.c中定义
reset_handler();
/*Step2:
*/
ret=board_init();
if(ret){
putstr("Failedaboard_init()procedure\r\n");
error();
}
/*Step3:
*/
mem_map_init();
mmu_init();
putstr("Succeedmemorymapping.\r\n");
/*Now,vivi
isrunningontheram.MMUisenabled.
*Step4:
*/
/*initializetheheaparea*/
ret=heap_init();
if(ret){
putstr("Failedinitailizingheapregion\r\n");
error();
}
/*Step5:
MTD*/
ret=mtd_dev_init();
/*Step6:
*/
init_priv_data();
/*Step7:
*/
misc();
init_builtin_cmds();
/*Step8:
*/
boot_or_vivi();
return0;
}
下面按照上面的步骤逐步来分析一下。
1、Step
1:
reset_handler()
reset_handler用于将内存清零,代码在lib/reset_handle.c中。
1 void
2 reset_handler(void)
3 {
4 intpressed;
5 pressed=is_pressed_pw_btn(); /*判断是硬件复位还是软件复位*/
6 if(pressed==PWBT_PRESS_LEVEL){
7 DPRINTK("HARD
RESET\r\n");
8 hard_reset_handle(); /*调用clear_mem对SDRAM清0*/
9 }else{
10 DPRINTK("SOFT
RESET\r\n");
11 soft_reset_handle(); /*此函数为空*/
12 }
13 }
在上电后,reset_handler调用第8行的hard_reset_handle(),此函数在lib/reset_handle.c中:
[main(intargc,char*argv[])->
reset_handler()->hard_reset_handle()]
1 staticvoid
2 hard_reset_handle(void)
3 {
4 #if0
5 clear_mem((unsigned
long)(DRAM_BASE+VIVI_RAM_ABS_POS),\
6 (unsignedlong)(DRAM_SIZE-
VIVI_RAM_ABS_POS));
7 #endif
/*lib/memory.c,将起始地址为USER_RAM_BASE,长度为USER_RAM_SIZE的内存清0*/
8 clear_mem((unsignedlong)USER_RAM_BASE,(unsignedlong)USER_RAM_SIZE);
9 }
2、Step
2:
board_init()
board_init调用2个函数用于初始化定时器和设置各GPIO引脚功能,代码在arch/s3c2410/smdk.c中:
[main(intargc,char*argv[])>
board_init()]
1 intboard_init(void)
2 {
3 init_time();
/*arch/s3c2410/proc.c*/
4 set_gpios();
/*arch/s3c2410/smdk.c
*/
5 return0;
6 }
init_time()这个函数对寄存器进行了简单的操作:
voidinit_time(void)
{
TCFG0=(TCFG0_DZONE(0)|TCFG0_PRE1(15)|TCFG0_PRE0(0));
/*s3c2410datasheetP298*/
/*TCFG0=0|0xf00|0*/
}
寄存器TCFG0由三部分组成,prescaler0,prescaler1,deadzone和reserve四部分,前三部分分别对应TCFG0_PRE0、TCFG0_PRE1、TCFG0_DZONE,TCFG0_PRE0(0)实际值为0x00,TCFG0_PRE1(15)实际值为0x0f00,而TCFG0_DZONE(0)实际值为0x000000。
实际中,vivi并未使用定时器,这个函数就能忽略。
set_gpios()用于选择GPA至GPH端口各引脚的功能及是否使用各引脚的内部上拉电阻,并设置外部中断源寄存器EXTINT0-2(vivi中未使用外部中断)。
1
voidset_gpios(void)
2 {
3
GPACON =vGPACON;
4
GPBCON =vGPBCON;
5
GPBUP =vGPBUP;
6
GPCCON =vGPCCON;
7
GPCUP =vGPCUP;
8
GPDCON =vGPDCON;
9
GPDUP =vGPDUP;
10
GPECON =vGPECON;
11
GPEUP =vGPEUP;
12
GPFCON =vGPFCON;
13
GPFUP =vGPFUP;
14
GPGCON =vGPGCON;
15
GPGUP =vGPGUP;
16
GPHCON =vGPHCON;
17
GPHUP =vGPHUP;
18
EXTINT0=vEXTINT0;
19
EXTINT1=vEXTINT1;
20
EXTINT2=vEXTINT2;
21 }
以第三行为例,vGPACON的值为0x007fffff,查找s3c2410用户手册可知,该参数将GPACON的23位全部置1。
各位功能需察看s3c2410用户手册。
3、Step
3:
建立页表和启动MMU
mem_map_init();
mmu_init();
mem_map_init函数用于建立页表,vivi使用段式页表,只需要一级页表。
他调用3个函数,代码在arch/s3c2410/mmu.c中:
[main(intargc,char*argv[])>
mem_map_init(void)]
1 voidmem_map_init(void)
2
{
3
#ifdefCONFIG_S3C2410_NAND_BOOT
/*CONFIG_S3C2410_NAND_BOOT
=y,在文件include/autoconf.h中定义*/
4
mem_map_nand_boot();
/*最终调用mem_mepping_linear,建立页表*/
5 #else
6
mem_map_nor();
7 #endif
8
cache_clean_invalidate();/*清空cache,使无效cache*/
9
tlb_invalidate();
/*使无效快表TLB*/
10 }
第9、10行的两个函数能不用管他,他们做的事情在下面的mmu_init函数里又重复了一遍。
对于本研发板,在.config中定义了CONFIG_S3C2410_NAND_BOOT。
mem_map_nand_boot()函数调用mem_mapping_linear()函数来最终完成建立页表的工作。
页表存放在SDRAM物理地址0x33dfc000开始处,共16K:
一个页表项4字节,共有4096个页表项;每个页表项对应1M地址空间,共4G。
mem_map_init先将4G虚拟地址映射到相同的物理地址上,NCNB(不使用cache,不使用writebuffer)?
?
这样,对寄存器的操作跟未启动MMU时是相同的;再将SDRAM对应的64M空间的页表项修改为使用cache。
mem_mapping_linear函数的代码在arch/s3c2410/mmu.c中:
[main(intargc,char*argv[])>
mem_map_init(void)>mem_map_nand_boot()>mem_mapping_linear(void)]
1 staticinlinevoidmem_mapping_linear(void)
2 {
3 unsignedlongpageoffset,sectionNumber;
4
putstr_hex("MMUtablebaseaddress=0x",(unsignedlong)
mmu_tlb_base);
5 /*4G虚拟地址映射到相同的物理地址.notcacacheable,notbufferable*/
6 /*mmu_tlb_base=0x33dfc000*/
7 for(sectionNumber=0;sectionNumber
>20))=pageoffset|
MMU_SECDESC;
10 }
11 /*makedramcacheable*/
12 /*SDRAM物理地址0x3000000-0x33ffffff,
13
DRAM_BASE=0x30000000,DRAM_SIZE=64M
14 */
15 for(pageoffset=DRAM_BASE;pageoffset1M){
17 //DPRINTK(3,
"MakeDRAMsectioncacheable:
0x%08lx\n",
pageoffset);
18 *(mmu_tlb_base+
(pageoffset>>20))=\
pageoffset|MMU_SECDESC|MMU_CACHEABLE;
19 }
20}
mmu_init()函数用于启动MMU,他直接调用arm920_setup()函数。
arm920_setup()的代码在arch/s3c2410/mmu.c中:
[main(intargc,char*argv[])>
mmu_init()>arm920_setup()]
1 staticinlinevoidarm920_setup(void)
2 {
3
unsignedlongttb=MMU_TABLE_BASE;
/*MMU_TABLE_BASE=0x33dfc000*/
4 __asm__(
5 /*Invalidatecaches*/
6 "mov r0,
#0\n"
7 "mcr p15,0,r0,
c7,c7,0\n" /*invalidateI,Dcachesonv4*/
8 "mcr p15,0,r0,
c7,c10,4\n" /*drainwritebufferonv4*/
9 "mcr p15,0,r0,
c8,c7,0\n" /*invalidateI,DTLBsonv4*/
10 /*Loadpagetablepointer*/
11 "mov r4,%0\n"
12 "mcr p15,0,r4,c2,
c0,0\n" /*loadpagetablepointer*/
13 /*Writedomainid(cp15_r3)*/
14 "mvn r0,
#0\n" /*Domains0b01=client,0b11=Manager*/
15 "mcr p15,0,r0,c3,
c0,0\n"
/*loaddomainaccessregister,writedomain15:
0,用户手册P548(accesspermissions)*/
16 /*Setcontrolregisterv4*/
17 "mrc p15,0,r0,c1,
c0,0\n" /*getcontrolregisterv4*/
/*数据手册P545:
readcontrolregister*/
18 /*Clearout’unwanted’bits(thenputthem
inifweneedthem)*/
19 /*..VI..RSB....CAM*/ /*这些位的含义在数据手册P546*/
20 "bicr0,r0,#0x3000\n" /*
..11............*/
/*I(bit[12])=0=Instructioncachedisabled*/
21 /*V[bit[13]](Baselocationofexception
registers)=0=Lowaddresses=0x00000000*/
22 "bicr0,r0,
#0x0300\n" /*......11........*/